使用通过 heroku foreman 从 .env 文件加载的 RSA 私钥对 JWT 进行签名时出错

Pla*_*rge 4 rsa heroku environment-variables node.js foreman

我有一个在 Heroku 上运行的 node.js Web 服务器,它将签署和验证 JSON Web 令牌。我生成了一个 RSA 公钥和私钥,它们看起来像这样:

private_key.pem

-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAz7pZEypvrFJCDshsbOamj9bmy/dXnUOyCo5b3xSvvTNIoFAC
5ePXozCD/5Byih1JB6ZYE6OceEW6oArkPzZOl8bFBlqV9k30oerMtVei18+CfF/u
FLWlJXs9FvXrRTKtsL43OmpLCH3LdzK9/+ZqhEx/TShp3JudUWuRW8ALqrBd8QW5
CWJHYozYVaIpFzwJ9KW6fJ9GpZfcToCOquLWo8iINnAovXmvcAtdmzgIqoucD988
f9oerll/CubJLy2rOiyeRvsAYouoefoyQZWN8IYPlnb5IB6Z7qnVL6rZz44dAjVw
S3uARW3lxpfeZn3TN7wpPkBssGBF0OSEHNrXVwIDAQABAoIBAC8HHCVnpRKZKNVZ
8JoS+cB0wZmJrK8w5TzYj9oIP+UQmC+bDZzoISiT0j5ogFXeXWs68JO5pbHg72hO
LvBUpiRcXryag3rYmTqTArdHWNmM5BiuSyMrIHFE3ka1dAcdew8ZcT1rVQNeH1Mk
DLnDe3fqLaPVM2o7XLlTJfxklP+WN6xWhBgDVgEawneo5svdgblYhg3u7cb4fsHg
cAf0sCYraVuqcUHa/AUVOx7n5U39x3ShOvOQvFlWEDD6uN4Yg/twW2UyfFDWD57p
2oPIEf06wOOu2XylPQwEU9w92Fr4yNqk0xksn8sOjbRyEPZncpDICsPTo1nsrz+R
0AcwWUkCgYEA7DfUujbbg6WrfSOyS718kTeej0Il5z19JYu11g+Sis4r8RWbT92q
weCp4dCGCpJrsPbs4+s4hT42sKfjUcUy5ZCGTDturQNbhH0RGxPp1KUTrytzdph8
4mqpCVYcN1AmLCCA0WtFqJ53taWuipcLtU48ZRC4jHI+stUSNCtaE8UCgYEA4R+6
x5mUjOWAK8GSTgHMWa72KqaxR/osYwmMPtHtjIFm1aOElQaXbGlZKd3dR5Tnw/4R
8hO/gJc+eQeaPGhri0IVmG66JNTw8q0M0Qd+l0OrarYS5c09XzjAUdGOatstsaNE
rhgRG90HvVYt0cHyKRa/C4+CnEBod/EoS/UnhGsCgYB5wT1Qzj3PWXFPCzs3du/i
Gf0Mclf/HN6In76WG2i5SxOzLCPlwqflTtvBnS25/Uas7FmmEPQNGcguvhqZZz+Y
vCm82VVusDBX1e8fOeBozr2aqJbXJjoYqkl+mnfoutMyI37Ccrxw8V1ar4+Lt9c9
GJpgrYGyQqC2pMTBRyci0QKBgHxc9uXE5ddgAQorCROm0qjIipzNMSo9/b9ISv15
Iu13nsNubZOV7JirKeKC+fbNP6t585fzaNs0sgJSPNYaKS7o9t0abiJisCifiHEA
3uHZNBzjMFVaqAiuZS/NwAsvwXJca1hxWyI1XE0wCmfR6GDie+96/AAtZIi95DDx
4T65AoGBAIE5LSP+glxJEd8jU/qc80D/dXf6icURyYDGARw8mziAgw6fL9cwbmqb
IGDxP1ke2FA8OZ0W4VybRi9UcprenvADYpPb+CPZv4gxGoDFg0Bb/JcFUKL29hC1
steX0GR4TKYNeXLC+zz7Qr0DzhpqRswSyHG5GckkIRdgHx4l/Uza
-----END RSA PRIVATE KEY-----
Run Code Online (Sandbox Code Playgroud)

