I'm working on a project for school and we are attempting to make use of encrypted private RSA keys in javascript. However, the example code in https://www.pidder.com/pidcrypt/?page=rsa isn't working for me. I generated a my key using the following command:
openssl genrsa -aes256
I gave it the passphrase "testing" and wound up with the following encrypted key (obviously this is a key created for this purpose, so there's no problem with me posting it here):
I'm using the following code (more or less copied from the example):
varpem="yyWMc56wVUa5NQ7b36Ccc0X3gvB4ikcvn5z0E (you get the point) xokIEfW50+WGtCRndbhs=";functiontest() {varaes=newpidCrypt.AES.CBC();//-----BEGIN RSA PRIVATE KEY-----//Proc-Type: 4,ENCRYPTED//DEK-Info: AES-256-CBC,3E54850C520D5DE0551F30669F9330A5// ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// ||| |||||||||||||||||||pem.iv|||||||// ||| ||||pem.salt||||// pem.bitsvarsalt=pidCryptUtil.convertFromHex("8CFED9A4C90B2531".toLowerCase());vark_and_iv=aes.createKeyAndIv({password:"testing",salt: salt,bits: 256});//initialize aes-cbc by values for cert decryption (OpenSSL aes-cbc//standard enc adds a byte 10 (A0) before byte pad, while cert //aes-cbc crypt seems not to => A0_PAD = false, also cert b64//decoded string is an octet string and is not UTF-8 encoded//=> UTF8 = false)aes.initByValues(pem,k_and_iv.key,"8CFED9A4C90B25317A4EC66561C510D9".toLowerCase(),{UTF8: false,A0_PAD: false,nBits: 256});//AES decryptionvarrsapem_decrypted=aes.decrypt();//rsapem_decrypted can be used for ASN.1 parsing$("#debug").html(aes.getAllMessages({}));console.log(pidCryptUtil.encodeBase64(rsapem_decrypted));}
Unfortunately, the result is basically gibberish. It's really a big pain in the butt to debug this because I can't find anything that says exactly how the passphrase is used to generate the key. There is a URL contained in the comments for aes.createKeyAndIv(…), but it's a broken link :-(.
Furthermore, ideally I would like this encryption to be very secure. For instance, users of our project may store their encrypted private key in a semi-trusted location. So if private keys get leaked in their encrypted form, we'd like them to be more or less secure. The code for aes.createKeyAndIv(…) seems to just do a couple rounds of salt + MD5, which doesn't seem too secure to me (rainbow table generation time would be very low). So if that algorithm is accurate, it would seem that maybe a stronger key derivation function might be necessary (e.g. PBKDF2?). Of course, a customized KDF kills my openssl compatibility (at least makes it more involved), but I'd rather see the private keys be secure and annoying than insecure and easy. :-)
Basically, what I'm looking for here is information on where I might have gone wrong and, with luck, a pointer to where I might find more information on the algorithm(s) used in OpenSSL's RSA private key encryption mechanism, specifically the key derivation function used.
PS - Please forgive me if I said anything really stupid there. I'm very new to cryptography.
PPS - I'm using the following version of OpenSSL (in case it matters): OpenSSL 1.0.0c-fips 2 Dec 2010
pete
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
So a working modification of your code example looks like this:
functiontest() {varaes=newpidCrypt.AES.CBC();//-----BEGIN RSA PRIVATE KEY-----//Proc-Type: 4,ENCRYPTED//DEK-Info: AES-256-CBC,8CFED9A4C90B25317A4EC66561C510D9// ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// ||| |||||||||||||||||||iv|||||||||||// ||| ||||salt||||// bits////yyWMc56wVUa5NQ7b36Ccc0X (you get the point) WGtCRndbhs=//|||||||||||||||||||||||||||||||||||||||||||||||||||||||//b64varbits=256;//createKeyAndIv needs the salt in hex, so don't convert!varsalt="8CFED9A4C90B2531".toLowerCase();variv="8CFED9A4C90B25317A4EC66561C510D9".toLowerCase();varb64="yyWMc56wVUa5NQ7b36Ccc0X3gvB4ikcvn5z0EV63teLLqRRBnnO\UxB2QH0DX6uRsL6eXtbdAD5DI6GhhhyWARkEciqwp5qksAceyowPD8dqXUxsFGb3t7/NjGC\NHkItNjehtFgGq8MSFMcY1C82/N0A5OhNcu2TAbockaL7pQN5j2RWUx92DMZoSIY88ESn4o\l/bpQDozw0YSla7BMlInxxzyiGKxzXCUKlhMahafZzHqFeLTz3bA93vFb717XPpQZH4q8um\gex0CgGaWYn6LJGL1xD/GDfuuHycL/5P0U0Dnl04Eajh/oFGsMy3oH2Uc3X1Hm+guiH2NYz\MIfcCjEhKT0tcNtg6gfu8cwuK5A3MiJTuqDfxrSr39Z2xiExetqbPDR+czULeZGmYn4GdsG\HxokIEfW50+WGtCRndbhs=";//you can find a certificate parsing function certParser(certificate)//in the RSA certificate parsing demo at //https://www.pidder.com/pidcrypt/?page=demo_rsa-certificate //to extract bits, salt and iv from your certificate rather than //setting the values manually.vark_and_iv=aes.createKeyAndIv({password:"testing",salt: salt,bits: bits});//initialize aes-cbc by values for cert decryption (OpenSSL aes-cbc//standard enc adds a byte 10 (A0) before byte pad, while cert//aes-cbc crypt seems not to => A0_PAD = false, also cert b64//decoded string is an octet string and is not UTF-8 encoded//=> UTF8 = false)aes.initByValues(b64,k_and_iv.key,iv,{UTF8: false,A0_PAD: false,nBits: bits});//AES decryptionvarrsapemDecrypted=aes.decrypt();//ASN decoding function needs a byte arrayvarrsapemDecryptedBytes=pidCryptUtil.toByteArray(rsapemDecrypted);//rsapemDecryptedBytes can be used for ASN.1 parsingvarasn=pidCrypt.ASN1.decode(rsapemDecryptedBytes);//asn.toHexTree() returns the parameters used for RSA intialization//@returns node: as javascript object tree with hex strings as values//e.g. RSA Public Key gives// {// type: SEQUENCE,// sub: [ // {type: 'INTEGER', value: version},// {type: 'INTEGER', value: modulus},// {type: 'INTEGER', value: public exponent},// {type: 'INTEGER', value: private exponent},// {type: 'INTEGER', value: prime1},// {type: 'INTEGER', value: prime2},// {type: 'INTEGER', value: exponent1},// {type: 'INTEGER', value: exponent2},// ]// }vartree=asn.toHexTree();//initializing RSA modulevarrsa=newpidCrypt.RSA();//set RSA parameters from ASN.1 structurersa.setPrivateKeyFromASN(tree);//RSA encryptionvarencryptedText=rsa.encrypt('Hello World');alert(encryptedText);//RSA decryptionvardecryptedText=rsa.decrypt(encryptedText);alert(decryptedText);}
There is a URL contained in the comments for aes.createKeyAndIv(…), but it's a broken link
So if that algorithm is accurate, it would seem that maybe a stronger key derivation function might be necessary
OpenSSL is one of the most popular tools for cryptographic funtions, and their project is very well maintained. That is also the reason why we decided to make our library compatible.
So rather than trying to implement something that breaks compatibility with OpenSSL we advise getting involved with that project if you think something should be improved - but that's beyond the scope of our little project :)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thank you for the fast reply! I will test this out as soon as I get a chance It works like a charm! You should probably change the example code on the website, though, as it implies you do need to convert from hex to a byte array:
No worries. It gave me a good excuse to spend about 3 hours last night researching the KDF used by OpenSSL. I'm still not 100% convinced that the KDF is secure enough for our ultimate needs, but it should be good enough for this project. If I continue work on the project after this semester, I'll probably do more research on the subject.
I don't think it's a matter of poor security in OpenSSL. I imagine it was a decision between high strength encryption and compliance with export laws (stupid export laws - ugh) and legacy compatibility (older systems may not be able to handle high strength KDFs in a timely fashion). As long as you're using a high strength passphrase, pretty much any KDF should be fine. But since our project targets "normal" users who might use weak passwords, a stronger KDF would offer those users more security. So essentially, it's not that OpenSSL is doing anything "wrong," it's just that they designed this system with different priorities in mind.
Whatever the case, it's been far too long since I took the kind of math classes I need to understand this stuff properly. I fear that I'm going to find myself back in a math classroom before I finish this degree… ;-)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Copied from our pidder support forum:
I'm working on a project for school and we are attempting to make use of encrypted private RSA keys in javascript. However, the example code in https://www.pidder.com/pidcrypt/?page=rsa isn't working for me. I generated a my key using the following command:
I gave it the passphrase "testing" and wound up with the following encrypted key (obviously this is a key created for this purpose, so there's no problem with me posting it here):
I'm using the following code (more or less copied from the example):
Unfortunately, the result is basically gibberish. It's really a big pain in the butt to debug this because I can't find anything that says exactly how the passphrase is used to generate the key. There is a URL contained in the comments for aes.createKeyAndIv(…), but it's a broken link :-(.
Furthermore, ideally I would like this encryption to be very secure. For instance, users of our project may store their encrypted private key in a semi-trusted location. So if private keys get leaked in their encrypted form, we'd like them to be more or less secure. The code for aes.createKeyAndIv(…) seems to just do a couple rounds of salt + MD5, which doesn't seem too secure to me (rainbow table generation time would be very low). So if that algorithm is accurate, it would seem that maybe a stronger key derivation function might be necessary (e.g. PBKDF2?). Of course, a customized KDF kills my openssl compatibility (at least makes it more involved), but I'd rather see the private keys be secure and annoying than insecure and easy. :-)
Basically, what I'm looking for here is information on where I might have gone wrong and, with luck, a pointer to where I might find more information on the algorithm(s) used in OpenSSL's RSA private key encryption mechanism, specifically the key derivation function used.
PS - Please forgive me if I said anything really stupid there. I'm very new to cryptography.
PPS - I'm using the following version of OpenSSL (in case it matters): OpenSSL 1.0.0c-fips 2 Dec 2010
Well, in your example you converted the salt from hex whereas createKeyAndIv() needs the salt in
hex representation, so don't convert!
The result of that will be an ASN.1 structure which you'll have to parse in order to get to the private key properties (see https://www.pidder.com/pidcrypt/?page=asn1).
So a working modification of your code example looks like this:
Sorry about that, it seems the forum covering the topcic that we linked to is gone.
However, a working link is http://www.jensign.com/JavaScience/dotnet/DeriveKeyM/
OpenSSL is one of the most popular tools for cryptographic funtions, and their project is very well maintained. That is also the reason why we decided to make our library compatible.
So rather than trying to implement something that breaks compatibility with OpenSSL we advise getting involved with that project if you think something should be improved - but that's beyond the scope of our little project :)
should read
Thank you for the fast reply!
I will test this out as soon as I get a chanceIt works like a charm! You should probably change the example code on the website, though, as it implies you do need to convert from hex to a byte array:Thanks, again!
Your library is great!
Ooops! That example was outdated. The salt is now converted within createKeyAndIv(params). I have changed the example code.
So it was our fault all along … ;)
No worries. It gave me a good excuse to spend about 3 hours last night researching the KDF used by OpenSSL. I'm still not 100% convinced that the KDF is secure enough for our ultimate needs, but it should be good enough for this project. If I continue work on the project after this semester, I'll probably do more research on the subject.
I don't think it's a matter of poor security in OpenSSL. I imagine it was a decision between high strength encryption and compliance with export laws (stupid export laws - ugh) and legacy compatibility (older systems may not be able to handle high strength KDFs in a timely fashion). As long as you're using a high strength passphrase, pretty much any KDF should be fine. But since our project targets "normal" users who might use weak passwords, a stronger KDF would offer those users more security. So essentially, it's not that OpenSSL is doing anything "wrong," it's just that they designed this system with different priorities in mind.
Whatever the case, it's been far too long since I took the kind of math classes I need to understand this stuff properly. I fear that I'm going to find myself back in a math classroom before I finish this degree… ;-)