/** * Create a key factory for algorithm. * * @param algorithm security algorithm (such as RSA, EC) * @return KeyFactory instance * @throws JwtException in case the algorithm is invalid */ public static KeyFactory getKeyFactory(String algorithm) throws JwtException { try { return KeyFactory.getInstance(algorithm); } catch (NoSuchAlgorithmException e) { throw new JwtException("Failed to create key factory for algorithm: \"" + algorithm + "\"", e); } }
/** * Create a signature for algorithm. * * @param signatureAlgorithm security signature algorithm (such as "SHA512withRSA") * @return Signature instance * @throws JwtException in case the algorithm is invalid or not supported by this JVM */ public static Signature getSignature(String signatureAlgorithm) throws JwtException { try { return Signature.getInstance(signatureAlgorithm); } catch (NoSuchAlgorithmException e) { throw new JwtException("Failed to get Signature instance for algorithm \"" + signatureAlgorithm + "\""); } }
/** * Create a MAC for algorithm. Similar to signature for symmetric ciphers (such as "HmacSHA256"). * * @param algorithm security MAC algorithm * @return Mac instance * @throws JwtException in case the algorithm is invalid or not supported by this JVM */ public static Mac getMac(String algorithm) throws JwtException { try { return Mac.getInstance(algorithm); } catch (NoSuchAlgorithmException e) { throw new JwtException("Failed to get Mac instance for algorithm \"" + algorithm + "\""); } }
private static PublicKey toPublicKey(KeyFactory kf, BigInteger modulus, BigInteger publicExponent) { try { return kf.generatePublic(new RSAPublicKeySpec(modulus, publicExponent)); } catch (InvalidKeySpecException e) { throw new JwtException("Failed to generate RSA public key", e); } }
private static List<X509Certificate> processCertChain(List<String> base64s) { LinkedList<X509Certificate> certs = new LinkedList<>(); CertificateFactory cf; try { cf = CertificateFactory.getInstance("X.509"); } catch (CertificateException e) { throw new JwtException("Failed to get certificate factory. This is JVM misconfiguration", e); } base64s.forEach(it -> { byte[] rawBytes = DECODER.decode(it); try { X509Certificate certificate = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(rawBytes)); certs.add(certificate); } catch (CertificateException e) { throw new JwtException("Failed to read certificate from JWK", e); } }); return certs; }
private static PrivateKey toPrivateKey(KeyFactory kf, BigInteger privKeyValue, ECParameterSpec keySpec) { ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(privKeyValue, keySpec); try { return kf.generatePrivate(privateKeySpec); } catch (InvalidKeySpecException e) { throw new JwtException("Failed to generate EC private key", e); } }
@Override String signatureAlgorithm() { String jwkAlg = algorithm(); String javaAlg = ALG_MAP.get(jwkAlg); if (null == javaAlg) { throw new JwtException("Unsupported algorithm for RSA: " + jwkAlg); } return javaAlg; }
/** * Extract a key value from json object that is base64-url encoded and convert it to big integer. * * @param json JsonObject to read key from * @param key key of the value we want to read * @param description description of the field for error handling * @return BigInteger value * @throws JwtException in case the key is not present or is of invalid content */ public static BigInteger asBigInteger(JsonObject json, String key, String description) throws JwtException { return getBigInteger(json, key, description) .orElseThrow(() -> new JwtException("Key \"" + key + "\" is mandatory for " + description)); }
@Override String signatureAlgorithm() { String jwkAlg = algorithm(); String javaAlg = ALG_MAP.get(jwkAlg); if (null == javaAlg) { throw new JwtException("Unsupported algorithm for Elliptic curve: " + jwkAlg); } return javaAlg; }
private static PublicKey toPublicKey(KeyFactory kf, ECPoint point, ECParameterSpec keySpec) { try { return kf.generatePublic(new ECPublicKeySpec(point, keySpec)); } catch (InvalidKeySpecException e) { throw new JwtException("Failed to generate EC public key", e); } }
/** * Extract a key value from json object that is string. * * @param json JsonObject to read key from * @param key key of the value we want to read * @param description description of the field for error handling * @return String value * @throws JwtException in case the key is not present or is of invalid content */ public static String asString(JsonObject json, String key, String description) throws JwtException { return getString(json, key) .orElseThrow(() -> new JwtException("Key \"" + key + "\" is mandatory for " + description)); }
/** * Extract a key value from json object that is a base64-url encoded byte array. * * @param json JsonObject to read key from * @param key key of the value we want to read * @param description description of the field for error handling * @return byte array value * @throws JwtException in case the key is not present, is of invalid content or not base64 encoded */ public static byte[] asByteArray(JsonObject json, String key, String description) throws JwtException { return getByteArray(json, key, description) .orElseThrow(() -> new JwtException("Key \"" + key + "\" is mandatory for " + description)); }
private static SignedJwt sign(Jwt jwt, JwkKeys jwks, String alg) { Jwk jwk = jwt.keyId() // if key id is defined, find it from keys .map(kid -> jwks.forKeyId(kid).orElseThrow(() -> new JwtException("Could not find JWK for kid: " + kid))) // else check that alg is none, if so, use none .orElseGet(() -> { if (Jwk.ALG_NONE.equals(alg)) { return Jwk.NONE_JWK; } else { throw new JwtException("JWT defined with signature algorithm " + alg + ", yet no key id (kid): " + jwt); } }); return sign(jwt, jwk); }
@Override public byte[] doSign(byte[] bytesToSign) { String alg = signatureAlgorithm(); if (ALG_NONE.equals(alg)) { return EMPTY_BYTES; } Signature signature = JwtUtil.getSignature(alg); try { PrivateKey privateKey = this.privateKey .orElseThrow(() -> new JwtException("To sign data, private key MUST be present")); signature.initSign(privateKey); signature.update(bytesToSign); return signature.sign(); } catch (Exception e) { throw new JwtException("Failed to sign data", e); } }
/** * Extract a key value from json object that is a base64-url encoded byte array, if present. * * @param json JsonObject to read key from * @param key key of the value we want to read * @param description description of the field for error handling * @return byte array value if present * @throws JwtException in case the key is of invalid content or not base64 encoded */ public static Optional<byte[]> getByteArray(JsonObject json, String key, String description) throws JwtException { return getString(json, key) .map(base64 -> { try { return URL_DECODER.decode(base64); } catch (Exception e) { throw new JwtException("Failed to decode base64 from json for: " + description + ", value: \"" + base64 + '"', e); } }); }
/** * Extract a key value from json object that is base64-url encoded and convert it to big integer if present. * * @param json JsonObject to read key from * @param key key of the value we want to read * @param description description of the field for error handling * @return BigInteger value if present * @throws JwtException in case the key is of invalid content */ public static Optional<BigInteger> getBigInteger(JsonObject json, String key, String description) throws JwtException { return getByteArray(json, key, description) .map(byteValue -> { // create BigInteger try { return new BigInteger(1, byteValue); } catch (Exception e) { throw new JwtException("Failed to get a big decimal for: " + description + ", from value of key " + key, e); } }); }
@Override public byte[] doSign(byte[] bytesToSign) { String alg = getSignatureAlgorithm(); if (ALG_NONE.equals(alg)) { return EMPTY_BYTES; } Mac mac = JwtUtil.getMac(alg); SecretKeySpec secretKey = new SecretKeySpec(keyBytes, alg); try { mac.init(secretKey); } catch (InvalidKeyException e) { throw new JwtException("Failed to init Mac for algorithm: " + alg, e); } return mac.doFinal(bytesToSign); }
private String getSignatureAlgorithm() { String jwkAlg = algorithm(); String javaAlg = ALG_MAP.get(algorithm()); if (null == javaAlg) { throw new JwtException("Unsupported algorithm for MAC: " + jwkAlg); } return javaAlg; }
private void verifyKeys(Config config) { verifyJwk(Resource.create(config, "jwk") .orElseThrow(() -> new JwtException("Failed to extract verify JWK from configuration"))); }
@Override public boolean doVerify(byte[] signedBytes, byte[] signatureToVerify) { String alg = signatureAlgorithm(); if (ALG_NONE.equals(alg)) { return verifyNoneAlg(signatureToVerify); } Signature signature = JwtUtil.getSignature(alg); try { signature.initVerify(publicKey); signature.update(signedBytes); return signature.verify(signatureToVerify); } catch (Exception e) { throw new JwtException("Failed to verify signature. It may still be valid, but an exception was thrown", e); } }