public_key.pem

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz7pZEypvrFJCDshsbOam
j9bmy/dXnUOyCo5b3xSvvTNIoFAC5ePXozCD/5Byih1JB6ZYE6OceEW6oArkPzZO
l8bFBlqV9k30oerMtVei18+CfF/uFLWlJXs9FvXrRTKtsL43OmpLCH3LdzK9/+Zq
hEx/TShp3JudUWuRW8ALqrBd8QW5CWJHYozYVaIpFzwJ9KW6fJ9GpZfcToCOquLW
o8iINnAovXmvcAtdmzgIqoucD988f9oerll/CubJLy2rOiyeRvsAYouoefoyQZWN
8IYPlnb5IB6Z7qnVL6rZz44dAjVwS3uARW3lxpfeZn3TN7wpPkBssGBF0OSEHNrX
VwIDAQAB
-----END PUBLIC KEY-----
Run Code Online (Sandbox Code Playgroud)

(请注意,这些是为本示例生成的,未在任何实时系统中使用)

当通过 fs 模块加载密钥(PEM 文件)并传递给jsonwebtoken 库时,签名和验证按预期完美工作:

应用程序.js

var fs = require('fs');
var jwt = require('jsonwebtoken');

var privateKey = fs.readFileSync('private_key.pem');

jwt.sign(data, privateKey, { 
    algorithm: 'RS256',
    expiresIn: '3 days'
}, function(token) {
    // works great
});
Run Code Online (Sandbox Code Playgroud)

根据最佳实践,我想通过 .env 文件和节点工头工具将公钥和私钥加载到节点进程中,而不是从文件系统加载密钥。我已经尝试以几种不同的方式格式化 .env 文件中的键,所有这些都会导致某种错误。例如,这是我尝试过的一种配置,结果出现错误:

.env

PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAz7pZEypvrFJCDshsbOamj9bmy/dXnUOyCo5b3xSvvTNIoFAC/n5ePXozCD/5Byih1JB6ZYE6OceEW6oArkPzZOl8bFBlqV9k30oerMtVei18+CfF/u/nFLWlJXs9FvXrRTKtsL43OmpLCH3LdzK9/+ZqhEx/TShp3JudUWuRW8ALqrBd8QW5/nCWJHYozYVaIpFzwJ9KW6fJ9GpZfcToCOquLWo8iINnAovXmvcAtdmzgIqoucD988/nf9oerll/CubJLy2rOiyeRvsAYouoefoyQZWN8IYPlnb5IB6Z7qnVL6rZz44dAjVw/nS3uARW3lxpfeZn3TN7wpPkBssGBF0OSEHNrXVwIDAQABAoIBAC8HHCVnpRKZKNVZ/n8JoS+cB0wZmJrK8w5TzYj9oIP+UQmC+bDZzoISiT0j5ogFXeXWs68JO5pbHg72hO/nLvBUpiRcXryag3rYmTqTArdHWNmM5BiuSyMrIHFE3ka1dAcdew8ZcT1rVQNeH1Mk/nDLnDe3fqLaPVM2o7XLlTJfxklP+WN6xWhBgDVgEawneo5svdgblYhg3u7cb4fsHg/ncAf0sCYraVuqcUHa/AUVOx7n5U39x3ShOvOQvFlWEDD6uN4Yg/twW2UyfFDWD57p/n2oPIEf06wOOu2XylPQwEU9w92Fr4yNqk0xksn8sOjbRyEPZncpDICsPTo1nsrz+R/n0AcwWUkCgYEA7DfUujbbg6WrfSOyS718kTeej0Il5z19JYu11g+Sis4r8RWbT92q/nweCp4dCGCpJrsPbs4+s4hT42sKfjUcUy5ZCGTDturQNbhH0RGxPp1KUTrytzdph8/n4mqpCVYcN1AmLCCA0WtFqJ53taWuipcLtU48ZRC4jHI+stUSNCtaE8UCgYEA4R+6/nx5mUjOWAK8GSTgHMWa72KqaxR/osYwmMPtHtjIFm1aOElQaXbGlZKd3dR5Tnw/4R/n8hO/gJc+eQeaPGhri0IVmG66JNTw8q0M0Qd+l0OrarYS5c09XzjAUdGOatstsaNE/nrhgRG90HvVYt0cHyKRa/C4+CnEBod/EoS/UnhGsCgYB5wT1Qzj3PWXFPCzs3du/i/nGf0Mclf/HN6In76WG2i5SxOzLCPlwqflTtvBnS25/Uas7FmmEPQNGcguvhqZZz+Y/nvCm82VVusDBX1e8fOeBozr2aqJbXJjoYqkl+mnfoutMyI37Ccrxw8V1ar4+Lt9c9/nGJpgrYGyQqC2pMTBRyci0QKBgHxc9uXE5ddgAQorCROm0qjIipzNMSo9/b9ISv15/nIu13nsNubZOV7JirKeKC+fbNP6t585fzaNs0sgJSPNYaKS7o9t0abiJisCifiHEA/n3uHZNBzjMFVaqAiuZS/NwAsvwXJca1hxWyI1XE0wCmfR6GDie+96/AAtZIi95DDx/n4T65AoGBAIE5LSP+glxJEd8jU/qc80D/dXf6icURyYDGARw8mziAgw6fL9cwbmqb/nIGDxP1ke2FA8OZ0W4VybRi9UcprenvADYpPb+CPZv4gxGoDFg0Bb/JcFUKL29hC1/nsteX0GR4TKYNeXLC+zz7Qr0DzhpqRswSyHG5GckkIRdgHx4l/Uza\n-----END RSA PRIVATE KEY-----"
PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz7pZEypvrFJCDshsbOam\nj9bmy/dXnUOyCo5b3xSvvTNIoFAC5ePXozCD/5Byih1JB6ZYE6OceEW6oArkPzZO\nl8bFBlqV9k30oerMtVei18+CfF/uFLWlJXs9FvXrRTKtsL43OmpLCH3LdzK9/+Zq\nhEx/TShp3JudUWuRW8ALqrBd8QW5CWJHYozYVaIpFzwJ9KW6fJ9GpZfcToCOquLW\no8iINnAovXmvcAtdmzgIqoucD988f9oerll/CubJLy2rOiyeRvsAYouoefoyQZWN\n8IYPlnb5IB6Z7qnVL6rZz44dAjVwS3uARW3lxpfeZn3TN7wpPkBssGBF0OSEHNrX\nVwIDAQAB\n-----END PUBLIC KEY-----"  
Run Code Online (Sandbox Code Playgroud)

