简体   繁体   English

无法使用椭圆曲线 (EC) 密码术签署 JWT

[英]Cannot sign a JWT using Elliptic Curve (EC) cryptography

I have generated a private key using Elliptic Curve cryptography:我使用椭圆曲线密码术生成了私钥:

openssl ecparam -genkey -name secp521r1 -noout | openssl pkcs8 -topk8 -nocrypt

I have used the following Java code to sign a JWT:我使用以下 Java 代码签署了 JWT:

    String privateKeyPEM = "MIHtAgEAMBAGByqGSM49AgEGBSuBBAAjBIHVMIHSAgEBBEEmSOGpmkjzKM+uWhya"
        + "Cl6sbSsmROUol4HaDbORnOI6klbEjbCkPEyxKRnrrtrGFShhu7TPPlGDK39f+K3G"
        + "IZhbYKGBiQOBhgAEAJQiOIKV7YmIVI30Y3y1UZIvgZFRviHFWvSiTXEG4IqzHKpF"
        + "jOIYs0rzn1F2zrFHKpmMtZ0Kh5OzyfJsGeu1GZPzANYLZQ9m13Joi3fhGFUgHLNL"
        + "0hsz/HQP89aTa9Qr8QqEP7r/vCvrcoKn9cKPGwRxOFkRgG4FWGv76F/hv+1Cj2Z7";

    byte[] encoded = Base64.decodeBase64(privateKeyPEM);
    KeyFactory keyFactory = KeyFactory.getInstance("EC");
    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
    ECPrivateKey privateKey = (ECPrivateKey) keyFactory.generatePrivate(keySpec);

    final JwtBuilder jwtBuilder = Jwts.builder()
        .setSubject("713f42c9-7df5-4271-8b53-112f30936c56")
        .signWith(SignatureAlgorithm.ES512, privateKey)
        .setHeaderParam("typ", "JWT");

    System.out.println(jwtBuilder.compact());

However, the resulting JWT always have an invalid signature:但是,生成的 JWT 始终具有无效签名:

eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJzdWIiOiI3MTNmNDJjOS03ZGY1LTQyNzEtOGI1My0xMTJmMzA5MzZjNTYifQ.MIGHAkFvCPq_BeXvATTN1duKjEf3K_Fja0ueoTuPQHC9kBc828wem7YO0vnlK6PVYXSkBk4gBaD0-OIMY_r-HS7-4-HaBwJCAMbj0k5YsBywMzme_adKTQq7YUsVvyZwGp8aVgX7vxsMhf-WNvQJSg7AG_zQiUaQ4jqtT9ZKzNoU4P5NZIGMDRCh

I can't figure out what's wrong with my code.我无法弄清楚我的代码有什么问题。

The posted private key is a PKCS#8 key.发布的私钥是 PKCS#8 密钥。 From this the following public X.509 key can be derived:由此可以导出以下公共 X.509 密钥:

-----BEGIN PUBLIC KEY-----
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAlCI4gpXtiYhUjfRjfLVRki+BkVG+
IcVa9KJNcQbgirMcqkWM4hizSvOfUXbOsUcqmYy1nQqHk7PJ8mwZ67UZk/MA1gtl
D2bXcmiLd+EYVSAcs0vSGzP8dA/z1pNr1CvxCoQ/uv+8K+tygqf1wo8bBHE4WRGA
bgVYa/voX+G/7UKPZns=
-----END PUBLIC KEY-----

If a JWT is created with the posted code, eg:如果使用发布的代码创建 JWT,例如:

eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJzdWIiOiI3MTNmNDJjOS03ZGY1LTQyNzEtOGI1My0xMTJmMzA5MzZjNTYifQ.AE0sx6wHk2xBPkbam24n8NE39qkB0YX4j65DhrWyBKtaQXRMZjuzV78vFir3scfXVolFOf2gpo2K6x_hu0jPz-0IAIMbYQsglePQHQ9OZMSb2XAxKCVXccdvW27QeBov-VGUxxlL-CFNviaPaAGbNny_sc8cRjIF97pDD4KjOPBKkZzt

then this can be verified without problems with this public key, check it eg here , ie the posted code produces a valid signature.那么这可以通过这个公钥进行验证而不会出现问题,例如在此处检查它,即发布的代码会产生有效的签名。


On the other hand, the JWT posted in the question:另一方面,问题中发布的 JWT:

eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJzdWIiOiI3MTNmNDJjOS03ZGY1LTQyNzEtOGI1My0xMTJmMzA5MzZjNTYifQ.MIGHAkFvCPq_BeXvATTN1duKjEf3K_Fja0ueoTuPQHC9kBc828wem7YO0vnlK6PVYXSkBk4gBaD0-OIMY_r-HS7-4-HaBwJCAMbj0k5YsBywMzme_adKTQq7YUsVvyZwGp8aVgX7vxsMhf-WNvQJSg7AG_zQiUaQ4jqtT9ZKzNoU4P5NZIGMDRCh

can indeed not be verified .确实无法考证 The signature of this JWT is Base64url decoded:这个 JWT 的签名是 Base64url 解码的:

