Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.3k views
in Technique[技术] by (71.8m points)

c# - Decrypt passphrase protected PEM containing private key

I have the following method that creates an Encrypted Private Key using Bouncy Castle for 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;
}

Which looks like this:

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

What I don't know is how to consume the password used to encrypt the private key in my Decrypt method. Right now, without knowing how to "decrypt" my private key using he password, I get the following Exception:

Org.BouncyCastle.OpenSsl.PemException : problem creating ENCRYPTED private key: System.NullReferenceException: Object reference not set to an instance of an object. at Org.BouncyCastle.OpenSsl.PemReader.ReadPrivateKey(PemObject pemObject)

Here is the code for the Decrypt method:

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;
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

It seems to me that you need to decrypt the private key to use it. Currently your password parameter isn't used. Unfortunately it doesn't seem to be all that easy to find out how do this.


Bouncy Castle, as many other Java API's, use a password handler to retrieve the password. The reason to do this is to allow the program to ask the user for the password only when it is required. This allows the program to leave the password in memory for the shortest amount of time.

So to allow for decryption, use the following constructor:

PemReader(TextReader reader, IPasswordFinder pFinder);

with an implementation of IPasswordFinder (Bouncy Castle for C# is mainly a Java port, otherwise a delegate would probably have been used).


For your convenience, the code. This code also reconstructs the entire key pair, not just the private key.

Import statements:

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;

the decoder:

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;
}

required helper class:

private class PasswordFinder : IPasswordFinder
{
    private string password;

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


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

Note that normally you should only use char[] instead of string for passwords as char[] can be cleared after use, while string cannot.

Now you have the private key decryption should be easy.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...