/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.cxf.rs.security.jose.jwe; import java.nio.ByteBuffer; import java.security.Key; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.X509Certificate; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.security.interfaces.RSAKey; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Properties; import java.util.logging.Logger; import javax.crypto.KeyAgreement; import javax.crypto.SecretKey; import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.message.Message; import org.apache.cxf.message.MessageUtils; import org.apache.cxf.phase.PhaseInterceptorChain; import org.apache.cxf.rs.security.jose.common.JoseConstants; import org.apache.cxf.rs.security.jose.common.JoseHeaders; import org.apache.cxf.rs.security.jose.common.JoseUtils; import org.apache.cxf.rs.security.jose.common.KeyManagementUtils; import org.apache.cxf.rs.security.jose.jwa.AlgorithmUtils; import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm; import org.apache.cxf.rs.security.jose.jwa.KeyAlgorithm; import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; import org.apache.cxf.rs.security.jose.jwk.JsonWebKeys; import org.apache.cxf.rs.security.jose.jwk.JwkUtils; import org.apache.cxf.rs.security.jose.jwk.KeyOperation; import org.apache.cxf.rs.security.jose.jwk.KeyType; import org.apache.cxf.rt.security.crypto.MessageDigestUtils; public final class JweUtils { private static final Logger LOG = LogUtils.getL7dLogger(JweUtils.class); private JweUtils() { } public static String encrypt(PublicKey key, KeyAlgorithm keyAlgo, ContentAlgorithm contentAlgo, byte[] content) { return encrypt(key, keyAlgo, contentAlgo, content, null); } public static String encrypt(PublicKey key, KeyAlgorithm keyAlgo, ContentAlgorithm contentAlgo, byte[] content, String ct) { KeyEncryptionProvider keyEncryptionProvider = getPublicKeyEncryptionProvider(key, keyAlgo); return encrypt(keyEncryptionProvider, contentAlgo, content, ct); } public static String encrypt(SecretKey key, KeyAlgorithm keyAlgo, ContentAlgorithm contentAlgo, byte[] content) { return encrypt(key, keyAlgo, contentAlgo, content, null); } public static String encrypt(SecretKey key, KeyAlgorithm keyAlgo, ContentAlgorithm contentAlgo, byte[] content, String ct) { if (keyAlgo != null) { KeyEncryptionProvider keyEncryptionProvider = getSecretKeyEncryptionAlgorithm(key, keyAlgo); return encrypt(keyEncryptionProvider, contentAlgo, content, ct); } else { return encryptDirect(key, contentAlgo, content, ct); } } public static String encrypt(JsonWebKey key, ContentAlgorithm contentAlgo, byte[] content, String ct) { KeyEncryptionProvider keyEncryptionProvider = getKeyEncryptionProvider(key); return encrypt(keyEncryptionProvider, contentAlgo, content, ct); } public static String encryptDirect(SecretKey key, ContentAlgorithm contentAlgo, byte[] content) { return encryptDirect(key, contentAlgo, content, null); } public static String encryptDirect(SecretKey key, ContentAlgorithm contentAlgo, byte[] content, String ct) { JweEncryptionProvider jwe = getDirectKeyJweEncryption(key, contentAlgo); return jwe.encrypt(content, toJweHeaders(ct)); } public static String encryptDirect(JsonWebKey key, byte[] content, String ct) { JweEncryptionProvider jwe = getDirectKeyJweEncryption(key); return jwe.encrypt(content, toJweHeaders(ct)); } public static byte[] decrypt(PrivateKey key, KeyAlgorithm keyAlgo, ContentAlgorithm contentAlgo, String content) { KeyDecryptionProvider keyDecryptionProvider = getPrivateKeyDecryptionProvider(key, keyAlgo); return decrypt(keyDecryptionProvider, contentAlgo, content); } public static byte[] decrypt(SecretKey key, KeyAlgorithm keyAlgo, ContentAlgorithm contentAlgo, String content) { if (keyAlgo != null) { KeyDecryptionProvider keyDecryptionProvider = getSecretKeyDecryptionProvider(key, keyAlgo); return decrypt(keyDecryptionProvider, contentAlgo, content); } else { return decryptDirect(key, contentAlgo, content); } } public static byte[] decrypt(JsonWebKey key, ContentAlgorithm contentAlgo, String content) { KeyDecryptionProvider keyDecryptionProvider = getKeyDecryptionProvider(key); return decrypt(keyDecryptionProvider, contentAlgo, content); } public static byte[] decryptDirect(SecretKey key, ContentAlgorithm contentAlgo, String content) { JweDecryptionProvider jwe = getDirectKeyJweDecryption(key, contentAlgo); return jwe.decrypt(content).getContent(); } public static byte[] decryptDirect(JsonWebKey key, String content) { JweDecryptionProvider jwe = getDirectKeyJweDecryption(key); return jwe.decrypt(content).getContent(); } public static KeyEncryptionProvider getKeyEncryptionProvider(JsonWebKey jwk) { return getKeyEncryptionProvider(jwk, null); } public static KeyEncryptionProvider getKeyEncryptionProvider(JsonWebKey jwk, KeyAlgorithm defaultAlgorithm) { KeyAlgorithm keyAlgo = jwk.getAlgorithm() == null ? defaultAlgorithm : KeyAlgorithm.getAlgorithm(jwk.getAlgorithm()); KeyEncryptionProvider keyEncryptionProvider = null; KeyType keyType = jwk.getKeyType(); if (KeyType.RSA == keyType) { keyEncryptionProvider = getPublicKeyEncryptionProvider(JwkUtils.toRSAPublicKey(jwk, true), keyAlgo); } else if (KeyType.OCTET == keyType) { keyEncryptionProvider = getSecretKeyEncryptionAlgorithm(JwkUtils.toSecretKey(jwk), keyAlgo); } else { ContentAlgorithm ctAlgo = null; Message m = PhaseInterceptorChain.getCurrentMessage(); if (m != null) { ctAlgo = getContentAlgo((String)m.get(JoseConstants.RSSEC_ENCRYPTION_CONTENT_ALGORITHM)); } keyEncryptionProvider = new EcdhAesWrapKeyEncryptionAlgorithm(JwkUtils.toECPublicKey(jwk), jwk.getStringProperty(JsonWebKey.EC_CURVE), keyAlgo, ctAlgo == null ? ContentAlgorithm.A128GCM : ctAlgo); } return keyEncryptionProvider; } public static KeyEncryptionProvider getPublicKeyEncryptionProvider(PublicKey key, KeyAlgorithm algo) { return getPublicKeyEncryptionProvider(key, null, algo); } public static KeyEncryptionProvider getPublicKeyEncryptionProvider(PublicKey key, Properties props, KeyAlgorithm algo) { if (algo == null) { algo = getDefaultPublicKeyAlgorithm(key); } if (key instanceof RSAPublicKey) { return new RSAKeyEncryptionAlgorithm((RSAPublicKey)key, algo); } else if (key instanceof ECPublicKey) { ContentAlgorithm ctAlgo = null; Message m = PhaseInterceptorChain.getCurrentMessage(); if (m != null) { ctAlgo = getContentAlgo((String)m.get(JoseConstants.RSSEC_ENCRYPTION_CONTENT_ALGORITHM)); } String curve = props == null ? JsonWebKey.EC_CURVE_P256 : props.getProperty(JoseConstants.RSSEC_EC_CURVE, JsonWebKey.EC_CURVE_P256); return new EcdhAesWrapKeyEncryptionAlgorithm((ECPublicKey)key, curve, algo, ctAlgo == null ? ContentAlgorithm.A128GCM : ctAlgo); } return null; } private static KeyAlgorithm getDefaultPublicKeyAlgorithm(PublicKey key) { if (key instanceof RSAPublicKey) { return KeyAlgorithm.RSA_OAEP; } else if (key instanceof ECPublicKey) { return KeyAlgorithm.ECDH_ES_A128KW; } else { return null; } } private static KeyAlgorithm getDefaultPrivateKeyAlgorithm(PrivateKey key) { if (key instanceof RSAPrivateKey) { return KeyAlgorithm.RSA_OAEP; } else if (key instanceof ECPrivateKey) { return KeyAlgorithm.ECDH_ES_A128KW; } else { return null; } } public static KeyEncryptionProvider getSecretKeyEncryptionAlgorithm(SecretKey key, KeyAlgorithm algo) { if (AlgorithmUtils.isAesKeyWrap(algo.getJwaName())) { return new AesWrapKeyEncryptionAlgorithm(key, algo); } else if (AlgorithmUtils.isAesGcmKeyWrap(algo.getJwaName())) { return new AesGcmWrapKeyEncryptionAlgorithm(key, algo); } return null; } public static KeyDecryptionProvider getKeyDecryptionProvider(JsonWebKey jwk) { return getKeyDecryptionProvider(jwk, null); } public static KeyDecryptionProvider getKeyDecryptionProvider(JsonWebKey jwk, KeyAlgorithm defaultAlgorithm) { KeyAlgorithm keyAlgo = jwk.getAlgorithm() == null ? defaultAlgorithm : KeyAlgorithm.getAlgorithm(jwk.getAlgorithm()); KeyDecryptionProvider keyDecryptionProvider = null; KeyType keyType = jwk.getKeyType(); if (KeyType.RSA == keyType) { keyDecryptionProvider = getPrivateKeyDecryptionProvider(JwkUtils.toRSAPrivateKey(jwk), keyAlgo); } else if (KeyType.OCTET == keyType) { keyDecryptionProvider = getSecretKeyDecryptionProvider(JwkUtils.toSecretKey(jwk), keyAlgo); } else { keyDecryptionProvider = getPrivateKeyDecryptionProvider(JwkUtils.toECPrivateKey(jwk), keyAlgo); } return keyDecryptionProvider; } public static KeyDecryptionProvider getPrivateKeyDecryptionProvider(PrivateKey key, KeyAlgorithm algo) { if (key instanceof RSAPrivateKey) { return new RSAKeyDecryptionAlgorithm((RSAPrivateKey)key, algo); } else if (key instanceof ECPrivateKey) { return new EcdhAesWrapKeyDecryptionAlgorithm((ECPrivateKey)key, algo); } return null; } public static KeyDecryptionProvider getSecretKeyDecryptionProvider(SecretKey key, KeyAlgorithm algo) { if (AlgorithmUtils.isAesKeyWrap(algo.getJwaName())) { return new AesWrapKeyDecryptionAlgorithm(key, algo); } else if (AlgorithmUtils.isAesGcmKeyWrap(algo.getJwaName())) { return new AesGcmWrapKeyDecryptionAlgorithm(key, algo); } return null; } public static ContentEncryptionProvider getContentEncryptionProvider(JsonWebKey jwk) { return getContentEncryptionProvider(jwk, null); } public static ContentEncryptionProvider getContentEncryptionProvider(JsonWebKey jwk, ContentAlgorithm defaultAlgorithm) { ContentAlgorithm ctAlgo = jwk.getAlgorithm() == null ? defaultAlgorithm : getContentAlgo(jwk.getAlgorithm()); KeyType keyType = jwk.getKeyType(); if (KeyType.OCTET == keyType) { return getContentEncryptionProvider(JwkUtils.toSecretKey(jwk), ctAlgo); } else { return null; } } public static ContentEncryptionProvider getContentEncryptionProvider(SecretKey key, ContentAlgorithm algorithm) { return getContentEncryptionProvider(key.getEncoded(), algorithm); } public static ContentEncryptionProvider getContentEncryptionProvider(byte[] key, ContentAlgorithm algorithm) { if (AlgorithmUtils.isAesGcm(algorithm.getJwaName())) { return new AesGcmContentEncryptionAlgorithm(key, null, algorithm); } return null; } public static ContentEncryptionProvider getContentEncryptionProvider(ContentAlgorithm algorithm) { return getContentEncryptionProvider(algorithm, false); } public static ContentEncryptionProvider getContentEncryptionProvider(ContentAlgorithm algorithm, boolean generateCekOnce) { if (AlgorithmUtils.isAesGcm(algorithm.getJwaName())) { return new AesGcmContentEncryptionAlgorithm(algorithm, generateCekOnce); } return null; } public static ContentDecryptionProvider getContentDecryptionProvider(ContentAlgorithm algorithm) { if (AlgorithmUtils.isAesGcm(algorithm.getJwaName())) { return new AesGcmContentDecryptionAlgorithm(algorithm); } return null; } public static SecretKey getContentDecryptionSecretKey(JsonWebKey jwk) { return getContentDecryptionSecretKey(jwk, null); } public static SecretKey getContentDecryptionSecretKey(JsonWebKey jwk, String defaultAlgorithm) { String ctEncryptionAlgo = jwk.getAlgorithm() == null ? defaultAlgorithm : jwk.getAlgorithm(); KeyType keyType = jwk.getKeyType(); if (KeyType.OCTET == keyType && AlgorithmUtils.isAesGcm(ctEncryptionAlgo)) { return JwkUtils.toSecretKey(jwk); } return null; } private static ContentAlgorithm getContentAlgo(String algo) { return ContentAlgorithm.getAlgorithm(algo); } public static JweEncryption getDirectKeyJweEncryption(JsonWebKey key) { return getDirectKeyJweEncryption(JwkUtils.toSecretKey(key), getContentAlgo(key.getAlgorithm())); } public static JweEncryption getDirectKeyJweEncryption(SecretKey key, ContentAlgorithm algo) { return getDirectKeyJweEncryption(key.getEncoded(), algo); } public static JweEncryption getDirectKeyJweEncryption(byte[] key, ContentAlgorithm algo) { if (AlgorithmUtils.isAesCbcHmac(algo.getJwaName())) { return new AesCbcHmacJweEncryption(algo, key, null, new DirectKeyEncryptionAlgorithm()); } else { return new JweEncryption(new DirectKeyEncryptionAlgorithm(), getContentEncryptionProvider(key, algo)); } } public static JweDecryption getDirectKeyJweDecryption(JsonWebKey key) { return getDirectKeyJweDecryption(JwkUtils.toSecretKey(key), getContentAlgo(key.getAlgorithm())); } public static JweDecryption getDirectKeyJweDecryption(SecretKey key, ContentAlgorithm algorithm) { return getDirectKeyJweDecryption(key.getEncoded(), algorithm); } public static JweDecryption getDirectKeyJweDecryption(byte[] key, ContentAlgorithm algorithm) { if (AlgorithmUtils.isAesCbcHmac(algorithm.getJwaName())) { return new AesCbcHmacJweDecryption(new DirectKeyDecryptionAlgorithm(key), algorithm); } else { return new JweDecryption(new DirectKeyDecryptionAlgorithm(key), getContentDecryptionProvider(algorithm)); } } public static JweEncryptionProvider loadEncryptionProvider(boolean required) { return loadEncryptionProvider(null, required); } public static JweEncryptionProvider loadEncryptionProvider(JweHeaders headers, boolean required) { Properties props = loadEncryptionOutProperties(required); if (props == null) { return null; } return loadEncryptionProvider(props, headers, required); } public static JweEncryptionProvider loadEncryptionProvider(Properties props, JweHeaders headers, boolean required) { Message m = PhaseInterceptorChain.getCurrentMessage(); boolean includeCert = headers != null && MessageUtils.getContextualBoolean( m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_CERT, false); boolean includeCertSha1 = headers != null && MessageUtils.getContextualBoolean( m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_CERT_SHA1, false); boolean includeCertSha256 = !includeCertSha1 && headers != null && MessageUtils.getContextualBoolean( m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_CERT_SHA256, false); KeyEncryptionProvider keyEncryptionProvider = null; KeyAlgorithm keyAlgo = getKeyEncryptionAlgorithm(m, props, null, null); ContentAlgorithm contentAlgo = getContentEncryptionAlgorithm(m, props, null, ContentAlgorithm.A128GCM); if (m != null) { m.put(JoseConstants.RSSEC_ENCRYPTION_CONTENT_ALGORITHM, contentAlgo.getJwaName()); } ContentEncryptionProvider ctEncryptionProvider = null; if (JoseConstants.HEADER_JSON_WEB_KEY.equals(props.get(JoseConstants.RSSEC_KEY_STORE_TYPE))) { JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, KeyOperation.ENCRYPT); if (KeyAlgorithm.DIRECT == keyAlgo) { contentAlgo = getContentEncryptionAlgorithm(m, props, ContentAlgorithm.getAlgorithm(jwk.getAlgorithm()), ContentAlgorithm.A128GCM); ctEncryptionProvider = getContentEncryptionProvider(jwk, contentAlgo); } else { keyAlgo = getKeyEncryptionAlgorithm(m, props, KeyAlgorithm.getAlgorithm(jwk.getAlgorithm()), getDefaultKeyAlgorithm(jwk)); keyEncryptionProvider = getKeyEncryptionProvider(jwk, keyAlgo); boolean includePublicKey = headers != null && MessageUtils.getContextualBoolean( m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_PUBLIC_KEY, false); boolean includeKeyId = headers != null && MessageUtils.getContextualBoolean( m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_KEY_ID, false); if (includeCert) { JwkUtils.includeCertChain(jwk, headers, keyAlgo.getJwaName()); } if (includeCertSha1) { String digest = KeyManagementUtils.loadDigestAndEncodeX509Certificate(m, props, MessageDigestUtils.ALGO_SHA_1); if (digest != null) { headers.setX509Thumbprint(digest); } } else if (includeCertSha256) { String digest = KeyManagementUtils.loadDigestAndEncodeX509Certificate(m, props, MessageDigestUtils.ALGO_SHA_256); if (digest != null) { headers.setX509ThumbprintSHA256(digest); } } if (includePublicKey) { JwkUtils.includePublicKey(jwk, headers, keyAlgo.getJwaName()); } if (includeKeyId && jwk.getKeyId() != null && headers != null) { headers.setKeyId(jwk.getKeyId()); } } } else { keyEncryptionProvider = getPublicKeyEncryptionProvider( KeyManagementUtils.loadPublicKey(m, props), props, keyAlgo); if (includeCert) { headers.setX509Chain(KeyManagementUtils.loadAndEncodeX509CertificateOrChain(m, props)); } if (includeCertSha1) { String digest = KeyManagementUtils.loadDigestAndEncodeX509Certificate(m, props, MessageDigestUtils.ALGO_SHA_1); if (digest != null) { headers.setX509Thumbprint(digest); } } else if (includeCertSha256) { String digest = KeyManagementUtils.loadDigestAndEncodeX509Certificate(m, props, MessageDigestUtils.ALGO_SHA_256); if (digest != null) { headers.setX509ThumbprintSHA256(digest); } } } String compression = props.getProperty(JoseConstants.RSSEC_ENCRYPTION_ZIP_ALGORITHM); return createJweEncryptionProvider(keyEncryptionProvider, ctEncryptionProvider, contentAlgo.getJwaName(), compression); } public static JweDecryptionProvider loadDecryptionProvider(boolean required) { return loadDecryptionProvider(null, required); } public static JweDecryptionProvider loadDecryptionProvider(JweHeaders inHeaders, boolean required) { Properties props = loadEncryptionInProperties(required); if (props == null) { return null; } return loadDecryptionProvider(props, inHeaders, required); } public static JweDecryptionProvider loadDecryptionProvider(Properties props, JweHeaders inHeaders, boolean required) { Message m = PhaseInterceptorChain.getCurrentMessage(); KeyDecryptionProvider keyDecryptionProvider = null; ContentAlgorithm contentAlgo = getContentEncryptionAlgorithm(m, props, null, ContentAlgorithm.A128GCM); SecretKey ctDecryptionKey = null; KeyAlgorithm keyAlgo = getKeyEncryptionAlgorithm(m, props, null, null); if (inHeaders != null && inHeaders.getHeader(JoseConstants.HEADER_X509_CHAIN) != null) { // Supporting loading a private key via a certificate for now List<X509Certificate> chain = KeyManagementUtils.toX509CertificateChain(inHeaders.getX509Chain()); KeyManagementUtils.validateCertificateChain(props, chain); X509Certificate cert = chain == null ? null : chain.get(0); PrivateKey privateKey = KeyManagementUtils.loadPrivateKey(m, props, cert, KeyOperation.DECRYPT); if (keyAlgo == null) { keyAlgo = getDefaultPrivateKeyAlgorithm(privateKey); } contentAlgo = inHeaders.getContentEncryptionAlgorithm(); keyDecryptionProvider = getPrivateKeyDecryptionProvider(privateKey, keyAlgo); } else if (inHeaders != null && inHeaders.getHeader(JoseConstants.HEADER_X509_THUMBPRINT) != null) { X509Certificate foundCert = KeyManagementUtils.getCertificateFromThumbprint(inHeaders.getX509Thumbprint(), MessageDigestUtils.ALGO_SHA_1, m, props); if (foundCert != null) { PrivateKey privateKey = KeyManagementUtils.loadPrivateKey(m, props, foundCert, KeyOperation.DECRYPT); if (keyAlgo == null) { keyAlgo = getDefaultPrivateKeyAlgorithm(privateKey); } contentAlgo = inHeaders.getContentEncryptionAlgorithm(); keyDecryptionProvider = getPrivateKeyDecryptionProvider(privateKey, keyAlgo); } } else if (inHeaders != null && inHeaders.getHeader(JoseConstants.HEADER_X509_THUMBPRINT_SHA256) != null) { X509Certificate foundCert = KeyManagementUtils.getCertificateFromThumbprint(inHeaders.getX509ThumbprintSHA256(), MessageDigestUtils.ALGO_SHA_256, m, props); if (foundCert != null) { PrivateKey privateKey = KeyManagementUtils.loadPrivateKey(m, props, foundCert, KeyOperation.DECRYPT); if (keyAlgo == null) { keyAlgo = getDefaultPrivateKeyAlgorithm(privateKey); } contentAlgo = inHeaders.getContentEncryptionAlgorithm(); keyDecryptionProvider = getPrivateKeyDecryptionProvider(privateKey, keyAlgo); } } else { if (JoseConstants.HEADER_JSON_WEB_KEY.equals(props.get(JoseConstants.RSSEC_KEY_STORE_TYPE))) { JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, KeyOperation.DECRYPT); if (jwk == null) { LOG.warning("Extracting the JsonWebKey failed"); throw new JweException(JweException.Error.KEY_DECRYPTION_FAILURE); } if (KeyAlgorithm.DIRECT == keyAlgo) { contentAlgo = getContentEncryptionAlgorithm(m, props, ContentAlgorithm.getAlgorithm(jwk.getAlgorithm()), ContentAlgorithm.A128GCM); ctDecryptionKey = getContentDecryptionSecretKey(jwk, contentAlgo.getJwaName()); } else { keyAlgo = getKeyEncryptionAlgorithm(m, props, KeyAlgorithm.getAlgorithm(jwk.getAlgorithm()), getDefaultKeyAlgorithm(jwk)); keyDecryptionProvider = getKeyDecryptionProvider(jwk, keyAlgo); } } else { PrivateKey privateKey = KeyManagementUtils.loadPrivateKey(m, props, KeyOperation.DECRYPT); if (keyAlgo == null) { keyAlgo = getDefaultPrivateKeyAlgorithm(privateKey); } keyDecryptionProvider = getPrivateKeyDecryptionProvider(privateKey, keyAlgo); } } return createJweDecryptionProvider(keyDecryptionProvider, ctDecryptionKey, contentAlgo); } public static List<JweEncryptionProvider> loadJweEncryptionProviders(String propLoc, Message m) { Properties props = loadJweProperties(m, propLoc); JweEncryptionProvider theEncProvider = loadEncryptionProvider(props, null, false); if (theEncProvider != null) { return Collections.singletonList(theEncProvider); } List<JweEncryptionProvider> theEncProviders = null; if (JoseConstants.HEADER_JSON_WEB_KEY.equals(props.get(JoseConstants.RSSEC_KEY_STORE_TYPE))) { List<JsonWebKey> jwks = JwkUtils.loadJsonWebKeys(m, props, KeyOperation.ENCRYPT); if (jwks != null) { theEncProviders = new ArrayList<>(jwks.size()); for (JsonWebKey jwk : jwks) { theEncProviders.add(getDirectKeyJweEncryption(jwk)); } } } if (theEncProviders == null) { LOG.warning("Providers are not available"); throw new JweException(JweException.Error.NO_ENCRYPTOR); } return theEncProviders; } public static List<JweDecryptionProvider> loadJweDecryptionProviders(String propLoc, Message m) { Properties props = loadJweProperties(m, propLoc); JweDecryptionProvider theDecProvider = loadDecryptionProvider(props, null, false); if (theDecProvider != null) { return Collections.singletonList(theDecProvider); } List<JweDecryptionProvider> theDecProviders = null; if (JoseConstants.HEADER_JSON_WEB_KEY.equals(props.get(JoseConstants.RSSEC_KEY_STORE_TYPE))) { List<JsonWebKey> jwks = JwkUtils.loadJsonWebKeys(m, props, KeyOperation.DECRYPT); if (jwks != null) { theDecProviders = new ArrayList<>(jwks.size()); for (JsonWebKey jwk : jwks) { theDecProviders.add(getDirectKeyJweDecryption(jwk)); } } } if (theDecProviders == null) { LOG.warning("Providers are not available"); throw new JweException(JweException.Error.NO_ENCRYPTOR); } return theDecProviders; } public static JweEncryptionProvider createJweEncryptionProvider(PublicKey key, KeyAlgorithm keyAlgo, ContentAlgorithm contentEncryptionAlgo) { return createJweEncryptionProvider(key, keyAlgo, contentEncryptionAlgo, null); } public static JweEncryptionProvider createJweEncryptionProvider(PublicKey key, KeyAlgorithm keyAlgo, ContentAlgorithm contentEncryptionAlgo, String compression) { KeyEncryptionProvider keyEncryptionProvider = getPublicKeyEncryptionProvider(key, keyAlgo); return createJweEncryptionProvider(keyEncryptionProvider, contentEncryptionAlgo, compression); } public static JweEncryptionProvider createJweEncryptionProvider(PublicKey key, JweHeaders headers) { KeyEncryptionProvider keyEncryptionProvider = getPublicKeyEncryptionProvider(key, headers.getKeyEncryptionAlgorithm()); return createJweEncryptionProvider(keyEncryptionProvider, headers); } public static JweEncryptionProvider createJweEncryptionProvider(SecretKey key, KeyAlgorithm keyAlgo, ContentAlgorithm contentEncryptionAlgo) { return createJweEncryptionProvider(key, keyAlgo, contentEncryptionAlgo, null); } public static JweEncryptionProvider createJweEncryptionProvider(SecretKey key, KeyAlgorithm keyAlgo, ContentAlgorithm contentEncryptionAlgo, String compression) { KeyEncryptionProvider keyEncryptionProvider = getSecretKeyEncryptionAlgorithm(key, keyAlgo); return createJweEncryptionProvider(keyEncryptionProvider, contentEncryptionAlgo, compression); } public static JweEncryptionProvider createJweEncryptionProvider(SecretKey key, JweHeaders headers) { KeyEncryptionProvider keyEncryptionProvider = getSecretKeyEncryptionAlgorithm(key, headers.getKeyEncryptionAlgorithm()); return createJweEncryptionProvider(keyEncryptionProvider, headers); } public static JweEncryptionProvider createJweEncryptionProvider(JsonWebKey key, ContentAlgorithm contentEncryptionAlgo) { return createJweEncryptionProvider(key, contentEncryptionAlgo, null); } public static JweEncryptionProvider createJweEncryptionProvider(JsonWebKey key, ContentAlgorithm contentEncryptionAlgo, String compression) { KeyEncryptionProvider keyEncryptionProvider = getKeyEncryptionProvider(key); return createJweEncryptionProvider(keyEncryptionProvider, contentEncryptionAlgo, compression); } public static JweEncryptionProvider createJweEncryptionProvider(JsonWebKey key, JweHeaders headers) { KeyEncryptionProvider keyEncryptionProvider = getKeyEncryptionProvider(key); return createJweEncryptionProvider(keyEncryptionProvider, headers); } public static JweEncryptionProvider createJweEncryptionProvider(KeyEncryptionProvider keyEncryptionProvider, ContentAlgorithm contentEncryptionAlgo, String compression) { JweHeaders headers = prepareJweHeaders(keyEncryptionProvider != null ? keyEncryptionProvider.getAlgorithm().getJwaName() : null, contentEncryptionAlgo.getJwaName(), compression); return createJweEncryptionProvider(keyEncryptionProvider, headers); } public static JweEncryptionProvider createJweEncryptionProvider(KeyEncryptionProvider keyEncryptionProvider, JweHeaders headers) { ContentAlgorithm contentEncryptionAlgo = headers.getContentEncryptionAlgorithm(); if (AlgorithmUtils.isAesCbcHmac(contentEncryptionAlgo.getJwaName())) { return new AesCbcHmacJweEncryption(contentEncryptionAlgo, keyEncryptionProvider); } else { return new JweEncryption(keyEncryptionProvider, getContentEncryptionProvider(contentEncryptionAlgo)); } } public static JweDecryptionProvider createJweDecryptionProvider(PrivateKey key, KeyAlgorithm keyAlgo, ContentAlgorithm contentDecryptionAlgo) { return createJweDecryptionProvider(getPrivateKeyDecryptionProvider(key, keyAlgo), contentDecryptionAlgo); } public static JweDecryptionProvider createJweDecryptionProvider(SecretKey key, KeyAlgorithm keyAlgo, ContentAlgorithm contentDecryptionAlgo) { return createJweDecryptionProvider(getSecretKeyDecryptionProvider(key, keyAlgo), contentDecryptionAlgo); } public static JweDecryptionProvider createJweDecryptionProvider(JsonWebKey key, ContentAlgorithm contentDecryptionAlgo) { return createJweDecryptionProvider(getKeyDecryptionProvider(key), contentDecryptionAlgo); } public static JweDecryptionProvider createJweDecryptionProvider(KeyDecryptionProvider keyDecryptionProvider, ContentAlgorithm contentDecryptionAlgo) { if (AlgorithmUtils.isAesCbcHmac(contentDecryptionAlgo.getJwaName())) { return new AesCbcHmacJweDecryption(keyDecryptionProvider, contentDecryptionAlgo); } else { return new JweDecryption(keyDecryptionProvider, getContentDecryptionProvider(contentDecryptionAlgo)); } } public static boolean validateCriticalHeaders(JoseHeaders headers) { //TODO: Validate JWE specific constraints return JoseUtils.validateCriticalHeaders(headers); } public static byte[] getECDHKey(JsonWebKey privateKey, JsonWebKey peerPublicKey, byte[] partyUInfo, byte[] partyVInfo, String algoName, int algoKeyBitLen) { return getECDHKey(JwkUtils.toECPrivateKey(privateKey), JwkUtils.toECPublicKey(peerPublicKey), partyUInfo, partyVInfo, algoName, algoKeyBitLen); } public static byte[] getECDHKey(ECPrivateKey privateKey, ECPublicKey peerPublicKey, byte[] partyUInfo, byte[] partyVInfo, String algoName, int algoKeyBitLen) { byte[] keyZ = generateKeyZ(privateKey, peerPublicKey); return calculateDerivedKey(keyZ, algoName, partyUInfo, partyVInfo, algoKeyBitLen); } public static byte[] getAdditionalAuthenticationData(String headersJson, byte[] aad) { byte[] headersAAD = JweHeaders.toCipherAdditionalAuthData(headersJson); if (aad != null) { // JWE JSON can provide the extra aad byte[] newAAD = Arrays.copyOf(headersAAD, headersAAD.length + 1 + aad.length); newAAD[headersAAD.length] = '.'; System.arraycopy(aad, 0, newAAD, headersAAD.length + 1, aad.length); return newAAD; } else { return headersAAD; } } private static byte[] calculateDerivedKey(byte[] keyZ, String algoName, byte[] apuBytes, byte[] apvBytes, int algoKeyBitLen) { final byte[] emptyPartyInfo = new byte[4]; if (apuBytes != null && apvBytes != null && Arrays.equals(apuBytes, apvBytes)) { LOG.warning("Derived key calculation problem: apu equals to apv"); throw new JweException(JweException.Error.KEY_ENCRYPTION_FAILURE); } byte[] algorithmId = concatenateDatalenAndData(StringUtils.toBytesASCII(algoName)); byte[] partyUInfo = apuBytes == null ? emptyPartyInfo : concatenateDatalenAndData(apuBytes); byte[] partyVInfo = apvBytes == null ? emptyPartyInfo : concatenateDatalenAndData(apvBytes); byte[] suppPubInfo = datalenToBytes(algoKeyBitLen); byte[] otherInfo = new byte[algorithmId.length + partyUInfo.length + partyVInfo.length + suppPubInfo.length]; System.arraycopy(algorithmId, 0, otherInfo, 0, algorithmId.length); System.arraycopy(partyUInfo, 0, otherInfo, algorithmId.length, partyUInfo.length); System.arraycopy(partyVInfo, 0, otherInfo, algorithmId.length + partyUInfo.length, partyVInfo.length); System.arraycopy(suppPubInfo, 0, otherInfo, algorithmId.length + partyUInfo.length + partyVInfo.length, suppPubInfo.length); byte[] concatKDF = new byte[36 + otherInfo.length]; concatKDF[3] = 1; System.arraycopy(keyZ, 0, concatKDF, 4, keyZ.length); System.arraycopy(otherInfo, 0, concatKDF, 36, otherInfo.length); try { byte[] round1Hash = MessageDigestUtils.createDigest(concatKDF, MessageDigestUtils.ALGO_SHA_256); return Arrays.copyOf(round1Hash, algoKeyBitLen / 8); } catch (Exception ex) { LOG.warning("Derived key calculation problem: round hash1 error"); throw new JweException(JweException.Error.KEY_ENCRYPTION_FAILURE); } } private static byte[] generateKeyZ(ECPrivateKey privateKey, ECPublicKey publicKey) { try { KeyAgreement ka = KeyAgreement.getInstance("ECDH"); ka.init(privateKey); ka.doPhase(publicKey, true); return ka.generateSecret(); } catch (Exception ex) { LOG.warning("Derived key calculation problem"); throw new JweException(JweException.Error.KEY_ENCRYPTION_FAILURE); } } private static byte[] concatenateDatalenAndData(byte[] bytesASCII) { final byte[] datalen = datalenToBytes(bytesASCII.length); byte[] all = new byte[4 + bytesASCII.length]; System.arraycopy(datalen, 0, all, 0, 4); System.arraycopy(bytesASCII, 0, all, 4, bytesASCII.length); return all; } private static byte[] datalenToBytes(int len) { ByteBuffer buf = ByteBuffer.allocate(4); return buf.putInt(len).array(); } private static JweHeaders prepareJweHeaders(String keyEncryptionAlgo, String contentEncryptionAlgo, String compression) { JweHeaders headers = new JweHeaders(); if (keyEncryptionAlgo != null) { headers.setKeyEncryptionAlgorithm(KeyAlgorithm.getAlgorithm(keyEncryptionAlgo)); } headers.setContentEncryptionAlgorithm(ContentAlgorithm.getAlgorithm(contentEncryptionAlgo)); if (compression != null) { headers.setZipAlgorithm(compression); } return headers; } private static JweEncryptionProvider createJweEncryptionProvider(KeyEncryptionProvider keyEncryptionProvider, ContentEncryptionProvider ctEncryptionProvider, String contentEncryptionAlgo, String compression) { if (keyEncryptionProvider == null && ctEncryptionProvider == null) { LOG.warning("Key or content encryptor is not available"); throw new JweException(JweException.Error.NO_ENCRYPTOR); } JweHeaders headers = prepareJweHeaders(keyEncryptionProvider != null ? keyEncryptionProvider.getAlgorithm().getJwaName() : null, contentEncryptionAlgo, compression); if (keyEncryptionProvider != null) { return createJweEncryptionProvider(keyEncryptionProvider, headers); } else { return new JweEncryption(new DirectKeyEncryptionAlgorithm(), ctEncryptionProvider); } } private static JweDecryptionProvider createJweDecryptionProvider(KeyDecryptionProvider keyDecryptionProvider, SecretKey ctDecryptionKey, ContentAlgorithm contentDecryptionAlgo) { if (keyDecryptionProvider == null && ctDecryptionKey == null) { LOG.warning("Key or content encryptor is not available"); throw new JweException(JweException.Error.NO_ENCRYPTOR); } if (keyDecryptionProvider != null) { return createJweDecryptionProvider(keyDecryptionProvider, contentDecryptionAlgo); } else { return getDirectKeyJweDecryption(ctDecryptionKey, contentDecryptionAlgo); } } public static KeyAlgorithm getKeyEncryptionAlgorithm(Message m, Properties props, KeyAlgorithm algo, KeyAlgorithm defaultAlgo) { if (algo == null) { algo = getKeyEncryptionAlgorithm(m, props, defaultAlgo); } return algo; } public static KeyAlgorithm getKeyEncryptionAlgorithm(Properties props, KeyAlgorithm defaultAlgo) { return getKeyEncryptionAlgorithm(PhaseInterceptorChain.getCurrentMessage(), props, defaultAlgo); } public static KeyAlgorithm getKeyEncryptionAlgorithm(Message m, Properties props, KeyAlgorithm defaultAlgo) { String algo = KeyManagementUtils.getKeyAlgorithm(m, props, JoseConstants.RSSEC_ENCRYPTION_KEY_ALGORITHM, defaultAlgo == null ? null : defaultAlgo.getJwaName()); return algo == null ? null : KeyAlgorithm.getAlgorithm(algo); } private static KeyAlgorithm getDefaultKeyAlgorithm(JsonWebKey jwk) { KeyType keyType = jwk.getKeyType(); if (KeyType.OCTET == keyType) { return KeyAlgorithm.A128GCMKW; } else if (KeyType.RSA == keyType) { return KeyAlgorithm.RSA_OAEP; } else { return KeyAlgorithm.ECDH_ES_A128KW; } } public static ContentAlgorithm getContentEncryptionAlgorithm(Message m, Properties props, ContentAlgorithm algo, ContentAlgorithm defaultAlgo) { if (algo == null) { algo = getContentEncryptionAlgorithm(m, props, defaultAlgo); } return algo; } public static ContentAlgorithm getContentEncryptionAlgorithm(Properties props, ContentAlgorithm defaultAlgo) { return getContentEncryptionAlgorithm(PhaseInterceptorChain.getCurrentMessage(), props, defaultAlgo); } public static ContentAlgorithm getContentEncryptionAlgorithm(Message m, Properties props, ContentAlgorithm defaultAlgo) { String algo = KeyManagementUtils.getKeyAlgorithm(m, props, JoseConstants.RSSEC_ENCRYPTION_CONTENT_ALGORITHM, defaultAlgo == null ? null : defaultAlgo.getJwaName()); return ContentAlgorithm.getAlgorithm(algo); } private static String encrypt(KeyEncryptionProvider keyEncryptionProvider, ContentAlgorithm contentAlgo, byte[] content, String ct) { JweEncryptionProvider jwe = createJweEncryptionProvider(keyEncryptionProvider, contentAlgo, null); return jwe.encrypt(content, toJweHeaders(ct)); } private static byte[] decrypt(KeyDecryptionProvider keyDecryptionProvider, ContentAlgorithm contentAlgo, String content) { JweDecryptionProvider jwe = createJweDecryptionProvider(keyDecryptionProvider, contentAlgo); return jwe.decrypt(content).getContent(); } private static JweHeaders toJweHeaders(String ct) { return new JweHeaders(Collections.<String, Object>singletonMap(JoseConstants.HEADER_CONTENT_TYPE, ct)); } public static void validateJweCertificateChain(List<X509Certificate> certs) { Properties props = loadEncryptionInProperties(true); KeyManagementUtils.validateCertificateChain(props, certs); } public static Properties loadEncryptionInProperties(boolean required) { Message m = PhaseInterceptorChain.getCurrentMessage(); return KeyManagementUtils.loadStoreProperties(m, required, JoseConstants.RSSEC_ENCRYPTION_IN_PROPS, JoseConstants.RSSEC_ENCRYPTION_PROPS); } public static Properties loadEncryptionOutProperties(boolean required) { Message m = PhaseInterceptorChain.getCurrentMessage(); return KeyManagementUtils.loadStoreProperties(m, required, JoseConstants.RSSEC_ENCRYPTION_OUT_PROPS, JoseConstants.RSSEC_ENCRYPTION_PROPS); } public static Properties loadEncryptionProperties(String propertiesName, boolean required) { Message m = PhaseInterceptorChain.getCurrentMessage(); return KeyManagementUtils.loadStoreProperties(m, required, propertiesName, null); } public static void checkEncryptionKeySize(Key key) { if (key instanceof RSAKey && ((RSAKey)key).getModulus().bitLength() < 2048) { LOG.fine("A key of size: " + ((RSAKey)key).getModulus().bitLength() + " was used with an RSA encryption algorithm. 2048 is the minimum size that is accepted"); throw new JweException(JweException.Error.KEY_DECRYPTION_FAILURE); } } public static JsonWebKeys loadPublicKeyEncryptionKeys(Message m, Properties props) { String storeType = props.getProperty(JoseConstants.RSSEC_KEY_STORE_TYPE); if ("jwk".equals(storeType)) { return JwkUtils.loadPublicJwkSet(m, props); } else { //TODO: consider loading all the public keys in the store PublicKey key = KeyManagementUtils.loadPublicKey(m, props); JsonWebKey jwk = JwkUtils.fromPublicKey(key, props, JoseConstants.RSSEC_ENCRYPTION_KEY_ALGORITHM); return new JsonWebKeys(jwk); } } private static Properties loadJweProperties(Message m, String propLoc) { try { return JoseUtils.loadProperties(propLoc, m.getExchange().getBus()); } catch (Exception ex) { LOG.warning("JWS init properties are not available"); throw new JweException(JweException.Error.NO_INIT_PROPERTIES); } } }