Расшифровать пароль защищен PEM, содержащий закрытый ключ


У меня есть следующий метод, который создает зашифрованный закрытый ключ с помощью Bouncy Castle для C#:

public string GetPrivateKey(AsymmetricCipherKeyPair keyPair, string password)
{
    var generator = new Pkcs8Generator(keyPair.Private, Pkcs8Generator.PbeSha1_3DES);
    generator.IterationCount = 4;
    generator.Password = password.ToCharArray();
    var pem = generator.Generate();

    TextWriter textWriter = new StringWriter();
    PemWriter pemWriter = new PemWriter(textWriter);
    pemWriter.WriteObject(pem);
    pemWriter.Writer.Flush();
    string privateKey = textWriter.ToString();
    return privateKey;
}

Который выглядит так:

-----BEGIN ENCRYPTED PRIVATE KEY-----
...
-----END ENCRYPTED PRIVATE KEY-----

Чего я не знаю, так это как использовать пароль, используемый для шифрования закрытого ключа в моем методе расшифровки. Прямо сейчас, не зная, как "расшифровать" мой закрытый ключ с помощью he password, я получаю следующее исключение:

Орг.После установки BouncyCastle.В OpenSSL.PemException: проблема создания зашифрованного закрытый ключ: Система.NullReferenceException: ссылка на объект не установлена к экземпляру объекта. около Org.После установки BouncyCastle.В OpenSSL.Пемридер.ReadPrivateKey(PemObject pemObject)

Вот код для метода расшифровки:

public string Decrypt(string base64Input, string privateKey, string password)
{
    var bytesToDecrypt = Convert.FromBase64String(base64Input);

    //get a stream from the string
    AsymmetricCipherKeyPair keyPair;
    var decryptEngine = new Pkcs1Encoding(new RsaEngine());

    using (var txtreader = new StringReader(privateKey))
    {
        var obj = new PemReader(txtreader).ReadObject();
        keyPair = (AsymmetricCipherKeyPair) obj;

        decryptEngine.Init(false, keyPair.Private);
    }

    var decrypted = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length));
    return decrypted;
}
1 2

1 ответ:

Мне кажется, что вам нужно расшифровать закрытый ключ, чтобы использовать его. В настоящее время Ваш пароль не используется. К сожалению, не так-то просто выяснить, как это сделать.


Bouncy Castle, как и многие другие Java API, использует обработчик паролей для получения пароля. Причина этого заключается в том, чтобы позволить программе запрашивать пароль у пользователя только тогда, когда это необходимо. Это позволяет программе оставлять пароль в памяти на максимально короткий срок. время.

Итак, чтобы разрешить расшифровку, используйте следующий конструктор:

PemReader(TextReader reader, IPasswordFinder pFinder);

С реализацией IPasswordFinder (Bouncy Castle для C# - это в основном Java-порт, иначе, вероятно, использовался бы делегат).


Для вашего удобства, код. Этот код также восстанавливает всю пару ключей, а не только закрытый ключ.

Операторы импорта:

using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using System.IO;

Декодер:

private static AsymmetricCipherKeyPair DecodePrivateKey(string encryptedPrivateKey, string password)
{
    TextReader textReader = new StringReader(encryptedPrivateKey);
    PemReader pemReader = new PemReader(textReader, new PasswordFinder(password));
    object privateKeyObject = pemReader.ReadObject();
    RsaPrivateCrtKeyParameters rsaPrivatekey = (RsaPrivateCrtKeyParameters)privateKeyObject;
    RsaKeyParameters rsaPublicKey = new RsaKeyParameters(false, rsaPrivatekey.Modulus, rsaPrivatekey.PublicExponent);
    AsymmetricCipherKeyPair kp = new AsymmetricCipherKeyPair(rsaPublicKey, rsaPrivatekey);
    return kp;
}

Требуемый вспомогательный класс:

private class PasswordFinder : IPasswordFinder
{
    private string password;

    public PasswordFinder(string password)
    {
        this.password = password;
    }


    public char[] GetPassword()
    {
        return password.ToCharArray();
    }
}

Обратите внимание, что обычно вы должны использовать только char[] вместо string для паролей, так как char[] может быть очищен после использования, в то время как string не может.

Теперь у вас есть секретный ключ, расшифровка должна быть легкой.