// --- BEGIN COPYRIGHT BLOCK --- // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; version 2 of the License. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // (C) 2007 Red Hat, Inc. // All rights reserved. // --- END COPYRIGHT BLOCK --- package com.netscape.kra; import java.io.ByteArrayOutputStream; import java.io.CharConversionException; import java.io.IOException; import java.math.BigInteger; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.spec.AlgorithmParameterSpec; import java.util.Hashtable; import java.util.Random; import javax.crypto.spec.RC2ParameterSpec; import org.mozilla.jss.CryptoManager; import org.mozilla.jss.asn1.OCTET_STRING; import org.mozilla.jss.crypto.Cipher; import org.mozilla.jss.crypto.CryptoToken; import org.mozilla.jss.crypto.EncryptionAlgorithm; import org.mozilla.jss.crypto.IVParameterSpec; import org.mozilla.jss.crypto.KeyGenerator; import org.mozilla.jss.crypto.KeyWrapAlgorithm; import org.mozilla.jss.crypto.KeyWrapper; import org.mozilla.jss.crypto.PBEAlgorithm; import org.mozilla.jss.crypto.PBEKeyGenParams; import org.mozilla.jss.crypto.SymmetricKey; import org.mozilla.jss.crypto.TokenException; import org.mozilla.jss.pkcs12.PasswordConverter; import org.mozilla.jss.pkcs7.ContentInfo; import org.mozilla.jss.pkcs7.EncryptedContentInfo; import org.mozilla.jss.pkix.primitive.AlgorithmIdentifier; import org.mozilla.jss.pkix.primitive.PBEParameter; import org.mozilla.jss.util.Password; import com.netscape.certsrv.apps.CMS; import com.netscape.certsrv.base.EBaseException; import com.netscape.certsrv.dbs.keydb.IKeyRecord; import com.netscape.certsrv.dbs.keydb.IKeyRepository; import com.netscape.certsrv.kra.EKRAException; import com.netscape.certsrv.kra.IKeyRecoveryAuthority; import com.netscape.certsrv.request.IRequest; import com.netscape.certsrv.request.IService; import com.netscape.certsrv.security.IStorageKeyUnit; import com.netscape.certsrv.security.ITransportKeyUnit; import com.netscape.cms.servlet.request.KeyRequestResource; import com.netscape.cmscore.dbs.KeyRecord; import com.netscape.cmsutil.util.Utils; /** * This implementation services SecurityData Recovery requests. *

