Nodejs `crypto.publicEncrypt` 不会采用 `ssh-keygen rsa` 生成的 public 密钥
Nodejs `crypto.publicEncrypt` would not take public key generated by `ssh-keygen rsa`
我使用 ssh-keygen rsa
生成了一个 RSA 密钥对。生成的 public 密钥如下所示:
ssh-rsa AAAAB3NzaC1yc2EAAA...
当我尝试在 Node.js 中使用 crypto
来加密纯字符串时,
const fs = require('fs');
const { publicEncrypt } = require('crypto');
const publicKey = fs.readFileSync('$path/to/publicKey').toString();
const encryptedToken = publicEncrypt(publicKey, Buffer.from('some plain string'));
它会给出以下错误:
Error: error:0909006C:PEM routines:get_name:no start line
at node:internal/crypto/cipher:78:12
...
library: 'PEM routines',
function: 'get_name',
reason: 'no start line',
code: 'ERR_OSSL_PEM_NO_START_LINE'
我对密码学还很陌生,只知道 public/private 密钥加密的一般概念,因此非常感谢任何建议。
编辑:
我知道 crypto
带有生成密钥对的方法,所以我想问题更多是关于 为什么 ssh-rsa
public 键在这里不起作用。
发布的 public 密钥在 OpenSSH format, which is not supported by NodeJS's crypto module, see documentation of crypto.publicEncrypt(key, buffer)
。
但是,OpenSSH 格式的密钥可以转换为 NodeJS 可以处理的格式。这可以通过工具来完成,例如ssh-keygen, or via libraries, e.g. node-sshpk.
或者,可以使用 OpenSSL 以所需格式直接生成密钥。
以下示例使用 node-sshpk 库并将 OpenSSH 私钥转换为 PEM 编码的 PKCS8 密钥,并将 OpenSSH public 密钥转换为 PEM 编码的 X.509/SPKI 密钥。 NodeJS 支持两种目标格式:
var sshpk = require('sshpk');
var crypto = require('crypto');
var privateKeyOpenSSH = `-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEApcFmUiObzYdRrKivbYrUwJ8plRfjcgA1nXIHpMKj4q0l4vatprYe
vNS2l06y2Qvz1Txu7AeS9+zT673zczwvLgENcZb6lXA576XMLjcmZhnNKXDRnRV/UCMlea
jdwdhQ93YJpsGRCUewD8k6IOO6G/MVAQ433ybBuCPJysTLWNk2Vv/J3VrLkBWGLiwOUcnZ
pR17/mm6QBOsLRAuBs1jELcid1Jdpc8wfULlrSdCfw/P2mXOqpuTMhSEBy4yE+W6V54MSI
Vw4BaxlrcAmC/Rh7OHJrhuIEmmOStqmMPe7HvFKNTjqzISAiyWmPWWwI9UuP4g7b0p7GAk
0YMevLPQnwAAA9jLWAaqy1gGqgAAAAdzc2gtcnNhAAABAQClwWZSI5vNh1GsqK9titTAny
mVF+NyADWdcgekwqPirSXi9q2mth681LaXTrLZC/PVPG7sB5L37NPrvfNzPC8uAQ1xlvqV
cDnvpcwuNyZmGc0pcNGdFX9QIyV5qN3B2FD3dgmmwZEJR7APyTog47ob8xUBDjffJsG4I8
nKxMtY2TZW/8ndWsuQFYYuLA5RydmlHXv+abpAE6wtEC4GzWMQtyJ3Ul2lzzB9QuWtJ0J/
D8/aZc6qm5MyFIQHLjIT5bpXngxIhXDgFrGWtwCYL9GHs4cmuG4gSaY5K2qYw97se8Uo1O
OrMhICLJaY9ZbAj1S4/iDtvSnsYCTRgx68s9CfAAAAAwEAAQAAAQAstgRxt6U5RX0kg8P+
WmqVItnGm9EAWUodFDs3mEE4zdfgZwXkaE/WQ9KU8eeQYIb/R/PruwdL1Rg9CNn4hY18bV
BBCabCVKlsGV8AQGQdOmx69zGzm67h4Pkk3gYjWcRNXAuybZg/1pSJTZBees8i5ukNhdZQ
XVX347908KyhZFb4jlYws7gbOkVBP7ludHSnnrodL91F2ouKrgplLfWu40sgU3fSpSybby
3Llt+FUW6UjCErp54c2LS5vZtvTJK+wVr2fYF4Y1Sgmv+JZ4bJUkZgaxzcHoOLF5GfuQFo
dvJVxbSXPUhk7JW7ur9hHLkEaJtYf0xf1TwJdPRQxu3xAAAAgQCui5wvYjZrCM9wWDXpWh
1dNGPIJQuX8aCxi2tfTkRYlkPUWFSYd+orQr53/FPTxLdwe9lAe8x0Xkr+wMga6771ywHw
sT6xk7nCabXcN6PQCn5DjYMtLPfa5rY3+yHR01pVSzo9l6JcvangU1xyw7MRYj0LGZVSbp
U/beRO/bXekQAAAIEA0uUTVG9wPiqZRTLT6H8rQ4ZK96VcTF+CnmJUO+3Fsp/D7jfYItZF
2GcqzWYv6tsvumdhZt0dziXBy7fAJDW88k/nq+BNTlDXEYzkEA+x13eaLAzfI08SwlAnF5
zxZUo8yIsjKtyt9gs1+VVgtwpvvqUVDUibjbWxaE4OWtdLjwUAAACBAMk02gcYkJylXw12
4me1vtWpw7/7pga3eK0Y4ZTZgpCZXhrLnl9yWXrVIlvV8dLJQI88s7+1CRU9HPi+2BGtgI
mJ5crYws7q0QgLODg1rDosd4vkkKok5XPUsrKDLxwme+Ipq26IdRyoJF2ZiRHvPG5ajjmK
gWByjcdktmXM+UpTAAAAHHRmbG9yZXNfZHQwMUB0ZmxvcmVzX2R0MDEtUEMBAgMEBQY=
-----END OPENSSH PRIVATE KEY-----`
var publicKeyOpenSSH = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQClwWZSI5vNh1GsqK9titTAnymVF+NyADWdcgekwqPirSXi9q2mth681LaXTrLZC/PVPG7sB5L37NPrvfNzPC8uAQ1xlvqVcDnvpcwuNyZmGc0pcNGdFX9QIyV5qN3B2FD3dgmmwZEJR7APyTog47ob8xUBDjffJsG4I8nKxMtY2TZW/8ndWsuQFYYuLA5RydmlHXv+abpAE6wtEC4GzWMQtyJ3Ul2lzzB9QuWtJ0J/D8/aZc6qm5MyFIQHLjIT5bpXngxIhXDgFrGWtwCYL9GHs4cmuG4gSaY5K2qYw97se8Uo1OOrMhICLJaY9ZbAj1S4/iDtvSnsYCTRgx68s9Cf whatever`;
// Convert
var privateKey = sshpk.parsePrivateKey(privateKeyOpenSSH, 'ssh');
var publicKey = sshpk.parseKey(publicKeyOpenSSH, 'ssh');
var privateKeyPkcs8 = privateKey.toBuffer('pkcs8');
var publicKeyX509 = publicKey.toBuffer('pkcs8');
console.log(privateKey.toString('ssh'));
console.log(publicKey.toString('ssh'));
console.log(privateKeyPkcs8.toString('utf8')); // -----BEGIN PRIVATE KEY-----...
console.log(publicKeyX509.toString('utf8')); // -----BEGIN PUBLIC KEY-----...
// Encrypt/Decrypt
var plaintext = Buffer.from('The quick brown fox jumps over the lazy dog', 'utf8');
var ciphertext = crypto.publicEncrypt(publicKeyX509.toString('utf8'), plaintext);
var decryptedText = crypto.privateDecrypt(privateKeyPkcs8.toString('utf8'), ciphertext);
console.log('Ciphertext, base64 encoded: ', ciphertext.toString('base64'));
console.log('Decrypted text: ', decryptedText.toString('utf8'));
我使用 ssh-keygen rsa
生成了一个 RSA 密钥对。生成的 public 密钥如下所示:
ssh-rsa AAAAB3NzaC1yc2EAAA...
当我尝试在 Node.js 中使用 crypto
来加密纯字符串时,
const fs = require('fs');
const { publicEncrypt } = require('crypto');
const publicKey = fs.readFileSync('$path/to/publicKey').toString();
const encryptedToken = publicEncrypt(publicKey, Buffer.from('some plain string'));
它会给出以下错误:
Error: error:0909006C:PEM routines:get_name:no start line
at node:internal/crypto/cipher:78:12
...
library: 'PEM routines',
function: 'get_name',
reason: 'no start line',
code: 'ERR_OSSL_PEM_NO_START_LINE'
我对密码学还很陌生,只知道 public/private 密钥加密的一般概念,因此非常感谢任何建议。
编辑:
我知道 crypto
带有生成密钥对的方法,所以我想问题更多是关于 为什么 ssh-rsa
public 键在这里不起作用。
发布的 public 密钥在 OpenSSH format, which is not supported by NodeJS's crypto module, see documentation of crypto.publicEncrypt(key, buffer)
。
但是,OpenSSH 格式的密钥可以转换为 NodeJS 可以处理的格式。这可以通过工具来完成,例如ssh-keygen, or via libraries, e.g. node-sshpk.
或者,可以使用 OpenSSL 以所需格式直接生成密钥。
以下示例使用 node-sshpk 库并将 OpenSSH 私钥转换为 PEM 编码的 PKCS8 密钥,并将 OpenSSH public 密钥转换为 PEM 编码的 X.509/SPKI 密钥。 NodeJS 支持两种目标格式:
var sshpk = require('sshpk');
var crypto = require('crypto');
var privateKeyOpenSSH = `-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEApcFmUiObzYdRrKivbYrUwJ8plRfjcgA1nXIHpMKj4q0l4vatprYe
vNS2l06y2Qvz1Txu7AeS9+zT673zczwvLgENcZb6lXA576XMLjcmZhnNKXDRnRV/UCMlea
jdwdhQ93YJpsGRCUewD8k6IOO6G/MVAQ433ybBuCPJysTLWNk2Vv/J3VrLkBWGLiwOUcnZ
pR17/mm6QBOsLRAuBs1jELcid1Jdpc8wfULlrSdCfw/P2mXOqpuTMhSEBy4yE+W6V54MSI
Vw4BaxlrcAmC/Rh7OHJrhuIEmmOStqmMPe7HvFKNTjqzISAiyWmPWWwI9UuP4g7b0p7GAk
0YMevLPQnwAAA9jLWAaqy1gGqgAAAAdzc2gtcnNhAAABAQClwWZSI5vNh1GsqK9titTAny
mVF+NyADWdcgekwqPirSXi9q2mth681LaXTrLZC/PVPG7sB5L37NPrvfNzPC8uAQ1xlvqV
cDnvpcwuNyZmGc0pcNGdFX9QIyV5qN3B2FD3dgmmwZEJR7APyTog47ob8xUBDjffJsG4I8
nKxMtY2TZW/8ndWsuQFYYuLA5RydmlHXv+abpAE6wtEC4GzWMQtyJ3Ul2lzzB9QuWtJ0J/
D8/aZc6qm5MyFIQHLjIT5bpXngxIhXDgFrGWtwCYL9GHs4cmuG4gSaY5K2qYw97se8Uo1O
OrMhICLJaY9ZbAj1S4/iDtvSnsYCTRgx68s9CfAAAAAwEAAQAAAQAstgRxt6U5RX0kg8P+
WmqVItnGm9EAWUodFDs3mEE4zdfgZwXkaE/WQ9KU8eeQYIb/R/PruwdL1Rg9CNn4hY18bV
BBCabCVKlsGV8AQGQdOmx69zGzm67h4Pkk3gYjWcRNXAuybZg/1pSJTZBees8i5ukNhdZQ
XVX347908KyhZFb4jlYws7gbOkVBP7ludHSnnrodL91F2ouKrgplLfWu40sgU3fSpSybby
3Llt+FUW6UjCErp54c2LS5vZtvTJK+wVr2fYF4Y1Sgmv+JZ4bJUkZgaxzcHoOLF5GfuQFo
dvJVxbSXPUhk7JW7ur9hHLkEaJtYf0xf1TwJdPRQxu3xAAAAgQCui5wvYjZrCM9wWDXpWh
1dNGPIJQuX8aCxi2tfTkRYlkPUWFSYd+orQr53/FPTxLdwe9lAe8x0Xkr+wMga6771ywHw
sT6xk7nCabXcN6PQCn5DjYMtLPfa5rY3+yHR01pVSzo9l6JcvangU1xyw7MRYj0LGZVSbp
U/beRO/bXekQAAAIEA0uUTVG9wPiqZRTLT6H8rQ4ZK96VcTF+CnmJUO+3Fsp/D7jfYItZF
2GcqzWYv6tsvumdhZt0dziXBy7fAJDW88k/nq+BNTlDXEYzkEA+x13eaLAzfI08SwlAnF5
zxZUo8yIsjKtyt9gs1+VVgtwpvvqUVDUibjbWxaE4OWtdLjwUAAACBAMk02gcYkJylXw12
4me1vtWpw7/7pga3eK0Y4ZTZgpCZXhrLnl9yWXrVIlvV8dLJQI88s7+1CRU9HPi+2BGtgI
mJ5crYws7q0QgLODg1rDosd4vkkKok5XPUsrKDLxwme+Ipq26IdRyoJF2ZiRHvPG5ajjmK
gWByjcdktmXM+UpTAAAAHHRmbG9yZXNfZHQwMUB0ZmxvcmVzX2R0MDEtUEMBAgMEBQY=
-----END OPENSSH PRIVATE KEY-----`
var publicKeyOpenSSH = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQClwWZSI5vNh1GsqK9titTAnymVF+NyADWdcgekwqPirSXi9q2mth681LaXTrLZC/PVPG7sB5L37NPrvfNzPC8uAQ1xlvqVcDnvpcwuNyZmGc0pcNGdFX9QIyV5qN3B2FD3dgmmwZEJR7APyTog47ob8xUBDjffJsG4I8nKxMtY2TZW/8ndWsuQFYYuLA5RydmlHXv+abpAE6wtEC4GzWMQtyJ3Ul2lzzB9QuWtJ0J/D8/aZc6qm5MyFIQHLjIT5bpXngxIhXDgFrGWtwCYL9GHs4cmuG4gSaY5K2qYw97se8Uo1OOrMhICLJaY9ZbAj1S4/iDtvSnsYCTRgx68s9Cf whatever`;
// Convert
var privateKey = sshpk.parsePrivateKey(privateKeyOpenSSH, 'ssh');
var publicKey = sshpk.parseKey(publicKeyOpenSSH, 'ssh');
var privateKeyPkcs8 = privateKey.toBuffer('pkcs8');
var publicKeyX509 = publicKey.toBuffer('pkcs8');
console.log(privateKey.toString('ssh'));
console.log(publicKey.toString('ssh'));
console.log(privateKeyPkcs8.toString('utf8')); // -----BEGIN PRIVATE KEY-----...
console.log(publicKeyX509.toString('utf8')); // -----BEGIN PUBLIC KEY-----...
// Encrypt/Decrypt
var plaintext = Buffer.from('The quick brown fox jumps over the lazy dog', 'utf8');
var ciphertext = crypto.publicEncrypt(publicKeyX509.toString('utf8'), plaintext);
var decryptedText = crypto.privateDecrypt(privateKeyPkcs8.toString('utf8'), ciphertext);
console.log('Ciphertext, base64 encoded: ', ciphertext.toString('base64'));
console.log('Decrypted text: ', decryptedText.toString('utf8'));