1 package com.atlassian.asap.nimbus.parser;
2
3 import com.atlassian.asap.api.AlgorithmType;
4 import com.atlassian.asap.api.JwsHeader;
5 import com.atlassian.asap.api.Jwt;
6 import com.atlassian.asap.api.JwtBuilder;
7 import com.atlassian.asap.api.JwtClaims;
8 import com.atlassian.asap.api.SigningAlgorithm;
9 import com.atlassian.asap.core.exception.SignatureMismatchException;
10 import com.atlassian.asap.core.exception.UnsupportedAlgorithmException;
11 import com.atlassian.asap.core.parser.VerifiableJwt;
12 import com.google.common.collect.Maps;
13 import com.nimbusds.jose.JOSEException;
14 import com.nimbusds.jose.JWSObject;
15 import com.nimbusds.jose.JWSVerifier;
16 import com.nimbusds.jose.crypto.ECDSAVerifier;
17 import com.nimbusds.jose.crypto.RSASSAVerifier;
18 import com.nimbusds.jwt.JWTClaimsSet;
19 import net.minidev.json.JSONObject;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 import javax.json.JsonObject;
24 import java.security.Provider;
25 import java.security.PublicKey;
26 import java.security.interfaces.ECPublicKey;
27 import java.security.interfaces.RSAPublicKey;
28 import java.util.Date;
29 import java.util.Locale;
30 import java.util.Map;
31 import java.util.Optional;
32 import java.util.Set;
33
34 import static com.google.common.base.Predicates.in;
35 import static com.google.common.base.Predicates.not;
36
37 public class NimbusVerifiableJwt implements VerifiableJwt {
38 private static final Logger logger = LoggerFactory.getLogger(NimbusVerifiableJwt.class);
39 private static final Set<String> REGISTERED_CLAIM_NAMES = JWTClaimsSet.getRegisteredNames();
40
41 private final Jwt unverifiedJwt;
42 private final JWSObject jwsObject;
43
44 private final Provider provider;
45
46 public NimbusVerifiableJwt(final Jwt unverifiedJwt, final JWSObject jwsObject, final Provider provider) {
47 this.unverifiedJwt = unverifiedJwt;
48 this.jwsObject = jwsObject;
49 this.provider = provider;
50 }
51
52
53
54
55
56
57
58
59
60
61 public static VerifiableJwt buildVerifiableJwt(final JWSObject jwsObject, final JWTClaimsSet claims, final Provider provider)
62 throws UnsupportedAlgorithmException {
63 Map<String, Object> customClaimsMap = Maps.filterKeys(claims.getClaims(), not(in(REGISTERED_CLAIM_NAMES)));
64 JsonObject customClaims = (JsonObject) NimbusJsr353Translator.nimbusToJsr353(new JSONObject(customClaimsMap));
65
66 Jwt unverifiedJwt = JwtBuilder.newJwt()
67 .algorithm(getSigningAlgorithm(jwsObject.getHeader().getAlgorithm().getName()))
68 .keyId(jwsObject.getHeader().getKeyID())
69 .issuer(claims.getIssuer())
70 .jwtId(claims.getJWTID())
71 .subject(Optional.ofNullable(claims.getSubject()))
72 .audience(claims.getAudience())
73 .expirationTime(claims.getExpirationTime().toInstant())
74 .issuedAt(claims.getIssueTime().toInstant())
75 .notBefore(Optional.ofNullable(claims.getNotBeforeTime()).map(Date::toInstant))
76 .customClaims(customClaims)
77 .build();
78
79 return new NimbusVerifiableJwt(unverifiedJwt, jwsObject, provider);
80 }
81
82 @Override
83 public void verifySignature(final PublicKey publicKey) throws SignatureMismatchException, UnsupportedAlgorithmException {
84 try {
85 if (!jwsObject.verify(verifierFor(unverifiedJwt.getHeader().getAlgorithm(), publicKey, provider))) {
86
87 logger.debug("Invalid JWT signature");
88 throw new SignatureMismatchException("Invalid JWT signature");
89 }
90 } catch (JOSEException e) {
91 logger.error("Unexpected error when verifying a JWT signature", e);
92 throw new SignatureMismatchException("Unexpected error when verifying JWT signature");
93 }
94 }
95
96 private static JWSVerifier verifierFor(final SigningAlgorithm algorithm, final PublicKey publicKey, final Provider provider)
97 throws UnsupportedAlgorithmException {
98 if ((algorithm.type() == AlgorithmType.RSA || algorithm.type() == AlgorithmType.RSASSA_PSS) &&
99 publicKey instanceof RSAPublicKey) {
100 RSASSAVerifier rsassaVerifier = new RSASSAVerifier((RSAPublicKey) publicKey);
101 rsassaVerifier.getJCAContext().setProvider(provider);
102 return rsassaVerifier;
103 } else if (algorithm.type() == AlgorithmType.ECDSA && publicKey instanceof ECPublicKey) {
104 try {
105 ECPublicKey ecPublicKey = (ECPublicKey) publicKey;
106 ECDSAVerifier ecdsaVerifier = new ECDSAVerifier(ecPublicKey);
107 ecdsaVerifier.getJCAContext().setProvider(provider);
108 return ecdsaVerifier;
109 } catch (JOSEException e) {
110
111 logger.debug("Unsupported signing algorithm {} or public key algorithm {}", algorithm, publicKey.getAlgorithm());
112 throw new UnsupportedAlgorithmException(algorithm.name(), e);
113 }
114 } else {
115
116 logger.debug("Unsupported signing algorithm {} or public key algorithm {}", algorithm, publicKey.getAlgorithm());
117 throw new UnsupportedAlgorithmException(algorithm.name());
118 }
119 }
120
121
122 private static SigningAlgorithm getSigningAlgorithm(String algorithm) throws UnsupportedAlgorithmException {
123 try {
124 return SigningAlgorithm.valueOf(algorithm.toUpperCase(Locale.ROOT));
125 } catch (IllegalArgumentException e) {
126 throw new UnsupportedAlgorithmException(algorithm + " is not a supported asymmetric JWS algorithm");
127 }
128 }
129
130 @Override
131 public JwsHeader getHeader() {
132 return unverifiedJwt.getHeader();
133 }
134
135 @Override
136 public JwtClaims getClaims() {
137 return unverifiedJwt.getClaims();
138 }
139 }