在 Java 中生成有效的 ES256 签名
Generate a valid ES256 signature in Java
我正在尝试在 URL 中集成需要签名查询参数的 Apple Map Web 快照。我能够在 NPM 的 JWA 包中成功生成和验证 ES256 签名,但在 Java 中不能。请帮助我找到等效的库来生成有效的签名,我在 Java.
中尝试了几个 JWA 库
// Required modules.
const { readFileSync } = require("fs");
const { sign } = require("jwa")("ES256");
/* Read your private key from the file system. (Never add your private key
* in code or in source control. Always keep it secure.)
*/
const privateKey = readFileSync("[file_system_path]");
// Replace the team ID and key ID values with your actual values.
const teamId = "[team ID]";
const keyId = "[key ID]";
// Creates the signature string and returns the full Snapshot request URL including the signature.
function sign(params) {
const snapshotPath = `/api/v1/snapshot?${params}`;
const completePath = `${snapshotPath}&teamId=${teamId}&keyId=${keyId}`;
const signature = sign(completePath, privateKey);
// In this example, the jwa module returns the signature as a Base64 URL-encoded string.
// Append the signature to the end of the request URL, and return.
return `${completePath}&signature=${signature}`;
}
// Call the sign function with a simple map request.
sign("center=apple+park")
// The return value expected is: "/api/v1/snapshot?center=apple+park&teamId=[team ID]&keyId=[key ID]&signature=[base64_url_encoded_signature]"
Apache CXF - 此库生成类似于节点中的 JWA 模块但未能通过身份验证。
String teamId = [Team Id];
String keyId = [Key id];
String privateKey = [private key path];
String privateKeyContent = getKeyFileContent(privateKey);
String API_VERSION_PATH = "/api/v1/snapshot?";
String param = [QueryParam];
//example -> param = "center=[city,country or lat,lang]&size=90x90&lang=en&radius=2";
String params = param + "&teamId="+ teamId + "&keyId=" + keyId;
String payload = API_VERSION_PATH + params;
PrivateKey key = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(
Base64.decodeBase64(privateKeyContent)));
JwsCompactProducer compactProducer = new JwsCompactProducer(payload);
compactProducer.getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.ES256);
//compactProducer.getJwsHeaders().setKeyId(keyId);
compactProducer.signWith(key);
String signed = compactProducer.getEncodedSignature();
String encodedSignature = new String(Base64.encodeBase64URLSafe(compactProducer.getEncodedSignature().getBytes()));
System.out.println(SNAPSHOT_API_PATH + payload + "&signature=" + signed);
JJWT - 此库生成大签名,然后在节点模块中生成签名。
String signed = new String(Base64.encodeBase64URLSafe(Jwts.builder().setPayload(payload)
.signWith(io.jsonwebtoken.SignatureAlgorithm.ES256, key).compact().getBytes()));
System.out.println(SNAPSHOT_API_PATH + payload + "&signature=" + signed);
示例输出签名
compactProducer.getEncodedSignature() signed --> qQ5G9_lwGJ9w158FVSmtPx_iH43xlg2_gx9BlHEJbER73xpAeIHtDRnT8wnveH_UEPxNe7Zgv4csJ48Oiq-ZIQ
Base64.encodeBase64URLSafe(signature) --> cVE1RzlfbHdHSjl3MTU4RlZTbXRQeF9pSDQzeGxnMl9neDlCbEhFSmJFUjczeHBBZUlIdERSblQ4d252ZUhfVUVQeE5lN1pndjRjc0o0OE9pcS1aSVE
JJWT signed -> ZXlKaGJHY2lPaUpGVXpJMU5pSjkuTDJGd2FTOTJNUzl6Ym1Gd2MyaHZkRDlqWlc1MFpYSTlRM1Z3WlhKMGFXNXZMRlZUUVNaMFpXRnRTV1E5V0ZaWU5GWlhSbEZUTXlaclpYbEpaRDFWUVRWTlNGWlhWMWhMLlExUEtoeGwzSjFoVWVUWGtmeXRLckliYm5zeDdZem5lZVpxTVc4WkJOVU9uLVlYeFhyTExVU05ZVTZCSG5Xc3FheFd3YVB5dlF0Yml4TVBSZGdjamJ3
NodeJS代码中的签名由jwa('ES256')#sign
方法生成,具有以下功能:
- ES256:使用 P-256 曲线和 SHA-256 哈希算法的 ECDSA [1]。
- 签名将是 (r, s) 对,其中 r 和 s 是 256 位无符号整数 [2]。
- 签名是 base64url 编码的[3]。
广告 1:在 Java 中可以使用 on-board 方法(SunEC 提供商,Java 1.7 或更高版本)在 Java 中实现 ES256 的相应实现,[4]:
Signature ecdsa = Signature.getInstance("SHA256withECDSA");
ecdsa.initSign(privateKey);
String payload = "The quick brown fox jumps over the lazy dog";
ecdsa.update(payload.getBytes(StandardCharsets.UTF_8));
byte[] signatureDER = ecdsa.sign();
这里的privateKey
是java.security.PrivateKey
类型的私钥,类比CXF代码中的key
广告 2:Java 代码 returns ASN.1 DER 格式的签名,因此必须转换为 (r,s) 格式 [5]. Either a user-defined method can be implemented or a method from a supporting library can be used, e.g. the method com.nimbusds.jose.crypto.impl.ECDSA.transcodeSignatureToConcat
of the Nimbus JOSE + JWT library [6][7][8]:
byte[] signature = transcodeSignatureToConcat(signatureDER, 64);
广告 3:在 Java 中可以使用 Base64url 编码,其中 on-board 表示 [9]:
String signatureBase64url = Base64.getUrlEncoder().withoutPadding().encodeToString(signature);
由于每次都会生成不同的签名,因此无法直接比较两种代码中生成的签名。但是,可以通过使用 jwa-npm 库验证 Java 代码中生成的签名来测试与 jwa-npm 库的兼容性:
const jwa = require("jwa");
const ecdsa = jwa('ES256');
var message = "The quick brown fox jumps over the lazy dog";
var verify = ecdsa.verify(message, signatureBase64url, publicKey);
这里,signatureBase64url
是用Java代码生成的签名。 publicKey
是 X.509 PEM 格式中对应的 public 密钥 (-----BEGIN PUBLIC KEY-----...
) [10].
jwa('ES256')#sign
方法的功能与发布的 JJWT 或 Apache CXF 代码不同:最后两个生成 JWT [11]。 header 是 {"alg": "ES256"}
的 base64url 编码。因此,签名是 Base64url 编码的 header 和 Base64url 编码的有效载荷,两者由点分隔:
String payload = "The quick brown fox jumps over the lazy dog";
//JJWT
String jwtJJWT = Jwts.builder().setPayload(payload).signWith(io.jsonwebtoken.SignatureAlgorithm.ES256, privateKey).compact();
//CXF
JwsCompactProducer compactProducer = new JwsCompactProducer(payload);
compactProducer.getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.ES256);
String jwtCXF = compactProducer.signWith(privateKey);
String signatureCXF = compactProducer.getEncodedSignature(); // signature, 3. portion of JWT
由此生成的 JWT 示例:
eyJhbGciOiJFUzI1NiJ9.VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw.rcrzqr3ovu7SH9ci-S6deLn6BuiQkNv9CmeOnUPwva30pfK9BOX0YOgjZP5T08wjxCBTTHV3aex0M76toL8qpw
我正在尝试在 URL 中集成需要签名查询参数的 Apple Map Web 快照。我能够在 NPM 的 JWA 包中成功生成和验证 ES256 签名,但在 Java 中不能。请帮助我找到等效的库来生成有效的签名,我在 Java.
中尝试了几个 JWA 库// Required modules.
const { readFileSync } = require("fs");
const { sign } = require("jwa")("ES256");
/* Read your private key from the file system. (Never add your private key
* in code or in source control. Always keep it secure.)
*/
const privateKey = readFileSync("[file_system_path]");
// Replace the team ID and key ID values with your actual values.
const teamId = "[team ID]";
const keyId = "[key ID]";
// Creates the signature string and returns the full Snapshot request URL including the signature.
function sign(params) {
const snapshotPath = `/api/v1/snapshot?${params}`;
const completePath = `${snapshotPath}&teamId=${teamId}&keyId=${keyId}`;
const signature = sign(completePath, privateKey);
// In this example, the jwa module returns the signature as a Base64 URL-encoded string.
// Append the signature to the end of the request URL, and return.
return `${completePath}&signature=${signature}`;
}
// Call the sign function with a simple map request.
sign("center=apple+park")
// The return value expected is: "/api/v1/snapshot?center=apple+park&teamId=[team ID]&keyId=[key ID]&signature=[base64_url_encoded_signature]"
Apache CXF - 此库生成类似于节点中的 JWA 模块但未能通过身份验证。
String teamId = [Team Id];
String keyId = [Key id];
String privateKey = [private key path];
String privateKeyContent = getKeyFileContent(privateKey);
String API_VERSION_PATH = "/api/v1/snapshot?";
String param = [QueryParam];
//example -> param = "center=[city,country or lat,lang]&size=90x90&lang=en&radius=2";
String params = param + "&teamId="+ teamId + "&keyId=" + keyId;
String payload = API_VERSION_PATH + params;
PrivateKey key = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(
Base64.decodeBase64(privateKeyContent)));
JwsCompactProducer compactProducer = new JwsCompactProducer(payload);
compactProducer.getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.ES256);
//compactProducer.getJwsHeaders().setKeyId(keyId);
compactProducer.signWith(key);
String signed = compactProducer.getEncodedSignature();
String encodedSignature = new String(Base64.encodeBase64URLSafe(compactProducer.getEncodedSignature().getBytes()));
System.out.println(SNAPSHOT_API_PATH + payload + "&signature=" + signed);
JJWT - 此库生成大签名,然后在节点模块中生成签名。
String signed = new String(Base64.encodeBase64URLSafe(Jwts.builder().setPayload(payload)
.signWith(io.jsonwebtoken.SignatureAlgorithm.ES256, key).compact().getBytes()));
System.out.println(SNAPSHOT_API_PATH + payload + "&signature=" + signed);
示例输出签名
compactProducer.getEncodedSignature() signed --> qQ5G9_lwGJ9w158FVSmtPx_iH43xlg2_gx9BlHEJbER73xpAeIHtDRnT8wnveH_UEPxNe7Zgv4csJ48Oiq-ZIQ
Base64.encodeBase64URLSafe(signature) --> cVE1RzlfbHdHSjl3MTU4RlZTbXRQeF9pSDQzeGxnMl9neDlCbEhFSmJFUjczeHBBZUlIdERSblQ4d252ZUhfVUVQeE5lN1pndjRjc0o0OE9pcS1aSVE
JJWT signed -> ZXlKaGJHY2lPaUpGVXpJMU5pSjkuTDJGd2FTOTJNUzl6Ym1Gd2MyaHZkRDlqWlc1MFpYSTlRM1Z3WlhKMGFXNXZMRlZUUVNaMFpXRnRTV1E5V0ZaWU5GWlhSbEZUTXlaclpYbEpaRDFWUVRWTlNGWlhWMWhMLlExUEtoeGwzSjFoVWVUWGtmeXRLckliYm5zeDdZem5lZVpxTVc4WkJOVU9uLVlYeFhyTExVU05ZVTZCSG5Xc3FheFd3YVB5dlF0Yml4TVBSZGdjamJ3
NodeJS代码中的签名由jwa('ES256')#sign
方法生成,具有以下功能:
- ES256:使用 P-256 曲线和 SHA-256 哈希算法的 ECDSA [1]。
- 签名将是 (r, s) 对,其中 r 和 s 是 256 位无符号整数 [2]。
- 签名是 base64url 编码的[3]。
广告 1:在 Java 中可以使用 on-board 方法(SunEC 提供商,Java 1.7 或更高版本)在 Java 中实现 ES256 的相应实现,[4]:
Signature ecdsa = Signature.getInstance("SHA256withECDSA");
ecdsa.initSign(privateKey);
String payload = "The quick brown fox jumps over the lazy dog";
ecdsa.update(payload.getBytes(StandardCharsets.UTF_8));
byte[] signatureDER = ecdsa.sign();
这里的privateKey
是java.security.PrivateKey
类型的私钥,类比CXF代码中的key
广告 2:Java 代码 returns ASN.1 DER 格式的签名,因此必须转换为 (r,s) 格式 [5]. Either a user-defined method can be implemented or a method from a supporting library can be used, e.g. the method com.nimbusds.jose.crypto.impl.ECDSA.transcodeSignatureToConcat
of the Nimbus JOSE + JWT library [6][7][8]:
byte[] signature = transcodeSignatureToConcat(signatureDER, 64);
广告 3:在 Java 中可以使用 Base64url 编码,其中 on-board 表示 [9]:
String signatureBase64url = Base64.getUrlEncoder().withoutPadding().encodeToString(signature);
由于每次都会生成不同的签名,因此无法直接比较两种代码中生成的签名。但是,可以通过使用 jwa-npm 库验证 Java 代码中生成的签名来测试与 jwa-npm 库的兼容性:
const jwa = require("jwa");
const ecdsa = jwa('ES256');
var message = "The quick brown fox jumps over the lazy dog";
var verify = ecdsa.verify(message, signatureBase64url, publicKey);
这里,signatureBase64url
是用Java代码生成的签名。 publicKey
是 X.509 PEM 格式中对应的 public 密钥 (-----BEGIN PUBLIC KEY-----...
) [10].
jwa('ES256')#sign
方法的功能与发布的 JJWT 或 Apache CXF 代码不同:最后两个生成 JWT [11]。 header 是 {"alg": "ES256"}
的 base64url 编码。因此,签名是 Base64url 编码的 header 和 Base64url 编码的有效载荷,两者由点分隔:
String payload = "The quick brown fox jumps over the lazy dog";
//JJWT
String jwtJJWT = Jwts.builder().setPayload(payload).signWith(io.jsonwebtoken.SignatureAlgorithm.ES256, privateKey).compact();
//CXF
JwsCompactProducer compactProducer = new JwsCompactProducer(payload);
compactProducer.getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.ES256);
String jwtCXF = compactProducer.signWith(privateKey);
String signatureCXF = compactProducer.getEncodedSignature(); // signature, 3. portion of JWT
由此生成的 JWT 示例:
eyJhbGciOiJFUzI1NiJ9.VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw.rcrzqr3ovu7SH9ci-S6deLn6BuiQkNv9CmeOnUPwva30pfK9BOX0YOgjZP5T08wjxCBTTHV3aex0M76toL8qpw