为什么.Net Framework和.Net Core中的ComputeSignature会产生不同的结果

时间:2018-08-03 11:04:32

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

我有问题。

<span>

.Net Framework 4.7和.Net Core 2.1中的这段代码会产生不同的结果...

我的消息长度是73个字节

.Net Framework中的结果-1520字节

.Net Core的结果-1523字节

为什么?

2 个答案:

答案 0 :(得分:4)

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

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

您可以通过设置DigestAlgorithm的{​​{1}}属性,将它们中的任何一种更改为所需的摘要算法。例如CmsSigner现在使它们两个都使用SHA-2-256。

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


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的标识符值更长。

答案 1 :(得分:0)

我使用方法“ ComputeSignature”中的BouncyCastle.NetCore解决了我的问题(在带有.Net Core 2.0的Debian 9中工作)。 结果长度为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;
        }
    }