1 package com.atlassian.asap.nimbus.serializer;
2
3 import com.atlassian.asap.api.AlgorithmType;
4 import com.atlassian.asap.api.Jwt;
5 import com.atlassian.asap.api.JwtClaims;
6 import com.atlassian.asap.api.SigningAlgorithm;
7 import com.atlassian.asap.core.SecurityProvider;
8 import com.atlassian.asap.core.exception.SigningException;
9 import com.atlassian.asap.core.exception.UnsupportedAlgorithmException;
10 import com.atlassian.asap.core.serializer.JwtSerializer;
11 import com.google.common.annotations.VisibleForTesting;
12 import com.nimbusds.jose.JOSEException;
13 import com.nimbusds.jose.JWSAlgorithm;
14 import com.nimbusds.jose.JWSHeader;
15 import com.nimbusds.jose.JWSObject;
16 import com.nimbusds.jose.JWSSigner;
17 import com.nimbusds.jose.Payload;
18 import com.nimbusds.jose.crypto.ECDSASigner;
19 import com.nimbusds.jose.crypto.RSASSASigner;
20 import net.minidev.json.JSONObject;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 import java.security.PrivateKey;
25 import java.security.Provider;
26 import java.security.interfaces.ECPrivateKey;
27 import java.security.interfaces.RSAPrivateKey;
28
29
30
31
32 public class NimbusJwtSerializer implements JwtSerializer {
33 private static final Logger logger = LoggerFactory.getLogger(NimbusJwtSerializer.class);
34
35 private final Provider provider;
36
37 public NimbusJwtSerializer() {
38 this(SecurityProvider.getProvider());
39 }
40
41 public NimbusJwtSerializer(Provider provider) {
42 this.provider = provider;
43 }
44
45 @Override
46 public String serialize(Jwt jwt, PrivateKey privateKey) throws SigningException, UnsupportedAlgorithmException {
47 JWSObject jwsObject = getSignedJwsObject(jwt, privateKey);
48
49 return jwsObject.serialize();
50 }
51
52 @VisibleForTesting
53 JWSObject getSignedJwsObject(Jwt jwt, PrivateKey privateKey) throws UnsupportedAlgorithmException {
54 SigningAlgorithm algorithm = jwt.getHeader().getAlgorithm();
55 JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.parse(algorithm.name()))
56 .keyID(jwt.getHeader().getKeyId())
57 .build();
58 Payload payload = new Payload(toJsonPayload(jwt.getClaims()));
59 JWSObject jwsObject = new JWSObject(header, payload);
60 try {
61 jwsObject.sign(getSigner(algorithm, privateKey));
62 } catch (JOSEException e) {
63 logger.error("Unexpected error when signing JWT token", e);
64 throw new SigningException();
65 }
66 return jwsObject;
67 }
68
69 private JWSSigner getSigner(SigningAlgorithm algorithm, PrivateKey privateKey) throws UnsupportedAlgorithmException {
70 if ((algorithm.type() == AlgorithmType.RSA || algorithm.type() == AlgorithmType.RSASSA_PSS) && privateKey instanceof RSAPrivateKey) {
71 return createRSASSASignerForKey((RSAPrivateKey) privateKey);
72 } else if (algorithm.type() == AlgorithmType.ECDSA && privateKey instanceof ECPrivateKey) {
73 return createECDSASignerForKey((ECPrivateKey) privateKey);
74 } else {
75 throw new UnsupportedAlgorithmException(String.format("Unsupported algorithm %s or signing key type", algorithm.name()));
76 }
77 }
78
79 @VisibleForTesting
80 protected JWSSigner createRSASSASignerForKey(RSAPrivateKey privateKey) {
81 final RSASSASigner rsassaSigner = new RSASSASigner(privateKey);
82 rsassaSigner.getJCAContext().setProvider(provider);
83 return rsassaSigner;
84 }
85
86 @VisibleForTesting
87 protected JWSSigner createECDSASignerForKey(ECPrivateKey privateKey) throws UnsupportedAlgorithmException {
88 try {
89 final ECDSASigner ecdsaSigner = new ECDSASigner(privateKey);
90 ecdsaSigner.getJCAContext().setProvider(provider);
91 return ecdsaSigner;
92 } catch (JOSEException e) {
93 throw new UnsupportedAlgorithmException(String.format("Unsupported algorithm %s or signing key type", privateKey.getAlgorithm()));
94 }
95 }
96
97 private static JSONObject toJsonPayload(JwtClaims claims) {
98 return (JSONObject) Jsr353NimbusTranslator.jsr353ToNimbus(claims.getJson());
99 }
100 }