/** * 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.rt.security.crypto; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.Key; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.PrivateKey; import java.security.Provider; import java.security.PublicKey; import java.security.SecureRandom; import java.security.Security; import java.security.Signature; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.ECGenParameterSpec; import java.security.spec.ECParameterSpec; import java.security.spec.ECPoint; import java.security.spec.ECPrivateKeySpec; import java.security.spec.ECPublicKeySpec; import java.security.spec.RSAPrivateCrtKeySpec; import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.apache.cxf.common.classloader.ClassLoaderUtils; import org.apache.cxf.common.util.Base64UrlUtility; import org.apache.cxf.common.util.Base64Utility; import org.apache.cxf.common.util.CompressionUtils; import org.apache.cxf.helpers.IOUtils; /** * Encryption helpers */ public final class CryptoUtils { private CryptoUtils() { } public static void installBouncyCastleProvider() throws Exception { final String bcClassName = "org.bouncycastle.jce.provider.BouncyCastleProvider"; if (Security.getProvider(bcClassName) == null) { Security.addProvider((Provider)ClassLoaderUtils.loadClass(bcClassName, CryptoUtils.class).newInstance()); } } public static void removeBouncyCastleProvider() { Security.removeProvider("org.bouncycastle.jce.provider.BouncyCastleProvider"); } public static String encodeSecretKey(SecretKey key) throws SecurityException { return encodeBytes(key.getEncoded()); } public static String encryptSecretKey(SecretKey secretKey, PublicKey publicKey) throws SecurityException { KeyProperties props = new KeyProperties(publicKey.getAlgorithm()); return encryptSecretKey(secretKey, publicKey, props); } public static String encryptSecretKey(SecretKey secretKey, PublicKey publicKey, KeyProperties props) throws SecurityException { byte[] encryptedBytes = wrapSecretKey(secretKey, publicKey, props); return encodeBytes(encryptedBytes); } public static byte[] generateSecureRandomBytes(int size) { SecureRandom sr = new SecureRandom(); byte[] bytes = new byte[size]; sr.nextBytes(bytes); return bytes; } public static RSAPublicKey getRSAPublicKey(String encodedModulus, String encodedPublicExponent) { try { return getRSAPublicKey(CryptoUtils.decodeSequence(encodedModulus), CryptoUtils.decodeSequence(encodedPublicExponent)); } catch (Exception ex) { throw new SecurityException(ex); } } public static RSAPublicKey getRSAPublicKey(byte[] modulusBytes, byte[] publicExponentBytes) { try { return getRSAPublicKey(KeyFactory.getInstance("RSA"), modulusBytes, publicExponentBytes); } catch (Exception ex) { throw new SecurityException(ex); } } public static RSAPublicKey getRSAPublicKey(KeyFactory factory, byte[] modulusBytes, byte[] publicExponentBytes) { BigInteger modulus = toBigInteger(modulusBytes); BigInteger publicExponent = toBigInteger(publicExponentBytes); return getRSAPublicKey(factory, modulus, publicExponent); } public static RSAPublicKey getRSAPublicKey(BigInteger modulusBytes, BigInteger publicExponentBytes) { try { return getRSAPublicKey(KeyFactory.getInstance("RSA"), modulusBytes, publicExponentBytes); } catch (Exception ex) { throw new SecurityException(ex); } } public static RSAPublicKey getRSAPublicKey(KeyFactory factory, BigInteger modulus, BigInteger publicExponent) { try { return (RSAPublicKey)factory.generatePublic( new RSAPublicKeySpec(modulus, publicExponent)); } catch (Exception ex) { throw new SecurityException(ex); } } public static RSAPrivateKey getRSAPrivateKey(String encodedModulus, String encodedPrivateExponent) { try { return getRSAPrivateKey(CryptoUtils.decodeSequence(encodedModulus), CryptoUtils.decodeSequence(encodedPrivateExponent)); } catch (Exception ex) { throw new SecurityException(ex); } } public static RSAPrivateKey getRSAPrivateKey(byte[] modulusBytes, byte[] privateExponentBytes) { BigInteger modulus = toBigInteger(modulusBytes); BigInteger privateExponent = toBigInteger(privateExponentBytes); try { KeyFactory factory = KeyFactory.getInstance("RSA"); return (RSAPrivateKey)factory.generatePrivate( new RSAPrivateKeySpec(modulus, privateExponent)); } catch (Exception ex) { throw new SecurityException(ex); } } //CHECKSTYLE:OFF public static RSAPrivateKey getRSAPrivateKey(String encodedModulus, String encodedPublicExponent, String encodedPrivateExponent, String encodedPrimeP, String encodedPrimeQ, String encodedPrimeExpP, String encodedPrimeExpQ, String encodedCrtCoefficient) { //CHECKSTYLE:ON try { return getRSAPrivateKey(CryptoUtils.decodeSequence(encodedModulus), CryptoUtils.decodeSequence(encodedPublicExponent), CryptoUtils.decodeSequence(encodedPrivateExponent), CryptoUtils.decodeSequence(encodedPrimeP), CryptoUtils.decodeSequence(encodedPrimeQ), CryptoUtils.decodeSequence(encodedPrimeExpP), CryptoUtils.decodeSequence(encodedPrimeExpQ), CryptoUtils.decodeSequence(encodedCrtCoefficient)); } catch (Exception ex) { throw new SecurityException(ex); } } //CHECKSTYLE:OFF public static RSAPrivateKey getRSAPrivateKey(byte[] modulusBytes, byte[] publicExponentBytes, byte[] privateExponentBytes, byte[] primePBytes, byte[] primeQBytes, byte[] primeExpPBytes, byte[] primeExpQBytes, byte[] crtCoefficientBytes) { //CHECKSTYLE:ON BigInteger modulus = toBigInteger(modulusBytes); BigInteger publicExponent = toBigInteger(publicExponentBytes); BigInteger privateExponent = toBigInteger(privateExponentBytes); BigInteger primeP = toBigInteger(primePBytes); BigInteger primeQ = toBigInteger(primeQBytes); BigInteger primeExpP = toBigInteger(primeExpPBytes); BigInteger primeExpQ = toBigInteger(primeExpQBytes); BigInteger crtCoefficient = toBigInteger(crtCoefficientBytes); try { KeyFactory factory = KeyFactory.getInstance("RSA"); return (RSAPrivateKey)factory.generatePrivate( new RSAPrivateCrtKeySpec(modulus, publicExponent, privateExponent, primeP, primeQ, primeExpP, primeExpQ, crtCoefficient)); } catch (Exception ex) { throw new SecurityException(ex); } } public static ECPrivateKey getECPrivateKey(String curve, String encodedPrivateKey) { try { return getECPrivateKey(curve, CryptoUtils.decodeSequence(encodedPrivateKey)); } catch (Exception ex) { throw new SecurityException(ex); } } public static ECPrivateKey getECPrivateKey(String curve, byte[] privateKey) { try { ECParameterSpec params = getECParameterSpec(curve, true); ECPrivateKeySpec keySpec = new ECPrivateKeySpec( toBigInteger(privateKey), params); KeyFactory kf = KeyFactory.getInstance("EC"); return (ECPrivateKey) kf.generatePrivate(keySpec); } catch (Exception ex) { throw new SecurityException(ex); } } private static ECParameterSpec getECParameterSpec(String curve, boolean isPrivate) throws Exception { KeyPair pair = generateECKeyPair(curve); return isPrivate ? ((ECPublicKey) pair.getPublic()).getParams() : ((ECPrivateKey) pair.getPrivate()).getParams(); } public static KeyPair generateECKeyPair(String curve) { try { KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC"); ECGenParameterSpec kpgparams = new ECGenParameterSpec("sec" + curve.toLowerCase().replace("-", "") + "r1"); kpg.initialize(kpgparams); return kpg.generateKeyPair(); } catch (Exception ex) { throw new SecurityException(ex); } } public static ECPublicKey getECPublicKey(String curve, String encodedXPoint, String encodedYPoint) { try { return getECPublicKey(curve, CryptoUtils.decodeSequence(encodedXPoint), CryptoUtils.decodeSequence(encodedYPoint)); } catch (Exception ex) { throw new SecurityException(ex); } } public static ECPublicKey getECPublicKey(String curve, byte[] xPoint, byte[] yPoint) { try { ECParameterSpec params = getECParameterSpec(curve, false); ECPoint ecPoint = new ECPoint(toBigInteger(xPoint), toBigInteger(yPoint)); ECPublicKeySpec keySpec = new ECPublicKeySpec(ecPoint, params); KeyFactory kf = KeyFactory.getInstance("EC"); return (ECPublicKey) kf.generatePublic(keySpec); } catch (Exception ex) { throw new SecurityException(ex); } } private static BigInteger toBigInteger(byte[] bytes) { return new BigInteger(1, bytes); } public static AlgorithmParameterSpec getContentEncryptionCipherSpec(int authTagLength, byte[] iv) { if (authTagLength > 0) { return CryptoUtils.getGCMParameterSpec(authTagLength, iv); } else if (iv.length > 0) { return new IvParameterSpec(iv); } else { return null; } } public static AlgorithmParameterSpec getGCMParameterSpec(int authTagLength, byte[] iv) { return new GCMParameterSpec(authTagLength, iv); } public static byte[] signData(byte[] data, PrivateKey key, String signAlgo) { return signData(data, key, signAlgo, null, null); } public static byte[] signData(byte[] data, PrivateKey key, String signAlgo, SecureRandom random, AlgorithmParameterSpec params) { try { Signature s = getSignature(key, signAlgo, random, params); s.update(data); return s.sign(); } catch (Exception ex) { throw new SecurityException(ex); } } public static Signature getSignature(PrivateKey key, String signAlgo, SecureRandom random, AlgorithmParameterSpec params) { try { Signature s = Signature.getInstance(signAlgo); if (random == null) { s.initSign(key); } else { s.initSign(key, random); } if (params != null) { s.setParameter(params); } return s; } catch (Exception ex) { throw new SecurityException(ex); } } public static Signature getVerificationSignature(PublicKey key, String signAlgo, AlgorithmParameterSpec params) { try { Signature s = Signature.getInstance(signAlgo); s.initVerify(key); if (params != null) { s.setParameter(params); } return s; } catch (Exception ex) { throw new SecurityException(ex); } } public static boolean verifySignature(byte[] data, byte[] signature, PublicKey key, String signAlgo) { return verifySignature(data, signature, key, signAlgo, null); } public static boolean verifySignature(byte[] data, byte[] signature, PublicKey key, String signAlgo, AlgorithmParameterSpec params) { try { Signature s = getVerificationSignature(key, signAlgo, params); s.update(data); return s.verify(signature); } catch (Exception ex) { throw new SecurityException(ex); } } public static SecretKey getSecretKey(String symEncAlgo) throws SecurityException { return getSecretKey(new KeyProperties(symEncAlgo)); } public static SecretKey getSecretKey(String symEncAlgo, int keySize) throws SecurityException { return getSecretKey(new KeyProperties(symEncAlgo, keySize)); } public static SecretKey getSecretKey(KeyProperties props) throws SecurityException { try { KeyGenerator keyGen = KeyGenerator.getInstance(props.getKeyAlgo()); AlgorithmParameterSpec algoSpec = props.getAlgoSpec(); SecureRandom random = props.getSecureRandom(); if (algoSpec != null) { if (random != null) { keyGen.init(algoSpec, random); } else { keyGen.init(algoSpec); } } else { int keySize = props.getKeySize(); if (keySize == -1) { keySize = 128; } if (random != null) { keyGen.init(keySize, random); } else { keyGen.init(keySize); } } return keyGen.generateKey(); } catch (Exception ex) { throw new SecurityException(ex); } } public static String decryptSequence(String encodedToken, String encodedSecretKey) throws SecurityException { return decryptSequence(encodedToken, encodedSecretKey, new KeyProperties("AES")); } public static String decryptSequence(String encodedData, String encodedSecretKey, KeyProperties props) throws SecurityException { SecretKey key = decodeSecretKey(encodedSecretKey, props.getKeyAlgo()); return decryptSequence(encodedData, key, props); } public static String decryptSequence(String encodedData, Key secretKey) throws SecurityException { return decryptSequence(encodedData, secretKey, null); } public static String decryptSequence(String encodedData, Key secretKey, KeyProperties props) throws SecurityException { byte[] encryptedBytes = decodeSequence(encodedData); byte[] bytes = decryptBytes(encryptedBytes, secretKey, props); try { return new String(bytes, StandardCharsets.UTF_8); } catch (Exception ex) { throw new SecurityException(ex); } } public static String encryptSequence(String sequence, Key secretKey) throws SecurityException { return encryptSequence(sequence, secretKey, null); } public static String encryptSequence(String sequence, Key secretKey, KeyProperties keyProps) throws SecurityException { try { byte[] bytes = encryptBytes(sequence.getBytes(StandardCharsets.UTF_8), secretKey, keyProps); return encodeBytes(bytes); } catch (Exception ex) { throw new SecurityException(ex); } } public static String encodeBytes(byte[] bytes) throws SecurityException { try { return Base64UrlUtility.encode(bytes); } catch (Exception ex) { throw new SecurityException(ex); } } public static byte[] encryptBytes(byte[] bytes, Key secretKey) throws SecurityException { return encryptBytes(bytes, secretKey, null); } public static byte[] encryptBytes(byte[] bytes, Key secretKey, KeyProperties keyProps) throws SecurityException { return processBytes(bytes, secretKey, keyProps, Cipher.ENCRYPT_MODE); } public static byte[] decryptBytes(byte[] bytes, Key secretKey) throws SecurityException { return decryptBytes(bytes, secretKey, null); } public static byte[] decryptBytes(byte[] bytes, Key secretKey, KeyProperties keyProps) throws SecurityException { return processBytes(bytes, secretKey, keyProps, Cipher.DECRYPT_MODE); } public static byte[] wrapSecretKey(byte[] keyBytes, String keyAlgo, Key wrapperKey, KeyProperties wrapperKeyProps) throws SecurityException { return wrapSecretKey(new SecretKeySpec(keyBytes, convertJCECipherToSecretKeyName(keyAlgo)), wrapperKey, wrapperKeyProps); } public static byte[] wrapSecretKey(Key secretKey, Key wrapperKey, KeyProperties keyProps) throws SecurityException { try { Cipher c = initCipher(wrapperKey, keyProps, Cipher.WRAP_MODE); return c.wrap(secretKey); } catch (Exception ex) { throw new SecurityException(ex); } } public static SecretKey unwrapSecretKey(byte[] wrappedBytes, String wrappedKeyAlgo, Key unwrapperKey, String unwrapperKeyAlgo) throws SecurityException { return unwrapSecretKey(wrappedBytes, wrappedKeyAlgo, unwrapperKey, new KeyProperties(unwrapperKeyAlgo)); } public static SecretKey unwrapSecretKey(byte[] wrappedBytes, String wrappedKeyAlgo, Key unwrapperKey, KeyProperties keyProps) throws SecurityException { return (SecretKey)unwrapKey(wrappedBytes, wrappedKeyAlgo, unwrapperKey, keyProps, Cipher.SECRET_KEY); } public static Key unwrapKey(byte[] wrappedBytes, String wrappedKeyAlgo, Key unwrapperKey, KeyProperties keyProps, int wrappedKeyType) throws SecurityException { try { Cipher c = initCipher(unwrapperKey, keyProps, Cipher.UNWRAP_MODE); return c.unwrap(wrappedBytes, wrappedKeyAlgo, wrappedKeyType); } catch (Exception ex) { throw new SecurityException(ex); } } private static byte[] processBytes(byte[] bytes, Key secretKey, KeyProperties keyProps, int mode) throws SecurityException { boolean compressionSupported = keyProps != null && keyProps.isCompressionSupported(); if (compressionSupported && mode == Cipher.ENCRYPT_MODE) { bytes = CompressionUtils.deflate(bytes, false); } try { Cipher c = initCipher(secretKey, keyProps, mode); byte[] result = new byte[0]; int blockSize = keyProps != null ? keyProps.getBlockSize() : -1; if (secretKey instanceof SecretKey && blockSize == -1) { result = c.doFinal(bytes); } else { if (blockSize == -1) { if (System.getProperty("java.version").startsWith("9")) { //the default block size is 256 when use private key under java9 blockSize = secretKey instanceof PublicKey ? 117 : 256; } else { blockSize = secretKey instanceof PublicKey ? 117 : 128; } } boolean updateRequired = keyProps != null && keyProps.getAdditionalData() != null; int offset = 0; for (; offset + blockSize < bytes.length; offset += blockSize) { byte[] next = !updateRequired ? c.doFinal(bytes, offset, blockSize) : c.update(bytes, offset, blockSize); result = addToResult(result, next); } if (offset < bytes.length) { result = addToResult(result, c.doFinal(bytes, offset, bytes.length - offset)); } else { result = addToResult(result, c.doFinal()); } } if (compressionSupported && mode == Cipher.DECRYPT_MODE) { result = IOUtils.readBytesFromStream(CompressionUtils.inflate(result, false)); } return result; } catch (Exception ex) { throw new SecurityException(ex); } } public static Cipher initCipher(Key secretKey, KeyProperties keyProps, int mode) throws SecurityException { try { String algorithm = keyProps != null && keyProps.getKeyAlgo() != null ? keyProps.getKeyAlgo() : secretKey.getAlgorithm(); Cipher c = Cipher.getInstance(algorithm); if (keyProps == null || keyProps.getAlgoSpec() == null && keyProps.getSecureRandom() == null) { c.init(mode, secretKey); } else { AlgorithmParameterSpec algoSpec = keyProps.getAlgoSpec(); SecureRandom random = keyProps.getSecureRandom(); if (algoSpec == null) { c.init(mode, secretKey, random); } else if (random == null) { c.init(mode, secretKey, algoSpec); } else { c.init(mode, secretKey, algoSpec, random); } } if (keyProps != null && keyProps.getAdditionalData() != null) { c.updateAAD(keyProps.getAdditionalData()); } return c; } catch (Exception ex) { throw new SecurityException(ex); } } private static byte[] addToResult(byte[] prefix, byte[] suffix) { if (suffix == null || suffix.length == 0) { return prefix; } else if (prefix.length == 0) { return suffix; } else { byte[] result = new byte[prefix.length + suffix.length]; System.arraycopy(prefix, 0, result, 0, prefix.length); System.arraycopy(suffix, 0, result, prefix.length, suffix.length); return result; } } public static SecretKey decodeSecretKey(String encodedSecretKey) throws SecurityException { return decodeSecretKey(encodedSecretKey, "AES"); } public static SecretKey decodeSecretKey(String encodedSecretKey, String secretKeyAlgo) throws SecurityException { byte[] secretKeyBytes = decodeSequence(encodedSecretKey); return createSecretKeySpec(secretKeyBytes, secretKeyAlgo); } public static SecretKey decryptSecretKey(String encodedEncryptedSecretKey, PrivateKey privateKey) { return decryptSecretKey(encodedEncryptedSecretKey, "AES", privateKey); } public static SecretKey decryptSecretKey(String encodedEncryptedSecretKey, String secretKeyAlgo, PrivateKey privateKey) throws SecurityException { KeyProperties props = new KeyProperties(privateKey.getAlgorithm()); return decryptSecretKey(encodedEncryptedSecretKey, secretKeyAlgo, props, privateKey); } public static SecretKey decryptSecretKey(String encodedEncryptedSecretKey, String secretKeyAlgo, KeyProperties props, PrivateKey privateKey) throws SecurityException { byte[] encryptedBytes = decodeSequence(encodedEncryptedSecretKey); return unwrapSecretKey(encryptedBytes, secretKeyAlgo, privateKey, props); } public static SecretKey createSecretKeySpec(String encodedBytes, String algo) { return new SecretKeySpec(decodeSequence(encodedBytes), algo); } public static SecretKey createSecretKeySpec(byte[] bytes, String algo) { return new SecretKeySpec(bytes, convertJCECipherToSecretKeyName(algo)); } public static byte[] decodeSequence(String encodedSequence) throws SecurityException { try { return Base64UrlUtility.decode(encodedSequence); } catch (Exception ex) { throw new SecurityException(ex); } } private static String convertJCECipherToSecretKeyName(String jceCipherName) { if (jceCipherName != null) { if (jceCipherName.startsWith("AES")) { return "AES"; } else if (jceCipherName.startsWith("DESede")) { return "DESede"; } else if (jceCipherName.startsWith("SEED")) { return "SEED"; } else if (jceCipherName.startsWith("Camellia")) { return "Camellia"; } } return null; } public static Certificate loadCertificate(InputStream storeLocation, char[] storePassword, String alias, String storeType) { KeyStore keyStore = loadKeyStore(storeLocation, storePassword, storeType); return loadCertificate(keyStore, alias); } public static Certificate loadCertificate(KeyStore keyStore, String alias) { try { if (alias == null) { throw new SecurityException("No keystore alias was defined"); } if (!keyStore.containsAlias(alias)) { throw new SecurityException("No alias exists in the keystore for: " + alias); } return keyStore.getCertificate(alias); } catch (Exception ex) { throw new SecurityException(ex); } } public static String encodeCertificate(Certificate cert) { try { return Base64Utility.encode(cert.getEncoded()); } catch (Exception ex) { throw new SecurityException(ex); } } public static Certificate decodeCertificate(String encodedCert) { try { byte[] decoded = Base64Utility.decode(encodedCert); return CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(decoded)); } catch (Exception ex) { throw new SecurityException(ex); } } public static PublicKey loadPublicKey(InputStream storeLocation, char[] storePassword, String alias, String storeType) { return loadCertificate(storeLocation, storePassword, alias, storeType).getPublicKey(); } public static PublicKey loadPublicKey(KeyStore keyStore, String alias) { return loadCertificate(keyStore, alias).getPublicKey(); } public static KeyStore loadKeyStore(InputStream storeLocation, char[] storePassword, String type) { try { KeyStore ks = KeyStore.getInstance(type == null ? KeyStore.getDefaultType() : type); ks.load(storeLocation, storePassword); return ks; } catch (Exception ex) { throw new SecurityException(ex); } } public static PrivateKey loadPrivateKey(InputStream storeLocation, char[] storePassword, char[] keyPassword, String alias, String storeType) { KeyStore keyStore = loadKeyStore(storeLocation, storePassword, storeType); return loadPrivateKey(keyStore, keyPassword, alias); } public static PrivateKey loadPrivateKey(KeyStore keyStore, char[] keyPassword, String alias) { try { if (alias == null) { throw new SecurityException("No keystore alias was defined"); } if (!keyStore.containsAlias(alias)) { throw new SecurityException("No alias exists in the keystore for: " + alias); } if (!keyStore.isKeyEntry(alias)) { throw new SecurityException("The given alias " + alias + " is not a private key in the keystore."); } KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, new KeyStore.PasswordProtection(keyPassword)); return pkEntry.getPrivateKey(); } catch (Exception ex) { throw new SecurityException(ex); } } }