C# RSA подписване с PSS подложка и MGF1 маска, използвайки хеш алгоритъм SHA-256

Първо, аз все още съм нов в крипто/подписването, така че имайте търпение при злоупотреба с термини, моля.

Трябва да създам подпис в 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. Изглежда, че PSS подплънките в .Net библиотеките по подразбиране използват MGF1. Вярвам обаче, че имам проблеми поради функцията за генериране на маска (MGF1), използваща 256-битов хеш в кода на Python, но по подразбиране SHA1 в C#. Прегледах документацията на .Net C# и изглежда, че няма начин да отменя това. Разгледах C# документацията на Bouncy Castle и просто имам проблеми с намирането на какъвто и да е подобен пример за това как да задам padding с персонализирани параметри. Някой има ли опит в тази област и може ли да даде няколко съвета?


person PHott    schedule 21.10.2020    source източник
comment
Разгледайте тук. BC/C# изглежда позволява спецификацията на дайджести (PssSigner(IAsymmetricBlockCipher cipher, IDigest contentDigest, IDigest mgfDigest, int saltLen)). В мрежата можете също да намерите примери, напр. тук. Но не го пробвах.   -  person user 9014097    schedule 21.10.2020
comment
Това го направи! Благодаря много!   -  person PHott    schedule 22.10.2020


Отговори (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();
    }

Има малко допълнително, защото трябваше да се изчисли и дължината на солта. Трябваше да погледна изходния код за крипто библиотеката, за да изчисля солта MAX_LENGTH, посочена в кода на Python тук:

    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 crypto lib AsymmetricAlgorithm в Bouncy Castle AsymmetricKeyParameter изисква функцията за преобразуване TransformRSAPrivateKey, видяна по-горе.

person PHott    schedule 22.10.2020