正如Benny所提到的,这个线程已经相当老了,但我也曾处于同样的情况,即找不到一个真正令人满意的解决方案,首先是将.cer文件转换为.pem文件,其次是提取(在我的情况下)旧的主题哈希,这对于Android是必要的。最终我能找到的最好的解决方案是使用
BouncyCastle nuget包。因此,以下代码基于该库。
using Org.BouncyCastle.X509;
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
namespace CertificateTest
{
public class CertificateService
{
private readonly X509Certificate certificate;
public string AsPem
{
get
{
var base64Pem = Convert.ToBase64String(certificate.GetEncoded());
var wrappedPem = string.Join("\n", SplitByLength(base64Pem, 64));
return $"-----BEGIN CERTIFICATE-----\n{wrappedPem}\n-----END CERTIFICATE-----";
}
}
public string SubjectHash => CreateHash(certificate.SubjectDN.GetDerEncoded(), SHA1.Create());
public string SubjectHashOld => CreateHash(certificate.SubjectDN.GetDerEncoded(), MD5.Create());
public CertificateService(string certificateFilePath)
{
certificate = new X509CertificateParser().ReadCertificate(File.ReadAllBytes(certificateFilePath));
}
private string CreateHash(byte[] subjectName, HashAlgorithm hashAlgorithm)
{
var hashBytes = hashAlgorithm.ComputeHash(subjectName);
ulong ret = (
((ulong)hashBytes[0]) |
((ulong)hashBytes[1] << 8) |
((ulong)hashBytes[2] << 16) |
((ulong)hashBytes[3] << 24)
) & 0xffffffff;
return ret.ToString("X2").PadLeft(8, '0').ToLower();
}
private IEnumerable<string> SplitByLength(string str, int maxLength)
{
for (var index = 0; index < str.Length; index += maxLength)
{
yield return str.Substring(index, Math.Min(maxLength, str.Length - index));
}
}
}
}
使用非常简单:
private static void Main(string[] args)
{
var service = new CertificateService("my-certificate.pem");
Debug.WriteLine(service.AsPem);
Debug.WriteLine(service.SubjectHash);
Debug.WriteLine(service.SubjectHashOld);
}
正如您所看到的,一些代码(例如哈希)我已经在像这样的线程中找到了。对我来说,真正的突破是使用BouncyCastle库而不是内置类,并完全加载证书并从中提取所有信息。手动解析和编码SubjectDN没有任何作用。
由于Convert.ToBase64String
无法自行在64个字符后拆分行,因此在此手动使用来自堆栈溢出的引用代码进行操作。