[英]How can I Get the details from Root-CA-Cert certificate (x509) chain using c#?
[英]C# How can I validate a Root-CA-Cert certificate (x509) chain?
假設我有三個證書(Base64 格式)
Root
|
--- CA
|
--- Cert (client/signing/whatever)
如何在 C# 中驗證證書和證書路徑/鏈? (所有這三個證書可能不在我的計算機證書商店中)
編輯:BouncyCastle 具有驗證功能。 但我盡量不使用任何第三方庫。
byte[] b1 = Convert.FromBase64String(x509Str1);
byte[] b2 = Convert.FromBase64String(x509Str2);
X509Certificate cer1 =
new X509CertificateParser().ReadCertificate(b1);
X509Certificate cer2 =
new X509CertificateParser().ReadCertificate(b2);
cer1.Verify(cer2.GetPublicKey());
如果 cer1 沒有被 cert2(CA 或 root)簽名,則會出現異常。 這正是我想要的。
X509Chain
類旨在實現此目的,您甚至可以自定義它如何執行鏈構建過程。
static bool VerifyCertificate(byte[] primaryCertificate, IEnumerable<byte[]> additionalCertificates)
{
var chain = new X509Chain();
foreach (var cert in additionalCertificates.Select(x => new X509Certificate2(x)))
{
chain.ChainPolicy.ExtraStore.Add(cert);
}
// You can alter how the chain is built/validated.
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreWrongUsage;
// Do the validation.
var primaryCert = new X509Certificate2(primaryCertificate);
return chain.Build(primaryCert);
}
如果需要, X509Chain
將包含有關Build() == false
后驗證失敗的其他信息。
編輯:這只會確保您的CA有效。 如果要確保鏈條相同,可以手動檢查指紋。 您可以使用以下方法來確保認證鏈是正確的,它期望鏈的順序為: ..., INTERMEDIATE2, INTERMEDIATE1 (Signer of INTERMEDIATE2), CA (Signer of INTERMEDIATE1)
static bool VerifyCertificate(byte[] primaryCertificate, IEnumerable<byte[]> additionalCertificates)
{
var chain = new X509Chain();
foreach (var cert in additionalCertificates.Select(x => new X509Certificate2(x)))
{
chain.ChainPolicy.ExtraStore.Add(cert);
}
// You can alter how the chain is built/validated.
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreWrongUsage;
// Do the preliminary validation.
var primaryCert = new X509Certificate2(primaryCertificate);
if (!chain.Build(primaryCert))
return false;
// Make sure we have the same number of elements.
if (chain.ChainElements.Count != chain.ChainPolicy.ExtraStore.Count + 1)
return false;
// Make sure all the thumbprints of the CAs match up.
// The first one should be 'primaryCert', leading up to the root CA.
for (var i = 1; i < chain.ChainElements.Count; i++)
{
if (chain.ChainElements[i].Certificate.Thumbprint != chain.ChainPolicy.ExtraStore[i - 1].Thumbprint)
return false;
}
return true;
}
我無法測試這個,因為我沒有完整的CA鏈,所以最好調試並逐步完成代碼。
如果您在機器上的受信任 CA 存儲中沒有根證書,則X509Chain
無法可靠地工作。
其他人會提倡使用充氣城堡。 我想避免為此任務引入另一個庫,所以我編寫了自己的庫。
如RFC3280 第 4.1 節所示,證書是ASN1
編碼結構,在其基礎級別僅由 3 個元素組成。
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING
}
C# 實際上有一個方便的工具來解析 ASN1,即System.Formats.Asn1.AsnDecoder 。
使用它,我們可以從證書中提取這 3 個元素來驗證鏈。
第一步是提取證書簽名,因為X509Certificate2
類不公開此信息,而這是證書驗證所必需的。
提取簽名值部分的示例代碼:
public static byte[] Signature(
this X509Certificate2 certificate,
AsnEncodingRules encodingRules = AsnEncodingRules.BER)
{
var signedData = certificate.RawDataMemory;
AsnDecoder.ReadSequence(
signedData.Span,
encodingRules,
out var offset,
out var length,
out _
);
var certificateSpan = signedData.Span[offset..(offset + length)];
AsnDecoder.ReadSequence(
certificateSpan,
encodingRules,
out var tbsOffset,
out var tbsLength,
out _
);
var offsetSpan = certificateSpan[(tbsOffset + tbsLength)..];
AsnDecoder.ReadSequence(
offsetSpan,
encodingRules,
out var algOffset,
out var algLength,
out _
);
return AsnDecoder.ReadBitString(
offsetSpan[(algOffset + algLength)..],
encodingRules,
out _,
out _
);
}
下一步是提取 TBS 證書。 這是已簽名的原始數據。
提取 TBS 證書數據的示例代碼:
public static ReadOnlySpan<byte> TbsCertificate(
this X509Certificate2 certificate,
AsnEncodingRules encodingRules = AsnEncodingRules.BER)
{
var signedData = certificate.RawDataMemory;
AsnDecoder.ReadSequence(
signedData.Span,
encodingRules,
out var offset,
out var length,
out _
);
var certificateSpan = signedData.Span[offset..(offset + length)];
AsnDecoder.ReadSequence(
certificateSpan,
encodingRules,
out var tbsOffset,
out var tbsLength,
out _
);
// include ASN1 4 byte header to get WHOLE TBS Cert
return certificateSpan.Slice(tbsOffset - 4, tbsLength + 4);
}
您可能會注意到,在提取 TBS 證書時,我需要在數據中包含 ASN1 標頭,這是因為 TBS 證書的簽名包含此數據(這讓我惱火了一段時間)。
微軟有史以來第一次不妨礙我們的API設計,我們可以直接從X509Certificate2
對象中獲取簽名算法。 然后我們只需要決定我們將在多大程度上實施不同的哈希算法。
var signature = signed.Signature();
var tbs = signed.TbsCertificate();
var alg = signed.SignatureAlgorithm;
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gpnap/a48b02b2-2a10-4eb0-bed4-1807a6d2f5ad
switch (alg)
{
case { Value: var value } when value?.StartsWith("1.2.840.113549.1.1.") ?? false:
return signedBy.GetRSAPublicKey()?.VerifyData(
tbs,
signature,
value switch {
"1.2.840.113549.1.1.11" => HashAlgorithmName.SHA256,
"1.2.840.113549.1.1.12" => HashAlgorithmName.SHA384,
"1.2.840.113549.1.1.13" => HashAlgorithmName.SHA512,
_ => throw new UnsupportedSignatureAlgorithm(alg)
},
RSASignaturePadding.Pkcs1
) ?? false;
case { Value: var value } when value?.StartsWith("1.2.840.10045.4.3.") ?? false:
return signedBy.GetECDsaPublicKey()?.VerifyData(
tbs,
signature,
value switch
{
"1.2.840.10045.4.3.2" => HashAlgorithmName.SHA256,
"1.2.840.10045.4.3.3" => HashAlgorithmName.SHA384,
"1.2.840.10045.4.3.4" => HashAlgorithmName.SHA512,
_ => throw new UnsupportedSignatureAlgorithm(alg)
},
DSASignatureFormat.Rfc3279DerSequence
) ?? false;
default: throw new UnsupportedSignatureAlgorithm(alg);
}
如上面的代碼所示, https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gpnap/a48b02b2-2a10-4eb0-bed4-1807a6d2f5ad是查看算法和 OID 映射的好資源.
您應該注意的另一件事是,有些文章聲稱對於橢圓曲線算法,Microsoft 需要R,S
格式的密鑰而不是 DER 格式的密鑰。 我嘗試將密鑰轉換為這種格式,但最終沒有成功。 我發現有必要使用DSASignatureFormat.Rfc3279DerSequence
參數。
除了鏈驗證之外,還可以進行額外的證書檢查,如“不在之前”和“不在之后”,或者 CRL 和 OCSP 檢查。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.