簡體   English   中英

在給定數據、證書和預生成簽名的情況下構建 CMS

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM