[英]Build a CMS given the data, certificates and pre-generated signature
我有一個第三方 web 服務器,它被提供了用戶名、密碼和消息的 SHA-256 hash 以代表該用戶名進行簽名。 它在兩個單獨的消息中返回簽名和證書鏈。 證書是 X.509,我認為簽名是原始字節,而不是任何 ASN.1 形式。
有沒有辦法將它們組合成 PKCS#7 中定義的適當 CMS 結構? 我不能在 Windows 中做到這一點(沒有 MSG API 需要原始字節來構建 CMS)但我希望 OpenSSL 可以成功地將它們組合起來。
我必須將此簽名嵌入到 PDF 中,如果是CryptMsgUpdate
中的 CryptMsgUpdate 進行簽名(將返回完整的 CMS 結構),我可以這樣做。
我可以研究 PKCS#7 結構並使用 ASN1 編譯器,但我希望有更簡單的方法。
只要你不想有簽名的屬性,你想要的都是可能的。 但是一旦您對屬性進行了簽名,那么輸入到 RSA(或其他)簽名算法中的數據就不同於基本數據(散列)。
雖然可能有 API 可以更直接地執行此操作(例如,OpenSSL 可能讓您直接使用 SignedData 和 SignerInfo 類型),但以下示例代碼(C#,使用 System.Formats.Asn1)表明可以創建 PKCS# 7 個文檔,僅來自您的輸入。 由於 .NET SignedData 類的設計,如果沒有原始數據(不僅僅是它的哈希值)就無法驗證簽名,但這沒關系......我知道它在這個示例中是什么:)。
此示例是根據IETF RFC 2315構建的。 可以使用更新的內容(例如 RFC 5652),這將允許基於 SubjectKeyIdentifier 的證書關聯……但請記住不能使用 signedAttrs……否則您將需要重新計算 RSA 簽名。
X509Certificate2 certificate;
byte[] hash = SHA256.HashData(ReadOnlySpan<byte>.Empty);
byte[] signature;
using (RSA key = RSA.Create(3072))
{
signature = key.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
CertificateRequest req = new CertificateRequest(
"CN=Test",
key,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
DateTimeOffset now = DateTimeOffset.UtcNow;
certificate = req.CreateSelfSigned(
now.AddMinutes(-5),
now.AddMinutes(5));
}
// The order doesn't matter in this collection, since the encoded PKCS#7
// will (if DER-encoded) order them from smallest encoding to largest)
X509Certificate2[] allCertificates = new[] { certificate };
byte[] pkcs7;
using (certificate)
{
AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
Asn1Tag context0 = new Asn1Tag(TagClass.ContextSpecific, 0);
// ContentInfo
using (writer.PushSequence())
{
// contentType = id-signedData
writer.WriteObjectIdentifier("1.2.840.113549.1.7.2");
// content
using (writer.PushSequence(context0))
{
// SignedData
using (writer.PushSequence())
{
// version: 1
writer.WriteInteger(1);
// digestAlgorithms
using (writer.PushSetOf())
{
// AlgorithmIdentifier (SHA-2-256)
using (writer.PushSequence())
{
writer.WriteObjectIdentifier("2.16.840.1.101.3.4.2.1");
}
}
// contentInfo
using (writer.PushSequence())
{
// contentType = id-data
writer.WriteObjectIdentifier("1.2.840.113549.1.7.1");
// since we don't have the original data, omit the content
}
// certificates
using (writer.PushSetOf(context0))
{
foreach (X509Certificate2 cert in allCertificates)
{
// X.509 certificates are written down with no special extra ceremony
writer.WriteEncodedValue(cert.RawData);
}
}
// no crls to encode
// signerInfos
using (writer.PushSetOf())
{
// SignerInfo
using (writer.PushSequence())
{
// version: 1
writer.WriteInteger(1);
// issuerAndSerialNumber
using (writer.PushSequence())
{
// issuer, an encoded X.500 Distinguished Name
writer.WriteEncodedValue(certificate.IssuerName.RawData);
// serialNumber
writer.WriteInteger(certificate.SerialNumberBytes.Span);
}
// digestAlgorithm: AlgorithmIdentifier (SHA-2-256)
using (writer.PushSequence())
{
writer.WriteObjectIdentifier("2.16.840.1.101.3.4.2.1");
}
// no authenticatedAttributes
// digestEncryptionAlgorithm,
// which really means signature-envelope algorithm
// AlgorithmIdentifier(RSA, NULL)
using (writer.PushSequence())
{
writer.WriteObjectIdentifier("1.2.840.113549.1.1.1");
writer.WriteNull();
}
// encryptedDigest, aka signature
writer.WriteOctetString(signature);
// no un-authenticated attributes, either
}
}
}
}
}
pkcs7 = writer.Encode();
}
SignedCms signedCms = new SignedCms(new ContentInfo(Array.Empty<byte>()), detached: true);
signedCms.Decode(pkcs7);
try
{
signedCms.CheckSignature(true);
Console.WriteLine("Signature check succeeded");
}
catch (CryptographicException)
{
Console.WriteLine("Signature check failed");
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.