* * @version $Revision$, $Date$ */ @SuppressWarnings("deprecation") public class SecurityDataRecoveryService implements IService { private IKeyRecoveryAuthority mKRA = null; private IKeyRepository mStorage = null; private IStorageKeyUnit mStorageUnit = null; private ITransportKeyUnit mTransportUnit = null; public static final String ATTR_SERIALNO = "serialNumber"; public static final String ATTR_KEY_RECORD = "keyRecord"; public SecurityDataRecoveryService(IKeyRecoveryAuthority kra) { mKRA = kra; mStorage = mKRA.getKeyRepository(); mStorageUnit = mKRA.getStorageKeyUnit(); mTransportUnit = mKRA.getTransportKeyUnit(); } /** * Performs the service (such as certificate generation) * represented by this request. *

* * @param request * The SecurityData recovery request that needs service. The service may use * attributes stored in the request, and may update the * values, or store new ones. * @return * an indication of whether this request is still pending. * 'false' means the request will wait for further notification. * @exception EBaseException indicates major processing failure. */ public boolean serviceRequest(IRequest request) throws EBaseException { //Pave the way for allowing generated IV vector byte iv[]= null; byte iv_default[] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 }; byte iv_in[] = null; Hashtable params = mKRA.getVolatileRequest( request.getRequestId()); if (params == null) { CMS.debug("Can't get volatile params."); throw new EBaseException("Can't obtain volatile params!"); } BigInteger serialno = request.getExtDataInBigInteger(ATTR_SERIALNO); request.setExtData(ATTR_KEY_RECORD, serialno); byte[] wrappedPassPhrase = null; byte[] wrappedSessKey = null; String transWrappedSessKeyStr = (String) params.get(IRequest.SECURITY_DATA_TRANS_SESS_KEY); if (transWrappedSessKeyStr != null) { wrappedSessKey = Utils.base64decode(transWrappedSessKeyStr); } String sessWrappedPassPhraseStr = (String) params.get(IRequest.SECURITY_DATA_SESS_PASS_PHRASE); if (sessWrappedPassPhraseStr != null) { wrappedPassPhrase = Utils.base64decode(sessWrappedPassPhraseStr); } String ivInStr = (String) params.get(IRequest.SECURITY_DATA_IV_STRING_IN); if (ivInStr != null) { iv_in = Utils.base64decode(ivInStr); } if (transWrappedSessKeyStr == null && sessWrappedPassPhraseStr == null) { //We may be in recovery case where no params were initially submitted. return false; } //Create the return IV if needed. iv = new byte[8]; try { Random rnd = new Random(); rnd.nextBytes(iv); } catch (Exception e) { iv = iv_default; } String ivStr = Utils.base64encode(iv); KeyRecord keyRecord = (KeyRecord) mStorage.readKeyRecord(serialno); SymmetricKey unwrappedSess = null; String dataType = (String) keyRecord.get(IKeyRecord.ATTR_DATA_TYPE); SymmetricKey symKey = null; byte[] unwrappedSecData = null; if (dataType.equals(KeyRequestResource.SYMMETRIC_KEY_TYPE)) { symKey = recoverSymKey(keyRecord); } else if (dataType.equals(KeyRequestResource.PASS_PHRASE_TYPE)) { unwrappedSecData = recoverSecurityData(keyRecord); } CryptoToken ct = mTransportUnit.getToken(); byte[] key_data = null; String pbeWrappedData = null; if (sessWrappedPassPhraseStr != null) { //We have a trans wrapped pass phrase, we will be doing PBE packaging byte[] unwrappedPass = null; Password pass = null; try { unwrappedSess = mTransportUnit.unwrap_sym(wrappedSessKey, SymmetricKey.Usage.DECRYPT); Cipher decryptor = ct.getCipherContext(EncryptionAlgorithm.DES3_CBC_PAD); decryptor.initDecrypt(unwrappedSess, new IVParameterSpec(iv_in)); unwrappedPass = decryptor.doFinal(wrappedPassPhrase); String passStr = new String(unwrappedPass, "UTF-8"); pass = new Password(passStr.toCharArray()); passStr = null; if (dataType.equals(KeyRequestResource.SYMMETRIC_KEY_TYPE)) { pbeWrappedData = createEncryptedContentInfo(ct, symKey, null, pass); } else if (dataType.equals(KeyRequestResource.PASS_PHRASE_TYPE)) { pbeWrappedData = createEncryptedContentInfo(ct, null, unwrappedSecData, pass); } params.put(IRequest.SECURITY_DATA_PASS_WRAPPED_DATA, pbeWrappedData); } catch (Exception e) { throw new EBaseException("Can't unwrap pass phase! " + e.toString()); } finally { if ( pass != null) { pass.clear(); } if ( unwrappedPass != null) { java.util.Arrays.fill(unwrappedPass, (byte) 0); } } } else { // No trans wrapped pass phrase, return session wrapped data. if (dataType.equals(KeyRequestResource.SYMMETRIC_KEY_TYPE)) { //wrap the key with session key try { unwrappedSess = mTransportUnit.unwrap_sym(wrappedSessKey, SymmetricKey.Usage.WRAP); KeyWrapper wrapper = ct.getKeyWrapper(KeyWrapAlgorithm.DES3_CBC_PAD); wrapper.initWrap(unwrappedSess, new IVParameterSpec(iv)); key_data = wrapper.wrap(symKey); } catch (Exception e) { throw new EBaseException("Can't wrap symmetric key! " + e.toString()); } } else if (dataType.equals(KeyRequestResource.PASS_PHRASE_TYPE)) { try { unwrappedSess = mTransportUnit.unwrap_sym(wrappedSessKey, SymmetricKey.Usage.ENCRYPT); Cipher encryptor = ct.getCipherContext(EncryptionAlgorithm.DES3_CBC_PAD); if (encryptor != null) { encryptor.initEncrypt(unwrappedSess, new IVParameterSpec(iv)); key_data = encryptor.doFinal(unwrappedSecData); } else { throw new IOException("Failed to create cipher"); } } catch (Exception e) { throw new EBaseException("Can't wrap pass phrase!"); } } String wrappedKeyData = Utils.base64encode(key_data); params.put(IRequest.SECURITY_DATA_SESS_WRAPPED_DATA, wrappedKeyData); params.put(IRequest.SECURITY_DATA_IV_STRING_OUT, ivStr); } return false; } public SymmetricKey recoverSymKey(KeyRecord keyRecord) throws EBaseException { try { SymmetricKey symKey = mStorageUnit.unwrap( keyRecord.getPrivateKeyData()); if (symKey == null) { throw new EKRAException(CMS.getUserMessage("CMS_KRA_RECOVERY_FAILED_1", "symmetric key unwrapping failure")); } return symKey; } catch (Exception e) { throw new EKRAException(CMS.getUserMessage("CMS_KRA_RECOVERY_FAILED_1", "recoverSymKey() " + e.toString())); } } public byte[] recoverSecurityData(KeyRecord keyRecord) throws EBaseException { byte[] decodedData = null; try { decodedData = mStorageUnit.decryptInternalPrivate( keyRecord.getPrivateKeyData()); if (decodedData == null) { throw new EKRAException(CMS.getUserMessage("CMS_KRA_RECOVERY_FAILED_1", "security data unwrapping failure")); } return decodedData; } catch (Exception e) { throw new EKRAException(CMS.getUserMessage("CMS_KRA_RECOVERY_FAILED_1", "recoverSecurityData() " + e.toString())); } } //ToDo: This might fit in JSS. private static EncryptedContentInfo createEncryptedContentInfoPBEOfSymmKey(PBEAlgorithm keyGenAlg, Password password, byte[] salt, int iterationCount, KeyGenerator.CharToByteConverter charToByteConverter, SymmetricKey symKey, CryptoToken token) throws CryptoManager.NotInitializedException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException, TokenException, CharConversionException { if (keyGenAlg == null) { throw new NoSuchAlgorithmException("Key generation algorithm is NULL"); } PBEAlgorithm pbeAlg = keyGenAlg; KeyGenerator kg = token.getKeyGenerator(keyGenAlg); PBEKeyGenParams pbekgParams = new PBEKeyGenParams( password, salt, iterationCount); if (charToByteConverter != null) { kg.setCharToByteConverter(charToByteConverter); } kg.initialize(pbekgParams); SymmetricKey key = kg.generate(); EncryptionAlgorithm encAlg = pbeAlg.getEncryptionAlg(); AlgorithmParameterSpec params = null; if (encAlg.getParameterClass().equals(IVParameterSpec.class)) { params = new IVParameterSpec(kg.generatePBE_IV()); } else if (encAlg.getParameterClass().equals( RC2ParameterSpec.class)) { params = new RC2ParameterSpec(key.getStrength(), kg.generatePBE_IV()); } KeyWrapper wrapper = token.getKeyWrapper( KeyWrapAlgorithm.DES3_CBC_PAD); wrapper.initWrap(key, params); byte encrypted[] = wrapper.wrap(symKey); PBEParameter pbeParam = new PBEParameter(salt, iterationCount); AlgorithmIdentifier encAlgID = new AlgorithmIdentifier( keyGenAlg.toOID(), pbeParam); EncryptedContentInfo encCI = new EncryptedContentInfo( ContentInfo.DATA, encAlgID, new OCTET_STRING(encrypted)); return encCI; } private static String createEncryptedContentInfo(CryptoToken ct, SymmetricKey symKey, byte[] securityData, Password password) throws EBaseException { EncryptedContentInfo cInfo = null; String retData = null; PBEAlgorithm keyGenAlg = PBEAlgorithm.PBE_SHA1_DES3_CBC; byte[] encoded = null; try { PasswordConverter passConverter = new PasswordConverter(); byte salt[] = { 0x01, 0x01, 0x01, 0x01 }; if (symKey != null) { cInfo = createEncryptedContentInfoPBEOfSymmKey(keyGenAlg, password, salt, 1, passConverter, symKey, ct); } else if (securityData != null) { cInfo = EncryptedContentInfo.createPBE(keyGenAlg, password, salt, 1, passConverter, securityData); } if(cInfo == null) { throw new EBaseException("Can't create a PBE wrapped EncryptedContentInfo!"); } ByteArrayOutputStream oStream = new ByteArrayOutputStream(); cInfo.encode(oStream); encoded = oStream.toByteArray(); retData = Utils.base64encode(encoded); } catch (Exception e) { throw new EBaseException("Can't create a PBE wrapped EncryptedContentInfo! " + e.toString()); } return retData; } }