And, so you have an RSA key pair which you use to sign your software or to protect your symmetric keys. For an intruder, this is one of the most valuable things they can gain, as they will also be able to sign for your software and decrypt your symmetric keys. One of the most popular ways of protecting the private key is to use PBE (Password-Based Encryption), which takes a low entropy password and encrypts the private key. One of the most popular methods for this is PBKDF2, and which slows down the generation of the encryption key. In this case, though, we will use the methods provided with Bouncy Castle, and aim to protect an RSA private key using the Bouncy Castle library.
PBE (Password Based Encryption) with RSA private key using Bouncy Castle and C# |
In this case, we will use the methods provided with Bouncy Castle, and aim to protect an RSA private key using the Bouncy Castle library. First, we can generate a random RSA key pair with:
RsaKeyPairGenerator pGen = new RsaKeyPairGenerator(); pGen.Init(new KeyGenerationParameters(new SecureRandom(), s)); AsymmetricCipherKeyPair pair = pGen.GenerateKeyPair();
Next, we can encrypt the private key using a defined password (msg) defined method, a salt value (nonce), and with a number of rounds:
var encKey = PrivateKeyFactory.EncryptKey(method, msg.ToCharArray(),nonce, 1, pair.Private);
Finally, we can decrypt the private key when we want it back:
var decKey = PrivateKeyFactory.DecryptKey( msg.ToCharArray(),encKey);
For Bouncy Castle, the methods of encryption we can use include PBEwithMD2andDES-CBC, PBEwithMD2andRC2-CBC, PBEwithMD5andDES-CBC, PBEwithMD5andRC2-CBC, PBEwithSHA1andDES-CBC, PBEwithSHA1andRC2-CBC, PBEwithSHA-1and128bitRC4, PBEwithSHA-1and40bitRC4, PBEwithSHA-1and3-keyDESEDE-CBC, PBEwithSHA-1and2-keyDESEDE-CBC, PBEwithSHA-1and128bitRC2-CBC, and PBEwithSHA-1and40bitRC2-CBC. With PBEwithMD2andDES-CBC, for example, we hash with MD2 and then encrypt with DES-CBC.
Now, let’s code this with:
namespace PBE { using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; class Program { static void Main(string[] args) { var msg="Hello"; var iv="00112233445566778899AABBCCDDEEFF00"; var s=64; var method="PBEwithSHA1andDES-CBC"; if (args.Length >0) msg=args[0]; if (args.Length >1) iv=args[1]; if (args.Length >2) s=Convert.ToInt32(args[2]); if (args.Length >3) method=args[3]; byte[] nonce = new byte[16]; Array.Copy(Convert.FromHexString(iv), nonce, 16); try { RsaKeyPairGenerator pGen = new RsaKeyPairGenerator(); pGen.Init(new KeyGenerationParameters(new SecureRandom(), s)); AsymmetricCipherKeyPair pair = pGen.GenerateKeyPair(); var encKey = PrivateKeyFactory.EncryptKey(method, msg.ToCharArray(),nonce, 1, pair.Private); var decKey = PrivateKeyFactory.DecryptKey( msg.ToCharArray(),encKey); Console.WriteLine("Password:\t{0}",msg); Console.WriteLine("Salt:\t\t{0}",iv); Console.WriteLine("Method:\t{0}",method); Console.WriteLine("\nEncrypted private key: {0}",Convert.ToBase64String(encKey)); Console.WriteLine("Encrypted private key: {0}",Convert.ToHexString(encKey)); RsaPrivateCrtKeyParameters before = ((RsaPrivateCrtKeyParameters)pair.Private); RsaPrivateCrtKeyParameters after = ((RsaPrivateCrtKeyParameters)decKey); Console.WriteLine("\nPrivate key P:\t\t\t{0}",before.P); Console.WriteLine("Decrypted private key P:\t{0}",after.P); Console.WriteLine("Private key Q:\t\t\t{0}",before.Q); Console.WriteLine("Decrypted private key Q:\t{0}",after.Q); Console.WriteLine("Private key d:\t\t\t{0}",before.Modulus); Console.WriteLine("Decrypted private key d:\t{0}",after.Modulus); Console.WriteLine("Private key d:\t\t\t{0}",before.Exponent); Console.WriteLine("Decrypted private key d:\t{0}",after.Exponent); Console.WriteLine("Private key e:\t\t\t{0}",before.PublicExponent); Console.WriteLine("Decrypted private key e:\t{0}",after.PublicExponent); } catch (Exception e) { Console.WriteLine("Error: {0}",e.Message); } } } }
And then we can show that the decrypted key is the same as the key generated with samples runs. With PBEwithMD2andDES-CBC and with a 64-bit RSA key we get:
Password: Hello World! Salt: 00112233445566778899AABBCCDDEEFF00 Method: PBEwithSHA-1and3-keyDESEDE-CBC Encrypted private key: MH8wIwYKKoZIhvcNAQwBAzAVBBAAESIzRFVmd4iZqrvM3e7/AgEBBFidOX7Jwc+2UyDO+nFDZw+ny4MTc0EN4Eb7HmPpNyx79zZ4CWKMgivkG/4sjCKnR+2QpIueAO+wC+8lAIi8RXJOE1VkxV9zJIjIWfgVc4qVVAXph/WI9ht7 Encrypted private key: 307F3023060A2A864886F70D010C01033015041000112233445566778899AABBCCDDEEFF02010104589D397EC9C1CFB65320CEFA7143670FA7CB831373410DE046FB1E63E9372C7BF7367809628C822BE41BFE2C8C22A747ED90A48B9E00EFB00BEF250088BC45724E135564C55F732488C859F815738A955405E987F588F61B7B Private key P: 3235731847 Decrypted private key P: 3235731847 Private key Q: 2996920249 Decrypted private key Q: 2996920249 Private key N: 9697230292608469903 Decrypted private key N: 9697230292608469903 Private key d: 708755864194884833 Decrypted private key d: 708755864194884833 Private key e: 65537 Decrypted private key e: 65537
The public key is (e,N) and the private key is (d,N). We can see that e=65537, d=708755864194884833 and N=9697230292608469903, and where we have recovered each of the values using the password of “Hello World!”. We can then use PBEwithSHA-1and3-keyDESEDE-CBC and a 256-bit RSA key to get:
Password: Hello World! Salt: 00112233445566778899AABBCCDDEEFF00 Method: PBEwithSHA-1and3-keyDESEDE-CBC Encrypted private key: MIHwMCMGCiqGSIb3DQEMAQMwFQQQABEiM0RVZneImaq7zN3u/wIBAQSByDnsUGpf3ESKDNbhBy0YMD/7vAwp8DMBiyYoi/WpMdGRnet1/Itf8PwqBLhlP1EEkJv4mNrizT7yr+AdN8lOPUbfAWmUDp5+0SPvqs7/fblfeHLPh8j3BXke5S42BDM4LgRV/0GQwm91rI4dgURYxkFeDECqil8c3qzVr/bNaRmljZQxSFwVNEBnkCPTONRei4b1oZE/byJ0WmRnixfmInbYTLgjFlpbKNPluGMUfcMnPs4XW8oMQwL6DHb/mYCEr5jISiDmC6LA Encrypted private key: 3081F03023060A2A864886F70D010C01033015041000112233445566778899AABBCCDDEEFF0201010481C839EC506A5FDC448A0CD6E1072D18303FFBBC0C29F033018B26288BF5A931D1919DEB75FC8B5FF0FC2A04B8653F5104909BF898DAE2CD3EF2AFE01D37C94E3D46DF0169940E9E7ED123EFAACEFF7DB95F7872CF87C8F705791EE52E360433382E0455FF4190C26F75AC8E1D814458C6415E0C40AA8A5F1CDEACD5AFF6CD6919A58D9431485C153440679023D338D45E8B86F5A1913F6F22745A64678B17E62276D84CB823165A5B28D3E5B863147DC3273ECE175BCA0C4302FA0C76FF998084AF98C84A20E60BA2C0 Private key P: 316903354222929006607505239513428102251 Decrypted private key P: 316903354222929006607505239513428102251 Private key P: 222792809260785375400273402593360083309 Decrypted private key P: 222792809260785375400273402593360083309 Private key N: 70603788551492125786298011267990832362898971511551990677753325277835130428559 Decrypted private key N: 70603788551492125786298011267990832362898971511551990677753325277835130428559 Private key d: 18117153853097381316632950173090038113398476181828039160558964520393724636473 Decrypted private key d: 18117153853097381316632950173090038113398476181828039160558964520393724636473 Private key e: 65537 Decrypted private key e: 65537
Conclusions
Your private keys are fundamentally important to protect, and PBE is one method we can use to protect these. The strength of the protection will obviously depend on the strength of the password used. Note that storing the private key in an HSM (Hardware Security Module) or to use key wrapping is a much better approach.