/** * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved * * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at * https://opensso.dev.java.net/public/CDDLv1.0.html or * opensso/legal/CDDLv1.0.txt * See the License for the specific language governing * permission and limitations under the License. * * When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at opensso/legal/CDDLv1.0.txt. * If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * $Id: JCEEncryption.java,v 1.3 2008/10/20 17:24:43 beomsuk Exp $ * */ /* * Portions Copyrighted 2010-2014 ForgeRock AS */ package com.iplanet.services.util; import com.sun.identity.shared.debug.Debug; import org.forgerock.openam.utils.CipherProvider; import org.forgerock.openam.utils.Providers; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; /** *

* This class provides encryption and decryption facility for the SDK based on * the existence of a JCE provider in the runtime. Unlike * JSSEncryption, this class can only handle a fixed algorithm * for key generation and encryption which is PBEWithMD5AndDES. * Since different JCE providers such as IAIK use slightly different names for * this algorithm, this class provides the facility to over-ride this hardcoded * value by setting the system properties for each of these algorithms. The * property name for specifying the key generation algorithm is * amKeyGenDescriptor and that for specifying encryption * algorithm is amCryptoDescriptor. *

*

* NOTE: The facility of overriding key generation and encryption * algorithms must be used very carefully. In particular, this facility is not * meant to force the use of an algorithm different from the specified default * algorithm PBEWithMD5AndDES since that will result in * incompatibility between the JSSEncryption if it is being used * by any peer entity such as agent or server. This would not be a problem if * all entities in the network were configured to use this encryption provider * and all had the same implementation of the specified algorithms available. */ public class JCEEncryption implements AMEncryption, ConfigurableKey { private static final byte VERSION = 1; private static final String CRYPTO_DESCRIPTOR; private static final String CRYPTO_DESCRIPTOR_PROPERTY_NAME = "amCryptoDescriptor"; private static final String CRYPTO_DESCRIPTOR_DEFAULT_VALUE = "PBEWithMD5AndDES"; private static final String CRYPTO_DESCRIPTOR_PROVIDER; private static final String CRYPTO_DESCRIPTOR_PROVIDER_PROPERTY_NAME = "amCryptoDescriptor.provider"; private static final String CRYPTO_DESCRIPTOR_PROVIDER_DEFAULT_VALUE = "SunJCE"; private static final String KEYGEN_ALGORITHM; private static final String KEYGEN_ALGORITHM_PROPERTY_NAME = "amKeyGenDescriptor"; private static final String KEYGEN_ALGORITHM_DEFAULT_VALUE = "PBEWithMD5AndDES"; private static final String KEYGEN_ALGORITHM_PROVIDER; private static final String KEYGEN_ALGORITHM_PROVIDER_PROPERTY_NAME = "amKeyGenDescriptor.provider"; private static final String KEYGEN_ALGORITHM_PROVIDER_DEFAULT_VALUE = "SunJCE"; private static final int DEFAULT_KEYGEN_ALG_INDEX = 2; private static final int DEFAULT_ENC_ALG_INDEX = 2; private static final int ITERATION_COUNT = 5; static { CRYPTO_DESCRIPTOR = System.getProperty(CRYPTO_DESCRIPTOR_PROPERTY_NAME, CRYPTO_DESCRIPTOR_DEFAULT_VALUE); KEYGEN_ALGORITHM = System.getProperty(KEYGEN_ALGORITHM_PROPERTY_NAME, KEYGEN_ALGORITHM_DEFAULT_VALUE); CRYPTO_DESCRIPTOR_PROVIDER = System.getProperty( CRYPTO_DESCRIPTOR_PROVIDER_PROPERTY_NAME, CRYPTO_DESCRIPTOR_PROVIDER_DEFAULT_VALUE); KEYGEN_ALGORITHM_PROVIDER = System.getProperty( KEYGEN_ALGORITHM_PROVIDER_PROPERTY_NAME, KEYGEN_ALGORITHM_PROVIDER_DEFAULT_VALUE); } private static final String CRYPTO_CACHE_SIZE_PROPERTY_NAME = "amCryptoCacheSize"; private static final int DEFAULT_CACHE_SIZE = 500; private static final int CACHE_SIZE = Integer.getInteger(CRYPTO_CACHE_SIZE_PROPERTY_NAME, DEFAULT_CACHE_SIZE); /** * Stores a per-thread copy of the underlying cipher, fetched from the standard {@link Cipher} implementation, * preferring the Sun JCE provider if available. */ private static final CipherProvider cipherProvider = Providers.cipherProvider(CRYPTO_DESCRIPTOR, CRYPTO_DESCRIPTOR_PROVIDER, CACHE_SIZE); /** * Method declaration * * @param clearText */ public byte[] encrypt(byte[] clearText) { return pbeEncrypt(clearText); } /** * Method declaration * * @param encText */ public byte[] decrypt(byte[] encText) { return pbeDecrypt(encText); } /** * Method declaration * * @param clearText */ private byte[] pbeEncrypt(final byte[] clearText) { byte[] result = null; if (clearText == null || clearText.length == 0) { return null; } if (_initialized) { try { byte type[] = new byte[2]; type[1] = (byte) DEFAULT_ENC_ALG_INDEX; type[0] = (byte) DEFAULT_KEYGEN_ALG_INDEX; final Cipher pbeCipher = cipherProvider.getCipher(); if (pbeCipher != null) { pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParameterSpec); result = pbeCipher.doFinal(clearText); byte[] iv = pbeCipher.getIV(); result = addPrefix(type, iv, result); } else { Debug debug = Debug.getInstance("amSDK"); if (debug != null) { debug.error("JCEEncryption: Failed to obtain Cipher"); } } } catch (Exception ex) { Debug debug = Debug.getInstance("amSDK"); if (debug != null) { debug.error("JCEEncryption:: failed to encrypt data", ex); } } } else { Debug debug = Debug.getInstance("amSDK"); if (debug != null) { debug.error("JCEEncryption:: not yet initialized"); } } return result; } /** * Method declaration * * @param type * @param iv * @param share */ private static byte[] addPrefix(byte type[], byte iv[], byte share[]) { byte data[] = new byte[share.length + 11]; data[0] = VERSION; data[1] = type[0]; data[2] = type[1]; for (int i = 0; i < 8; i++) { data[3 + i] = iv[i]; } for (int i = 0; i < share.length; i++) { data[11 + i] = share[i]; } return data; } /** * Method declaration * * @param cipherText */ private byte[] pbeDecrypt(byte[] cipherText) { byte[] result = null; if (_initialized) { try { byte share[] = cipherText; if (share[0] != VERSION) { Debug debug = Debug.getInstance("amSDK"); if (debug != null) { debug.error("JCEEncryption:: Unsupported version: " + share[0]); } return null; } byte raw[] = getRaw(share); final Cipher pbeCipher = cipherProvider.getCipher(); if (pbeCipher != null) { pbeCipher.init(Cipher.DECRYPT_MODE, pbeKey, pbeParameterSpec); result = pbeCipher.doFinal(raw); } else { Debug debug = Debug.getInstance("amSDK"); if (debug != null) { debug.error("JCEEncryption: Failed to obtain Cipher"); } } } catch (Exception ex) { Debug debug = Debug.getInstance("amSDK"); if (debug != null) { debug.error("JCEEncryption:: failed to decrypt data", ex); } } } else { Debug debug = Debug.getInstance("amSDK"); if (debug != null) { debug.error("JCEEncryption:: not yet initialized"); } } return result; } /** * Method declaration * * @param share */ private static byte[] getRaw(byte share[]) { byte data[] = new byte[share.length - 11]; for (int i = 11; i < share.length; i++) { data[i - 11] = share[i]; } return data; } /** * Sets password-based key to use */ public void setPassword(String password) throws Exception { pbeKey = SecretKeyFactory.getInstance(KEYGEN_ALGORITHM, KEYGEN_ALGORITHM_PROVIDER) .generateSecret(new PBEKeySpec(password.toCharArray())); _initialized = true; } private static final byte[] ___y = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; private volatile SecretKey pbeKey; private volatile boolean _initialized = false; private static final PBEParameterSpec pbeParameterSpec = new PBEParameterSpec( ___y, ITERATION_COUNT); }