30818702416f08fabf05e5ef0134cdd5db8a8c47f72bf1636b4b9ea13b8f4070bd90173cdbcc1e9bb60ed2f9e52ba3d56174a4064e2005a0f4f8e20c63fafe1d2efee3e1da07024200c6e3d24e58b01cb033399efda74a4d0abb614b15bf26701a9f1a5605fbbf1b0c85ff9636f4094a0ec01bfcd0894690e23aad4fd64accda14e0fe4d64818c0d10a1

and thus ASN.1 encoded , s.因此ASN.1 编码,s。 here and here .这里这里 However, JWTs use a signature encoded as r|s , see eg here .但是,JWT 使用编码为r|s的签名,例如参见此处 If the signature is converted to this encoding, the result is:如果将签名转换为这种编码,则结果为:

6f08fabf05e5ef0134cdd5db8a8c47f72bf1636b4b9ea13b8f4070bd90173cdbcc1e9bb60ed2f9e52ba3d56174a4064e2005a0f4f8e20c63fafe1d2efee3e1da07c6e3d24e58b01cb033399efda74a4d0abb614b15bf26701a9f1a5605fbbf1b0c85ff9636f4094a0ec01bfcd0894690e23aad4fd64accda14e0fe4d64818c0d10a1

If this is Base64url encoded and used in the posted JWT (instead of the old signature), that is:如果这是 Base64url 编码并用于发布的 JWT(而不是旧签名),即:

eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJzdWIiOiI3MTNmNDJjOS03ZGY1LTQyNzEtOGI1My0xMTJmMzA5MzZjNTYifQ.bwj6vwXl7wE0zdXbioxH9yvxY2tLnqE7j0BwvZAXPNvMHpu2DtL55Suj1WF0pAZOIAWg9PjiDGP6_h0u_uPh2gfG49JOWLAcsDM5nv2nSk0Ku2FLFb8mcBqfGlYF-78bDIX_ljb0CUoOwBv80IlGkOI6rU_WSszaFOD-TWSBjA0QoQ

the JWT can be successfully validated . JWT 可以验证成功

Since the posted code generates an RFC compliant JWT (with r|s signature), the JWT posted in the question was probably not generated with the posted code (because of the ASN.1 signature).由于发布的代码生成符合 RFC 的 JWT(带有r|s签名),问题中发布的 JWT 可能不是使用发布的代码生成的(因为ASN.1签名)。


Update: According to the jjwt bugtracker there is a bug ( #125 ) that causes the signature to be signed incorrectly with ASN.1.更新:根据jjwt bugtracker ,有一个错误 ( #125 ) 会导致使用 ASN.1 错误地签署签名。 This bug should be fixed with jjwt 0.7 and would be a plausible explanation for your issue, provided you are working with an affected version (the bug is from 05.2016).如果您使用的是受影响的版本(该错误来自 05.2016),则此错误应使用jjwt 0.7修复,并且是对您的问题的合理解释。
I have tested your code with jjwt 0.9.1 (from 07.2018), which generates a valid signature, meaning it works.我已经使用jjwt 0.9.1 (从 07.2018 开始)测试了您的代码,它生成了一个有效的签名,这意味着它可以工作。
The current version is jjwt 0.11.2 (from 06.2020), which also works according to the other answer .当前版本是jjwt 0.11.2 (从 06.2020 开始),根据其他答案也可以使用。
So if you are working with an affected version, it would be best to use a newer / the current jjwt version.因此,如果您正在使用受影响的版本,最好使用更新的/当前的jjwt版本。 If this is not possible for some reason you can of course convert the signature manually from ASN.1 to r|s encoding.如果由于某种原因无法做到这一点,您当然可以将签名从ASN.1手动转换为r|s编码。

My IntelliJ is claiming that the "signWith"-line is deprecated.我的 IntelliJ 声称“signWith”行已弃用。

So changing your code所以改变你的代码

final JwtBuilder jwtBuilder = Jwts.builder()
  .setSubject("713f42c9-7df5-4271-8b53-112f30936c56")
  .signWith(SignatureAlgorithm.ES512, privateKey)
  .setHeaderParam("typ", "JWT");

to

final JwtBuilder jwtBuilder = Jwts.builder()
  .setSubject("713f42c9-7df5-4271-8b53-112f30936c56")
  .signWith(privateKey, SignatureAlgorithm.ES512)
  .setHeaderParam("typ", "JWT");

is giving this JWT:给出这个 JWT:

eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJzdWIiOiI3MTNmNDJjOS03ZGY1LTQyNzEtOGI1My0xMTJmMzA5MzZjNTYifQ.ALFk_BGerAstughF4ssl5eGQmx0mu5jvWb13QB228hAD5g8dwM-NvBsyevCuYUXLBJKzIUdPL-LVwQoPkwIbYrKhACzKwUwRN_v3IX2GIPW2ctTcRGPwA7gUaDWrOtwqcHALSfk20QZXT2TQfOnXX8tv0vhXLK_SnnHH5o1b96sa_HSR

As you only provided the ec private key I used OpenSSL to generate the EC Public key and passed the JWT and public key to the Online JWT verifier https://jwt.io/ and got the result " Signature verified ".由于您只提供了 ec 私钥,我使用 OpenSSL 生成 EC 公钥并将 JWT 和公钥传递给在线 JWT 验证器https://jwt.io/并得到结果“签名已验证”。

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM