I started developing an app in node.js for the german Sparkasse (banking provider).
They provide an API to their service.
German Link to their API
in the secondstep I get a RSA-2048-SHA1 publickey from their server.
in the fourth step, I should use that public key, to encrypt a session key created in step 3. but when encrypting with:
var key = 'password';
var sha256 = crypto.createHash('sha256');
sha256.update(key);
var iv = new Buffer([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
var cipher = crypto.createCipheriv('aes-256-cbc', sha256.digest(), iv);
let sessionKey = cipher.final('base64')
let publicKey = response.publicKey.value
crypto.publicEncrypt(publicKey,new Buffer(sessionKey))
I get the following Error message, that I think is from OpenSSL. I don't really know how to fix that issue as I cannot evaluate if the publicKey I get from the API is valid.
Error: error:0906D06C:PEM routines:PEM_read_bio:no start line
The public key you received is in the wrong format. It needs to be a "PEM" formatted key. You'll have to convert it by wrapping it in "BEGIN ..." and "END..." lines, and possibly splitting into 64 character lines.
Here is an example of a properly formatted public key:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAytBCDJR5/6JAlB7ErBge
22YwN/u0K63wrnCMLde+hQQCYs7pBuYtbyxXF2PBFuHS+ytD9PSpY9t3NiGbk/9U
s9GYCnqaK+vg2hz+T86LjJVkTJe0HWuE6g+HQ9GjyDGiO7ZBQw31HKxHYA2cMMVj
tiO97VKLR9Fp6c6X33uNtdAaUZg57PjyNl6TjPwc52tJz8H5g0aV4tYelsTMaSSE
4nVwPLBoDzZaT84ktW1RuGToC4gEB/bctFrRBVaxp/KSebpds9P2xGMVweWgrvml
cLnHGLKBxcCxh9kbgHS/nrgYXPjj92hxk2se/C7QmYeRSUs4ikEWO06NJ7Cgk+bQ
8wIDAQAB
-----END PUBLIC KEY-----
on each new line start you shuould put : /n
if you keep it as string on config file , on windows you should save it like that:
"JWTPRIVATE": "-----BEGIN RSA PRIVATE KEY-----\nMIIJKAIBAAKCA...
just to show you how it looks before i deleted the spaces:
"-----BEGIN PUBLIC KEY-----/n
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAytBCDJR5/6JAlB7ErBge/n
22YwN/u0K63wrnCMLde+hQQCYs7pBuYtbyxXF2PBFuHS+ytD9PSpY9t3NiGbk/9U/n
..."
for new line as you can see here: Why does "\n" give a new line on Windows?
Related
I have some public key which looks like MIIBIjANBgkqhkiG9w0BAQEFAAO... (392 chars).
It used in the browser to encrypt some strings with JSEncrypt.
How can I encrypt strings with that public key using NodeJS crypto module?
I tried this:
const crypto = require('crypto')
const encrypted = crypto.publicEncrypt('MIIBIjA....', '111111')
console.log(encrypted.toString('base64'))
But got error:0909006C:PEM routines:get_name:no start line.
I also tried to convert the public key and the string into the buffer, got the same error.
If the key in crypto.publicEncrypt(key, buffer) is passed as a string, then it is interpreted as PEM encoded key by default.
A PEM encoded key consists of a header line, followed by the Base64 encoded body (i.e. the Base64 encoded data of the DER encoded key), and a footer line.
In the posted code snippet, the header line is missing (and presumably the footer line as well), which causes the error.
crypto.publicEncrypt(key, buffer) uses OAEP as padding by default. JSEncrypt on the other hand only supports PKCS#1 v1.5 padding. So if the implementation should be compatible to JSEncrypt, i.e. if the ciphertext should be decryptable with JSEncrypt, then PKCS#1 v1.5 padding must be specified explicitly.
The following NodeJS code encrypts a message with the crypto module and decrypts the ciphertext with JSEncrypt:
const crypto = require('crypto')
const { JSEncrypt } = require('js-encrypt')
const publicKey = `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNvs/qUMjkfq2E9o0qn03+KJE7
ASczEbn6q+kkthNBdmTsskikWsykpDPnLWhAVkmjz4alQyqw+mHYP9xhx8qUC4A3
tXY0ObxANUUKhUvR7zNj4vk4t8F2nP3erWvaG8J+sN3Ubr40ZYIYLS6UHYRFrqRD
CDhUtyjwERlz8KhLyQIDAQAB
-----END PUBLIC KEY-----`
const privateKey = `-----BEGIN PRIVATE KEY-----
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAM2+z+pQyOR+rYT2
jSqfTf4okTsBJzMRufqr6SS2E0F2ZOyySKRazKSkM+ctaEBWSaPPhqVDKrD6Ydg/
3GHHypQLgDe1djQ5vEA1RQqFS9HvM2Pi+Ti3wXac/d6ta9obwn6w3dRuvjRlghgt
LpQdhEWupEMIOFS3KPARGXPwqEvJAgMBAAECgYADxGqqL7B9/pPOy3TqQuB6tuNx
4SOGm9x76onqUisoF7LhYqJR4Be/LAKHSR2PkATpKvOcMw6lDvCbtQ+j+rSK2PkN
4iDi1RYqbLUbZBS8vhrgU0CPlmgSSp1NBsqMK9265CaJox3frxmBK1yuf22RboIK
pqOzcluuA4aqLegmwQJBAP0+gM/tePzx+53DrxpYQvlfi9UJo7KeqIFL8TjMziKt
EaRGeOZ6UX/r6CQHojYKnNti7pjAwonsdwCTcv1yy7sCQQDP+/ww49VFHErON/MO
w5iYCsrM5Lx+Yc2JAjetCDpkMrRT92cgQ0nxR5+jNeh+gE2AmB9iKlNxsHJoRaPQ
lBRLAkEAl9hiZEp/wStXM8GhvKovfldMAPFGtlNrthtTCDvFXgVoDpgy5f9x3sIU
74WkPcMfSmyHpA/wlcKzmCTRTicHAQJBALUjq7MQ2tAEIgqUo/W52I6i55mnpZsU
pyOqcL8cqW5W0sNGd+SbdizTym8lJkX2jIlw8/RVFLOxjxLNhCzGqx0CQQDeUMnw
7KGP3F7BnbsXCp64YDdihzSO5X/Mfwxw6+S/pyKZ0/X4uwt24kZuoDnFzGWJYlea
sDQC6enIru+ne5es
-----END PRIVATE KEY-----`
// Encrypt with crypto module (RSA - PKCS#1 v1.5 Padding)
var ciphertext = crypto.publicEncrypt(
{
key: publicKey,
padding: crypto.constants.RSA_PKCS1_PADDING
},
Buffer.from('The quick brown fox jumps over the lazy dog','utf8')
)
console.log(ciphertext.toString('base64'))
// Decrypt with JSEncrypt
var decrypt = new JSEncrypt()
decrypt.setPrivateKey(privateKey)
var decrypted = decrypt.decrypt(ciphertext.toString('base64'))
console.log(decrypted) // The quick brown fox jumps over the lazy dog
I'm trying to send a file to some server using SFTP. During this process I'm getting the exception
Renci.SshNet.Common.SshException: Invalid private key file. at Renci.SshNet.PrivateKeyFile.Open(Stream privateKey, String passPhrase)
Generated the keys using PuTTYgen, shown below is an sample format of private key file. It has both the public and the private keys.
PuTTY-User-Key-File-2: ssh-rsa
Encryption:none
comment: rsa-key-20190327
Public-Lines: 4
AAAAB.....
......
Private-Lines: 8
AAAAgQ......
.......
Private-MAC: 54901783....
I copied the private key part from the above file in the config file and I'm accessing it as SftpKey in my code.
Got an OpenSSH format of the above key which looks like
------BEGIN RSA PRIVATE KEY-----
MIIE....
.......
------END RSA PRIVATE KEY-------
I copied only the key part from the above file and copied in my config file and ran my code. Issue was not resolved.
Below is the code i'm using for SFTP upload
var fileLength = data.Length;
var keyStr = ConfigurationManager.ConnectionStrings["SftpKey"].ConnectionString;
using (var keystrm = new MemoryStream(Convert.FromBase64String(keyStr)))
{
var privateKey = new PrivateKeyFile(keystrm);
using (var ftp = new SftpClient(_ftpServer, _ftpUser, new[] { privateKey }))
{
ftp.ErrorOccurred += ErrorOccurred;
ftp.Connect();
ftp.ChangeDirectory(_ftpPath);
using (var dataStream = new MemoryStream(Encoding.UTF8.GetBytes(data)))
{
ftp.UploadFile(dataStream, Path.GetFileName(message.MessageId), true,
(length) => result = fileLength == (int)length);
}
ftp.Disconnect();
}
}
Is there anything wrong with the code or what could be the issue? Any help is much appreciated.
As this is the top answer for that error message, so I think it's worthwhile expanding on a point in your original question - converting to an OpenSSH format key.
Renci.SshNet can't use PuTTY keys that start with:
PuTTY-User-Key-File-2: ssh-rsa
You can use puttygen.exe to convert to the OpenSSH format
load your key file in puttygen.exe
Conversions > Export OpenSSH key (not the "force new file format" option)
This will make a key that starts with:
-----BEGIN RSA PRIVATE KEY-----
and that will work
I copied only the key part from the above file
You need to have complete key file in the MemoryStream. And exactly as in the file (as if you were using FileStream with your text key file). So no Convert.FromBase64String.
var keyStr = #"-----BEGIN RSA PRIVATE KEY-----
MIIEoQIBAAKCAQEAiCYlBq7NITBpCCe48asfXKMpnJJJK+7FQj6wIRJCNuBk76tL
jNooDDPPrnrE9VKxRds4olPftjRj87s9gjm4EirbvijZ9PoDlW9CWFhjJPwCPJpA
onkhaiA7SV+abRDQHm/lst5Fk9tzl+DZcS/EleilGDV7rCYEP692UJRsi3GvzngQ
dpRvVvO4o2rXnEkdp+254KHsah0pSxri23+jqbxPguHKGIMylrswokMI0QKcfm+1
/pjrV64EQCxli3i2yPl4WVh/QaNyHMKoze/WN00Pia99QhE1Rm3YCCarFWFeX+R5
7LgIUhtrE7vZGvimfZN7oBdR2pEq10PIc+8q9QIBJQKCAQAWFAYBFW1fU/VbRLY1
Bv4qsqzNSCeKlWwYlItDohiTRvucfKR3tKyMW23JRFdKYG/GI4yks6e8roy/vX+Y
k7z8BvMzl+v+NmFyLbe7TJp0sz6iCy0TbZa3Q388VLFCHmbwLdI4rmwl0I9JD7SO
5SbMM5BkymcU/z71khMvqV21vym5Ge/ApvX0K0XNJs/N/OLnX46Z8taYEyTmreSR
rxAbma4I5BhqXbH0CMOI5u8zCyycghytl5sYyMr+LIWQKWLzQU+mPNN0qIy0pO5t
r8lGNJh5Lnmu1lQw9yAGo2IPPIERP90X64pVrteIjPtt30n87bWDS8gOiam8S/qk
2ZJVAoGBAPZi6E/KpYpzYGKPAfialu0QN1X7uFio1MUmDum+phk5+xeQb/VvlP6Y
d+/o03EMnhvUsop9p7E2CwLZfT6DO7x3LKtumfceq5dPE5hQSWXi9RkBhcOJaZvZ
z+36c8N8iSZZzlxdA5TeDTUqtuVli4HLrcsXaAaVMxEr/G2JwUgTAoGBAI12Gnoy
k/gsiHz4pDLgxWQE6R8vkBMXfQCWhkzvzKca4twQ8z4ZAb/yt+BCiioJn5g58CVS
dP2zd3Lx8e9kkxggZLcUR3Ao6HceYKeD6mx4vkpHiyCtKJI+qfnkw2A64xwbtvTR
h/O5Aq90SjqP4YcaK9E0W/mWYoL3ctFG8DHXAoGBAKZ6LkPARlag+++RDytvXw7h
cX9JN15/6bWkF+oLMfVehw/r+J7qh0Q9gXiWZVo49TVmM3JU5u1b3e0rKxxmgk7o
vVE85JI3UVhl3M6yyc84fBfQmKa2ytEWoT/uaeTzR+l68zd9Hhh6W/N9udlEnIgh
1kr0I7FruriTV4hIUinHAoGASKRudhn49Q/zD73zdBKO4FWMd8xQ5zWTN6c+C9UW
EJ8ajK7CGPgVp8HUC2BwdnOk+ySrwCNsgkdm2ik3DDqQuVy+GNMP7XzKZq68Av6N
IvHlLQ/7VfgN6jvavpgRTRdSB4Pafbe0hBLltAtItknig6WnzEtR0zGMiHE69dhR
1GcCgYBckoyMXpT0HzOjLXWClSiIaDDfgGcmgEKbYJ7c3mncjLinbCVFdJ0UcrqJ
tiauWBvmecAhnJvQGnmInawNUHetAgJoCbqd7cckjI8VtBgHlQyT93wo9fSDz0Kt
dDHspRvVQkhiR/6IWz1PtCT0QGrHP8fJq/PCbLnJf/EJqJv/xA==
-----END RSA PRIVATE KEY-----
";
using (var keystrm = new MemoryStream(Encoding.ASCII.GetBytes(keyStr)))
{
var privateKey = new PrivateKeyFile(keystrm);
}
Actually with the earlier code i was showing there is an existing implementation where the file is successfully sent.
Then your connection string does not contain what you claim. Check the implementation of PrivateKeyFile.Open. It explicitly checks that the stream starts with ---- BEGIN ... PRIVATE KEY. If not, it throws "Invalid private key file."
It's actually probably even impossible (or difficult) to store multi-line contents into the connection string. If your code ever worked, it must be because your SftpKey connection strings contain a complete key file (including the BEGIN ... PRIVATE KEY envelope) but encoded (again) in Base64 encoding (as a single line). Like this:
Convert.ToBase64String(File.ReadAllBytes(#"C:\path\to\key"))
Which will give you string like:
LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQ0KTUlJRW9RSUJBQUtDQVFFQWlDWWxCcTdOSVRCcENDZTQ4YXNmWEtNcG5KSkpLKzdGUWo2d0lSSkNOdUJrNzZ0TA0Kak5vb0REUFBybnJFOVZLeFJkczRvbFBmdGpSajg3czlnam00RWlyYnZpalo5UG9EbFc5Q1dGaGpKUHdDUEpwQQ0Kb25raGFpQTdTVithYlJEUUhtL2xzdDVGazl0emwrRFpjUy9FbGVpbEdEVjdyQ1lFUDY5MlVKUnNpM0d2em5nUQ0KZHBSdlZ2TzRvMnJYbkVrZHArMjU0S0hzYWgwcFN4cmkyMytqcWJ4UGd1SEtHSU15bHJzd29rTUkwUUtjZm0rMQ0KL3BqclY2NEVRQ3hsaTNpMnlQbDRXVmgvUWFOeUhNS296ZS9XTjAwUGlhOTlRaEUxUm0zWUNDYXJGV0ZlWCtSNQ0KN0xnSVVodHJFN3ZaR3ZpbWZaTjdvQmRSMnBFcTEwUEljKzhxOVFJQkpRS0NBUUFXRkFZQkZXMWZVL1ZiUkxZMQ0KQnY0cXNxek5TQ2VLbFd3WWxJdERvaGlUUnZ1Y2ZLUjN0S3lNVzIzSlJGZEtZRy9HSTR5a3M2ZThyb3kvdlgrWQ0Kazd6OEJ2TXpsK3YrTm1GeUxiZTdUSnAwc3o2aUN5MFRiWmEzUTM4OFZMRkNIbWJ3TGRJNHJtd2wwSTlKRDdTTw0KNVNiTU01Qmt5bWNVL3o3MWtoTXZxVjIxdnltNUdlL0FwdlgwSzBYTkpzL04vT0xuWDQ2Wjh0YVlFeVRtcmVTUg0KcnhBYm1hNEk1QmhxWGJIMENNT0k1dTh6Q3l5Y2doeXRsNXNZeU1yK0xJV1FLV0x6UVUrbVBOTjBxSXkwcE81dA0KcjhsR05KaDVMbm11MWxRdzl5QUdvMklQUElFUlA5MFg2NHBWcnRlSWpQdHQzMG44N2JXRFM4Z09pYW04Uy9xaw0KMlpKVkFvR0JBUFppNkUvS3BZcHpZR0tQQWZpYWx1MFFOMVg3dUZpbzFNVW1EdW0rcGhrNSt4ZVFiL1Z2bFA2WQ0KZCsvbzAzRU1uaHZVc29wOXA3RTJDd0xaZlQ2RE83eDNMS3R1bWZjZXE1ZFBFNWhRU1dYaTlSa0JoY09KYVp2Wg0KeiszNmM4TjhpU1paemx4ZEE1VGVEVFVxdHVWbGk0SExyY3NYYUFhVk14RXIvRzJKd1VnVEFvR0JBSTEyR25veQ0Kay9nc2lIejRwRExneFdRRTZSOHZrQk1YZlFDV2hrenZ6S2NhNHR3UTh6NFpBYi95dCtCQ2lpb0puNWc1OENWUw0KZFAyemQzTHg4ZTlra3hnZ1pMY1VSM0FvNkhjZVlLZUQ2bXg0dmtwSGl5Q3RLSkkrcWZua3cyQTY0eHdidHZUUg0KaC9PNUFxOTBTanFQNFljYUs5RTBXL21XWW9MM2N0Rkc4REhYQW9HQkFLWjZMa1BBUmxhZysrK1JEeXR2WHc3aA0KY1g5Sk4xNS82YldrRitvTE1mVmVody9yK0o3cWgwUTlnWGlXWlZvNDlUVm1NM0pVNXUxYjNlMHJLeHhtZ2s3bw0KdlZFODVKSTNVVmhsM002eXljODRmQmZRbUthMnl0RVdvVC91YWVUelIrbDY4emQ5SGhoNlcvTjl1ZGxFbklnaA0KMWtyMEk3RnJ1cmlUVjRoSVVpbkhBb0dBU0tSdWRobjQ5US96RDczemRCS080RldNZDh4UTV6V1RONmMrQzlVVw0KRUo4YWpLN0NHUGdWcDhIVUMyQndkbk9rK3lTcndDTnNna2RtMmlrM0REcVF1VnkrR05NUDdYektacTY4QXY2Tg0KSXZIbExRLzdWZmdONmp2YXZwZ1JUUmRTQjRQYWZiZTBoQkxsdEF0SXRrbmlnNlduekV0UjB6R01pSEU2OWRoUg0KMUdjQ2dZQmNrb3lNWHBUMEh6T2pMWFdDbFNpSWFERGZnR2NtZ0VLYllKN2MzbW5jakxpbmJDVkZkSjBVY3JxSg0KdGlhdVdCdm1lY0Fobkp2UUdubUluYXdOVUhldEFnSm9DYnFkN2Nja2pJOFZ0QmdIbFF5VDkzd285ZlNEejBLdA0KZERIc3BSdlZRa2hpUi82SVd6MVB0Q1QwUUdySFA4ZkpxL1BDYkxuSmYvRUpxSnYveEE9PQ0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0NCg==
The same error can also occur if the private key is incorrectly formatted such as being squashed onto a single line whereby it will not match the regular expression at https://github.com/sshnet/SSH.NET/blob/develop/src/Renci.SshNet/PrivateKeyFile.cs#L156
The private key format must be over multiple lines wrapping at 80 cols.
https://github.com/sshnet/SSH.NET/blob/develop/src/Renci.SshNet/PrivateKeyFile.cs#L68 - the expression contains "{1,80}".
In my case the private key is not protected with a passphrase so I was able to use the following code to correctly reformat it; reintroducing the newlines; before constructing the memory stream.
// PEM Format Private Key substituting newlines with a space
var privateKeyString = #"-----BEGIN RSA PRIVATE KEY----- line1 line2 line3 -----END RSA PRIVATE KEY-----";
// Group 1: "-----BEGIN RSA PRIVATE KEY-----"
// Group 2: " line1 line2 line3 "
// Group 3: "-----END RSA PRIVATE KEY-----"
var regex = new Regex(#"^\s*(-+[^-]+-+)([^-]+)(-+[^-]+-+)");
var matches = regex.Match(privateKeyString);
var formatted = string.Concat(
matches.Groups[1].Value,
matches.Groups[2].Value.Replace(" ", "\r\n"),
matches.Groups[3].Value);
// ASCII encoding is fine because we're dealing with the base64 alphabet.
var ms = new MemoryStream(Encoding.ASCII.GetBytes(formatted));
var privateKeyFile = new PrivateKeyFile(ms);
I wanted to encrypt and decrypt a message in node using public and private keys stored in my system. I was using the following java code to read the file and use the keys.
Java Code:
byte[] keyBytes = Files.readAllBytes(new File(publicKeyFileName).toPath());
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
publicKey=kf.generatePublic(spec);
I am able to use the above java method without any issues to read the public key from file. However, I want to achieve similar functionality in node.
I have tried using crypto for achieving the same but it gives me error while passing the key to publicEncrypt method.
Node:
var encryptStringWithRsaPublicKey = function(toEncrypt, relativeOrAbsolutePathToPublicKey) {
var absolutePath = path.resolve(relativeOrAbsolutePathToPublicKey);
var publicKey = fs.read(absolutepath, "utf-8");
console.log(publicKey);
var buffer = Buffer.from(toEncrypt);
var encrypted = crypto.publicEncrypt(publicKey, buffer);
return encrypted.toString("base64");
};
Error
internal/crypto/cipher.js:43
return method(toBuf(key), buffer, padding, passphrase);
^
Error: error:0906D06C:PEM routines:PEM_read_bio:no start line
Please help. Thanks
Your problem is located in the file format you are actually using with Java. You probably save the
private and the public in encoded ("byte array") to a file and rebuild the keys e.g. with
X509EncodedKeySpec.
This format is not compatible to Node.JS and you have 3 ways to solve it:
a) you write the keys in Java with re neccessary format for usage in Node.JS
b) you write a converter in Node.JS to get the correct format
c) you convert the files with a tool like OPENSSL.
Here I show you the "c-way" as you are handling just one keypair and probably don't need a programatically solution.
Let's say you have two files with the private key ("rsa_privatekey_2048.der") and the public key ("rsa_publickey_2048.der").
In OPENSSL you are using the command line with
openssl rsa -inform der -in rsa_privatekey_2048.der -outform pem -out rsa_privatekey_2048.pem
openssl rsa -inform der -pubin -in rsa_publickey_2048.der -outform pem -RSAPublicKey_out -out rsa_publickey_2048.pem
to convert the files to their PEM-encoded formats.
Below you can find the two sample files I created.
rsa_privatekey_2048.pem:
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAmbeKgSAwVe0nZ84XlbDhMkUDjx1C0duA16MkzHTg1uh9SouO
KK0e3gPtTJ9LssaHlXSYhjpMDMWGO6ujd85XRosI2u9eSMNRYY25AQuBriSTVdi9
BHqWAuWuo6VuvTrkgWTL69vNWvLXTOkTiIyrgnhiavjNvm4UVy2AcO2Y3ER+dKgJ
pQAYlEP1jvuQuf6dfNdSBoN0DZbxZXYbQqoA9R/u0GZHCXY+r8A54RejG34pnnuH
koyROZz5H9LbKGOiaETryornQ1TRvB/p9tgIoCJFI71WsKsqeWQPG3Ymg/FoEWXN
Y0yopZEjpkZa3tU+hrOmAFIRg+/bedKfjYFi/QIDAQABAoIBAD3XZ3N3fbq0BExw
z3A7jv3oYfwrq3w+MOGQEvfmdaZANlfNOU4ICAkNz2QqGgw8bsOj+tDVl070EILl
FIjYjKgmu1NJRcdEPPNgTvOqq2th75xz6+dnYf6cZNwVbC3ZCaE86gVjkoRqek/I
3UDsRvvgbsfWfP+Fzc0c0zWbgQnsK6qivU1uzJX+5xsvgQlZboeZOO2lsdQMgfnu
iGlW1bVVM4Sy7AngqfiKMzihUnYEBIi0Y+mfxAPcBLUW8mrOvIOPPuNNUPxUtkBF
bDEzZ6loXCLLD8UBqXeDbCUPPFdTGcc7INhVgFdl2FL6rHB0+p6eUt8MI/XkZI2d
2AnkBUkCgYEA34cKLs2l5bPjyKybbj6ZG7RhDRsnPypEGU63DMV21twISqt7ZQNv
i3iTP+FYHM3ImECbNRIOZpyLuWLPmh5+5egQH13jRDempoxVSVcghbIserlCz2EU
nD2V6ZKuaDbn395O6Qe/PE/yKHLWbXwJrBBm+o7GGNm/Jd3KJib23PcCgYEAsAxB
esEsxxL8hqg/qf+ij7JJt492svpK/6QXhqeeP/FVOtYSgoNtSrXh9qahGpzMSF0m
FqwIgrOX0RkK3v6ofGVfIKObxOVyhwddS1Ru+SnjBFnTMKS57q0WNrIrBNM6Q0xE
Wd3tiljwmg8xF90U/BXu+m0v5XWKxSn7VLiCBqsCgYEAgl0xtSY/EP6fZJQ2ek+L
4DqNN6WUeCRgXxonbA1mR91AALyOVNVyIreJuYHlb7ccvJ9BZexH9dRrMQ3N4ibS
/6cecAzD1S9XxF6oBwQHdbH6ewC9VFFcQdsxKW5gxWrwRQJUp1fbUoOVyb1gDa5/
vZg7VvoZ0rh74Mu/cAzdgPUCgYEAiNANdwt29AKyUyefykpLGCczGL8aPP88l6z7
R38uAX1Ygg/pdJoUrnHo+FkIbHkcXMRfHFF3j7NoMWynwSLg50OUiPX80SiLN5qm
iytDzskZjsEL2gq6IF1NHRabTfWlmrVDjR9mQhTabq+NtIDwlPOqs9100nrlbFIy
6uU0z18CgYEAkDxQg5UjOzbYIighp/e0LkfKlwUE/cMtISUq1A6Yio9AKZYdBV8p
dd4osUW0gZvDflXlQaBIaNlOwl035lyEZtyuKmFh1oSmoir/TTMbIk0avSgKCLGg
fnhabaQRHl4RdXWcEsioRv3aZUsGkb46Y8xODyAUPRHBPhBsd71gnZ8=
-----END RSA PRIVATE KEY-----
rsa_publickey_2048.pem:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAmbeKgSAwVe0nZ84XlbDhMkUDjx1C0duA16MkzHTg1uh9SouOKK0e
3gPtTJ9LssaHlXSYhjpMDMWGO6ujd85XRosI2u9eSMNRYY25AQuBriSTVdi9BHqW
AuWuo6VuvTrkgWTL69vNWvLXTOkTiIyrgnhiavjNvm4UVy2AcO2Y3ER+dKgJpQAY
lEP1jvuQuf6dfNdSBoN0DZbxZXYbQqoA9R/u0GZHCXY+r8A54RejG34pnnuHkoyR
OZz5H9LbKGOiaETryornQ1TRvB/p9tgIoCJFI71WsKsqeWQPG3Ymg/FoEWXNY0yo
pZEjpkZa3tU+hrOmAFIRg+/bedKfjYFi/QIDAQAB
-----END RSA PUBLIC KEY-----
There's potentially a few issues with your code or the encryption key you're using:
You're using fs.read incorrectly as Node is asynchronous and it needs a callback function to properly read the file.
The encryption key you're using is formatted incorrectly for crypto.publicEncrypt. You must have the proper RSA headers.
I modified your code to use fs.readFile properly instead in the standard Node callback form, and here's an example encryption key in the correct RSA format to use:
var path = require('path');
var crypto = require('crypto');
var fs = require('fs');
var encryptStringWithRsaPublicKey = function(toEncrypt, relativeOrAbsolutePathToPublicKey, callback) {
var absolutePath = path.resolve(relativeOrAbsolutePathToPublicKey);
fs.readFile(absolutePath, 'utf-8', (err, publicKey) => {
// The value of `publicKey` is in the callback, not the return value
console.log(publicKey);
var buffer = Buffer.from(toEncrypt);
var encrypted = crypto.publicEncrypt(publicKey, buffer);
if (err) {
callback(err);
} else {
callback(null, encrypted.toString("base64"));
}
});
};
encryptStringWithRsaPublicKey('hello world', 'test.pub', (err, encrypted) => {
// If you're using a callback in a function,
// the original function must have a callback as well
console.log(encrypted);
});
Example encryption key at test.pub (must have the RSA headers as shown below):
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA+xGZ/wcz9ugFpP07Nspo6U17l0YhFiFpxxU4pTk3Lifz9R3zsIsu
ERwta7+fWIfxOo208ett/jhskiVodSEt3QBGh4XBipyWopKwZ93HHaDVZAALi/2A
+xTBtWdEo7XGUujKDvC2/aZKukfjpOiUI8AhLAfjmlcD/UZ1QPh0mHsglRNCmpCw
mwSXA9VNmhz+PiB+Dml4WWnKW/VHo2ujTXxq7+efMU4H2fny3Se3KYOsFPFGZ1TN
QSYlFuShWrHPtiLmUdPoP6CV2mML1tk+l7DIIqXrQhLUKDACeM5roMx0kLhUWB8P
+0uj1CNlNN4JRZlC7xFfqiMbFRU9Z4N6YwIDAQAB
-----END RSA PUBLIC KEY-----
As of 2020, there are also other ways of making the code cleaner, such as with using the Promises version of the fs module and async / await, though I wanted to keep this answer as simple as possible for now.
I have the following problem:
After recreating the public key from a signed transaction, I try to encrypt some payload with it.
However the node.js-module named "crypto" is expecting a pem-formatted key in the publicEncrypt(key, payload) function.
My Question:
How can I create the pem-formatted key from a raw hex-encoded key?
Here is the recreated 64 Byte public key:
9f9f445051e788461952124dc08647035c0b31d51f6b4653485723f04c9837adb275d41731309f6125c14ea1546d86a27158eec4164c00bab4724eed925e9c60
Information:
I know, that a pem-format-key consists of base64 encoded data, a header and a footer.
-----BEGIN RSA PUBLIC KEY-----
BASE64 ENCODED DATA
-----END RSA PUBLIC KEY-----
I have also found out that within the base64 encoded data the following DER-structure is present:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
So the only question is how to get from the raw hex-encoded key to this DER-structure.
I would appreciate any help!
Problem solved
Thanks to Maarten Bodewes and his comment regarding the key being secp256k1 and not RSA.
After some further research, I finally managed to encrypt/decrypt a message asymmetrically with secp256k1 keys.
With the help of Cryptos ECDH class I managed to create a key-object and then assign the private key to it. When assigned, you can easily derive the public key with getPublicKey(). All participants would create a key object for themselves and assign their private keys to it. Then they share their retrieved public keys (in my case over a shared medium). In addition I used a npm-package named standard-ecies which provides the ECIES encryption-scheme.
Code:
const crypto = require('crypto');
const ecies = require('standard-ecies');
var buffer = new Buffer("Hello World");
var ecdh = crypto.createECDH('secp256k1');
ecdh.setPrivateKey(privateKey);
var encryptedText = ecies.encrypt(ecdh.getPublicKey(), buffer);
var decryptedText = new Buffer(ecies.decrypt(ecdh, encryptedText));
I should have noticed this, because crypto's encryption function (link to the api-doc) clearly works only with RSA keys and not with secp256k1 keys.
Anyway if someone has a similar issue, I hope this answer helps!
I am trying to load the Private and Public keys from a PEM file using .Net Core.
My code looks like this:
var localPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
var path = Path.Combine(localPath, this._configManager.JwtPem);
var rsaCryptoServiceProvider = new RSACryptoServiceProvider();
var linesList = File.ReadAllLines(path).ToList();
var line = string.Concat(linesList.GetRange(1, linesList.Count - 2));
rsaCryptoServiceProvider.ImportCspBlob(Convert.FromBase64String(line));
The exception I am getting is:
Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException : Bad Version of provider
at Internal.NativeCrypto.CapiHelper.ImportKeyBlob(SafeProvHandle saveProvHandle, CspProviderFlags flags, Boolean addNoSaltFlag, Byte[] keyBlob, SafeKeyHandle& safeKeyHandle)
at System.Security.Cryptography.RSACryptoServiceProvider.ImportCspBlob(Byte[] keyBlob)
at StepNexusCA.ServiceLayer.Authorization.TokenService.GenerateToken(List`1 claims)
The PEM file containing the PKCS1 format of my development Private/Public keys is here:
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAwgs8kmIwk+4geRO7dGZjzYpgD2OiaUrnOOIk+ObXt/CcjhwX
lSst+jBmfMF1Wp/mF4aUQsePxN59MYV2BsqPLEkzVdq/fb/7V2wbZcooJAQKkJwT
emtYHrBN00KBBeu9uQZlFOw365ij4GrbP7mcr4tNFZ3TPnRFUUFqhvB6mEG1aZsb
lOn1lgL34tAycQHNxttXz/aGfPyTefQ+yISvSY2n8288OVlyfu6wKDONQYS+/stC
tCV+a+/dDUSUjaZsXM1+BMSflsINqIcCTCMvPa6fb5Z+USfPDcDNwzUyX20LBzH5
wFwPLIvuoqJeeczcaHaT+dS2ZZREj6kgUsdC+QIBAwKCAQEAgVzTDEF1t/QVpg0n
ou7tM7GVX5fBm4dE0JbDUJnlJUsTCWgPuMdz/CBEUyujkb/uulm4LIUKgz7+IQOk
BIcKHYYiOTx/qSqnj51nmTFwGAKxtb1iUZzlacrejNcArp0pJgRDY0glR7sX6vHn
f9ETH7IzY76M1E2Di4Dxr0r8ZX/3ozsrSXp+GMJLeN9sCjKSyxoE5Y71eDBTCX2N
tShJJjhqUDz61bhKlX9j5c3jWvTXx46dE8wjoJ/BW1XJo5J1gzHQ/OLYeOXIdxlj
jVSlEuU69UT588B7UEEK9N9xK5K/c0Yw5gd02RUv/o7qdpYQICeGtQMMaFkm75xy
nUOxwwKBgQD/orUvgNJfFKyvGY8XJTuek5q8IcFD8AFO3b7pNnPynw8llyEpACAv
Onf9aJSPZvtrabSqrpO8k8Ijyhe2Ino39GuRV8RURl46GmFN31RoYV1wHI4K7Emh
68cdKbCEBudog+kImImldBAfo+QmBtqhS+u4B5qQwwnFa8DriQoiYwKBgQDCUg0r
Jd/ZXDLXk/H5PHpTApmUVd7SWLLIDfkBAlRO8Sni4/Ka+KTTZDec5uoo0hoP6cCs
Z9+MZz4XOiwv9dCEI5czMawGmwsm23+fGM/PP/lW4yD8dz10KZggKjWElymDVl+n
zsc6ctwHAOfYwREi7E+R4rWTBgTEvH2I3deV8wKBgQCqbHjKVeGUuHMfZl9kw30U
YmcoFoDX9VY0k9SbeaKhv19uZMDGABV00aVTmw2071JHm83HHw0oYoFtMWUkFvwl
TZ0Lj9g4Lul8EZYz6jhFlj5KvbQHSDEWnS9oxnWtWe+bAptbEFvDorVqbULEBJHA
3UfQBRG111vY8oCdBgbBlwKBgQCBjAjHbpU7ksyPt/amKFGMrGZi4+nhkHcwCVCr
VuLfS3FB7UxnUG3iQs+970bF4Wa1RoBy7+pdmilk0XLKo+BYF7oiIR1ZvLIZ56pq
EIqKKqY57MCoT35NcRAVcXkDD3ECOZUaidom9z1aAJqQgLYXSDUL7HkMrq3YfakF
6Tpj9wKBgEPCSW7EMFjK2NzmB+4b+skxXcfCZ0ldNtwoUDijuAMFg8ueC3j2qFUX
bAXSApi3mQMow1/JwQxiZ+b+GDLdTcE/PrBVBRkL/5RkmnVagbjBrdZhVjpC+dUo
eEkCChClGGpRyPJ+DYYRyX1Fk9Und8Xbd49Vv+/6RL76ys3gGQl8
-----END RSA PRIVATE KEY-----
Why can't I import the key using ImportCspBlob(...)? I have not found much info online regarding the exception but where is my code wrong? I am aware of BouncyCastle but I am trying to do this natively using .Net Core.
The format for ImportCspBlob is the format from ExportCspBlob, which is the PRIVATEKEY blob format required by CryptImportKey. Since .NET just transparently passes that on to Windows CAPI, the ImportCspBlob method throws on non-Windows platforms.
Another answer that I've given in the past for importing private keys (including PKCS#1 RSAPrivateKey) is a bit of a meta-answer, which includes links to just get things working: Digital signature in c# without using BouncyCastle.
.NET Core 3.0's daily builds have the functionality built-in. Mostly. The PEM format is easy in practice, but somewhat annoying in the spec, so the methods leave it up to the caller to "un-PEM" the data... for the default formatting on a single-value payload with no attributes (like you have in your example) you can do it with daily builds via
private static RSA ReadKeyFromFile(string filename)
{
string pemContents = System.IO.File.ReadAllText(filename);
const string RsaPrivateKeyHeader = "-----BEGIN RSA PRIVATE KEY-----";
const string RsaPrivateKeyFooter = "-----END RSA PRIVATE KEY-----";
if (pemContents.StartsWith(RsaPrivateKeyHeader))
{
int endIdx = pemContents.IndexOf(
RsaPrivateKeyFooter,
RsaPrivateKeyHeader.Length,
StringComparison.Ordinal);
string base64 = pemContents.Substring(
RsaPrivateKeyHeader.Length,
endIdx - RsaPrivateKeyHeader.Length);
byte[] der = Convert.FromBase64String(base64);
RSA rsa = RSA.Create();
rsa.ImportRSAPrivateKey(der, out _);
return rsa;
}
// "BEGIN PRIVATE KEY" (ImportPkcs8PrivateKey),
// "BEGIN ENCRYPTED PRIVATE KEY" (ImportEncryptedPkcs8PrivateKey),
// "BEGIN PUBLIC KEY" (ImportSubjectPublicKeyInfo),
// "BEGIN RSA PUBLIC KEY" (ImportRSAPublicKey)
// could any/all be handled here.
throw new InvalidOperationException();
}
Daily builds of the .NET Core SDK can be obtained from https://github.com/dotnet/core-sdk/#installers-and-binaries
There are a lot of Convert PEM to XML online tools, just convert your pem to xml, then
RSA.FromXmlString(string xmlString)
If you don't need to convert from PEM to DER in the code, you can use openssl to get the DER encoded private key file:
openssl rsa -in key.pem -out key.der -outform der
The .Net Cryptographic API does not support the industry widely used PEM files so we need to convert it to the XML format, introduced by Microsoft. Basically, the solution was found in another similar question here C# Extract public key from RSA PEM private key.