如何从 Apple public 密钥 JSON 响应中获取 public 密钥 Java?
How to get public key from Apple public key JSON response in Java?
我们正在尝试在我们的 ios 应用程序中添加 Sign In with Apple。当客户端正常工作时,我们的后端是用 Java 编写的,我们无法解码 Apple 的 public 密钥。当您点击 URL https://appleid.apple.com/auth/keys 时,它会为您提供 public 键。但是当我尝试使 PublicKey
对象为 Java 时,它无法识别那里的 n
和 e
值。那些是 Base64 编码的吗?
当我尝试解码 Base64 中的 n
和 e
值时,它给了我 illegal character 2d
。我怀疑它是 base64 的原因是在 NodeJS 包 (https://www.npmjs.com/package/node-rsa) 中,他们正在通过 base64 解码 n 和 e 值。但问题是指数值 (e) 是 AQAB
,它永远不可能是 base64。如何从中创建 PublicKey 对象?
我使用的代码是:
HttpResponse<String> responsePublicKeyApple = Unirest.get("https://appleid.apple.com/auth/keys").asString();
ApplePublicKeyResponse applePublicKeyResponse = new Gson().fromJson(responsePublicKeyApple.getBody(),ApplePublicKeyResponse.class);
System.out.println("N: "+applePublicKeyResponse.getKeys().get(0).getN());
System.out.println("E: "+applePublicKeyResponse.getKeys().get(0).getE());
byte[] decodedBytesE = Base64.getDecoder().decode(applePublicKeyResponse.getKeys().get(0).getE());
String decodedE = new String(decodedBytesE);
System.out.println("decode E: "+decodedE);
byte[] decodedBytesN = Base64.getDecoder().decode(applePublicKeyResponse.getKeys().get(0).getN());
String decodedN = new String(decodedBytesN);
System.out.println("decode N: "+decodedN);
BigInteger bigIntegerN = new BigInteger(decodedN,16);
BigInteger bigIntegerE = new BigInteger(decodedE,16);
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(bigIntegerN,bigIntegerE);
KeyFactory keyFactory = KeyFactory.getInstance(SignatureAlgorithm.RS256.getValue());
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
解码部分 n
和 e
值失败。另一件事是 Apple 的回应是他们使用 RS256
算法对令牌进行签名,但是当我尝试这样做时
KeyFactory keyFactory = KeyFactory.getInstance(SignatureAlgorithm.RS256.getValue());
它说 RS256 keyfactory is not available
。
我该如何解决这两个问题?请帮忙。
实际上 N
和 E
值是使用 base64url as explained in RFC7518 编码的。此代码将向您展示如何执行您的请求。我用 Jackson 阅读了你提供的 JSON :
String json = Files.lines(Paths.get("src/main/resources/test.json")).collect(Collectors.joining());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// here is the parsing of PublicKey
ApplePublicKeyResponse applePublicKeyResponse = objectMapper.readValue(json, ApplePublicKeyResponse.class);
Key key = applePublicKeyResponse.getKeys().get(0);
byte[] nBytes = Base64.getUrlDecoder().decode(key.getN());
byte[] eBytes = Base64.getUrlDecoder().decode(key.getE());
BigInteger n = new BigInteger(1, nBytes);
BigInteger e = new BigInteger(1, eBytes);
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(n,e);
KeyFactory keyFactory = KeyFactory.getInstance(key.getKty()); //kty will be "RSA"
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
另外如前所述,key.getKty()
将 return "RSA"
。因此,您应该将此值传递给 KeyFactory.getInstance
,因为您要解析 RSA 密钥,而 RS256
是使用 RSA 和 SHA-256 哈希的签名算法的名称。
我使用了 BigInteger
的构造函数,它接受符号和原始字节。 Signum 设置为 1 以获得正值。
我们正在尝试在我们的 ios 应用程序中添加 Sign In with Apple。当客户端正常工作时,我们的后端是用 Java 编写的,我们无法解码 Apple 的 public 密钥。当您点击 URL https://appleid.apple.com/auth/keys 时,它会为您提供 public 键。但是当我尝试使 PublicKey
对象为 Java 时,它无法识别那里的 n
和 e
值。那些是 Base64 编码的吗?
当我尝试解码 Base64 中的 n
和 e
值时,它给了我 illegal character 2d
。我怀疑它是 base64 的原因是在 NodeJS 包 (https://www.npmjs.com/package/node-rsa) 中,他们正在通过 base64 解码 n 和 e 值。但问题是指数值 (e) 是 AQAB
,它永远不可能是 base64。如何从中创建 PublicKey 对象?
我使用的代码是:
HttpResponse<String> responsePublicKeyApple = Unirest.get("https://appleid.apple.com/auth/keys").asString();
ApplePublicKeyResponse applePublicKeyResponse = new Gson().fromJson(responsePublicKeyApple.getBody(),ApplePublicKeyResponse.class);
System.out.println("N: "+applePublicKeyResponse.getKeys().get(0).getN());
System.out.println("E: "+applePublicKeyResponse.getKeys().get(0).getE());
byte[] decodedBytesE = Base64.getDecoder().decode(applePublicKeyResponse.getKeys().get(0).getE());
String decodedE = new String(decodedBytesE);
System.out.println("decode E: "+decodedE);
byte[] decodedBytesN = Base64.getDecoder().decode(applePublicKeyResponse.getKeys().get(0).getN());
String decodedN = new String(decodedBytesN);
System.out.println("decode N: "+decodedN);
BigInteger bigIntegerN = new BigInteger(decodedN,16);
BigInteger bigIntegerE = new BigInteger(decodedE,16);
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(bigIntegerN,bigIntegerE);
KeyFactory keyFactory = KeyFactory.getInstance(SignatureAlgorithm.RS256.getValue());
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
解码部分 n
和 e
值失败。另一件事是 Apple 的回应是他们使用 RS256
算法对令牌进行签名,但是当我尝试这样做时
KeyFactory keyFactory = KeyFactory.getInstance(SignatureAlgorithm.RS256.getValue());
它说 RS256 keyfactory is not available
。
我该如何解决这两个问题?请帮忙。
实际上 N
和 E
值是使用 base64url as explained in RFC7518 编码的。此代码将向您展示如何执行您的请求。我用 Jackson 阅读了你提供的 JSON :
String json = Files.lines(Paths.get("src/main/resources/test.json")).collect(Collectors.joining());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// here is the parsing of PublicKey
ApplePublicKeyResponse applePublicKeyResponse = objectMapper.readValue(json, ApplePublicKeyResponse.class);
Key key = applePublicKeyResponse.getKeys().get(0);
byte[] nBytes = Base64.getUrlDecoder().decode(key.getN());
byte[] eBytes = Base64.getUrlDecoder().decode(key.getE());
BigInteger n = new BigInteger(1, nBytes);
BigInteger e = new BigInteger(1, eBytes);
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(n,e);
KeyFactory keyFactory = KeyFactory.getInstance(key.getKty()); //kty will be "RSA"
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
另外如前所述,key.getKty()
将 return "RSA"
。因此,您应该将此值传递给 KeyFactory.getInstance
,因为您要解析 RSA 密钥,而 RS256
是使用 RSA 和 SHA-256 哈希的签名算法的名称。
我使用了 BigInteger
的构造函数,它接受符号和原始字节。 Signum 设置为 1 以获得正值。