/** * 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 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); } }
@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); } }
/** * Sign the bytes to sign using this JWK type and algorithm. * * @param bytesToSign byte to be signed (e.g. content of a JWT, raw bytes) * @return signature bytes (raw bytes) */ public final byte[] sign(byte[] bytesToSign) { if (supports(USE_SIGNATURE, OPERATION_SIGN)) { return doSign(bytesToSign); } else { throw new JwtException("This key (" + this + ") does not support signing of requests"); } }
/** * Verify that the signature is indeed for the signed bytes based on this JWK type * and algorithm. * * @param signedBytes bytes that are signed (e.g. content of a JWT, raw bytes) * @param signature signature bytes (raw bytes) * @return true if signature is valid, false otherwise */ public final boolean verifySignature(byte[] signedBytes, byte[] signature) { if (supports(USE_SIGNATURE, OPERATION_VERIFY)) { return doVerify(signedBytes, signature); } else { throw new JwtException("This key (" + this + ") does not support verification of requests"); } }
private OutboundSecurityResponse impersonate(JwtOutboundTarget ot, String username) { Map<String, List<String>> headers = new HashMap<>(); Jwk jwk = signKeys.forKeyId(ot.jwkKid) .orElseThrow(() -> new JwtException("Signing JWK with kid: " + ot.jwkKid + " is not defined.")); Jwt.Builder builder = Jwt.builder(); builder.addPayloadClaim("name", username); builder.subject(username) .preferredUsername(username) .issuer(issuer) .algorithm(jwk.algorithm()); ot.update(builder); Jwt jwt = builder.build(); SignedJwt signed = SignedJwt.sign(jwt, jwk); ot.outboundHandler.header(headers, signed.tokenContent()); return OutboundSecurityResponse.withHeaders(headers); }