提问人:vcima 提问时间:3/27/2020 最后编辑:Zoe is on strikevcima 更新时间:9/20/2021 访问量:15065
如何在 Java 中验证 Azure AD JWT 令牌?
How can I validate an Azure AD JWT Token in Java?
问:
我有一个使用 Msal 库获取的 Azure AD JWT 令牌,但当我尝试验证此令牌时,会出现以下问题:
客户端:SharePoint Web 部件
const config = {
auth: {
clientId: "xxxxx",
authority: "https://login.microsoftonline.com/yyyyyy"
}
};
const myMSALObj = new UserAgentApplication(config);
let accessTokenRequest = {
scopes: ["user.read"],
loginHint: this.context.pageContext.user.loginName,
extraQueryParameters: {domain_hint: 'organizations'}
}
myMSALObj.acquireTokenSilent(accessTokenRequest).then(
function(accessTokenResponse) {
// Acquire token silent success
let accessToken = accessTokenResponse.accessToken;
我正在尝试从以下位置检索公钥:https://login.microsoftonline.com/tenant-id/.well-known/openid-configuration 但是使用此公钥,我永远无法重定向到:https://login.microsoftonline.com/common/discovery/keys
在这种情况下,有没有其他方法可以获取公钥?
服务器:验证
public PublicKey getPublicKeyFromParams(String e, String n){
byte[] modulusBytes = Base64.getUrlDecoder().decode(n);
BigInteger modulusInt = new BigInteger(1, modulusBytes);
byte[] exponentBytes = Base64.getUrlDecoder().decode(e);
BigInteger exponentInt = new BigInteger(1, exponentBytes);
KeyFactory keyFactory;
RSAPublicKeySpec publicSpec = new RSAPublicKeySpec(modulusInt, exponentInt);
try {
keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(publicSpec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException ex) {
ex.printStackTrace();
}
return null;
}
@Test
public void name() throws Exception {
String jwt = "xxxxxxx";
KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.RS256);
String e = Base64.getUrlEncoder().encodeToString((((RSAPublicKeyImpl) keyPair.getPublic()).getPublicExponent()).toByteArray());
String n = Base64.getUrlEncoder().encodeToString((((RSAPublicKeyImpl) keyPair.getPublic()).getModulus()).toByteArray());
System.out.println("Public Key: " + Base64.getUrlEncoder().encodeToString(keyPair.getPublic().getEncoded()));
System.out.println("Public Key Exponent: " + e);
System.out.println("Public Key Modulus: " + n);
String jws = Jwts.builder().setSubject("pepe").signWith(keyPair.getPrivate()).compact();
System.out.println("Generated JWS:" + jws);
PublicKey publicKey = getPublicKeyFromParams(e, n);
Jwt parsedJWs = Jwts.parserBuilder().setSigningKey(publicKey).build().parse(jws);
System.out.println("Parsed JWS: " + parsedJWs);
publicKey = getPublicKeyFromParams(eValue, nValue);
System.out.println("Azure PublicKey fron n-e: " + publicKey);
CertificateFactory factory = CertificateFactory.getInstance("X.509");
Certificate cert = factory.generateCertificate(new ByteArrayInputStream(
DatatypeConverter.parseBase64Binary("cccccccccccccccccc")));
publicKey = cert.getPublicKey();
System.out.println("Azure PublicKey from x5c: " + publicKey);
Jwt jwtParsed = Jwts.parserBuilder().setSigningKey(publicKey).build().parse(jwt);
System.out.println(jwtParsed);
}
public static PublicKey getPublicKey(String key){
try{
byte[] byteKey = Base64.getDecoder().decode(key);
X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(byteKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(X509publicKey);
}
catch(Exception e){
e.printStackTrace();
}
return null;
}
答:
6赞
Jim Xu
3/31/2020
#1
如果要验证 Azure AD 访问令牌,我们可以尝试使用 SDK 并实现它。java-jwt
jwks-rsa
例如
- 通过 maven 安装 SDK
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-storage</artifactId>
<version>8.6.2</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>jwks-rsa</artifactId>
<version>0.11.0</version>
</dependency>
法典
A. 验证签名
String token="<your AD token>"; DecodedJWT jwt = JWT.decode(token); System.out.println(jwt.getKeyId()); JwkProvider provider = null; Jwk jwk =null; Algorithm algorithm=null; try { provider = new UrlJwkProvider(new URL("https://login.microsoftonline.com/common/discovery/keys")); jwk = provider.get(jwt.getKeyId()); algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null); algorithm.verify(jwt);// if the token signature is invalid, the method will throw SignatureVerificationException } catch (MalformedURLException e) { e.printStackTrace(); } catch (JwkException e) { e.printStackTrace(); }catch(SignatureVerificationException e){ System.out.println(e.getMessage()); }
评论
0赞
vcima
3/31/2020
嗨,JIm!。感谢您的回复。使用您的代码,我遇到了同样的问题:我已通过以下命令获取令牌: myMSALObj.acquireTokenSilent(accessTokenRequest).then(function(accessTokenResponse) { // 获取令牌 silent success // 使用 token 调用 API let accessToken = accessTokenResponse.accessToken;
1赞
vcima
3/31/2020
您的示例的验证错误是这样的:使用算法验证时,令牌的签名无效:SHA256withRSA 你有什么想法吗?
0赞
Jim Xu
3/31/2020
@vcima 是你用sdk来需要令牌吗?msal.js
0赞
vcima
3/31/2020
嗨@Jim XU,是的,我正在 Sharepoint WebPart 中使用 Msal 库并导入:import {UserAgentApplication} from “msal”;。这有问题吗?
0赞
vcima
3/31/2020
const config = { auth: { clientId: “xxxxx”, authority: “login.microsoftonline.com/yyyyyy” } };const myMSALObj = new UserAgentApplication(config);let accessTokenRequest = { scopes: [“user.read”], loginHint: this.context.pageContext.user.loginName, extraQueryParameters: {domain_hint: 'organizations'} } myMSALObj.acquireTokenSilent(accessTokenRequest).then( function(accessTokenResponse) { // 获取令牌 silent success let accessToken = accessTokenResponse.accessToken;
5赞
vcima
4/1/2020
#2
通过此更改,验证工作正常。
let accessTokenRequest = {
scopes:["clientId/.default"],
loginHint: this.context.pageContext.user.loginName,
extraQueryParameters: {domain_hint: 'organizations'}
}
评论
0赞
atGuz
5/25/2021
#3
您需要使用 Microsoft 的新密钥才能使上述代码正常工作。改变:
provider = new UrlJwkProvider(new URL(“https://login.microsoftonline.com/common/discovery/keys”));
自
provider = new UrlJwkProvider(new URL(“https://login.microsoftonline.com/common/discovery/v2.0/keys”));
对于上面的示例,缺少一些依赖项,正确的示例应该是:
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-storage</artifactId>
<version>8.6.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.16.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.auth0/jwks-rsa -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>jwks-rsa</artifactId>
<version>0.18.0</version>
</dependency>
和代码:
String token="YOUR JWT";
DecodedJWT jwt = JWT.decode(token);
System.out.println(jwt.getKeyId());
JwkProvider provider = null;
Jwk jwk =null;
Algorithm algorithm=null;
try {
provider = new UrlJwkProvider(new URL("https://login.microsoftonline.com/common/discovery/v2.0/keys"));
jwk = provider.get(jwt.getKeyId());
algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null);
algorithm.verify(jwt);// if the token signature is invalid, the method will throw SignatureVerificationException
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (JwkException e) {
e.printStackTrace();
}catch(SignatureVerificationException e){
System.out.println(e.getMessage());
}
评论
0赞
Java Backend Developer
5/27/2021
当我尝试此代码时,我在以下行出现 sslhand shake 异常。jwk = provider.get(jwt.getKeyId());我们是否需要在 JKS 中添加任何公共证书?
0赞
Paramesh Korrakuti
9/26/2021
这甚至可以验证过期的令牌。有没有办法自定义验证选项以包括到期时间、颁发者等?
评论