为什么 EC 密钥被视为 JWT 和 JWK 的有效 RSA 密钥?
Why EC key is treated as a valid RSA key for the JWT and JWK?
我最近一直致力于实现一个 Web 服务,该服务可以签署和发布 JWT,并且还公开 JWKs 端点以用于 JWT 验证目的。
根据 IETF 规范,使用 JWT/JWK 非常简单,但我注意到一些奇怪的事情,我现在还无法解释:
TL;DR: why EC P-256 source key works for the signing JWT with RSA algo?
长话短说:
我正在使用预先存在的私钥文件对 JWT 进行签名,并将 JWK 导入 node-jose
密钥库。
密钥库:
const jose = require('node-jose');
const fs = require('fs')
const PRIVATE_KEY_PATH = process.env.PRIVATE_KEY_PATH
const privateKey = fs.readFileSync(PRIVATE_KEY_PATH,"utf8");
console.log('Begin import of private key to the JWKS...')
let jwkJson = {}
const jwks = jose.JWK.createKeyStore();
jwks.add(privateKey, 'pem').then(function(jwk) {
console.log(`Imported JWK:\n${JSON.stringify(jwk, null, 2)}`)
jwkJson = jwks.toJSON(true)
});
const getPrivateKey = () => {
return privateKey;
}
const getJwks = () => {
return jwkJson
}
module.exports = {
getPrivateKey,
getJwks
}
智威汤逊:
const jwt = require('jsonwebtoken')
const keystore = require('../util/keystore')
const payload = {
cert_hash: 'whatever'
}
const signOptions = {
algorithm: "RS256",
expiresIn: Math.floor(Date.now() / 1000) + 1800,
issuer: 'whatever',
subject: 'whatever'
}
return jwt.sign(payload, keystore.getPrivateKey(), signOptions);
看起来很标准,似乎可以正常工作。
要点在密钥文件 type 中。它是 Elliptic Curve (ES) P-256
或在通过 openssl
:
创建的 JWT-world ES256
中
openssl ecparam -genkey -name P-256 -noout -out api-gw.key
示例密钥:
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDNkiJxzt3uGhxh
OzlG1Zs4HRx0rn1caAcyrzGiIK3LxD4ewcUPqjco8ONbrMSxhurMZbbZyasUyDs5
2guveFhGVYQzfgMmJLK77eAkEB6zHcAP76tpv6LYFuDNTep9pUc+5tQIyNDY5hKe
UMkxrgzckTVyLpzcH2tsugjUUT4A7ad4brXR1/3hqusA53DNucI4Sv7CJTAGMenN
8SwifbIwxvlORFQ8QvYCBdW8HZ1TvoBz3E6vk7gypNUvRNAVMquQapb6LS0VkmSg
bk5cSaopcvyn2Oq+cxnSh7glgZcfeopZ80j4bFmsZnK2UDJnAGm+RC7Q1SvMDhhb
7F/1x97fAgMBAAECggEAPMLj8XWuvVD0cHzb2icLARQBtC9bGHQbJI0KA5zbIe54
Wgj2IUIzmaNR4Gf5n2t8fTvXRxpHuhXRA7GCYLQWi3t9XubxMVYJimiuJpqpKFIz
0cIKjXA6RtrESYqtM8Qlgd8ibxJEQMgIWskQHuIOJSe6f2xtqtaSnwmB0JfO1uDg
RnZoUEd+oZMqu2vqz0cL0e1Q6UR9WYpSH68uS05KgYlZOzOsHMadkXfwW6BEqYd6
2gh9I1GkrY0ZsuCOVhCAc7rtuUQRMnpACNuWci/qPKQLcvSJBojS/W9xEVrVBXkQ
AZyZltjw1B5CJ9/2lbFVRTM4D/JSS84BsQEwsZm2MQKBgQDrL9kIlKCTXNl4lTYk
G8JTKV85/MUY2seWmg2K18EL8ovovY15TjKF5N6g518phmiMUPg+QxkFiAY3gZ4A
+aXsdkxo2XeTEOxFQ3ysbI4H0L0+GMCCAvqCGJr4c1kGSB1kFnGDlo7bSXBUxwlZ
4tTnVQEX0H+mfTJ7yOxv7A/lWQKBgQDfw1XtbnykCEbWXNsmgzzrmk3vvZKlNfc8
CR6PD6fCCa/yYwCv0ItJMVcF14z0vIlsaw6TmsotQIpn6tzv+ttA9KxIKPVtUNFi
O5RTgTFQUq4NO0uUje6Wj1M6uRJRo9yHUted+wcCHoQ4k1xGg7jCMiw/8O7K3Diy
Kxz9Ve2G9wKBgQDbsPd4t3WEElCm/iLz+eY9TsFAZqkqfXvBZ6hM0RvocCpXP3G/
Jde2EUQRY/AV1xMkN6KcbosaCqVcBj01Rf7DcwIPU00KWN2MGe2FF2ZZUJjmP7Lb
/7JIAnoIqZ84afbifsCMngBWQTSoTMCkcWpVqab6uu3y9LJKxTZvmkCDCQKBgGNb
LNBceuOq+Sk92eFj7K0AuxJ0rqTFLZ5uvi7v2KGEA6gw5aErjG1XhziE2YXiIXMO
pk5MMPGe8tXpp2i3jpttCQKRjUiY1iA0LExX1TnBPJ+LcKfpzcL0qRQuEUBG7ij4
U91GFXqPak5kwFhfLK6t8JADv0Q8PMB//ENQ4ENJAoGAf8fdXSYwWEhdcQcxA3JV
MHDKSOblhapT52oXnNN6VW63ccv0mRD0gRQL5nOUQdGghvtFixBoasIMz0K2i8tH
kRz56CTLpLFop+gMz+j1PPL+hjRJOGpqoPjnHkWbvK6iGadTE4nT8iqGq4p11yuc
K+HDZxjTgVXEEbOuIpbAd/8=
-----END PRIVATE KEY-----
虽然我明确声明我在 JWT 案例中使用 RS256
作为算法,但在导入密钥时 node-jose
推导出它本身是 RSA
类型:
{
"keys": [
{
"kty": "RSA",
"kid": "gxGP1sgIVSrKTUlfBKKqdKk97UQEhEDFSMRY2vstxQk",
"n": "zZIicc7d7hocYTs5RtWbOB0cdK59XGgHMq8xoiCty8Q-HsHFD6o3KPDjW6zEsYbqzGW22cmrFMg7OdoLr3hYRlWEM34DJiSyu-3gJBAesx3AD--rab-i2BbgzU3qfaVHPubUCMjQ2OYSnlDJMa4M3JE1ci6c3B9rbLoI1FE-AO2neG610df94arrAOdwzbnCOEr-wiUwBjHpzfEsIn2yMMb5TkRUPEL2AgXVvB2dU76Ac9xOr5O4MqTVL0TQFTKrkGqW-i0tFZJkoG5OXEmqKXL8p9jqvnMZ0oe4JYGXH3qKWfNI-GxZrGZytlAyZwBpvkQu0NUrzA4YW-xf9cfe3w",
"e": "AQAB"
}
]
}
有人可以解释一下这是怎么回事吗?我的理解是基础密钥类型必须与签名算法匹配。
更新:crypto输出:
const keystore = require('./util/keystore')
const key = require('crypto').createPrivateKey(
{
key: keystore.getPrivateKey(),
format: 'pem',
encoding: 'utf8'
}
)
console.log(`key.asymmetricKeyType: ${key.asymmetricKeyType}`) // rsa
console.log(`key.asymmetricKeyDetails: ${key.asymmetricKeyDetails}`) // undefined
显然,问题不在 JWT/JWK 依赖项中,而是在密钥和 X509 生成过程中涉及的 openssl
命令链中。
其中一个命令 - openssl req
- 错误地包含了参数 -keyout
(而不是 -key
),它隐式生成了一个 RSA 密钥,但在其源代码中没有提及密钥类型,以及覆盖原始 EC 密钥。
不幸的是,我找不到一个简单的 openssl
命令来验证密钥类型——这是我在发布这个问题之前尝试做的——但我的一般建议是在关键源文件。看起来它要么被明确声明,要么被隐含地假设为 RSA
.
我最近一直致力于实现一个 Web 服务,该服务可以签署和发布 JWT,并且还公开 JWKs 端点以用于 JWT 验证目的。
根据 IETF 规范,使用 JWT/JWK 非常简单,但我注意到一些奇怪的事情,我现在还无法解释:
TL;DR: why EC P-256 source key works for the signing JWT with RSA algo?
长话短说:
我正在使用预先存在的私钥文件对 JWT 进行签名,并将 JWK 导入 node-jose
密钥库。
密钥库:
const jose = require('node-jose');
const fs = require('fs')
const PRIVATE_KEY_PATH = process.env.PRIVATE_KEY_PATH
const privateKey = fs.readFileSync(PRIVATE_KEY_PATH,"utf8");
console.log('Begin import of private key to the JWKS...')
let jwkJson = {}
const jwks = jose.JWK.createKeyStore();
jwks.add(privateKey, 'pem').then(function(jwk) {
console.log(`Imported JWK:\n${JSON.stringify(jwk, null, 2)}`)
jwkJson = jwks.toJSON(true)
});
const getPrivateKey = () => {
return privateKey;
}
const getJwks = () => {
return jwkJson
}
module.exports = {
getPrivateKey,
getJwks
}
智威汤逊:
const jwt = require('jsonwebtoken')
const keystore = require('../util/keystore')
const payload = {
cert_hash: 'whatever'
}
const signOptions = {
algorithm: "RS256",
expiresIn: Math.floor(Date.now() / 1000) + 1800,
issuer: 'whatever',
subject: 'whatever'
}
return jwt.sign(payload, keystore.getPrivateKey(), signOptions);
看起来很标准,似乎可以正常工作。
要点在密钥文件 type 中。它是 Elliptic Curve (ES) P-256
或在通过 openssl
:
ES256
中
openssl ecparam -genkey -name P-256 -noout -out api-gw.key
示例密钥:
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDNkiJxzt3uGhxh
OzlG1Zs4HRx0rn1caAcyrzGiIK3LxD4ewcUPqjco8ONbrMSxhurMZbbZyasUyDs5
2guveFhGVYQzfgMmJLK77eAkEB6zHcAP76tpv6LYFuDNTep9pUc+5tQIyNDY5hKe
UMkxrgzckTVyLpzcH2tsugjUUT4A7ad4brXR1/3hqusA53DNucI4Sv7CJTAGMenN
8SwifbIwxvlORFQ8QvYCBdW8HZ1TvoBz3E6vk7gypNUvRNAVMquQapb6LS0VkmSg
bk5cSaopcvyn2Oq+cxnSh7glgZcfeopZ80j4bFmsZnK2UDJnAGm+RC7Q1SvMDhhb
7F/1x97fAgMBAAECggEAPMLj8XWuvVD0cHzb2icLARQBtC9bGHQbJI0KA5zbIe54
Wgj2IUIzmaNR4Gf5n2t8fTvXRxpHuhXRA7GCYLQWi3t9XubxMVYJimiuJpqpKFIz
0cIKjXA6RtrESYqtM8Qlgd8ibxJEQMgIWskQHuIOJSe6f2xtqtaSnwmB0JfO1uDg
RnZoUEd+oZMqu2vqz0cL0e1Q6UR9WYpSH68uS05KgYlZOzOsHMadkXfwW6BEqYd6
2gh9I1GkrY0ZsuCOVhCAc7rtuUQRMnpACNuWci/qPKQLcvSJBojS/W9xEVrVBXkQ
AZyZltjw1B5CJ9/2lbFVRTM4D/JSS84BsQEwsZm2MQKBgQDrL9kIlKCTXNl4lTYk
G8JTKV85/MUY2seWmg2K18EL8ovovY15TjKF5N6g518phmiMUPg+QxkFiAY3gZ4A
+aXsdkxo2XeTEOxFQ3ysbI4H0L0+GMCCAvqCGJr4c1kGSB1kFnGDlo7bSXBUxwlZ
4tTnVQEX0H+mfTJ7yOxv7A/lWQKBgQDfw1XtbnykCEbWXNsmgzzrmk3vvZKlNfc8
CR6PD6fCCa/yYwCv0ItJMVcF14z0vIlsaw6TmsotQIpn6tzv+ttA9KxIKPVtUNFi
O5RTgTFQUq4NO0uUje6Wj1M6uRJRo9yHUted+wcCHoQ4k1xGg7jCMiw/8O7K3Diy
Kxz9Ve2G9wKBgQDbsPd4t3WEElCm/iLz+eY9TsFAZqkqfXvBZ6hM0RvocCpXP3G/
Jde2EUQRY/AV1xMkN6KcbosaCqVcBj01Rf7DcwIPU00KWN2MGe2FF2ZZUJjmP7Lb
/7JIAnoIqZ84afbifsCMngBWQTSoTMCkcWpVqab6uu3y9LJKxTZvmkCDCQKBgGNb
LNBceuOq+Sk92eFj7K0AuxJ0rqTFLZ5uvi7v2KGEA6gw5aErjG1XhziE2YXiIXMO
pk5MMPGe8tXpp2i3jpttCQKRjUiY1iA0LExX1TnBPJ+LcKfpzcL0qRQuEUBG7ij4
U91GFXqPak5kwFhfLK6t8JADv0Q8PMB//ENQ4ENJAoGAf8fdXSYwWEhdcQcxA3JV
MHDKSOblhapT52oXnNN6VW63ccv0mRD0gRQL5nOUQdGghvtFixBoasIMz0K2i8tH
kRz56CTLpLFop+gMz+j1PPL+hjRJOGpqoPjnHkWbvK6iGadTE4nT8iqGq4p11yuc
K+HDZxjTgVXEEbOuIpbAd/8=
-----END PRIVATE KEY-----
虽然我明确声明我在 JWT 案例中使用 RS256
作为算法,但在导入密钥时 node-jose
推导出它本身是 RSA
类型:
{
"keys": [
{
"kty": "RSA",
"kid": "gxGP1sgIVSrKTUlfBKKqdKk97UQEhEDFSMRY2vstxQk",
"n": "zZIicc7d7hocYTs5RtWbOB0cdK59XGgHMq8xoiCty8Q-HsHFD6o3KPDjW6zEsYbqzGW22cmrFMg7OdoLr3hYRlWEM34DJiSyu-3gJBAesx3AD--rab-i2BbgzU3qfaVHPubUCMjQ2OYSnlDJMa4M3JE1ci6c3B9rbLoI1FE-AO2neG610df94arrAOdwzbnCOEr-wiUwBjHpzfEsIn2yMMb5TkRUPEL2AgXVvB2dU76Ac9xOr5O4MqTVL0TQFTKrkGqW-i0tFZJkoG5OXEmqKXL8p9jqvnMZ0oe4JYGXH3qKWfNI-GxZrGZytlAyZwBpvkQu0NUrzA4YW-xf9cfe3w",
"e": "AQAB"
}
]
}
有人可以解释一下这是怎么回事吗?我的理解是基础密钥类型必须与签名算法匹配。
更新:crypto输出:
const keystore = require('./util/keystore')
const key = require('crypto').createPrivateKey(
{
key: keystore.getPrivateKey(),
format: 'pem',
encoding: 'utf8'
}
)
console.log(`key.asymmetricKeyType: ${key.asymmetricKeyType}`) // rsa
console.log(`key.asymmetricKeyDetails: ${key.asymmetricKeyDetails}`) // undefined
显然,问题不在 JWT/JWK 依赖项中,而是在密钥和 X509 生成过程中涉及的 openssl
命令链中。
其中一个命令 - openssl req
- 错误地包含了参数 -keyout
(而不是 -key
),它隐式生成了一个 RSA 密钥,但在其源代码中没有提及密钥类型,以及覆盖原始 EC 密钥。
不幸的是,我找不到一个简单的 openssl
命令来验证密钥类型——这是我在发布这个问题之前尝试做的——但我的一般建议是在关键源文件。看起来它要么被明确声明,要么被隐含地假设为 RSA
.