如何从Java中的Apple公钥JSON响应中获取公钥?

时间:2019-12-05 07:41:51

标签: java ios spring sign-in-with-apple

我们正在尝试在ios应用程序中添加“使用Apple登录”。当客户端运行良好时,我们的后端是用Java编写的,因此我们无法解码Apple的公钥。当您点击URL https://appleid.apple.com/auth/keys时,它将为您提供公共密钥。但是,当我尝试使PublicKey对象为Java时,它无法从那里识别ne的值。那些Base64编码了吗?

当我尝试在Base64中解码ne值时,它给了我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);

无法解码ne值的一部分。另一件事是苹果的回应是说他们使用RS256算法对令牌进行签名,但是当我尝试这样做时

KeyFactory keyFactory = KeyFactory.getInstance(SignatureAlgorithm.RS256.getValue());

RS256 keyfactory is not available

如何解决这两个问题?请帮忙。

1 个答案:

答案 0 :(得分:1)

实际上,如base64url中所述,这些NE值是使用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()将返回"RSA"。因此,在解析RSA密钥时,应将此值传递给KeyFactory.getInstance,而RS256是使用RSA和SHA-256哈希的签名算法的名称。

然后我使用了BigInteger的构造函数,该构造函数使用符号和原始字节。将Signum设置为1可获得正值。