【问题标题】:Why ComputeSignature in .Net Framework and .Net Core do different result为什么 .Net Framework 和 .Net Core 中的 ComputeSignature 会产生不同的结果
【发布时间】:2018-08-07 17:29:16
【问题描述】:

我有问题。

        var contentInfo = new ContentInfo(message);
        var signedCms = new SignedCms(contentInfo);
        var cmsSigner = new CmsSigner(certificate);

        signedCms.ComputeSignature(cmsSigner);
        data = signedCms.Encode();

.Net Framework 4.7 和 .Net Core 2.1 中的代码执行不同的结果...

我的消息长度是 73 个字节

.Net Framework - 1520 字节

.Net Core - 1523 字节

为什么?

【问题讨论】:

    标签: .net .net-core pkcs#7


    【解决方案1】:

    在 .NET Framework 上,如果您的 .exe 以 4.7 或更早版本为目标,则 new CmsSigner(cert) 表示签名者应使用 SHA-1 作为其摘要,在 4.7.1 或更高版本中,默认算法更改为 SHA-2-256 .

    在 .NET Core 2.1 上,默认值已经是 SHA-2-256。

    您可以通过设置CmsSignerDigestAlgorithm 属性将它们中的任何一个更改为您喜欢的任何摘要算法。例如cmsSigner.DigestAlgorithm = new Oid("2.16.840.1.101.3.4.2.1"); 现在让他们都使用 SHA-2-256。

    此外,.NET Framework 将 RSA 签名算法标识符编码为 { id-rsaEncryption, NULL },而 .NET Core 将其编码为 { id-rsaEncryption }(省略可选的第二个值)。这部分不会改变内容的语义解释,但意味着当在两者上使用相同的摘要算法时,它们仍然不会得到相同的长度。


    SHA-2-256算法标识符的DER编码比SHA-1标识符的DER编码长两个字节(假设省略了SHA-2-256的参数值,对于SHA-1它一片空白)。这被记录了两次(+4 到核心),然后 RSA 签名算法标识符的表示方式的差异使 .NET Core 版本小 2 个字节(净:+2 到核心)

    您看到 三个 字节差异而不是两个字节的原因是文档结构的某些部分最终通过了长度前缀(可变长度)需要的阈值之一获得一个额外的字节来保存该值。除了 127 -> 128 之外,这些点是标准的“字节边界”(255 -> 256、65535 -> 65536 等)。您的 SignerInfo 块在您的 .NET Framework 文档上可能是 254 或 255 个字节,并跨越到256 或 257,因为 SHA-2-256 的标识符值较长。

    【讨论】:

    • 谢谢,但我希望 .NET Core 2.1 像 .NET Framework 一样工作。我的应用程序必须将签名计算为 1520 字节。如何配置 .net Core (sha1 - Oid("1.3.14.3.2.26") 计算 1515 字节)?
    • 我设置了 SHA-1 cmsSigner.DigestAlgorithm = new Oid("1.3.14.3.2.26");最终结果长度是 1515 字节,而不是 1520。
    • 没有选项可以让它们产生完全相同的字节。 .NET Core 在 RSA 的标识符中留下了 DER NULL,并且没有选项会将其放回原处。但是两个产品都会说对方的输出是有效的。
    • Bad(( 检查我的签名的外部应用程序只有在我在 .Net Framework 中计算(1520 字节)时才获得。
    【解决方案2】:

    我在方法“ComputeSignature”中使用 BouncyCastle.NetCore 解决了我的问题(在 Debian 9 中使用 .Net Core 2.0)。 结果长度为 2537 字节,但外部应用程序可以很好地使用此签名。

        public static byte[] ComputeSignature(byte[] message, string PrivateKeyPath, string password, string CertPath)
        {
            try
            {
                // Load end certificate and signing key
                var signerCert = ReadCertFromFile(PrivateKeyPath, password, out AsymmetricKeyParameter key);
    
                // Read CA cert
                var caCert = ReadCertFromFile(CertPath);
                var certChain = new X509Certificate[] { caCert };
    
                var result = SignWithSystem(
                  message,
                  key,
                  signerCert,
                  certChain);
                return result;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Failed : " + ex.ToString());
                return null;
            }
        }
    
        protected static byte[] SignWithSystem(byte[] data, AsymmetricKeyParameter key, X509Certificate cert, X509Certificate[] chain)
        {
            var generator = new CmsSignedDataGenerator();
            // Add signing key
            generator.AddSigner(
              key,
              cert,
              "2.16.840.1.101.3.4.2.1"); // SHA256 digest ID
            var storeCerts = new List<X509Certificate>
            {
                cert // NOTE: Adding end certificate too
            };
            storeCerts.AddRange(chain); // I'm assuming the chain collection doesn't contain the end certificate already
                                        // Construct a store from the collection of certificates and add to generator
            var storeParams = new X509CollectionStoreParameters(storeCerts);
            var certStore = X509StoreFactory.Create("CERTIFICATE/COLLECTION", storeParams);
            generator.AddCertificates(certStore);
    
            // Generate the signature
            var signedData = generator.Generate(
              new CmsProcessableByteArray(data),
              false); // encapsulate = false for detached signature
            return signedData.GetEncoded();
        }
    
        public static X509Certificate ReadCertFromFile(string strCertificatePath)
        {
            // Create file stream object to read certificate
            using (var keyStream = new FileStream(strCertificatePath, FileMode.Open, FileAccess.Read))
            {
                var parser = new X509CertificateParser();
                return parser.ReadCertificate(keyStream);
            }
        }
    
        // This reads a certificate from a file.
        // Thanks to: http://blog.softwarecodehelp.com/2009/06/23/CodeForRetrievePublicKeyFromCertificateAndEncryptUsingCertificatePublicKeyForBothJavaC.aspx
        public static X509Certificate ReadCertFromFile(string strCertificatePath, string strCertificatePassword, out AsymmetricKeyParameter key)
        {
            key = null;
            // Create file stream object to read certificate
            using (var keyStream = new FileStream(strCertificatePath, FileMode.Open, FileAccess.Read))
            {
                // Read certificate using BouncyCastle component
                var inputKeyStore = new Pkcs12Store();
                inputKeyStore.Load(keyStream, strCertificatePassword.ToCharArray());
    
                var keyAlias = inputKeyStore.Aliases.Cast<string>().FirstOrDefault(n => inputKeyStore.IsKeyEntry(n));
    
                // Read Key from Aliases  
                if (keyAlias == null)
                    throw new NotImplementedException("Alias");
                key = inputKeyStore.GetKey(keyAlias).Key;
                //Read certificate into 509 format
                return (X509Certificate)inputKeyStore.GetCertificate(keyAlias).Certificate;
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-03-11
      • 2021-12-30
      • 2017-02-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多