jwt - 使用 mbedtls 生成的 ECDSA 签名在 JOSE 中无法验证(而代码使用 RSA key )

标签 jwt ecdsa mbedtls jose prime256v1

我有一个在 ESP32 开发板上运行的小应用程序(我将 Arduino IDE 与附带的 mbedtls 一起使用),用于发布和验证 JWT token 。我最初成功地使用了 RSA 签名,但现在想要更短的签名,因此尝试使用 ECDSA。 应用程序本身可以颁发 token 并验证它们,但是如果我尝试在应用程序之外验证 token - 例如使用 JOSEDebugger - 我收到验证失败的消息,但我无法完全理解为什么会发生这种情况。

这是一个示例 token (该 token 实际上不包含信息):

eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NzI0MTMxNjgsImV4cCI6MTY3MjQxNjc2OH0.MEUCIAjwEDXI424qjrAkSzZ_ydcVLOSAvfQ8YVddYvzDzMvQAiEAkVy4d-hZ01KpcMNKhPHk8E_SDYiB4JKwhm-Kc-Z81rI

这是相应的公钥(除了在此提出问题的目的之外,此 key 不会在任何地方使用):

-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUGnNIOhPhZZSOg4A4BqAFtGO13W4BGDQpQ0ieTvLU9/CXrY7W77o7pNx7tvugeIoYJxS0NjmxvT4TMpo4Z8P7A== -----END PUBLIC KEY-----

据我了解,JWT token 可以使用 ECDSA 发行和验证。所谓的“ES256”方法is supposed to use prime256v1结合 SHA256,因此我使用以下命令生成了 key Material :

openssl ecparam -name prime256v1 -genkey -noout -out ecc-private.pem
openssl ec -in ecc-private.pem -pubout -out ecc-public.pem

对于签名部分,私钥加载如下,其中 ecc_priv 是包含 key 的 PEM 表示形式的字符串:

//get the key
byte *keybuffer = (byte*)malloc((ecc_priv.length()+1)*sizeof(byte));
ecc_priv.getBytes(keybuffer, ecc_priv.length() + 1);
mbedtls_pk_context pk_context;
mbedtls_pk_init(&pk_context);
int rc = mbedtls_pk_parse_key(&pk_context, keybuffer, ecc_priv.length() + 1, NULL, 0);
if (rc != 0){
 printf("Failed to mbedtls_pk_parse_key: %d (-0x%x): %s\n", rc, -rc, mbedtlsError(rc));
 return -1;
}
free(keybuffer);

由于这对我来说适用于 RSA key ,因此我只是替换了 key 并保留了所有其他代码来签署实际消息。据我了解,这应该可以通过 mbedtls_pk 方法实现:

//mbedtls context
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ctr_drbg_init(&ctr_drbg);
mbedtls_entropy_init(&entropy);

const char* pers="some entropy";
                
mbedtls_ctr_drbg_seed(
  &ctr_drbg,
  mbedtls_entropy_func,
  &entropy,
  (const unsigned char*)pers,
  strlen(pers));
//get the header and payload bytes    
byte *headerAndPayloadbytes = (byte*)malloc((headerAndPayload.length()+1)*sizeof(byte));
headerAndPayload.getBytes(headerAndPayloadbytes, headerAndPayload.length() + 1);
//prepare digest
uint8_t digest[32];
rc = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), headerAndPayloadbytes, headerAndPayload.length(), digest);
if (rc != 0) {
  printf("Failed to mbedtls_md: %d (-0x%x): %s\n", rc, -rc, mbedtlsError(rc));
  return -1;        
}
free(headerAndPayloadbytes);
//prepare output
byte *oBuf = (byte*)malloc(5000*sizeof(byte));
size_t retSize;
//sign digest
rc = mbedtls_pk_sign(&pk_context, MBEDTLS_MD_SHA256, digest, sizeof(digest), oBuf, &retSize, mbedtls_ctr_drbg_random, &ctr_drbg);
if (rc != 0) {
  printf("Failed to mbedtls_pk_sign: %d (-0x%x): %s\n", rc, -rc, mbedtlsError(rc));
  return -1;        
}
//encode signature to base64
unsigned int osize = encode_base64_length(retSize);
byte *output = (byte*)malloc((osize+1)*sizeof(byte));
encode_base64(oBuf, retSize, output);
String sig = String((char*)output);
free(output);
//base64 URL specific
sig.replace('+','-');
sig.replace('/','_');
sig.replace("=","");
String completejwt = headerAndPayload + "." + sig;
//free resources
mbedtls_ctr_drbg_free( &ctr_drbg );
mbedtls_entropy_free( &entropy );
mbedtls_pk_free(&pk_context);
free(oBuf);

我的期望是,我可以简单地将 RSA key 替换为 ECDSA (prime256v1) key ,并保持其他所有内容不变,但生成的 token 在我的应用程序之外无法验证。我想再次强调,在我的应用程序内部,我绝对可以验证 token ,并且即使在我的应用程序外部,代码也可以与 RSA key 完美配合。我确信这里一定有什么东西不见了。 非常感谢任何帮助或研究指导。

编辑:这是一个 minimal compilable example (Arduino草图)

最佳答案

您的 ECDSA 签名是 DER 编码的 ASN.1 结构,而不是简单的 r || IEEE-P1363 提议的串联,这是 JOSE 规范的要求。

关于jwt - 使用 mbedtls 生成的 ECDSA 签名在 JOSE 中无法验证(而代码使用 RSA key ),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74963265/

相关文章:

ASP.NET Core Azure AD OpenID - 生产服务器上的 token 签名验证失败

关于本地主机 repo 中私钥的安全相关问题

c - 代码1和代码2之间的区别

c - MBED TLS 对称 key 包装

Django JWT 获取用户信息

javascript - 在nodejs中多次尝试记录后锁定用户

jwt - 为什么我可以在 jwt.io 上轻松解码 auth0 id_token?

javascript - 无法在 Node js 中使用 ecdsa 模块进行签名

java - 如何使用 Bouncy CaSTLe 从 Java 中的 ECDSA 私钥获取公钥?

使用 BouncyCaSTLe 使用 SHA-256 和 ECDSA 的 C# 签名数据每次都会产生不同的签名