应用程序.js

var jwt = require('jsonwebtoken');

jwt.sign(data, process.env.PRIVATE_KEY, { 
    algorithm: 'RS256',
    expiresIn: '3 days'
}, function(token) {
    // process crashes before getting here
});
Run Code Online (Sandbox Code Playgroud)

输出

> nf start

[OKAY] Loaded ENV .env File as KEY=VALUE Format
[OKAY] Trimming display Output to 481 Columns
7:03:14 PM web.1 |  restify is listening on port http://[::]:5000
7:03:45 PM web.1 |  events.js:141
7:03:45 PM web.1 |        throw er; // Unhandled 'error' event
7:03:45 PM web.1 |        ^
7:03:45 PM web.1 |  Error: error:0906D06C:PEM routines:PEM_read_bio:no start line
7:03:45 PM web.1 |      at Error (native)
7:03:45 PM web.1 |      at Sign.sign (crypto.js:279:26)
7:03:45 PM web.1 |      at Object.sign (C:\example\node_modules\jsonwebtoken\node_modules\jws\node_modules\jwa\index.js:54:45)
7:03:45 PM web.1 |      at jwsSign (C:\example\node_modules\jsonwebtoken\node_modules\jws\lib\sign-stream.js:23:24)
7:03:45 PM web.1 |      at SignStream.sign (C:\example\node_modules\jsonwebtoken\node_modules\jws\lib\sign-stream.js:49:21)
7:03:45 PM web.1 |      at SignStream.<anonymous> (C:\example\node_modules\jsonwebtoken\node_modules\jws\lib\sign-stream.js:37:12)
7:03:45 PM web.1 |      at DataStream.g (events.js:260:16)
7:03:45 PM web.1 |      at emitNone (events.js:67:13)
7:03:45 PM web.1 |      at DataStream.emit (events.js:166:7)
7:03:45 PM web.1 |      at DataStream.<anonymous> (C:\example\node_modules\jsonwebtoken\node_modules\jws\lib\data-stream.js:32:12)
7:03:45 PM web.1 |      at doNTCallback0 (node.js:419:9)
7:03:45 PM web.1 |      at process._tickDomainCallback (node.js:389:13)
[DONE] Killing all processes with signal  null
7:03:45 PM web.1 Exited Abnormally
Run Code Online (Sandbox Code Playgroud)

我的问题是,在 .env 文件中存储这些公钥和私钥的正确格式是什么,以便它们可以按照描述使用?

这是一个开发环境,而不是生产环境。我在使用该heroku local命令时遇到了同样的问题,因为在该工具的幕后使用了节点工头。

值得注意的是,我使用的是windows系统,node版本为4.2.3,jsonwebtoken版本为5.7.0。

小智 10

Please try this.

process.env.PRIVATE_KEY.replace(/\\n/g, '\n')
Run Code Online (Sandbox Code Playgroud)

  • 以防万一它对任何人有帮助,关于设置 Heroku 配置,我省略了引号并包含了上面的内容,瞧。 (4认同)