【问题标题】:C# RSA signing with PSS padding and a MGF1 mask using a SHA-256 hash algorithm使用 SHA-256 哈希算法使用 PSS 填充和 MGF1 掩码进行 C# RSA 签名
【发布时间】:2021-02-04 16:26:11
【问题描述】:

首先,我对加密/签名还是新手,所以请容忍任何滥用术语。

我需要在 C# 中创建一个由 Python 库验证的签名。在 Python 中,解码/验证签名是一段简单的代码:

    from cryptography.hazmat.primitives import hashes
    from cryptography.hazmat.primitives.asymmetric import padding

    public_key.verify(signature, payload_contents,
                      padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
                                  salt_length=padding.PSS.MAX_LENGTH,
                                  ), hashes.SHA256(), )

我当前的 C# 代码如下所示:

    private static string CreateSignature(byte[] data, string privateKeyFileLocation)
    {
        var cert = new X509Certificate2(privateKeyFileLocation);
        
        byte[] signedBytes;
        using (var rsa = cert.GetRSAPrivateKey())
        {
            signedBytes = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
        }
        var finalString = Convert.ToBase64String(signedBytes);
        return finalString;
    }

但是,签名未通过 Python 代码中的验证检查。看起来 .Net 库中的 PSS 填充默认使用 MGF1。但是,我相信由于掩码生成函数 (MGF1) 在 Python 代码中使用 256 位哈希,但在 C# 中默认为 SHA1,我遇到了问题。我已经浏览了 .Net C# 文档,看起来没有办法覆盖它。我查看了 Bouncy Castle 的 C# 文档,只是找不到任何类似的如何使用自定义参数设置填充的示例。有没有人有这方面的经验,可以提供一些提示?

【问题讨论】:

标签: c# cryptography rsa bouncycastle signing


【解决方案1】:

仅适用于其他处理此问题的人。这是我最终得到的代码。

    private static string GenerateSignatureForData(string data, string privateKeyFileLocation)
    {
        var cert = new X509Certificate2(privateKeyFileLocation, "12345", X509KeyStorageFlags.Exportable);
        var bcCert = TransformRSAPrivateKey(cert.PrivateKey);
        var keyLen = (int)Math.Ceiling((cert.GetRSAPrivateKey().KeySize - 1) / 8.0);

        byte[] signedBytes = CreateSignature(Encoding.ASCII.GetBytes(data), bcCert, keyLen);
        return Convert.ToBase64String(signedBytes);
    }

    private static AsymmetricKeyParameter TransformRSAPrivateKey(AsymmetricAlgorithm privateKey)
    {
        RSACryptoServiceProvider prov = privateKey as RSACryptoServiceProvider;
        RSAParameters parameters = prov.ExportParameters(true);

        return new RsaPrivateCrtKeyParameters(
            new BigInteger(1, parameters.Modulus),
            new BigInteger(1, parameters.Exponent),
            new BigInteger(1, parameters.D),
            new BigInteger(1, parameters.P),
            new BigInteger(1, parameters.Q),
            new BigInteger(1, parameters.DP),
            new BigInteger(1, parameters.DQ),
            new BigInteger(1, parameters.InverseQ));
    }

    private static byte[] CreateSignature(byte[] data, AsymmetricKeyParameter privateKey, int keyLength)
    {
        var digest = new Sha256Digest();
        var saltLength = keyLength - digest.GetDigestSize() - 2;

        PssSigner signer = new PssSigner(new RsaEngine(), new Sha256Digest(), digest, saltLength);
        signer.Init(true, new ParametersWithRandom((RsaPrivateCrtKeyParameters)privateKey));
        signer.BlockUpdate(data, 0, data.Length);
        return signer.GenerateSignature();
    }

其中还有一些额外的内容,因为还必须计算盐的长度。我不得不查看加密库的源代码来计算此处 python 代码中指示的盐 MAX_LENGTH:

    salt_length=padding.PSS.MAX_LENGTH

这是执行此操作所需的严格代码:

    var cert = new X509Certificate2(privateKeyFileLocation, "12345", X509KeyStorageFlags.Exportable);
    var bcCert = TransformRSAPrivateKey(cert.PrivateKey);
    var keyLen = (int)Math.Ceiling((cert.GetRSAPrivateKey().KeySize - 1) / 8.0);
    var digest = new Sha256Digest();
    var saltLength = keyLength - digest.GetDigestSize() - 2;

同样,将 .Net 加密库 AsymmetricAlgorithm 转换为 Bouncy Castle AsymmetricKeyParameter 需要上面看到的转换函数 TransformRSAPrivateKey。

【讨论】:

    猜你喜欢
    • 2016-08-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-03
    • 2013-04-19
    • 1970-01-01
    • 2021-10-29
    • 2013-07-20
    • 2014-08-19
    相关资源
    最近更新 更多