diff options
author | PKI Team <PKI Team@c9f7a03b-bd48-0410-a16d-cbbf54688b0b> | 2008-03-18 22:36:57 +0000 |
---|---|---|
committer | PKI Team <PKI Team@c9f7a03b-bd48-0410-a16d-cbbf54688b0b> | 2008-03-18 22:36:57 +0000 |
commit | d0f2e4efbd3eb0f1d7f5a28e7f97c1fb4ec027bb (patch) | |
tree | 7e7473fae8af5ad7e6cda7eabbef787093fc59a7 /pki/base/kra/src | |
parent | 273f8d85df5c31293a908185622b378c8f3cf7e8 (diff) | |
download | pki-d0f2e4efbd3eb0f1d7f5a28e7f97c1fb4ec027bb.tar.gz pki-d0f2e4efbd3eb0f1d7f5a28e7f97c1fb4ec027bb.tar.xz pki-d0f2e4efbd3eb0f1d7f5a28e7f97c1fb4ec027bb.zip |
Initial open source version based upon proprietary Red Hat Certificate System (RHCS) 7.3.
git-svn-id: svn+ssh://svn.fedorahosted.org/svn/pki/trunk@2 c9f7a03b-bd48-0410-a16d-cbbf54688b0b
Diffstat (limited to 'pki/base/kra/src')
-rw-r--r-- | pki/base/kra/src/com/netscape/kra/EncryptionUnit.java | 534 | ||||
-rw-r--r-- | pki/base/kra/src/com/netscape/kra/EnrollmentService.java | 951 | ||||
-rw-r--r-- | pki/base/kra/src/com/netscape/kra/KRANotify.java | 53 | ||||
-rw-r--r-- | pki/base/kra/src/com/netscape/kra/KRAPolicy.java | 76 | ||||
-rw-r--r-- | pki/base/kra/src/com/netscape/kra/KRAService.java | 98 | ||||
-rw-r--r-- | pki/base/kra/src/com/netscape/kra/KeyRecoveryAuthority.java | 1478 | ||||
-rw-r--r-- | pki/base/kra/src/com/netscape/kra/NetkeyKeygenService.java | 556 | ||||
-rw-r--r-- | pki/base/kra/src/com/netscape/kra/RecoveryService.java | 476 | ||||
-rw-r--r-- | pki/base/kra/src/com/netscape/kra/StorageKeyUnit.java | 962 | ||||
-rw-r--r-- | pki/base/kra/src/com/netscape/kra/TokenKeyRecoveryService.java | 532 | ||||
-rw-r--r-- | pki/base/kra/src/com/netscape/kra/TransportKeyUnit.java | 201 |
11 files changed, 5917 insertions, 0 deletions
diff --git a/pki/base/kra/src/com/netscape/kra/EncryptionUnit.java b/pki/base/kra/src/com/netscape/kra/EncryptionUnit.java new file mode 100644 index 000000000..b426259d1 --- /dev/null +++ b/pki/base/kra/src/com/netscape/kra/EncryptionUnit.java @@ -0,0 +1,534 @@ +// --- 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.util.*; +import java.io.*; +import java.net.*; +import java.security.*; +import java.security.cert.*; +import java.security.cert.X509Certificate; +import netscape.security.x509.*; +import netscape.security.provider.*; +import netscape.security.util.*; +import com.netscape.certsrv.logging.*; +import com.netscape.cmscore.util.*; +import com.netscape.cmscore.util.Debug; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.kra.*; +import com.netscape.certsrv.security.*; +//import com.netscape.cmscore.kra.*; +import com.netscape.cmscore.cert.*; +import com.netscape.certsrv.apps.CMS; +import org.mozilla.jss.util.*; +import org.mozilla.jss.crypto.*; +import org.mozilla.jss.*; +import org.mozilla.jss.crypto.PrivateKey; + + +/** + * A class represents the transport key pair. This key pair + * is used to protected EE's private key in transit. + * + * @author thomask + * @version $Revision: 14563 $, $Date: 2007-05-01 10:35:23 -0700 (Tue, 01 May 2007) $ + */ +public abstract class EncryptionUnit implements IEncryptionUnit { + + private byte iv[] = {0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1}; + private IVParameterSpec IV = null; + + public EncryptionUnit() { +/* + org.mozilla.jss.pkcs11.PK11SecureRandom random = + new org.mozilla.jss.pkcs11.PK11SecureRandom(); + random.nextBytes(iv); +*/ + IV = new IVParameterSpec(iv); + } + + public abstract CryptoToken getToken(); + + public abstract CryptoToken getInternalToken(); + + public abstract PublicKey getPublicKey(); + + public abstract PrivateKey getPrivateKey(); + + /** + * Protects the private key so that it can be stored in + * internal database. + */ + public byte[] encryptInternalPrivate(byte priKey[]) + throws EBaseException { + try { + CryptoToken token = getToken(); + CryptoToken internalToken = getInternalToken(); + + // (1) generate session key + org.mozilla.jss.crypto.KeyGenerator kg = + internalToken.getKeyGenerator(KeyGenAlgorithm.DES3); + SymmetricKey sk = kg.generate(); + + // (2) wrap private key with session key + Cipher cipher = internalToken.getCipherContext( + EncryptionAlgorithm.DES3_CBC_PAD); + + cipher.initEncrypt(sk, IV); + byte pri[] = cipher.doFinal(priKey); + + // (3) wrap session with transport public + KeyWrapper rsaWrap = internalToken.getKeyWrapper( + KeyWrapAlgorithm.RSA); + + rsaWrap.initWrap(getPublicKey(), null); + byte session[] = rsaWrap.wrap(sk); + + // use MY own structure for now: + // SEQUENCE { + // encryptedSession OCTET STRING, + // encryptedPrivate OCTET STRING + // } + + DerOutputStream tmp = new DerOutputStream(); + DerOutputStream out = new DerOutputStream(); + + tmp.putOctetString(session); + tmp.putOctetString(pri); + out.write(DerValue.tag_Sequence, tmp); + + return out.toByteArray(); + } catch (TokenException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_INTERNAL", e.toString())); + Debug.trace("EncryptionUnit::encryptInternalPrivate " + e.toString()); + return null; + } catch (NoSuchAlgorithmException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_INTERNAL", e.toString())); + Debug.trace("EncryptionUnit::encryptInternalPrivate " + e.toString()); + return null; + } catch (CharConversionException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_INTERNAL", e.toString())); + Debug.trace("EncryptionUnit::encryptInternalPrivate " + e.toString()); + return null; + } catch (InvalidAlgorithmParameterException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_INTERNAL", e.toString())); + Debug.trace("EncryptionUnit::encryptInternalPrivate " + e.toString()); + return null; + } catch (InvalidKeyException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_INTERNAL", e.toString())); + Debug.trace("EncryptionUnit::encryptInternalPrivate " + e.toString()); + return null; + } catch (BadPaddingException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_INTERNAL", e.toString())); + Debug.trace("EncryptionUnit::encryptInternalPrivate " + e.toString()); + return null; + } catch (IllegalBlockSizeException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_INTERNAL", e.toString())); + Debug.trace("EncryptionUnit::encryptInternalPrivate " + e.toString()); + return null; + } catch (IOException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_INTERNAL", e.toString())); + Debug.trace("EncryptionUnit::encryptInternalPrivate " + e.toString()); + return null; + } + } + + /** + * Wraps the data using transport public key. + */ + public byte[] wrap(PrivateKey priKey) throws EBaseException { + try { + + CryptoToken token = getToken(); + CryptoToken internalToken = getInternalToken(); + + // (1) generate session key + org.mozilla.jss.crypto.KeyGenerator kg = + token.getKeyGenerator(KeyGenAlgorithm.DES3); + // internalToken.getKeyGenerator(KeyGenAlgorithm.DES3); + SymmetricKey.Usage usages[] = new SymmetricKey.Usage[3]; + usages[0] = SymmetricKey.Usage.ENCRYPT; + usages[1] = SymmetricKey.Usage.WRAP; + usages[2] = SymmetricKey.Usage.UNWRAP; + kg.setKeyUsages(usages); + kg.temporaryKeys(true); + SymmetricKey sk = kg.generate(); + CMS.debug("EncryptionUnit:wrap() session key generated on slot: "+token.getName()); + + // (2) wrap private key with session key + // KeyWrapper wrapper = internalToken.getKeyWrapper( + KeyWrapper wrapper = token.getKeyWrapper( + KeyWrapAlgorithm.DES3_CBC_PAD); + CMS.debug("EncryptionUnit:wrap() got key wrapper"); + + wrapper.initWrap(sk, IV); + CMS.debug("EncryptionUnit:wrap() key wrapper initialized"); + byte pri[] = wrapper.wrap(priKey); + CMS.debug("EncryptionUnit:wrap() privKey wrapped"); + + // (3) wrap session with transport public + KeyWrapper rsaWrap = token.getKeyWrapper( + KeyWrapAlgorithm.RSA); + + rsaWrap.initWrap(getPublicKey(), null); + byte session[] = rsaWrap.wrap(sk); + CMS.debug("EncryptionUnit:wrap() sessin key wrapped"); + + // use MY own structure for now: + // SEQUENCE { + // encryptedSession OCTET STRING, + // encryptedPrivate OCTET STRING + // } + + DerOutputStream tmp = new DerOutputStream(); + DerOutputStream out = new DerOutputStream(); + + tmp.putOctetString(session); + tmp.putOctetString(pri); + out.write(DerValue.tag_Sequence, tmp); + + return out.toByteArray(); + } catch (TokenException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_WRAP", e.toString())); + Debug.trace("EncryptionUnit::wrap " + e.toString()); + return null; + } catch (NoSuchAlgorithmException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_WRAP", e.toString())); + Debug.trace("EncryptionUnit::wrap " + e.toString()); + return null; + } catch (CharConversionException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_WRAP", e.toString())); + Debug.trace("EncryptionUnit::wrap " + e.toString()); + return null; + } catch (InvalidAlgorithmParameterException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_WRAP", e.toString())); + Debug.trace("EncryptionUnit::wrap " + e.toString()); + return null; + } catch (InvalidKeyException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_WRAP", e.toString())); + Debug.trace("EncryptionUnit::wrap " + e.toString()); + return null; + } catch (IOException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_WRAP", e.toString())); + Debug.trace("EncryptionUnit::wrap " + e.toString()); + return null; + } + } + + /** + * External unwrapping. Unwraps the data using + * the transport private key. + */ + public SymmetricKey unwrap_sym(byte encSymmKey[], SymmetricKey.Usage usage) + { + try { + CryptoToken token = getToken(); + CryptoToken internalToken = getInternalToken(); + + // (1) unwrap the session + CMS.debug("EncryptionUnit::unwrap_sym() on slot: "+token.getName()); + PrivateKey priKey = getPrivateKey(); + String priKeyAlgo = priKey.getAlgorithm(); + CMS.debug("EncryptionUnit::unwrap_sym() private key algo: " + priKeyAlgo); + KeyWrapper rsaWrap = null; + if (priKeyAlgo.equals("EC")) { + rsaWrap = token.getKeyWrapper(KeyWrapAlgorithm.AES_CBC); + rsaWrap.initUnwrap(priKey, IV); + } else { + rsaWrap = token.getKeyWrapper(KeyWrapAlgorithm.RSA); + rsaWrap.initUnwrap(priKey, null); + } + SymmetricKey sk = rsaWrap.unwrapSymmetric(encSymmKey, + SymmetricKey.DES3, usage, + 0); + return sk; + } catch (Exception e) { + CMS.debug("EncryptionUnit::unwrap_sym() error:" + + e.toString()); + return null; + } + } + + public SymmetricKey unwrap_sym(byte encSymmKey[]) + { + return unwrap_sym(encSymmKey, SymmetricKey.Usage.WRAP); + } + + public SymmetricKey unwrap_encrypt_sym(byte encSymmKey[]) + { + return unwrap_sym(encSymmKey, SymmetricKey.Usage.ENCRYPT); + } + + /** + * Decrypts the user private key. + */ + public byte[] decryptExternalPrivate(byte encSymmKey[], + String symmAlgOID, byte symmAlgParams[], + byte encValue[]) + throws EBaseException { + try { + + CryptoToken token = getToken(); + CryptoToken internalToken = getInternalToken(); + + // (1) unwrap the session + KeyWrapper rsaWrap = token.getKeyWrapper( + KeyWrapAlgorithm.RSA); + + rsaWrap.initUnwrap(getPrivateKey(), null); + SymmetricKey sk = rsaWrap.unwrapSymmetric(encSymmKey, + SymmetricKey.DES3, SymmetricKey.Usage.DECRYPT, + 0); + + // (2) unwrap the pri + Cipher cipher = token.getCipherContext( + EncryptionAlgorithm.DES3_CBC_PAD // XXX + ); + + cipher.initDecrypt(sk, new IVParameterSpec( + symmAlgParams)); + return cipher.doFinal(encValue); + } catch (IllegalBlockSizeException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_EXTERNAL", e.toString())); + Debug.trace("EncryptionUnit::decryptExternalPrivate " + e.toString()); + return null; + } catch (BadPaddingException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_EXTERNAL", e.toString())); + Debug.trace("EncryptionUnit::decryptExternalPrivate " + e.toString()); + return null; + } catch (TokenException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_EXTERNAL", e.toString())); + Debug.trace("EncryptionUnit::decryptExternalPrivate " + e.toString()); + return null; + } catch (NoSuchAlgorithmException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_EXTERNAL", e.toString())); + Debug.trace("EncryptionUnit::decryptExternalPrivate " + e.toString()); + return null; + } catch (InvalidAlgorithmParameterException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_EXTERNAL", e.toString())); + Debug.trace("EncryptionUnit::decryptExternalPrivate " + e.toString()); + return null; + } catch (InvalidKeyException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_EXTERNAL", e.toString())); + Debug.trace("EncryptionUnit::decryptExternalPrivate " + e.toString()); + return null; + } + } + + /** + * External unwrapping. Unwraps the data using + * the transport private key. + */ + public PrivateKey unwrap(byte encSymmKey[], + String symmAlgOID, byte symmAlgParams[], + byte encValue[], PublicKey pubKey) + throws EBaseException { + try { + CryptoToken token = getToken(); + CryptoToken internalToken = getInternalToken(); + + // (1) unwrap the session + KeyWrapper rsaWrap = token.getKeyWrapper( + KeyWrapAlgorithm.RSA); + + rsaWrap.initUnwrap(getPrivateKey(), null); + SymmetricKey sk = rsaWrap.unwrapSymmetric(encSymmKey, + SymmetricKey.DES3, SymmetricKey.Usage.UNWRAP, + 0); + + // (2) unwrap the pri + KeyWrapper wrapper = token.getKeyWrapper( + KeyWrapAlgorithm.DES3_CBC_PAD // XXX + ); + + wrapper.initUnwrap(sk, new IVParameterSpec( + symmAlgParams)); + PrivateKey pk = wrapper.unwrapPrivate(encValue, + PrivateKey.RSA, pubKey); + + return pk; + } catch (TokenException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString())); + Debug.trace("EncryptionUnit::unwrap " + e.toString()); + return null; + } catch (NoSuchAlgorithmException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString())); + Debug.trace("EncryptionUnit::unwrap " + e.toString()); + return null; + } catch (InvalidAlgorithmParameterException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString())); + Debug.trace("EncryptionUnit::unwrap " + e.toString()); + return null; + } catch (InvalidKeyException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString())); + Debug.trace("EncryptionUnit::unwrap " + e.toString()); + return null; + } + } + + public byte[] decryptInternalPrivate(byte wrappedKeyData[]) + throws EBaseException { + try { + DerValue val = new DerValue(wrappedKeyData); + // val.tag == DerValue.tag_Sequence + DerInputStream in = val.data; + DerValue dSession = in.getDerValue(); + byte session[] = dSession.getOctetString(); + DerValue dPri = in.getDerValue(); + byte pri[] = dPri.getOctetString(); + + CryptoToken token = getToken(); + CryptoToken internalToken = getInternalToken(); + + // (1) unwrap the session + CMS.debug("decryptInternalPrivate(): getting key wrapper on slot:"+ token.getName()); + KeyWrapper rsaWrap = token.getKeyWrapper( + KeyWrapAlgorithm.RSA); + + rsaWrap.initUnwrap(getPrivateKey(), null); + SymmetricKey sk = rsaWrap.unwrapSymmetric(session, + SymmetricKey.DES3, SymmetricKey.Usage.DECRYPT, 0); + + // (2) unwrap the pri + Cipher cipher = token.getCipherContext( + EncryptionAlgorithm.DES3_CBC_PAD); + + cipher.initDecrypt(sk, IV); + return cipher.doFinal(pri); + } catch (IllegalBlockSizeException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_DECRYPT", e.toString())); + Debug.trace("EncryptionUnit::decryptInternalPrivate " + e.toString()); + return null; + } catch (BadPaddingException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_DECRYPT", e.toString())); + Debug.trace("EncryptionUnit::decryptInternalPrivate " + e.toString()); + return null; + } catch (TokenException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_DECRYPT", e.toString())); + Debug.trace("EncryptionUnit::decryptInternalPrivate " + e.toString()); + return null; + } catch (NoSuchAlgorithmException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_DECRYPT", e.toString())); + Debug.trace("EncryptionUnit::decryptInternalPrivate " + e.toString()); + return null; + } catch (InvalidAlgorithmParameterException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_DECRYPT", e.toString())); + Debug.trace("EncryptionUnit::decryptInternalPrivate " + e.toString()); + return null; + } catch (InvalidKeyException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_DECRYPT", e.toString())); + Debug.trace("EncryptionUnit::decryptInternalPrivate " + e.toString()); + return null; + } catch (IOException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_DECRYPT", e.toString())); + Debug.trace("EncryptionUnit::decryptInternalPrivate " + e.toString()); + return null; + } + } + + /** + * Internal unwrapping. + */ + public PrivateKey unwrap_temp(byte wrappedKeyData[], PublicKey pubKey) + throws EBaseException { + return _unwrap(wrappedKeyData, pubKey, true); + } + + /** + * Internal unwrapping. + */ + public PrivateKey unwrap(byte wrappedKeyData[], PublicKey pubKey) + throws EBaseException { + return _unwrap(wrappedKeyData, pubKey, false); + } + + /** + * Internal unwrapping. + */ + private PrivateKey _unwrap(byte wrappedKeyData[], PublicKey + pubKey, boolean temporary) + throws EBaseException { + try { + DerValue val = new DerValue(wrappedKeyData); + // val.tag == DerValue.tag_Sequence + DerInputStream in = val.data; + DerValue dSession = in.getDerValue(); + byte session[] = dSession.getOctetString(); + DerValue dPri = in.getDerValue(); + byte pri[] = dPri.getOctetString(); + + CryptoToken token = getToken(); + // (1) unwrap the session + KeyWrapper rsaWrap = token.getKeyWrapper( + KeyWrapAlgorithm.RSA); + + rsaWrap.initUnwrap(getPrivateKey(), null); + SymmetricKey sk = rsaWrap.unwrapSymmetric(session, + SymmetricKey.DES3, SymmetricKey.Usage.UNWRAP, 0); + + // (2) unwrap the pri + KeyWrapper wrapper = token.getKeyWrapper( + KeyWrapAlgorithm.DES3_CBC_PAD); + + wrapper.initUnwrap(sk, IV); + + PrivateKey pk = null; + if (temporary) { + pk = wrapper.unwrapTemporaryPrivate(pri, + PrivateKey.RSA, pubKey); + } else { + pk = wrapper.unwrapPrivate(pri, + PrivateKey.RSA, pubKey); + } + return pk; + } catch (TokenException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString())); + Debug.trace("EncryptionUnit::unwrap " + e.toString()); + CMS.debug(e); + return null; + } catch (NoSuchAlgorithmException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString())); + Debug.trace("EncryptionUnit::unwrap " + e.toString()); + return null; + } catch (InvalidAlgorithmParameterException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString())); + Debug.trace("EncryptionUnit::unwrap " + e.toString()); + return null; + } catch (InvalidKeyException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString())); + Debug.printStackTrace(e); + return null; + } catch (IOException e) { + CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString())); + Debug.trace("EncryptionUnit::unwrap " + e.toString()); + return null; + } catch (Exception e) { + Debug.printStackTrace(e); + return null; + } + } + + /** + * Verify the given key pair. + */ + public void verify(PublicKey publicKey, PrivateKey privateKey) throws + EBaseException { + } +} + diff --git a/pki/base/kra/src/com/netscape/kra/EnrollmentService.java b/pki/base/kra/src/com/netscape/kra/EnrollmentService.java new file mode 100644 index 000000000..062de3673 --- /dev/null +++ b/pki/base/kra/src/com/netscape/kra/EnrollmentService.java @@ -0,0 +1,951 @@ +// --- 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.util.StringTokenizer; +import java.util.Vector; +import java.io.IOException; +import java.io.ByteArrayInputStream; +import java.math.BigInteger; +import java.security.*; +// ADDED next line and COMMENTED out following line by MLH on 1/9/99 +import netscape.security.provider.RSAPublicKey; +// import java.security.interfaces.RSAPublicKey; +import java.security.cert.CertificateException; +import netscape.security.util.*; +import netscape.security.util.BigInt; +import netscape.security.x509.*; +import org.mozilla.jss.asn1.*; +import org.mozilla.jss.pkix.crmf.*; +import org.mozilla.jss.pkix.primitive.*; +import org.mozilla.jss.pkix.primitive.AVA; +import com.netscape.certsrv.util.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.security.*; +import com.netscape.cmscore.crmf.*; +import com.netscape.certsrv.kra.*; +import com.netscape.certsrv.base.*; +//import com.netscape.cmscore.ca.*; +import com.netscape.cmscore.dbs.*; +import com.netscape.certsrv.profile.*; +import com.netscape.certsrv.dbs.keydb.*; +import com.netscape.certsrv.request.*; +import com.netscape.certsrv.authentication.*; +import com.netscape.certsrv.apps.CMS; + + +/** + * A class represents archival request processor. It + * passes the request to the policy processor, and + * process the request according to the policy decision. + * <P> + * If policy returns ACCEPTED, the request will be + * processed immediately. + * <P> + * Upon processing, the incoming user key is unwrapped + * with the transport key of KRA, and then wrapped + * with the storage key. The encrypted key is stored + * in the internal database for long term storage. + * <P> + * + * @author thomask + * @version $Revision: 14563 $, $Date: 2007-05-01 10:35:23 -0700 (Tue, 01 May 2007) $ + */ +public class EnrollmentService implements IService { + + // constants + public static final String CRMF_REQUEST = "CRMFRequest"; + public final static String ATTR_KEY_RECORD = "keyRecord"; + public final static String ATTR_PROOF_OF_ARCHIVAL = + "proofOfArchival"; + + // private + private IKeyRecoveryAuthority mKRA = null; + private ITransportKeyUnit mTransportUnit = null; + private IStorageKeyUnit mStorageUnit = null; + private ILogger mSignedAuditLogger = CMS.getSignedAuditLogger(); + + + private final static byte EOL[] = { Character.LINE_SEPARATOR }; + private final static String + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST = + "LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST_4"; + private final static String + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_PROCESSED = + "LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_PROCESSED_3"; + private final static String LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST = + "LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_4"; + private final static String LOGGING_SIGNED_AUDIT_KEY_RECOVERY_PROCESSED = + "LOGGING_SIGNED_AUDIT_KEY_RECOVERY_PROCESSED_4"; + /** + * Constructs request processor. + * <P> + * + * @param kra key recovery authority + */ + public EnrollmentService(IKeyRecoveryAuthority kra) { + mKRA = kra; + mTransportUnit = kra.getTransportKeyUnit(); + mStorageUnit = kra.getStorageKeyUnit(); + } + + public PKIArchiveOptions toPKIArchiveOptions(byte options[]) { + ByteArrayInputStream bis = new ByteArrayInputStream(options); + PKIArchiveOptions archOpts = null; + + try { + archOpts = (PKIArchiveOptions) + (new PKIArchiveOptions.Template()).decode(bis); + } catch (Exception e) { + CMS.debug("EnrollProfile: getPKIArchiveOptions " + e.toString()); + } + return archOpts; + } + + /** + * Services an enrollment/archival request. + * <P> + * + * @param request enrollment request + * @return serving successful or not + * @exception EBaseException failed to serve + */ + public boolean serviceRequest(IRequest request) + throws EBaseException { + + IStatsSubsystem statsSub = (IStatsSubsystem)CMS.getSubsystem("stats"); + if (statsSub != null) { + statsSub.startTiming("archival", true /* main action */); + } + + String auditMessage = null; + String auditSubjectID = auditSubjectID(); + String auditRequesterID = auditRequesterID(); + String auditArchiveID = ILogger.UNIDENTIFIED; + String auditPublicKey = ILogger.UNIDENTIFIED; + + String id = request.getRequestId().toString(); + if (id != null) { + auditArchiveID = id.trim(); + } + if (CMS.debugOn()) + CMS.debug("EnrollmentServlet: KRA services enrollment request"); + + SessionContext sContext = SessionContext.getContext(); + String agentId = (String) sContext.get(SessionContext.USER_ID); + AuthToken authToken = (AuthToken) sContext.get(SessionContext.AUTH_TOKEN); + + mKRA.log(ILogger.LL_INFO, "KRA services enrollment request"); + // unwrap user key with transport + byte unwrapped[] = null; + PKIArchiveOptionsContainer aOpts[] = null; + + String profileId = request.getExtDataInString("profileId"); + + if (profileId == null || profileId.equals("")) { + try { + aOpts = CRMFParser.getPKIArchiveOptions( + request.getExtDataInString(IRequest.HTTP_PARAMS, CRMF_REQUEST)); + } catch (IOException e) { + + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST, + auditSubjectID, + ILogger.FAILURE, + auditRequesterID, + auditArchiveID); + + audit(auditMessage); + throw new EKRAException( + CMS.getUserMessage("CMS_KRA_INVALID_PRIVATE_KEY")); + } + } else { + // profile-based request + PKIArchiveOptions options = (PKIArchiveOptions) + toPKIArchiveOptions( + request.getExtDataInByteArray(IEnrollProfile.REQUEST_ARCHIVE_OPTIONS)); + + aOpts = new PKIArchiveOptionsContainer[1]; + aOpts[0] = new PKIArchiveOptionsContainer(options, + 0/* not matter */); + + request.setExtData("dbStatus", "NOT_UPDATED"); + } + + for (int i = 0; i < aOpts.length; i++) { + ArchiveOptions opts = new ArchiveOptions(aOpts[i].mAO); + + if (statsSub != null) { + statsSub.startTiming("decrypt_user_key"); + } + mKRA.log(ILogger.LL_INFO, "KRA decrypts external private"); + if (CMS.debugOn()) + CMS.debug("EnrollmentService::about to decryptExternalPrivate"); + unwrapped = mTransportUnit.decryptExternalPrivate( + opts.getEncSymmKey(), + opts.getSymmAlgOID(), + opts.getSymmAlgParams(), + opts.getEncValue()); + if (statsSub != null) { + statsSub.endTiming("decrypt_user_key"); + } + if (CMS.debugOn()) + CMS.debug("EnrollmentService::finished decryptExternalPrivate"); + if (unwrapped == null) { + mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_UNWRAP_USER_KEY")); + + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST, + auditSubjectID, + ILogger.FAILURE, + auditRequesterID, + auditArchiveID); + + audit(auditMessage); + throw new EKRAException( + CMS.getUserMessage("CMS_KRA_INVALID_PRIVATE_KEY")); + } + + // retrieve pubic key + X509Key publicKey = getPublicKey(request, aOpts[i].mReqPos); + byte publicKeyData[] = publicKey.getEncoded(); + + if (publicKeyData == null) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_PUBLIC_NOT_FOUND")); + + + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST, + auditSubjectID, + ILogger.FAILURE, + auditRequesterID, + auditArchiveID); + + audit(auditMessage); + throw new EKRAException( + CMS.getUserMessage("CMS_KRA_INVALID_PUBLIC_KEY")); + } + + /* Bugscape #54948 - verify public and private key before archiving key */ + + if (statsSub != null) { + statsSub.startTiming("verify_key"); + } + if (verifyKeyPair(publicKeyData, unwrapped) == false) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_PUBLIC_NOT_FOUND")); + + + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST, + auditSubjectID, + ILogger.FAILURE, + auditRequesterID, + auditArchiveID); + + audit(auditMessage); + throw new EKRAException( + CMS.getUserMessage("CMS_KRA_INVALID_PUBLIC_KEY")); + } + if (statsSub != null) { + statsSub.endTiming("verify_key"); + } + + /** + mTransportKeyUnit.verify(pKey, unwrapped); + **/ + // retrieve owner name + String owner = getOwnerName(request, aOpts[i].mReqPos); + + if (owner == null) { + mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_OWNER_NAME_NOT_FOUND")); + + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST, + auditSubjectID, + ILogger.FAILURE, + auditRequesterID, + auditArchiveID); + + audit(auditMessage); + throw new EKRAException(CMS.getUserMessage("CMS_KRA_INVALID_KEYRECORD")); + } + + // + // privateKeyData ::= SEQUENCE { + // sessionKey OCTET_STRING, + // encKey OCTET_STRING, + // } + // + mKRA.log(ILogger.LL_INFO, "KRA encrypts internal private"); + if (statsSub != null) { + statsSub.startTiming("encrypt_user_key"); + } + byte privateKeyData[] = mStorageUnit.encryptInternalPrivate( + unwrapped); + if (statsSub != null) { + statsSub.endTiming("encrypt_user_key"); + } + + if (privateKeyData == null) { + mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_WRAP_USER_KEY")); + + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST, + auditSubjectID, + ILogger.FAILURE, + auditRequesterID, + auditArchiveID); + + audit(auditMessage); + throw new EKRAException( + CMS.getUserMessage("CMS_KRA_INVALID_PRIVATE_KEY")); + } + + // create key record + KeyRecord rec = new KeyRecord(null, publicKeyData, + privateKeyData, owner, + publicKey.getAlgorithmId().getOID().toString(), agentId); + + if (rec == null) { + + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST, + auditSubjectID, + ILogger.FAILURE, + auditRequesterID, + auditArchiveID); + + audit(auditMessage); + throw new EKRAException(CMS.getUserMessage("CMS_KRA_INVALID_KEYRECORD")); + } + + // we deal with RSA key only + try { + RSAPublicKey rsaPublicKey = new RSAPublicKey(publicKeyData); + + rec.setKeySize(Integer.valueOf(rsaPublicKey.getKeySize())); + } catch (InvalidKeyException e) { + + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST, + auditSubjectID, + ILogger.FAILURE, + auditRequesterID, + auditArchiveID); + + audit(auditMessage); + throw new EKRAException(CMS.getUserMessage("CMS_KRA_INVALID_KEYRECORD")); + } + + + // if record alreay has a serial number, yell out. + if (rec.getSerialNumber() != null) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_INVALID_SERIAL_NUMBER", + rec.getSerialNumber().toString())); + + + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST, + auditSubjectID, + ILogger.FAILURE, + auditRequesterID, + auditArchiveID); + + audit(auditMessage); + throw new EKRAException(CMS.getUserMessage("CMS_KRA_INVALID_STATE")); + } + IKeyRepository storage = mKRA.getKeyRepository(); + BigInteger serialNo = storage.getNextSerialNumber(); + + if (serialNo == null) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_GET_NEXT_SERIAL")); + + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST, + auditSubjectID, + ILogger.FAILURE, + auditRequesterID, + auditArchiveID); + + audit(auditMessage); + throw new EKRAException(CMS.getUserMessage("CMS_KRA_INVALID_STATE")); + } + if (i == 0) { + rec.set(KeyRecord.ATTR_ID, serialNo); + request.setExtData(ATTR_KEY_RECORD, serialNo); + } else { + rec.set(KeyRecord.ATTR_ID + i, serialNo); + request.setExtData(ATTR_KEY_RECORD + i, serialNo); + } + + mKRA.log(ILogger.LL_INFO, "KRA adding key record " + serialNo); + if (statsSub != null) { + statsSub.startTiming("store_key"); + } + storage.addKeyRecord(rec); + if (statsSub != null) { + statsSub.endTiming("store_key"); + } + + if (CMS.debugOn()) + CMS.debug("EnrollmentService: key record 0x" + serialNo.toString(16) + + " (" + owner + ") archived"); + + mKRA.log(ILogger.LL_INFO, "key record 0x" + + serialNo.toString(16) + + " (" + owner + ") archived"); + + // for audit log + String authMgr = AuditFormat.NOAUTH; + + if (authToken != null) { + authMgr = + authToken.getInString(AuthToken.TOKEN_AUTHMGR_INST_NAME); + } + CMS.getLogger().log(ILogger.EV_AUDIT, + ILogger.S_KRA, + AuditFormat.LEVEL, + AuditFormat.FORMAT, + new Object[] { + IRequest.KEYARCHIVAL_REQUEST, + request.getRequestId(), + AuditFormat.FROMAGENT + " agentID: " + agentId, + authMgr, + "completed", + owner, + "serial number: 0x" + serialNo.toString(16)} + ); + + + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST, + auditSubjectID, + ILogger.SUCCESS, + auditRequesterID, + auditArchiveID); + + audit(auditMessage); + + // store a message in the signed audit log file + auditPublicKey = auditPublicKey(rec); + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_PROCESSED, + auditSubjectID, + ILogger.SUCCESS, + auditPublicKey); + + audit(auditMessage); + + // Xxx - should sign this proof of archival + ProofOfArchival mProof = new ProofOfArchival(serialNo, + owner, mKRA.getX500Name().toString(), + rec.getCreateTime()); + + DerOutputStream mProofOut = new DerOutputStream(); + mProof.encode(mProofOut); + if (i == 0) { + request.setExtData(ATTR_PROOF_OF_ARCHIVAL, + mProofOut.toByteArray()); + } else { + request.setExtData(ATTR_PROOF_OF_ARCHIVAL + i, + mProofOut.toByteArray()); + } + + } // for + + /* + request.delete(IEnrollProfile.REQUEST_SUBJECT_NAME); + request.delete(IEnrollProfile.REQUEST_EXTENSIONS); + request.delete(IEnrollProfile.REQUEST_VALIDITY); + request.delete(IEnrollProfile.REQUEST_KEY); + request.delete(IEnrollProfile.REQUEST_SIGNING_ALGORITHM); + request.delete(IEnrollProfile.REQUEST_LOCALE); + */ + + request.setExtData(IRequest.RESULT, IRequest.RES_SUCCESS); + + // update request + mKRA.log(ILogger.LL_INFO, "KRA updating request"); + mKRA.getRequestQueue().updateRequest(request); + + if (statsSub != null) { + statsSub.endTiming("archival"); + } + + return true; + } + + public boolean verifyKeyPair(byte publicKeyData[], byte privateKeyData[]) + { + try { + DerValue publicKeyVal = new DerValue(publicKeyData); + DerInputStream publicKeyIn = publicKeyVal.data; + publicKeyIn.getSequence(0); + DerValue publicKeyDer = new DerValue(publicKeyIn.getBitString()); + DerInputStream publicKeyDerIn = publicKeyDer.data; + BigInt publicKeyModulus = publicKeyDerIn.getInteger(); + BigInt publicKeyExponent = publicKeyDerIn.getInteger(); + + DerValue privateKeyVal = new DerValue(privateKeyData); + if (privateKeyVal.tag != DerValue.tag_Sequence) + return false; + DerInputStream privateKeyIn = privateKeyVal.data; + privateKeyIn.getInteger(); + privateKeyIn.getSequence(0); + DerValue privateKeyDer = new DerValue(privateKeyIn.getOctetString()); + DerInputStream privateKeyDerIn = privateKeyDer.data; + BigInt privateKeyVersion = privateKeyDerIn.getInteger(); + BigInt privateKeyModulus = privateKeyDerIn.getInteger(); + BigInt privateKeyExponent = privateKeyDerIn.getInteger(); + + if (!publicKeyModulus.equals(privateKeyModulus)) { + CMS.debug("verifyKeyPair modulus mismatch publicKeyModulus=" + publicKeyModulus + " privateKeyModulus=" + privateKeyModulus); + return false; + } + + if (!publicKeyExponent.equals(privateKeyExponent)) { + CMS.debug("verifyKeyPair exponent mismatch publicKeyExponent=" + publicKeyExponent + " privateKeyExponent=" + privateKeyExponent); + return false; + } + + return true; + } catch (Exception e) { + CMS.debug("verifyKeyPair error " + e); + return false; + } + } + + private static final OBJECT_IDENTIFIER PKIARCHIVEOPTIONS_OID = + new OBJECT_IDENTIFIER(new long[] {1, 3, 6, 1, 5, 5, 7, 5, 1, 4} + ); + + /** + * Retrieves PKIArchiveOptions from CRMF request. + * + * @param crmfBlob CRMF request + * @return PKIArchiveOptions + * @exception EBaseException failed to extrace option + */ + public static PKIArchiveOptionsContainer[] getPKIArchiveOptions(String crmfBlob) + throws EBaseException { + Vector options = new Vector(); + + if (CMS.debugOn()) + CMS.debug("EnrollmentService::getPKIArchiveOptions> crmfBlob=" + crmfBlob); + byte[] crmfBerBlob = null; + + crmfBerBlob = com.netscape.osutil.OSUtil.AtoB(crmfBlob); + ByteArrayInputStream crmfBerBlobIn = new + ByteArrayInputStream(crmfBerBlob); + SEQUENCE crmfmsgs = null; + + try { + crmfmsgs = (SEQUENCE) new + SEQUENCE.OF_Template(new + CertReqMsg.Template()).decode( + crmfBerBlobIn); + } catch (IOException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "[crmf msgs]" + e.toString())); + } catch (InvalidBERException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "[crmf msgs]" + e.toString())); + } + + for (int z = 0; z < crmfmsgs.size(); z++) { + CertReqMsg certReqMsg = (CertReqMsg) + crmfmsgs.elementAt(z); + CertRequest certReq = certReqMsg.getCertReq(); + + // try to locate PKIArchiveOption control + AVA archAva = null; + + try { + for (int i = 0; i < certReq.numControls(); i++) { + AVA ava = certReq.controlAt(i); + OBJECT_IDENTIFIER oid = ava.getOID(); + + if (oid.equals(PKIARCHIVEOPTIONS_OID)) { + archAva = ava; + break; + } + } + } catch (Exception e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "no PKIArchiveOptions found " + e.toString())); + } + if (archAva != null) { + + ASN1Value archVal = archAva.getValue(); + ByteArrayInputStream bis = new ByteArrayInputStream(ASN1Util.encode(archVal)); + PKIArchiveOptions archOpts = null; + + try { + archOpts = (PKIArchiveOptions) + (new PKIArchiveOptions.Template()).decode(bis); + } catch (IOException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "[PKIArchiveOptions]" + e.toString())); + } catch (InvalidBERException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "[PKIArchiveOptions]" + e.toString())); + } + options.addElement(new PKIArchiveOptionsContainer(archOpts, z)); + } + } + if (options.size() == 0) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "PKIArchiveOptions found")); + } else { + PKIArchiveOptionsContainer p[] = new PKIArchiveOptionsContainer[options.size()]; + + options.copyInto(p); + return p; + } + } + + /** + * Retrieves public key from request. + * + * @param request CRMF request + * @return JSS public key + * @exception EBaseException failed to retrieve public key + */ + private X509Key getPublicKey(IRequest request, int i) throws EBaseException { + String profileId = request.getExtDataInString("profileId"); + + if (profileId != null && !profileId.equals("")) { + byte[] certKeyData = request.getExtDataInByteArray(IEnrollProfile.REQUEST_KEY); + if (certKeyData != null) { + try { + CertificateX509Key x509key = new CertificateX509Key( + new ByteArrayInputStream(certKeyData)); + + return (X509Key) x509key.get(CertificateX509Key.KEY); + } catch (Exception e1) { + CMS.debug("EnrollService: (Archival) getPublicKey " + + e1.toString()); + } + } + return null; + } + + // retrieve x509 Key from request + X509CertInfo certInfo[] = + request.getExtDataInCertInfoArray(IRequest.CERT_INFO); + CertificateX509Key pX509Key = null; + + try { + pX509Key = (CertificateX509Key) + certInfo[i].get(X509CertInfo.KEY); + } catch (IOException e) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_GET_PUBLIC_KEY", e.toString())); + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "[" + X509CertInfo.KEY + "]" + e.toString())); + } catch (CertificateException e) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_GET_PUBLIC_KEY", e.toString())); + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "[" + X509CertInfo.KEY + "]" + e.toString())); + } + X509Key pKey = null; + + try { + pKey = (X509Key) pX509Key.get( + CertificateX509Key.KEY); + } catch (IOException e) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_GET_PUBLIC_KEY", e.toString())); + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "[" + CertificateX509Key.KEY + "]" + e.toString())); + } + return pKey; + } + + /** + * Retrieves key's owner name from request. + * + * @param request CRMF request + * @return owner name (subject name) + * @exception EBaseException failed to retrieve public key + */ + private String getOwnerName(IRequest request, int i) + throws EBaseException { + + String profileId = request.getExtDataInString("profileId"); + + if (profileId != null && !profileId.equals("")) { + CertificateSubjectName sub = request.getExtDataInCertSubjectName( + IEnrollProfile.REQUEST_SUBJECT_NAME); + if (sub != null) { + return sub.toString(); + } + } + + X509CertInfo certInfo[] = + request.getExtDataInCertInfoArray(IRequest.CERT_INFO); + CertificateSubjectName pSub = null; + + try { + pSub = (CertificateSubjectName) + certInfo[0].get(X509CertInfo.SUBJECT); + } catch (IOException e) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_GET_OWNER_NAME", e.toString())); + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "[" + X509CertInfo.SUBJECT + "]" + e.toString())); + } catch (CertificateException e) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_GET_OWNER_NAME", e.toString())); + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "[" + X509CertInfo.SUBJECT + "]" + e.toString())); + } + String owner = pSub.toString(); + + return owner; + } + + /** + * Signed Audit Log Public Key + * + * This method is called to obtain the public key from the passed in + * "KeyRecord" for a signed audit log message. + * <P> + * + * @param rec a Key Record + * @return key string containing the certificate's public key + */ + private String auditPublicKey(KeyRecord rec) { + // if no signed audit object exists, bail + if (mSignedAuditLogger == null) { + return null; + } + + if (rec == null) { + return ILogger.SIGNED_AUDIT_EMPTY_VALUE; + } + + byte rawData[] = null; + + try { + rawData = rec.getPublicKeyData(); + } catch (EBaseException e) { + return ILogger.SIGNED_AUDIT_EMPTY_VALUE; + } + + String key = ""; + + // convert "rawData" into "base64Data" + if (rawData != null) { + String base64Data = null; + + base64Data = CMS.BtoA(rawData).trim(); + + // extract all line separators from the "base64Data" + StringTokenizer st = new StringTokenizer(base64Data, "\r\n"); + while (st.hasMoreTokens()) { + key += st.nextToken(); + } + } + + if (key != null) { + key = key.trim(); + + if (key.equals("")) { + return ILogger.SIGNED_AUDIT_EMPTY_VALUE; + } else { + return key; + } + } else { + return ILogger.SIGNED_AUDIT_EMPTY_VALUE; + } + } + /** + * Signed Audit Log Subject ID + * + * This method is called to obtain the "SubjectID" for + * a signed audit log message. + * <P> + * + * @return id string containing the signed audit log message SubjectID + */ + + private String auditSubjectID() { + // if no signed audit object exists, bail + if (mSignedAuditLogger == null) { + return null; + } + + String subjectID = null; + + // Initialize subjectID + SessionContext auditContext = SessionContext.getExistingContext(); + + if (auditContext != null) { + subjectID = (String) + auditContext.get(SessionContext.USER_ID); + + if (subjectID != null) { + subjectID = subjectID.trim(); + } else { + subjectID = ILogger.NONROLEUSER; + } + } else { + subjectID = ILogger.UNIDENTIFIED; + } + + return subjectID; + } + /** + * Signed Audit Log Requester ID + * + * This method is called to obtain the "RequesterID" for + * a signed audit log message. + * <P> + * + * @return id string containing the signed audit log message RequesterID + */ + private String auditRequesterID() { + // if no signed audit object exists, bail + if (mSignedAuditLogger == null) { + return null; + } + + String requesterID = null; + + // Initialize requesterID + SessionContext auditContext = SessionContext.getExistingContext(); + + if (auditContext != null) { + requesterID = (String) + auditContext.get(SessionContext.REQUESTER_ID); + + if (requesterID != null) { + requesterID = requesterID.trim(); + } else { + requesterID = ILogger.UNIDENTIFIED; + } + } else { + requesterID = ILogger.UNIDENTIFIED; + } + + return requesterID; + } + + /** + * Signed Audit Log Recovery ID + * + * This method is called to obtain the "RecoveryID" for + * a signed audit log message. + * <P> + * + * @return id string containing the signed audit log message RecoveryID + */ + private String auditRecoveryID() { + // if no signed audit object exists, bail + if (mSignedAuditLogger == null) { + return null; + } + + String recoveryID = null; + + // Initialize recoveryID + SessionContext auditContext = SessionContext.getExistingContext(); + + if (auditContext != null) { + recoveryID = (String) + auditContext.get(SessionContext.RECOVERY_ID); + + if (recoveryID != null) { + recoveryID = recoveryID.trim(); + } else { + recoveryID = ILogger.UNIDENTIFIED; + } + } else { + recoveryID = ILogger.UNIDENTIFIED; + } + + return recoveryID; + } + + + /** + * Signed Audit Log + * + * This method is called to store messages to the signed audit log. + * <P> + * + * @param msg signed audit log message + */ + private void audit(String msg) { + // in this case, do NOT strip preceding/trailing whitespace + // from passed-in String parameters + + if (mSignedAuditLogger == null) { + return; + } + + mSignedAuditLogger.log(ILogger.EV_SIGNED_AUDIT, + null, + ILogger.S_SIGNED_AUDIT, + ILogger.LL_SECURITY, + msg); + } +} + + +/** + * Parsed and Flattened structure of PKIArchiveOptions. + */ +class ArchiveOptions { + private String mSymmAlgOID = null; + private byte mSymmAlgParams[] = null; + private byte mEncSymmKey[] = null; + private byte mEncValue[] = null; + public ArchiveOptions(PKIArchiveOptions opts) throws EBaseException { + try { + EncryptedKey key = opts.getEncryptedKey(); + EncryptedValue val = key.getEncryptedValue(); + AlgorithmIdentifier symmAlg = val.getSymmAlg(); + + mSymmAlgOID = symmAlg.getOID().toString(); + mSymmAlgParams = ((OCTET_STRING) ((ANY) symmAlg.getParameters()).decodeWith(OCTET_STRING.getTemplate())).toByteArray(); + BIT_STRING encSymmKey = val.getEncSymmKey(); + + mEncSymmKey = encSymmKey.getBits(); + BIT_STRING encVal = val.getEncValue(); + + mEncValue = encVal.getBits(); + } catch (InvalidBERException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "[PKIArchiveOptions]" + e.toString())); + } + + } + + public String getSymmAlgOID() { + return mSymmAlgOID; + } + + public byte[] getSymmAlgParams() { + return mSymmAlgParams; + } + + public byte[] getEncSymmKey() { + return mEncSymmKey; + } + + public byte[] getEncValue() { + return mEncValue; + } +} diff --git a/pki/base/kra/src/com/netscape/kra/KRANotify.java b/pki/base/kra/src/com/netscape/kra/KRANotify.java new file mode 100644 index 000000000..5f272be6b --- /dev/null +++ b/pki/base/kra/src/com/netscape/kra/KRANotify.java @@ -0,0 +1,53 @@ +// --- 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 com.netscape.certsrv.base.*; +import com.netscape.certsrv.request.*; +import com.netscape.certsrv.kra.*; + + +/** + * A class represents a KRA request queue notify. This + * object will be invoked by the request subsystem + * when a request is requested for processing. + * + * @author thomask + * @version $Revision: 14563 $, $Date: 2007-05-01 10:35:23 -0700 (Tue, 01 May 2007) $ + */ +public class KRANotify extends ARequestNotifier { + private IKeyRecoveryAuthority mKRA = null; + + /** + * default constructor + */ + public KRANotify() { + super(); + } + + /** + * Creates KRA notify. + */ + public KRANotify(IKeyRecoveryAuthority kra) { + super(); + mKRA = kra; + } + + // XXX may want to do something else with mKRA ? +} diff --git a/pki/base/kra/src/com/netscape/kra/KRAPolicy.java b/pki/base/kra/src/com/netscape/kra/KRAPolicy.java new file mode 100644 index 000000000..1dc7cd13f --- /dev/null +++ b/pki/base/kra/src/com/netscape/kra/KRAPolicy.java @@ -0,0 +1,76 @@ +// --- 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 com.netscape.certsrv.policy.*; +import com.netscape.certsrv.request.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.logging.*; +import com.netscape.cmscore.util.*; +import com.netscape.certsrv.kra.*; +import com.netscape.cmscore.policy.*; + + +/** + * KRA Policy. + * + * @version $Revision: 14563 $, $Date: 2007-05-01 10:35:23 -0700 (Tue, 01 May 2007) $ + */ +public class KRAPolicy implements IPolicy { + IConfigStore mConfig = null; + IKeyRecoveryAuthority mKRA = null; + + public GenericPolicyProcessor mPolicies = new GenericPolicyProcessor(false); + + public KRAPolicy() { + } + + public void init(ISubsystem owner, IConfigStore config) + throws EBaseException { + mKRA = (IKeyRecoveryAuthority) owner; + mConfig = config; + mPolicies.init(mKRA, mConfig); + } + + public IPolicyProcessor getPolicyProcessor() { + return mPolicies; + } + + /** + */ + public PolicyResult apply(IRequest r) { + if (Debug.ON) + Debug.trace("KRA applies policies"); + mKRA.log(ILogger.LL_INFO, "KRA applies policies"); + PolicyResult result = mPolicies.apply(r); + + if (result.equals(PolicyResult.DEFERRED)) { + // For KRA request, there is deferred + if (Debug.ON) + Debug.trace("KRA policies return DEFERRED"); + return PolicyResult.REJECTED; + } else { + if (Debug.ON) + Debug.trace("KRA policies return ACCEPTED"); + return mPolicies.apply(r); + } + } + +} + diff --git a/pki/base/kra/src/com/netscape/kra/KRAService.java b/pki/base/kra/src/com/netscape/kra/KRAService.java new file mode 100644 index 000000000..f45792bfd --- /dev/null +++ b/pki/base/kra/src/com/netscape/kra/KRAService.java @@ -0,0 +1,98 @@ +// --- 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.util.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.request.*; +import com.netscape.certsrv.kra.*; +import com.netscape.certsrv.policy.*; +import com.netscape.cmscore.util.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.apps.CMS; + + +/** + * A class represents a KRA request queue service. This + * is the service object that is registered with + * the request queue. And it acts as a broker to + * distribute request into different KRA specific + * services. This service registration allows us to support + * new request easier. + * <P> + * + * @author thomask + * @version $Revision: 14563 $, $Date: 2007-05-01 10:35:23 -0700 (Tue, 01 May 2007) $ + */ +public class KRAService implements IService { + + public final static String ENROLLMENT = + IRequest.ENROLLMENT_REQUEST; + public final static String RECOVERY = IRequest.KEYRECOVERY_REQUEST; + public final static String NETKEY_KEYGEN = IRequest.NETKEY_KEYGEN_REQUEST; + public final static String NETKEY_KEYRECOVERY = IRequest.NETKEY_KEYRECOVERY_REQUEST; + + // private variables + private IKeyRecoveryAuthority mKRA = null; + private Hashtable mServices = new Hashtable(); + + /** + * Constructs KRA service. + */ + public KRAService(IKeyRecoveryAuthority kra) { + mKRA = kra; + mServices.put(ENROLLMENT, new EnrollmentService(kra)); + mServices.put(RECOVERY, new RecoveryService(kra)); + mServices.put(NETKEY_KEYGEN, new NetkeyKeygenService(kra)); + mServices.put(NETKEY_KEYRECOVERY, new TokenKeyRecoveryService(kra)); + } + + /** + * Processes a KRA request. This method is invoked by + * request subsystem. + * + * @param r request from request subsystem + * @exception EBaseException failed to serve + */ + public boolean serviceRequest(IRequest r) throws EBaseException { + if (Debug.ON) + Debug.trace("KRA services request " + + r.getRequestId().toString()); + mKRA.log(ILogger.LL_INFO, "KRA services request " + + r.getRequestId().toString()); + IService s = (IService) mServices.get( + r.getRequestType()); + + if (s == null) { + r.setExtData(IRequest.RESULT, IRequest.RES_ERROR); + r.setExtData(IRequest.ERROR, new EBaseException( + CMS.getUserMessage("CMS_BASE_INVALID_OPERATION"))); + return true; + } + try { + return s.serviceRequest(r); + } catch (EBaseException e) { + r.setExtData(IRequest.RESULT, IRequest.RES_ERROR); + r.setExtData(IRequest.ERROR, e); + // return true; + // #546508 + return false; + } + } +} diff --git a/pki/base/kra/src/com/netscape/kra/KeyRecoveryAuthority.java b/pki/base/kra/src/com/netscape/kra/KeyRecoveryAuthority.java new file mode 100644 index 000000000..9ca87dd0c --- /dev/null +++ b/pki/base/kra/src/com/netscape/kra/KeyRecoveryAuthority.java @@ -0,0 +1,1478 @@ +// --- 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.lang.*; +import java.util.*; +import java.security.cert.X509Certificate; +import java.security.cert.*; +import java.math.*; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import netscape.security.x509.*; +import netscape.security.util.DerOutputStream; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.authority.*; +import com.netscape.certsrv.listeners.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.dbs.*; +import com.netscape.certsrv.usrgrp.*; +import com.netscape.certsrv.dbs.keydb.*; +import com.netscape.cmscore.dbs.*; +import com.netscape.certsrv.policy.*; +import com.netscape.certsrv.kra.*; +import com.netscape.certsrv.request.*; +import com.netscape.certsrv.security.*; +import com.netscape.cmscore.request.*; +import com.netscape.certsrv.apps.*; + +import org.mozilla.jss.*; +import org.mozilla.jss.crypto.*; + + +/** + * A class represents an key recovery authority (KRA). A KRA + * is responsible to maintain key pairs that have been + * escrowed. It provides archive and recovery key pairs + * functionalities. + * <P> + * + * @author thomask + * @version $Revision: 14563 $, $Date: 2007-05-01 10:35:23 -0700 (Tue, 01 May 2007) $ + */ +public class KeyRecoveryAuthority implements IAuthority, IKeyService, IKeyRecoveryAuthority { + + public final static String OFFICIAL_NAME = "Data Recovery Manager"; + + /** + * Internal Constants + */ + + private static final String PR_INTERNAL_TOKEN_NAME = "internal"; + private static final String PARAM_CREDS = "creds"; + private static final String PARAM_LOCK = "lock"; + private static final String PARAM_PK12 = "pk12"; + private static final String PARAM_ERROR = "error"; + private static final String PARAM_AGENT = "agent"; + + private final static String KEY_RESP_NAME = "keyRepository"; + + private Hashtable mRequestProcessor = new Hashtable(); + + protected boolean mInitialized = false; + protected IConfigStore mConfig = null; + protected ILogger mLogger = CMS.getLogger(); + protected KRAPolicy mPolicy = null; + protected X500Name mName = null; + protected boolean mQueueRequests = false; + protected String mId = null; + protected IRequestQueue mRequestQueue = null; + protected TransportKeyUnit mTransportKeyUnit = null; + protected StorageKeyUnit mStorageKeyUnit = null; + protected Hashtable mAutoRecovery = new Hashtable(); + protected boolean mAutoRecoveryOn = false; + protected KeyRepository mKeyDB = null; + protected IRequestNotifier mNotify = null; + protected IRequestNotifier mPNotify = null; + protected ISubsystem mOwner = null; + protected int mRecoveryIDCounter = 0; + protected Hashtable mRecoveryParams = new Hashtable(); + protected org.mozilla.jss.crypto.X509Certificate mJssCert = null; + protected CryptoToken mKeygenToken = null; + + // holds the number of bits of entropy to collect for each keygen + private int mEntropyBitsPerKeyPair=0; + + // the number of milliseconds which it is acceptable to block while + // getting entropy - anything longer will cause a warning. + // 0 means this warning is disabled + private int mEntropyBlockWarnMilliseconds = 0; + + + + // for the notification listener + public IRequestListener mReqInQListener = null; + + private ILogger mSignedAuditLogger = CMS.getSignedAuditLogger(); + private final static byte EOL[] = { Character.LINE_SEPARATOR }; + private final static String SIGNED_AUDIT_AGENT_DELIMITER = ", "; + private final static String + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST = + "LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST_4"; + private final static String + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_PROCESSED = + "LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_PROCESSED_3"; + private final static String LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST = + "LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_4"; + private final static String LOGGING_SIGNED_AUDIT_KEY_RECOVERY_PROCESSED = + "LOGGING_SIGNED_AUDIT_KEY_RECOVERY_PROCESSED_4"; + + /** + * Constructs an escrow authority. + * <P> + */ + public KeyRecoveryAuthority() { + super(); + } + + /** + * Retrieves subsystem identifier. + * + * @return subsystem id + */ + public String getId() { + return mId; + } + + /** + * Sets subsystem identifier. + * + * @param id subsystem id + * @exception EBaseException failed to set id + */ + public void setId(String id) throws EBaseException { + mId = id; + } + + public IPolicyProcessor getPolicyProcessor() { + return mPolicy.getPolicyProcessor(); + } + + // initialize entropy collection parameters + private void initEntropy(IConfigStore config) + { + mEntropyBitsPerKeyPair = 0; + mEntropyBlockWarnMilliseconds = 50; + // initialize entropy collection + IConfigStore ecs = config.getSubStore("entropy"); + if (ecs != null) { + try { + mEntropyBitsPerKeyPair = ecs.getInteger("bitsperkeypair",0); + mEntropyBlockWarnMilliseconds = ecs.getInteger("blockwarnms",50); + } catch (EBaseException eb) { + // ok - we deal with missing parameters above + } + } + CMS.debug("KeyRecoveryAuthority Entropy bits = "+mEntropyBitsPerKeyPair); + if (mEntropyBitsPerKeyPair == 0) { + //log(ILogger.LL_INFO, + //CMS.getLogMessage("CMSCORE_KRA_ENTROPY_COLLECTION_DISABLED")); + } else { + //log(ILogger.LL_INFO, + //CMS.getLogMessage("CMSCORE_KRA_ENTROPY_COLLECTION_ENABLED")); + CMS.debug("KeyRecoveryAuthority about to add Entropy"); + addEntropy(false); + CMS.debug("KeyRecoveryAuthority back from add Entropy"); + } + + } + + + public void addEntropy(boolean logflag) { + CMS.debug("KeyRecoveryAuthority addEntropy()"); + if (mEntropyBitsPerKeyPair == 0) { + CMS.debug("KeyRecoveryAuthority returning - disabled()"); + return; + } + long start = System.currentTimeMillis(); + try { + com.netscape.cmscore.security.JssSubsystem.getInstance(). + addEntropy(mEntropyBitsPerKeyPair); + } catch (Exception e) { + CMS.debug("KeyRecoveryAuthority returning - error - see log file"); + CMS.debug("exception: "+e.getMessage()); + CMS.debug(e); + if (logflag) { + log(ILogger.LL_INFO, + CMS.getLogMessage("CMSCORE_KRA_ENTROPY_ERROR", + e.getMessage())); + } + } + long end = System.currentTimeMillis(); + long duration = end-start; + + if (mEntropyBlockWarnMilliseconds > 0 && + duration > mEntropyBlockWarnMilliseconds) { + + CMS.debug("KeyRecoveryAuthority returning - warning - entropy took too long (ms="+ + duration+")"); + if (logflag) { + log(ILogger.LL_INFO, + CMS.getLogMessage("CMSCORE_KRA_ENTROPY_BLOCKED_WARNING", + ""+(int)duration)); + } + } + CMS.debug("KeyRecoveryAuthority returning "); + } + + + + /** + * Starts this subsystem. It loads and initializes all + * necessary components. This subsystem is started by + * KRASubsystem. + * <P> + * + * @param owner owner of this subsystem + * @param config configuration store for this subsystem + * @exception EBaseException failed to start subsystem + */ + public void init(ISubsystem owner, IConfigStore config) + throws EBaseException { + CMS.debug("KeyRecoveryAuthority init() begins"); + if (mInitialized) + return; + + mConfig = config; + mOwner = owner; + + // initialize policy processor + mPolicy = new KRAPolicy(); + mPolicy.init(this, mConfig.getSubStore(PROP_POLICY)); + + // create key repository + int keydb_inc = mConfig.getInteger(PROP_KEYDB_INC, 5); + + mKeyDB = new KeyRepository(getDBSubsystem(), + keydb_inc, + "ou=" + KEY_RESP_NAME + ",ou=" + + getId() + "," + + getDBSubsystem().getBaseDN()); + + // read transport key from internal database + mTransportKeyUnit = new TransportKeyUnit(); + try { + mTransportKeyUnit.init(this, mConfig.getSubStore( + PROP_TRANSPORT_KEY)); + } catch (EBaseException e) { + CMS.debug("KeyRecoveryAuthority: transport unit exception " + e.toString()); +//XXX throw e; + return; + } + + // retrieve the authority name from transport cert + try { + mJssCert = mTransportKeyUnit.getCertificate(); + X509CertImpl certImpl = new + X509CertImpl(mJssCert.getEncoded()); + + mName = (X500Name) certImpl.getSubjectDN(); + } catch (CertificateEncodingException e) { + CMS.debug("KeyRecoveryAuthority: " + e.toString()); + throw new EBaseException(CMS.getUserMessage("CMS_BASE_LOAD_FAILED", + "transport cert " + e.toString())); + } catch (CertificateException e) { + CMS.debug("KeyRecoveryAuthority: " + e.toString()); + throw new EBaseException(CMS.getUserMessage("CMS_BASE_LOAD_FAILED", + "transport cert " + e.toString())); + } + + // read transport key from storage key + mStorageKeyUnit = new StorageKeyUnit(); + try { + mStorageKeyUnit.init(this, + mConfig.getSubStore(PROP_STORAGE_KEY)); + } catch (EBaseException e) { + CMS.debug("KeyRecoveryAuthority: storage unit exception " + e.toString()); + throw e; + } + + // setup token for server-side key generation for user enrollments + String serverKeygenTokenName = mConfig.getString("serverKeygenTokenName", null); + if (serverKeygenTokenName == null) { + CMS.debug("serverKeygenTokenName set to nothing"); + if (mStorageKeyUnit.getToken() != null) { + try { + String storageToken = mStorageKeyUnit.getToken().getName(); + if (!storageToken.equals("internal")) { + CMS.debug("Auto set serverKeygenTokenName to " + storageToken); + serverKeygenTokenName = storageToken; + } + } catch (Exception e) { + } + } + } + if (serverKeygenTokenName == null) { + serverKeygenTokenName = "internal"; + } + if (serverKeygenTokenName.equalsIgnoreCase(PR_INTERNAL_TOKEN_NAME)) + serverKeygenTokenName = PR_INTERNAL_TOKEN_NAME; + + try { + if (serverKeygenTokenName.equalsIgnoreCase(PR_INTERNAL_TOKEN_NAME)) { + CMS.debug("KeyRecoveryAuthority: getting internal crypto token for serverkeygen"); + mKeygenToken = CryptoManager.getInstance().getInternalKeyStorageToken(); + } else { + CMS.debug("KeyRecoveryAuthority: getting HSM token for serverkeygen"); + mKeygenToken = CryptoManager.getInstance().getTokenByName(serverKeygenTokenName); + } + CMS.debug("KeyRecoveryAuthority: set up keygenToken"); + } catch (NoSuchTokenException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_TOKEN_NOT_FOUND", serverKeygenTokenName)); + } catch (Exception e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_CRYPTOMANAGER_UNINITIALIZED")); + } + + CMS.debug("KeyRecoveryAuthority: about to init entropy"); + initEntropy(mConfig); + CMS.debug("KeyRecoveryAuthority: completed init of entropy"); + + getLogger().log(ILogger.EV_SYSTEM, ILogger.S_KRA, + ILogger.LL_INFO, mName.toString() + " is started"); + + // setup the KRA request queue + IService service = new KRAService(this); + + mNotify = new KRANotify(this); + mPNotify = new ARequestNotifier(); + IRequestSubsystem reqSub = RequestSubsystem.getInstance(); + int reqdb_inc = mConfig.getInteger("reqdbInc", 5); + + mRequestQueue = reqSub.getRequestQueue(getId(), reqdb_inc, + mPolicy, service, mNotify, mPNotify); + + // init request scheduler if configured + String schedulerClass = + mConfig.getString("requestSchedulerClass", null); + + if (schedulerClass != null) { + try { + IRequestScheduler scheduler = (IRequestScheduler) + Class.forName(schedulerClass).newInstance(); + + mRequestQueue.setRequestScheduler(scheduler); + } catch (Exception e) { + // do nothing here + } + } + initNotificationListeners(); + } + + public CryptoToken getKeygenToken() { + return mKeygenToken; + } + + public IRequestListener getRequestInQListener() { + return mReqInQListener; + } + + public org.mozilla.jss.crypto.X509Certificate getTransportCert() { + return mJssCert; + } + + /** + * Clears up system during garbage collection. + */ + public void finalize() { + shutdown(); + } + + /** + * Starts this service. When this method is called, all + * service + * + * @exception EBaseException failed to startup this subsystem + */ + public void startup() throws EBaseException { + CMS.debug("KeyRecoveryAuthority startup() begins"); + + if (mRequestQueue != null) { + // setup administration operations if everything else is fine + mRequestQueue.recover(); + CMS.debug("KeyRecoveryAuthority startup() call request Q recover"); + + // Note that we use our instance id for registration. + // This helps us to support multiple instances + // of a subsystem within server. + + // register remote admin interface + mInitialized = true; + } else { + CMS.debug("KeyRecoveryAuthority: mRequestQueue is null, could be in preop mode"); + } + } + + /** + * Shutdowns this subsystem. + */ + public void shutdown() { + if (!mInitialized) + return; + + mTransportKeyUnit.shutdown(); + mStorageKeyUnit.shutdown(); + getLogger().log(ILogger.EV_SYSTEM, ILogger.S_KRA, + ILogger.LL_INFO, mName.toString() + " is stopped"); + mInitialized = false; + } + + /** + * Retrieves the configuration store of this subsystem. + * <P> + * + * @return configuration store + */ + public IConfigStore getConfigStore() { + return mConfig; + } + + /** + * Changes the auto recovery state. + * + * @param cs list of recovery agent credentials + * @param on turn of auto recovery or not + * @return operation success or not + */ + public boolean setAutoRecoveryState(Credential cs[], boolean on) { + if (on == true) { + // check credential before enabling it + try { + getStorageKeyUnit().login(cs); + } catch (Exception e) { + return false; + } + } + // maintain in-memory variable; don't store it in config + mAutoRecoveryOn = on; + return true; + } + + /** + * Retrieves the current auto recovery state. + * + * @return enable or not + */ + public boolean getAutoRecoveryState() { + // maintain in-memory variable; don't store it in config + return mAutoRecoveryOn; + } + + /** + * Returns a list of users who are in auto + * recovery mode. + * + * @return list of user IDs that are accepted in the + * auto recovery mode + */ + public Enumeration getAutoRecoveryIDs() { + return mAutoRecovery.keys(); + } + + /** + * Adds auto recovery mode to the given user id. + * + * @param id new identifier to the auto recovery mode + * @param creds list of credentials + */ + public void addAutoRecovery(String id, Credential creds[]) { + mAutoRecovery.put(id, creds); + } + + /** + * Removes auto recovery mode from the given user id. + * + * @param id id of user to be removed from auto + * recovery mode + */ + public void removeAutoRecovery(String id) { + mAutoRecovery.remove(id); + } + + /** + * Retrieves logger from escrow authority. + * + * @return logger + */ + public ILogger getLogger() { + return CMS.getLogger(); + } + + /** + * Retrieves number of required agents for + * recovery operation. + * + * @return number of required agents + * @exception EBaseException failed to retrieve info + */ + public int getNoOfRequiredAgents() throws EBaseException { + if (mConfig.getBoolean("keySplitting")) { + return mStorageKeyUnit.getNoOfRequiredAgents(); + } else { + int ret = -1; + ret = mConfig.getInteger("noOfRequiredRecoveryAgents", 1); + if (ret <= 0) { + throw new EBaseException("Invalid parameter noOfRequiredecoveryAgents"); + } + return ret; + } + } + + /** + * Distributed recovery. + */ + public String getRecoveryID() { + return Integer.toString(mRecoveryIDCounter++); + } + + public Hashtable createRecoveryParams(String recoveryID) + throws EBaseException { + Hashtable h = new Hashtable(); + + h.put(PARAM_CREDS, new Vector()); + h.put(PARAM_LOCK, new Object()); + mRecoveryParams.put(recoveryID, h); + return h; + } + + public void destroyRecoveryParams(String recoveryID) + throws EBaseException { + mRecoveryParams.remove(recoveryID); + } + + public Hashtable getRecoveryParams(String recoveryID) + throws EBaseException { + return (Hashtable) mRecoveryParams.get(recoveryID); + } + + public void createPk12(String recoveryID, byte[] pk12) + throws EBaseException { + Hashtable h = getRecoveryParams(recoveryID); + + h.put(PARAM_PK12, pk12); + } + + public byte[] getPk12(String recoveryID) + throws EBaseException { + return (byte[]) getRecoveryParams(recoveryID).get(PARAM_PK12); + } + + public void createError(String recoveryID, String error) + throws EBaseException { + Hashtable h = getRecoveryParams(recoveryID); + + h.put(PARAM_ERROR, error); + } + + public String getError(String recoveryID) + throws EBaseException { + return (String) getRecoveryParams(recoveryID).get(PARAM_ERROR); + } + + /** + * Retrieve the current approval agents + */ + public Vector getAppAgents( + String recoveryID) throws EBaseException { + Hashtable h = getRecoveryParams(recoveryID); + Vector dc = (Vector) h.get(PARAM_CREDS); + + return dc; + } + + /** + * Retrieves a list credentials. This puts KRA in a waiting + * mode, it never returns until all the necessary passwords + * are collected. + */ + public Credential[] getDistributedCredentials( + String recoveryID) + throws EBaseException { + Hashtable h = getRecoveryParams(recoveryID); + Vector dc = (Vector) h.get(PARAM_CREDS); + Object lock = (Object) h.get(PARAM_LOCK); + + synchronized (lock) { + while (dc.size() < getNoOfRequiredAgents()) { + try { + lock.wait(); + } catch (InterruptedException e) { + } + } + Credential creds[] = new Credential[dc.size()]; + + dc.copyInto(creds); + return creds; + } + } + + /** + * Verifies credential. + */ + private void verifyCredential(Vector creds, String uid, + String pwd) throws EBaseException { + // see if we have the uid already + + if (!mConfig.getBoolean("keySplitting")) { + // check if the uid is in the specified group + IUGSubsystem ug = (IUGSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_UG); + if (!ug.isMemberOf(uid, mConfig.getString("recoveryAgentGroup"))) { + // invalid group + throw new EBaseException(CMS.getUserMessage("CMS_KRA_CREDENTIALS_NOT_EXIST")); + } + } + + for (int i = 0; i < creds.size(); i++) { + Credential c = (Credential) creds.elementAt(i); + + if (c.getIdentifier().equals(uid)) { + // duplicated uid + throw new EBaseException(CMS.getUserMessage("CMS_KRA_CREDENTIALS_EXIST")); + } + } + if (mConfig.getBoolean("keySplitting")) { + mStorageKeyUnit.checkPassword(uid, pwd); + } + } + + /** + * Adds password. + */ + public void addDistributedCredential(String recoveryID, + String uid, String pwd) throws EBaseException { + Hashtable h = getRecoveryParams(recoveryID); + Vector dc = (Vector) h.get(PARAM_CREDS); + Object lock = (Object) h.get(PARAM_LOCK); + + synchronized (lock) { + verifyCredential(dc, uid, pwd); + // verify password + dc.addElement(new Credential(uid, pwd)); + // modify status object + lock.notify(); + } + } + + /** + * Archives key. This creates a key record in the key + * repository. + * <P> + * + * <ul> + * <li>signed.audit LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST used + * whenever a user private key archive request is made (this is when the + * DRM receives the request) + * <li>signed.audit LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_PROCESSED used + * whenever a user private key archive request is processed (this is when + * the DRM processes the request) + * </ul> + * @param rec key record to be archived + * @return executed request + * @exception EBaseException failed to archive key + * @return the request + * <P> + */ + public IRequest archiveKey(KeyRecord rec) + throws EBaseException { + String auditMessage = null; + String auditSubjectID = auditSubjectID(); + String auditRequesterID = auditRequesterID(); + String auditPublicKey = auditPublicKey(rec); + String auditArchiveID = ILogger.UNIDENTIFIED; + + IRequestQueue queue = null; + IRequest r = null; + String id = null; + + // ensure that any low-level exceptions are reported + // to the signed audit log and stored as failures + try { + queue = getRequestQueue(); + + r = queue.newRequest(KRAService.ENROLLMENT); + + if (r != null) { + // overwrite "auditArchiveID" if and only if "id" != null + id = r.getRequestId().toString(); + if (id != null) { + auditArchiveID = id.trim(); + } + } + + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST, + auditSubjectID, + ILogger.SUCCESS, + auditRequesterID, + auditArchiveID); + + audit(auditMessage); + } catch (EBaseException eAudit1) { + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST, + auditSubjectID, + ILogger.FAILURE, + auditRequesterID, + auditArchiveID); + + audit(auditMessage); + + throw eAudit1; + } + + // ensure that any low-level exceptions are reported + // to the signed audit log and stored as failures + try { + if (r != null) { + r.setExtData(EnrollmentService.ATTR_KEY_RECORD, rec.getSerialNumber()); + queue.processRequest(r); + } + + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_PROCESSED, + auditSubjectID, + ILogger.SUCCESS, + auditPublicKey); + + audit(auditMessage); + } catch (EBaseException eAudit1) { + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_PROCESSED, + auditSubjectID, + ILogger.FAILURE, + auditPublicKey); + + audit(auditMessage); + + throw eAudit1; + } + + return r; + } + + /** + * Recovers key for administrators. This method is + * invoked by the agent operation of the key recovery servlet. + * <P> + * + * <ul> + * <li>signed.audit LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST used whenever + * a user private key recovery request is made (this is when the DRM + * receives the request) + * <li>signed.audit LOGGING_SIGNED_AUDIT_KEY_RECOVERY_PROCESSED used whenever + * a user private key recovery request is processed (this is when the DRM + * processes the request) + * </ul> + * @param kid key identifier + * @param creds list of recovery agent credentials + * @param password password of the PKCS12 package + * @param cert certficate that will be put in PKCS12 + * @param delivery file, mail or something else + * @param nickname string containing the nickname of the id cert for this + * subsystem + * @exception EBaseException failed to recover key + * @return a byte array containing the key + */ + public byte[] doKeyRecovery(BigInteger kid, + Credential creds[], String password, + X509CertImpl cert, + String delivery, String nickname) + throws EBaseException { + String auditMessage = null; + String auditSubjectID = auditSubjectID(); + String auditRecoveryID = auditRecoveryID(); + String auditPublicKey = auditPublicKey(cert); + String auditAgents = ILogger.SIGNED_AUDIT_EMPTY_VALUE; + + IRequestQueue queue = null; + IRequest r = null; + Hashtable params = null; + + + // ensure that any low-level exceptions are reported + // to the signed audit log and stored as failures + try { + queue = getRequestQueue(); + r = queue.newRequest(KRAService.RECOVERY); + + // set transient parameters + params = createVolatileRequest(r.getRequestId()); + + if (mConfig.getBoolean("keySplitting")) { + params.put(RecoveryService.ATTR_AGENT_CREDENTIALS, creds); + } + params.put(RecoveryService.ATTR_TRANSPORT_PWD, password); + + r.setExtData(RecoveryService.ATTR_SERIALNO, kid); + r.setExtData(RecoveryService.ATTR_USER_CERT, cert); + if (nickname != null) { + nickname = nickname.trim(); + if (!nickname.equals("")) { + r.setExtData(RecoveryService.ATTR_NICKNAME, nickname); + } + } + + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST, + auditSubjectID, + ILogger.SUCCESS, + auditRecoveryID, + auditPublicKey); + + audit(auditMessage); + } catch (EBaseException eAudit1) { + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST, + auditSubjectID, + ILogger.FAILURE, + auditRecoveryID, + auditPublicKey); + + audit(auditMessage); + + throw eAudit1; + } + + // ensure that any low-level exceptions are reported + // to the signed audit log and stored as failures + try { + queue.processRequest(r); + + if (r.getExtDataInString(IRequest.ERROR) == null) { + byte pkcs12[] = (byte[]) params.get( + RecoveryService.ATTR_PKCS12); + + auditAgents = auditAgents(creds); + + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_KEY_RECOVERY_PROCESSED, + auditSubjectID, + ILogger.SUCCESS, + auditRecoveryID, + auditAgents); + + audit(auditMessage); + + destroyVolatileRequest(r.getRequestId()); + + return pkcs12; + } else { + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_KEY_RECOVERY_PROCESSED, + auditSubjectID, + ILogger.FAILURE, + auditRecoveryID, + auditAgents); + + audit(auditMessage); + + throw new EBaseException(r.getExtDataInString(IRequest.ERROR)); + } + } catch (EBaseException eAudit1) { + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_KEY_RECOVERY_PROCESSED, + auditSubjectID, + ILogger.FAILURE, + auditRecoveryID, + auditAgents); + + audit(auditMessage); + + throw eAudit1; + } + } + + /** + * Constructs a recovery request and submits it + * to the request subsystem for processing. + * + * @param kid key identifier + * @param creds list of recovery agent credentials + * @param password password of the PKCS12 package + * @param cert certficate that will be put in PKCS12 + * @param delivery file, mail or something else + * @return executed request + * @exception EBaseException failed to recover key + */ + public IRequest recoverKey(BigInteger kid, + Credential creds[], String password, + X509CertImpl cert, + String delivery) throws EBaseException { + IRequestQueue queue = getRequestQueue(); + IRequest r = queue.newRequest("recovery"); + + r.setExtData(RecoveryService.ATTR_SERIALNO, kid); + r.setExtData(RecoveryService.ATTR_TRANSPORT_PWD, password); + r.setExtData(RecoveryService.ATTR_USER_CERT, cert); + r.setExtData(RecoveryService.ATTR_DELIVERY, delivery); + queue.processRequest(r); + return r; + } + + /** + * Recovers key for end-entities. + * + * @param creds list of credentials + * @param encryptionChain certificate chain + * @param signingCert signing cert + * @param transportCert certificate to protect in-transit key + * @param ownerName owner name + * @return executed request + * @exception EBaseException failed to recover key + */ + public IRequest recoverKey(Credential creds[], CertificateChain + encryptionChain, X509CertImpl signingCert, + X509CertImpl transportCert, + X500Name ownerName) throws EBaseException { + IRequestQueue queue = getRequestQueue(); + IRequest r = queue.newRequest("recovery"); + + ByteArrayOutputStream certChainOut = new ByteArrayOutputStream(); + try { + encryptionChain.encode(certChainOut); + r.setExtData(RecoveryService.ATTR_ENCRYPTION_CERTS, + certChainOut.toByteArray()); + } catch (IOException e) { + log(ILogger.LL_FAILURE, + "Error encoding certificate chain"); + } + + r.setExtData(RecoveryService.ATTR_SIGNING_CERT, signingCert); + r.setExtData(RecoveryService.ATTR_TRANSPORT_CERT, transportCert); + + DerOutputStream ownerNameOut = new DerOutputStream(); + try { + ownerName.encode(ownerNameOut); + r.setExtData(RecoveryService.ATTR_OWNER_NAME, + ownerNameOut.toByteArray()); + } catch (IOException e) { + log(ILogger.LL_FAILURE, + "Error encoding X500Name for owner name"); + } + + queue.processRequest(r); + return r; + } + + /** + * Retrieves the storage key unit. The storage key + * is used to wrap the user key for long term + * storage. + * + * @return storage key unit. + */ + public IStorageKeyUnit getStorageKeyUnit() { + return mStorageKeyUnit; + } + + /** + * Retrieves the transport key unit. + * + * @return transport key unit + */ + public ITransportKeyUnit getTransportKeyUnit() { + return mTransportKeyUnit; + } + + /** + * Returns the name of this subsystem. This name is + * extracted from the transport certificate. + * + * @return KRA name + */ + public X500Name getX500Name() { + return mName; + } + + public String getNickName() { + return getNickname(); + } + + /** + * Returns the nickname for the id cert of this + * subsystem. + * + * @return nickname of the transport certificate + */ + public String getNickname() { + try { + return mTransportKeyUnit.getNickName(); + } catch (EBaseException e) { + return null; + } + } + + public void setNickname(String str) { + try { + mTransportKeyUnit.setNickName(str); + } catch (EBaseException e) { + } + } + + public String getNewNickName() throws EBaseException { + return mConfig.getString(PROP_NEW_NICKNAME, ""); + } + + public void setNewNickName(String name) { + mConfig.putString(PROP_NEW_NICKNAME, name); + } + + public IPolicy getPolicy() { + return mPolicy; + } + + /** + * Retrieves KRA request repository. + * <P> + * + * @return request repository + */ + public IRequestQueue getRequestQueue() { + return mRequestQueue; + } + + /** + * Retrieves the key repository. The key repository + * stores archived keys. + * <P> + */ + public IKeyRepository getKeyRepository() { + return mKeyDB; + } + + /** + * Retrieves the DN of this escrow authority. + * <P> + * + * @return distinguished name + */ + protected String getDN() { + return getX500Name().toString(); + } + + /** + * Retrieves database connection. + */ + public IDBSubsystem getDBSubsystem() { + return DBSubsystem.getInstance(); + } + + /** + * Logs an event. + * + * @param level log level + * @param msg message to log + */ + public void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, + level, msg); + } + + /** + * Registers a request listener. + * + * @param l request listener + */ + public void registerRequestListener(IRequestListener l) { + // it's initialized. + if (mNotify != null) + mNotify.registerListener(l); + } + + public void registerPendingListener(IRequestListener l) { + mPNotify.registerListener(l); + } + + /** + * init notification related listeners - + * right now only RequestInQueue listener is available for KRA + */ + private void initNotificationListeners() { + IConfigStore nc = null; + + try { + nc = mConfig.getSubStore(PROP_NOTIFY_SUBSTORE); + if (nc != null && nc.size() > 0) { + // Initialize Request In Queue notification listener + IConfigStore rq = nc.getSubStore(PROP_REQ_IN_Q_SUBSTORE); + IAuthority cSub = (IAuthority) this; + + String requestInQListenerClassName = nc.getString("certificateIssuedListenerClassName", "com.netscape.cms.listeners.RequestInQListener"); + + try { + mReqInQListener = (IRequestListener) Class.forName(requestInQListenerClassName).newInstance(); + mReqInQListener.init(this, nc); + } catch (Exception e1) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_REGISTER_LISTENER", requestInQListenerClassName)); + } + } else { + log(ILogger.LL_INFO, + "No KRA notification Module configuration found"); + } + } catch (EPropertyNotFound e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_NOTIFY_ERROR", e.toString())); + } catch (EListenersException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_NOTIFY_ERROR", e.toString())); + } catch (EBaseException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_NOTIFY_ERROR", e.toString())); + } + } + + /** + * temporary accepted ras. + */ + /* code no longer used + public X500Name[] getAcceptedRAs() { + // temporary. use usr/grp for real thing. + X500Name radn = null; + String raname = null; + + try { + raname = mConfig.getString("acceptedRA", null); + if (raname != null) { + radn = new X500Name(raname); + } + } catch (IOException e) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_KRA, + ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_INVALID_RA_NAME", raname, e.toString())); + } catch (EBaseException e) { + // ignore - set to null. + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_KRA, + ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_INVALID_RA_SETUP", e.toString())); + } + return new X500Name[] { radn }; + } + */ + + public Hashtable mVolatileRequests = new Hashtable(); + + /** + * Creates a request object to store attributes that + * will not be serialized. Currently, request queue + * framework will try to serialize all the attribute into + * persistent storage. Things like passwords are not + * desirable to be stored. + */ + public Hashtable createVolatileRequest(RequestId id) { + Hashtable params = new Hashtable(); + + mVolatileRequests.put(id.toString(), params); + return params; + } + + public Hashtable getVolatileRequest(RequestId id) { + return (Hashtable) mVolatileRequests.get(id.toString()); + } + + public void destroyVolatileRequest(RequestId id) { + mVolatileRequests.remove(id.toString()); + } + + public String getOfficialName() { + return OFFICIAL_NAME; + } + + /** + * Signed Audit Log + * + * This method is called to store messages to the signed audit log. + * <P> + * + * @param msg signed audit log message + */ + private void audit(String msg) { + // in this case, do NOT strip preceding/trailing whitespace + // from passed-in String parameters + + if (mSignedAuditLogger == null) { + return; + } + + mSignedAuditLogger.log(ILogger.EV_SIGNED_AUDIT, + null, + ILogger.S_SIGNED_AUDIT, + ILogger.LL_SECURITY, + msg); + } + + /** + * Signed Audit Log Subject ID + * + * This method is called to obtain the "SubjectID" for + * a signed audit log message. + * <P> + * + * @return id string containing the signed audit log message SubjectID + */ + private String auditSubjectID() { + // if no signed audit object exists, bail + if (mSignedAuditLogger == null) { + return null; + } + + String subjectID = null; + + // Initialize subjectID + SessionContext auditContext = SessionContext.getExistingContext(); + + if (auditContext != null) { + subjectID = (String) + auditContext.get(SessionContext.USER_ID); + + if (subjectID != null) { + subjectID = subjectID.trim(); + } else { + subjectID = ILogger.NONROLEUSER; + } + } else { + subjectID = ILogger.UNIDENTIFIED; + } + + return subjectID; + } + + /** + * Signed Audit Log Requester ID + * + * This method is called to obtain the "RequesterID" for + * a signed audit log message. + * <P> + * + * @return id string containing the signed audit log message RequesterID + */ + private String auditRequesterID() { + // if no signed audit object exists, bail + if (mSignedAuditLogger == null) { + return null; + } + + String requesterID = null; + + // Initialize requesterID + SessionContext auditContext = SessionContext.getExistingContext(); + + if (auditContext != null) { + requesterID = (String) + auditContext.get(SessionContext.REQUESTER_ID); + + if (requesterID != null) { + requesterID = requesterID.trim(); + } else { + requesterID = ILogger.UNIDENTIFIED; + } + } else { + requesterID = ILogger.UNIDENTIFIED; + } + + return requesterID; + } + + /** + * Signed Audit Log Recovery ID + * + * This method is called to obtain the "RecoveryID" for + * a signed audit log message. + * <P> + * + * @return id string containing the signed audit log message RecoveryID + */ + private String auditRecoveryID() { + // if no signed audit object exists, bail + if (mSignedAuditLogger == null) { + return null; + } + + String recoveryID = null; + + // Initialize recoveryID + SessionContext auditContext = SessionContext.getExistingContext(); + + if (auditContext != null) { + recoveryID = (String) + auditContext.get(SessionContext.RECOVERY_ID); + + if (recoveryID != null) { + recoveryID = recoveryID.trim(); + } else { + recoveryID = ILogger.UNIDENTIFIED; + } + } else { + recoveryID = ILogger.UNIDENTIFIED; + } + + return recoveryID; + } + + /** + * Signed Audit Log Public Key + * + * This method is called to obtain the public key from the passed in + * "X509Certificate" for a signed audit log message. + * <P> + * + * @param cert an X509Certificate + * @return key string containing the certificate's public key + */ + private String auditPublicKey(X509Certificate cert) { + // if no signed audit object exists, bail + if (mSignedAuditLogger == null) { + return null; + } + + if (cert == null) { + return ILogger.SIGNED_AUDIT_EMPTY_VALUE; + } + + byte rawData[] = cert.getPublicKey().getEncoded(); + String key = null; + + // convert "rawData" into "base64Data" + if (rawData != null) { + String base64Data = null; + + base64Data = CMS.BtoA(rawData).trim(); + + // extract all line separators from the "base64Data" + for (int i = 0; i < base64Data.length(); i++) { + if (base64Data.substring(i, i).getBytes() != EOL) { + key += base64Data.substring(i, i); + } + } + } + + if (key != null) { + key = key.trim(); + + if (key.equals("")) { + return ILogger.SIGNED_AUDIT_EMPTY_VALUE; + } else { + return key; + } + } else { + return ILogger.SIGNED_AUDIT_EMPTY_VALUE; + } + } + + /** + * Signed Audit Log Public Key + * + * This method is called to obtain the public key from the passed in + * "KeyRecord" for a signed audit log message. + * <P> + * + * @param rec a Key Record + * @return key string containing the certificate's public key + */ + private String auditPublicKey(KeyRecord rec) { + // if no signed audit object exists, bail + if (mSignedAuditLogger == null) { + return null; + } + + if (rec == null) { + return ILogger.SIGNED_AUDIT_EMPTY_VALUE; + } + + byte rawData[] = null; + + try { + rawData = rec.getPublicKeyData(); + } catch (EBaseException e) { + return ILogger.SIGNED_AUDIT_EMPTY_VALUE; + } + + String key = null; + + // convert "rawData" into "base64Data" + if (rawData != null) { + String base64Data = null; + + base64Data = CMS.BtoA(rawData).trim(); + + // extract all line separators from the "base64Data" + for (int i = 0; i < base64Data.length(); i++) { + if (base64Data.substring(i, i).getBytes() != EOL) { + key += base64Data.substring(i, i); + } + } + } + + if (key != null) { + key = key.trim(); + + if (key.equals("")) { + return ILogger.SIGNED_AUDIT_EMPTY_VALUE; + } else { + return key; + } + } else { + return ILogger.SIGNED_AUDIT_EMPTY_VALUE; + } + } + + /** + * Signed Audit Agents + * + * This method is called to extract agent uids from the passed in + * "Credentials[]" and return a string of comma-separated agent uids. + * <P> + * + * @param creds array of credentials + * @return a comma-separated string of agent uids + */ + private String auditAgents(Credential creds[]) { + if (creds == null) + return null; + + // if no signed audit object exists, bail + if (mSignedAuditLogger == null) { + return null; + } + + String agents = ILogger.SIGNED_AUDIT_EMPTY_VALUE; + + String uid = null; + + for (int i = 0; i < creds.length; i++) { + uid = creds[i].getIdentifier(); + + if (uid != null) { + uid = uid.trim(); + } + + if (uid != null && + !uid.equals("")) { + + if (i == 0) { + agents = uid; + } else { + agents += SIGNED_AUDIT_AGENT_DELIMITER + uid; + } + } + } + + return agents; + } +} + diff --git a/pki/base/kra/src/com/netscape/kra/NetkeyKeygenService.java b/pki/base/kra/src/com/netscape/kra/NetkeyKeygenService.java new file mode 100644 index 000000000..59f3a0a55 --- /dev/null +++ b/pki/base/kra/src/com/netscape/kra/NetkeyKeygenService.java @@ -0,0 +1,556 @@ +// --- 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 com.netscape.cmscore.util.Debug; +import java.util.StringTokenizer; +import java.util.Vector; +import java.io.IOException; +import java.io.ByteArrayInputStream; + +import java.math.BigInteger; +import java.security.*; +import java.security.KeyPair; +import java.security.cert.CertificateException; +import netscape.security.util.*; +import netscape.security.util.BigInt; +import netscape.security.pkcs.*; +import netscape.security.x509.*; +import netscape.security.provider.*; +import org.mozilla.jss.*; +import org.mozilla.jss.crypto.*; +import org.mozilla.jss.util.*; +import org.mozilla.jss.crypto.PrivateKey; +import org.mozilla.jss.asn1.*; +import org.mozilla.jss.crypto.KeyPairGenerator; +import org.mozilla.jss.pkix.crmf.*; +import org.mozilla.jss.pkix.primitive.*; +import org.mozilla.jss.pkix.primitive.AVA; +import org.mozilla.jss.pkcs11.*; +import com.netscape.certsrv.common.*; +import com.netscape.cmscore.util.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.security.*; +import com.netscape.cmscore.crmf.*; +import com.netscape.certsrv.kra.*; +import com.netscape.certsrv.base.*; +import com.netscape.cmscore.cert.*; +//import com.netscape.cmscore.ca.*; +import com.netscape.cmscore.dbs.*; +import com.netscape.certsrv.dbs.*; +import com.netscape.certsrv.dbs.repository.*; +import com.netscape.certsrv.profile.*; +import com.netscape.certsrv.dbs.keydb.*; +import com.netscape.certsrv.request.*; +import com.netscape.certsrv.policy.*; +import com.netscape.certsrv.authentication.*; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.apps.CMS; + +//for b64 encoding +import org.mozilla.jss.util.Base64OutputStream; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.io.*; + +/** + * A class representing keygen/archival request procesor for requests + * from netkey RAs. + * the user private key of the encryption cert is wrapped with a + * session symmetric key. The session symmetric key is wrapped with the + * storage key and stored in the internal database for long term + * storage. + * The user private key of the encryption cert is to be wrapped with the + * DES key which came in in the request wrapped with the KRA + * transport cert. The wrapped user private key is then sent back to + * the caller (netkey RA) ...netkey RA should already has kek-wrapped + * des key from the TKS. They are to be sent together back to + * the token. + * + * @author Christina Fu (cfu) + * @version $Revision: 14563 $, $Date: 2007-05-01 10:35:23 -0700 (Tue, 01 May 2007) $ + */ + +public class NetkeyKeygenService implements IService { + public final static String ATTR_KEY_RECORD = "keyRecord"; + public final static String ATTR_PROOF_OF_ARCHIVAL = + "proofOfArchival"; + + // private + private final static String + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST = + "LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST_4"; + private final static String + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_PROCESSED = + "LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_PROCESSED_3"; + // these need to be defined in LogMessages_en.properties later when we do this + private final static String + LOGGING_SIGNED_AUDIT_SERVER_SIDE_KEYGEN_REQUEST = + "LOGGING_SIGNED_AUDIT_SERVER_SIDE_KEYGEN_REQUEST_3"; + private final static String + LOGGING_SIGNED_AUDIT_SERVER_SIDE_KEYGEN_PROCESSED = + "LOGGING_SIGNED_AUDIT_SERVER_SIDE_KEYGEN_PROCESSED_3"; + private IKeyRecoveryAuthority mKRA = null; + private ITransportKeyUnit mTransportUnit = null; + private IStorageKeyUnit mStorageUnit = null; + private ILogger mSignedAuditLogger = CMS.getSignedAuditLogger(); + + /** + * Constructs request processor. + * <P> + * + * @param kra key recovery authority + */ + public NetkeyKeygenService(IKeyRecoveryAuthority kra) { + mKRA = kra; + mTransportUnit = kra.getTransportKeyUnit(); + mStorageUnit = kra.getStorageKeyUnit(); + } + + public PKIArchiveOptions toPKIArchiveOptions(byte options[]) { + ByteArrayInputStream bis = new ByteArrayInputStream(options); + PKIArchiveOptions archOpts = null; + + try { + archOpts = (PKIArchiveOptions) + (new PKIArchiveOptions.Template()).decode(bis); + } catch (Exception e) { + CMS.debug("NetkeyKeygenService: getPKIArchiveOptions " + e.toString()); + } + return archOpts; + } + + public KeyPair generateKeyPair( + KeyPairAlgorithm kpAlg, int keySize, PQGParams pqg) + throws NoSuchAlgorithmException, TokenException, InvalidAlgorithmParameterException, + InvalidParameterException, PQGParamGenException { + + CryptoToken token = mKRA.getKeygenToken(); + + CMS.debug("NetkeyKeygenService: key pair is to be generated on slot: "+token.getName()); + KeyPairGenerator kpGen = token.getKeyPairGenerator(kpAlg); + // make it temporary so can work with HSM + kpGen.temporaryPairs(true); + kpGen.sensitivePairs(true); + kpGen.extractablePairs(true); + + if (kpAlg == KeyPairAlgorithm.DSA) { + if (pqg == null) { + kpGen.initialize(keySize); + } else { + kpGen.initialize(pqg); + } + } else { + kpGen.initialize(keySize); + } + + if (pqg == null) { + KeyPair kp; + synchronized (new Object()) { + kp = kpGen.genKeyPair(); + mKRA.addEntropy(true); + } + return kp; + } else { + // DSA + KeyPair kp = null; + + /* no DSA for now... netkey prototype + do { + // 602548 NSS bug - to overcome it, we use isBadDSAKeyPair + kp = kpGen.genKeyPair(); + } + while (isBadDSAKeyPair(kp)); + */ + return kp; + } + } + + + + public KeyPair generateKeyPair( String alg, + int keySize, PQGParams pqg) throws EBaseException { + + KeyPairAlgorithm kpAlg = null; + + if (alg.equals("RSA")) + kpAlg = KeyPairAlgorithm.RSA; + else + kpAlg = KeyPairAlgorithm.DSA; + + try { + KeyPair kp = generateKeyPair( kpAlg, keySize, pqg); + + return kp; + } catch (InvalidParameterException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEYSIZE_PARAMS", + "" + keySize)); + } catch (PQGParamGenException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_PQG_GEN_FAILED")); + } catch (NoSuchAlgorithmException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_ALG_NOT_SUPPORTED", + kpAlg.toString())); + } catch (TokenException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_TOKEN_ERROR_1", e.toString())); + } catch (InvalidAlgorithmParameterException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_ALG_NOT_SUPPORTED", "DSA")); + } + } + + private static String base64Encode(byte[] bytes) throws IOException { + // All this streaming is lame, but Base64OutputStream needs a + // PrintStream + ByteArrayOutputStream output = new ByteArrayOutputStream(); + Base64OutputStream b64 = new Base64OutputStream(new + PrintStream(new + FilterOutputStream(output) + ) + ); + + b64.write(bytes); + b64.flush(); + + // This is internationally safe because Base64 chars are + // contained within 8859_1 + return output.toString("8859_1"); + } + + // this encrypts bytes with a symmetric key + public byte[] encryptIt(byte[] toBeEncrypted, SymmetricKey symKey, CryptoToken token, + IVParameterSpec IV) + { + try { + Cipher cipher = token.getCipherContext( + EncryptionAlgorithm.DES3_CBC_PAD); + + cipher.initEncrypt(symKey, IV); + byte pri[] = cipher.doFinal(toBeEncrypted); + return pri; + } catch (Exception e) { + CMS.debug("NetkeyKeygenService:initEncrypt() threw exception: "+e.toString()); + return null; + } + + } + + + /** + * Services an archival request from netkey. + * <P> + * + * @param request enrollment request + * @return serving successful or not + * @exception EBaseException failed to serve + */ + public boolean serviceRequest(IRequest request) + throws EBaseException { + String auditMessage = null; + String auditSubjectID = null; + String auditRequesterID = "TPSagent"; + String auditArchiveID = ILogger.UNIDENTIFIED; + String auditPublicKey = ILogger.UNIDENTIFIED; + byte[] wrapped_des_key; + + byte iv[] = {0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1}; + String iv_s =""; +/* + org.mozilla.jss.pkcs11.PK11SecureRandom random = + new org.mozilla.jss.pkcs11.PK11SecureRandom(); + random.nextBytes(iv); +*/ + + IVParameterSpec algParam = new IVParameterSpec(iv); + + wrapped_des_key = null; + boolean archive = true; + PK11SymKey sk= null; + byte[] publicKeyData = null;; + String PubKey = ""; + + String id = request.getRequestId().toString(); + if (id != null) { + auditArchiveID = id.trim(); + } + + String rArchive = request.getExtDataInString(IRequest.NETKEY_ATTR_ARCHIVE_FLAG); + if (rArchive.equals("true")) { + archive = true; + CMS.debug("NetkeyKeygenService: serviceRequest " +"archival requested for serverSideKeyGen"); + } else { + archive = false; + CMS.debug("NetkeyKeygenService: serviceRequest " +"archival not requested for serverSideKeyGen"); + } + + String rCUID = request.getExtDataInString(IRequest.NETKEY_ATTR_CUID); + String rUserid = request.getExtDataInString(IRequest.NETKEY_ATTR_USERID); + String rKeysize = request.getExtDataInString(IRequest.NETKEY_ATTR_KEY_SIZE); + int keysize = Integer.parseInt(rKeysize); + auditSubjectID=rCUID+":"+rUserid; + + SessionContext sContext = SessionContext.getContext(); + String agentId=""; + if (sContext != null) { + agentId = + (String) sContext.get(SessionContext.USER_ID); + } + + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_SERVER_SIDE_KEYGEN_REQUEST, + auditSubjectID, + ILogger.SUCCESS, + agentId); + + audit(auditMessage); + + + String rWrappedDesKeyString = request.getExtDataInString(IRequest.NETKEY_ATTR_DRMTRANS_DES_KEY); + // CMS.debug("NetkeyKeygenService: received DRM-trans-wrapped DES key ="+rWrappedDesKeyString); + wrapped_des_key = com.netscape.cmsutil.util.Utils.SpecialDecode(rWrappedDesKeyString); + CMS.debug("NetkeyKeygenService: wrapped_des_key specialDecoded"); + + // get the token for generating user keys + CryptoToken keygenToken = mKRA.getKeygenToken(); + if (keygenToken == null) { + CMS.debug("NetkeyKeygenService: failed getting keygenToken"); + request.setExtData(IRequest.RESULT, Integer.valueOf(10)); + return false; + } else + CMS.debug("NetkeyKeygenService: got keygenToken"); + + if ((wrapped_des_key != null) && + (wrapped_des_key.length > 0)) { + + // unwrap the DES key + sk= (PK11SymKey) mTransportUnit.unwrap_sym(wrapped_des_key); + + /* XXX could be done in HSM*/ + KeyPair keypair = null; + + CMS.debug("NetkeyKeygenService: about to generate key pair"); + + keypair = generateKeyPair("RSA"/*alg*/, + keysize /*Integer.parseInt(len)*/, null /*pqgParams*/); + + if (keypair == null) { + CMS.debug("NetkeyKeygenService: failed generating key pair for "+rCUID+":"+rUserid); + request.setExtData(IRequest.RESULT, Integer.valueOf(4)); + + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_SERVER_SIDE_KEYGEN_PROCESSED, + auditSubjectID, + ILogger.FAILURE, + agentId); + + audit(auditMessage); + + return false; + } else { + CMS.debug("NetkeyKeygenService: finished generate key pair for " +rCUID+":"+rUserid); + CMS.debug("NetkeyKeygenService: server-side key generated at keysize "+keysize); + + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_SERVER_SIDE_KEYGEN_PROCESSED, + auditSubjectID, + ILogger.SUCCESS, + agentId); + + audit(auditMessage); + + } + + //...extract the private key handle (not privatekeydata) + java.security.PrivateKey privKey = + keypair.getPrivate(); + + if (privKey == null) { + request.setExtData(IRequest.RESULT, Integer.valueOf(4)); + CMS.debug("NetkeyKeygenService: failed getting private key"); + return false; + } else { + CMS.debug("NetkeyKeygenService: got private key"); + } + + try { + + if (sk == null) { + CMS.debug("NetkeyKeygenService: no DES key"); + request.setExtData(IRequest.RESULT, Integer.valueOf(4)); + return false; + } else { + CMS.debug("NetkeyKeygenService: received DES key"); + } + + // 3 wrapping should be done in HSM + // wrap private key with DES + KeyWrapper symWrap = + keygenToken.getKeyWrapper(KeyWrapAlgorithm.DES3_CBC_PAD); + CMS.debug("NetkeyKeygenService: wrapper token=" + keygenToken.getName()); + CMS.debug("NetkeyKeygenService: got key wrapper"); + + CMS.debug("NetkeyKeygenService: key transport key is on slot: "+sk.getOwningToken().getName()); + symWrap.initWrap((SymmetricKey)sk, algParam); + byte wrapped[] = symWrap.wrap((PrivateKey)privKey); + /* + CMS.debug("NetkeyKeygenService: wrap called"); + CMS.debug(wrapped); + */ + /* This is for using with my decryption tool and ASN1 + decoder to see if the private key is indeed PKCS#8 format + { // cfu debug + String oFilePath = "/tmp/wrappedPrivKey.bin"; + File file = new File(oFilePath); + FileOutputStream ostream = new FileOutputStream(oFilePath); + ostream.write(wrapped); + ostream.close(); + } + */ + String wrappedPrivKeyString = /*base64Encode(wrapped);*/ + com.netscape.cmsutil.util.Utils.SpecialEncode(wrapped); + if (wrappedPrivKeyString == null) { + request.setExtData(IRequest.RESULT, Integer.valueOf(4)); + CMS.debug("NetkeyKeygenService: failed generating wrapped private key"); + return false; + } else { + request.setExtData("wrappedUserPrivate", wrappedPrivKeyString); + } + + publicKeyData = keypair.getPublic().getEncoded(); + if (publicKeyData == null) { + request.setExtData(IRequest.RESULT, Integer.valueOf(4)); + CMS.debug("NetkeyKeygenService: failed getting publickey encoded"); + return false; + } else { + //CMS.debug("NetkeyKeygenService: public key binary length ="+ publicKeyData.length); + PubKey = base64Encode(publicKeyData); + + //CMS.debug("NetkeyKeygenService: public key length =" + PubKey.length()); + request.setExtData("public_key", PubKey); + } + + iv_s = /*base64Encode(iv);*/com.netscape.cmsutil.util.Utils.SpecialEncode(iv); + request.setExtData("iv_s", iv_s); + + + /* + * archival - option flag "archive" controllable by the caller - TPS + */ + if (archive) { + // + // privateKeyData ::= SEQUENCE { + // sessionKey OCTET_STRING, + // encKey OCTET_STRING, + // } + // + // mKRA.log(ILogger.LL_INFO, "KRA encrypts internal private"); + + CMS.debug("KRA encrypts private key to put on internal ldap db"); + byte privateKeyData[] = + mStorageUnit.wrap((org.mozilla.jss.crypto.PrivateKey) privKey); + + if (privateKeyData == null) { + request.setExtData(IRequest.RESULT, Integer.valueOf(4)); + CMS.debug("NetkeyKeygenService: privatekey encryption by storage unit failed"); + return false; + } else + CMS.debug("NetkeyKeygenService: privatekey encryption by storage unit successful"); + + // create key record + KeyRecord rec = new KeyRecord(null, publicKeyData, + privateKeyData, rCUID+":"+rUserid, + keypair.getPublic().getAlgorithm(), + agentId); + + if (rec == null) { + request.setExtData(IRequest.RESULT, Integer.valueOf(11)); + CMS.debug("NetkeyKeygenService: privatekey recording failed"); + return false; + } else + CMS.debug("NetkeyKeygenService: got key record"); + + // we deal with RSA key only + try { + RSAPublicKey rsaPublicKey = new RSAPublicKey(publicKeyData); + + rec.setKeySize(Integer.valueOf(rsaPublicKey.getKeySize())); + } catch (InvalidKeyException e) { + request.setExtData(IRequest.RESULT, Integer.valueOf(11)); + CMS.debug("NetkeyKeygenService: failed:InvalidKeyException"); + return false; + } + //?? + IKeyRepository storage = mKRA.getKeyRepository(); + BigInteger serialNo = storage.getNextSerialNumber(); + + if (serialNo == null) { + request.setExtData(IRequest.RESULT, Integer.valueOf(11)); + CMS.debug("NetkeyKeygenService: serialNo null"); + return false; + } + CMS.debug("NetkeyKeygenService: before addKeyRecord"); + rec.set(KeyRecord.ATTR_ID, serialNo); + request.setExtData(ATTR_KEY_RECORD, serialNo); + storage.addKeyRecord(rec); + CMS.debug("NetkeyKeygenService: key archived for "+rCUID+":"+rUserid); + + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_PROCESSED, + auditSubjectID, + ILogger.SUCCESS, + PubKey); + + audit(auditMessage); + + } //if archive + + request.setExtData(IRequest.RESULT, Integer.valueOf(1)); + } catch (Exception e) { + CMS.debug("NetKeyKeygenService: " + e.toString()); + Debug.printStackTrace(e); + request.setExtData(IRequest.RESULT, Integer.valueOf(4)); + + } + } else + request.setExtData(IRequest.RESULT, Integer.valueOf(2)); + + return true; + } //serviceRequest + + /** + * Signed Audit Log + *y + * This method is called to store messages to the signed audit log. + * <P> + * + * @param msg signed audit log message + */ + private void audit(String msg) { + // in this case, do NOT strip preceding/trailing whitespace + // from passed-in String parameters + + if (mSignedAuditLogger == null) { + return; + } + + mSignedAuditLogger.log(ILogger.EV_SIGNED_AUDIT, + null, + ILogger.S_SIGNED_AUDIT, + ILogger.LL_SECURITY, + msg); + } +} diff --git a/pki/base/kra/src/com/netscape/kra/RecoveryService.java b/pki/base/kra/src/com/netscape/kra/RecoveryService.java new file mode 100644 index 000000000..39253ab68 --- /dev/null +++ b/pki/base/kra/src/com/netscape/kra/RecoveryService.java @@ -0,0 +1,476 @@ +// --- 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.util.*; +import java.io.*; +import java.net.*; +import java.math.*; +import java.security.*; +import java.security.cert.*; +import java.security.KeyPair; +import netscape.security.util.*; +import netscape.security.pkcs.*; +import netscape.security.x509.*; +import com.netscape.cmscore.util.*; +import com.netscape.certsrv.util.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.base.*; + +import com.netscape.certsrv.dbs.*; +import com.netscape.certsrv.security.*; +import com.netscape.certsrv.kra.*; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.dbs.repository.*; +import com.netscape.certsrv.dbs.keydb.*; +import com.netscape.cmscore.cert.*; +import com.netscape.cmscore.dbs.*; +import com.netscape.cmscore.dbs.*; +import com.netscape.certsrv.request.*; +import com.netscape.certsrv.authentication.*; + +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.asn1.*; +import org.mozilla.jss.crypto.PBEAlgorithm; +import org.mozilla.jss.pkcs12.*; +import org.mozilla.jss.pkix.primitive.*; + + +/** + * A class represents recovery request processor. There + * are 2 types of recovery modes: (1) administrator or + * (2) end-entity. + * <P> + * Administrator recovery will create a PKCS12 file where + * stores the certificate and the recovered key. + * <P> + * End Entity recovery will send RA or CA a response where + * stores the recovered key. + * + * @author thomask + * @version $Revision: 14563 $, $Date: 2007-05-01 10:35:23 -0700 (Tue, 01 May 2007) $ + */ +public class RecoveryService implements IService { + + public static final String ATTR_NICKNAME = "nickname"; + public static final String ATTR_OWNER_NAME = "ownerName"; + public static final String ATTR_SERIALNO = "serialNumber"; + public static final String ATTR_PUBLIC_KEY_DATA = "publicKeyData"; + public static final String ATTR_PRIVATE_KEY_DATA = "privateKeyData"; + public static final String ATTR_TRANSPORT_CERT = "transportCert"; + public static final String ATTR_TRANSPORT_PWD = "transportPwd"; + public static final String ATTR_SIGNING_CERT = "signingCert"; + public static final String ATTR_PKCS12 = "pkcs12"; + public static final String ATTR_ENCRYPTION_CERTS = + "encryptionCerts"; + public static final String ATTR_AGENT_CREDENTIALS = + "agentCredentials"; + // same as encryption certs + public static final String ATTR_USER_CERT = "cert"; + public static final String ATTR_DELIVERY = "delivery"; + + private IKeyRecoveryAuthority mKRA = null; + private IKeyRepository mStorage = null; + private IStorageKeyUnit mStorageUnit = null; + + /** + * Constructs request processor. + */ + public RecoveryService(IKeyRecoveryAuthority kra) { + mKRA = kra; + mStorage = mKRA.getKeyRepository(); + mStorageUnit = mKRA.getStorageKeyUnit(); + } + + /** + * Processes a recovery request. Based on the recovery mode + * (either Administrator or End-Entity), the method reads + * the key record from the database, and tried to recover the + * key with the storage key unit. + * + * @param request recovery request + * @return operation success or not + * @exception EBaseException failed to serve + */ + public boolean serviceRequest(IRequest request) throws EBaseException { + + IStatsSubsystem statsSub = (IStatsSubsystem)CMS.getSubsystem("stats"); + if (statsSub != null) { + statsSub.startTiming("recovery", true /* main action */); + } + + if (Debug.ON) + Debug.trace("KRA services recovery request"); + mKRA.log(ILogger.LL_INFO, "KRA services recovery request"); + + // byte publicKey[] = (byte[])request.get(ATTR_PUBLIC_KEY_DATA); + // X500Name owner = (X500Name)request.get(ATTR_OWNER_NAME); + + Hashtable params = mKRA.getVolatileRequest( + request.getRequestId()); + + if (params == null) { + // possibly we are in recovery mode + return true; + } + + // retrieve based on serial no + BigInteger serialno = request.getExtDataInBigInteger(ATTR_SERIALNO); + + mKRA.log(ILogger.LL_INFO, "KRA reading key record"); + if (statsSub != null) { + statsSub.startTiming("get_key"); + } + KeyRecord keyRecord = (KeyRecord) mStorage.readKeyRecord(serialno); + if (statsSub != null) { + statsSub.endTiming("get_key"); + } + + // see if the certificate matches the key + byte pubData[] = keyRecord.getPublicKeyData(); + X509Certificate x509cert = + request.getExtDataInCert(ATTR_USER_CERT); + byte inputPubData[] = x509cert.getPublicKey().getEncoded(); + + if (inputPubData.length != pubData.length) { + mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_PUBLIC_KEY_LEN")); + throw new EKRAException( + CMS.getUserMessage("CMS_KRA_PUBLIC_KEY_NOT_MATCHED")); + } + for (int i = 0; i < pubData.length; i++) { + if (pubData[i] != inputPubData[i]) { + mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_PUBLIC_KEY_LEN")); + throw new EKRAException( + CMS.getUserMessage("CMS_KRA_PUBLIC_KEY_NOT_MATCHED")); + } + } + + // Unwrap the archived private key + byte privateKeyData[] = null; + X509Certificate transportCert = + request.getExtDataInCert(ATTR_TRANSPORT_CERT); + + if (transportCert == null) { + if (statsSub != null) { + statsSub.startTiming("recover_key"); + } + privateKeyData = recoverKey(params, keyRecord); + if (statsSub != null) { + statsSub.endTiming("recover_key"); + } + + if (statsSub != null) { + statsSub.startTiming("verify_key"); + } + if (verifyKeyPair(pubData, privateKeyData) == false) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_PUBLIC_NOT_FOUND")); + throw new EKRAException( + CMS.getUserMessage("CMS_KRA_INVALID_PUBLIC_KEY")); + } + if (statsSub != null) { + statsSub.endTiming("verify_key"); + } + + if (statsSub != null) { + statsSub.startTiming("create_p12"); + } + createPFX(request, params, privateKeyData); + if (statsSub != null) { + statsSub.endTiming("create_p12"); + } + } else { + + if (CMS.getConfigStore().getBoolean("kra.keySplitting")) { + Credential creds[] = (Credential[]) + params.get(ATTR_AGENT_CREDENTIALS); + mKRA.getStorageKeyUnit().login(creds); + } + if (statsSub != null) { + statsSub.startTiming("unwrap_key"); + } + PrivateKey privateKey = mKRA.getStorageKeyUnit().unwrap( + keyRecord.getPrivateKeyData(), null); + if (statsSub != null) { + statsSub.endTiming("unwrap_key"); + } + + if (CMS.getConfigStore().getBoolean("kra.keySplitting")) { + mKRA.getStorageKeyUnit().logout(); + } + } + mKRA.log(ILogger.LL_INFO, "key " + + serialno.toString() + + " recovered"); + + // for audit log + String authMgr = AuditFormat.NOAUTH; + String initiative = AuditFormat.FROMUSER; + SessionContext sContext = SessionContext.getContext(); + + if (sContext != null) { + String agentId = + (String) sContext.get(SessionContext.USER_ID); + + initiative = AuditFormat.FROMAGENT + " agentID: " + agentId; + AuthToken authToken = (AuthToken) sContext.get(SessionContext.AUTH_TOKEN); + + if (authToken != null) { + authMgr = + authToken.getInString(AuthToken.TOKEN_AUTHMGR_INST_NAME); + } + } + CMS.getLogger().log(ILogger.EV_AUDIT, + ILogger.S_KRA, + AuditFormat.LEVEL, + AuditFormat.FORMAT, + new Object[] { + IRequest.KEYRECOVERY_REQUEST, + request.getRequestId(), + initiative, + authMgr, + "completed", + ((X509CertImpl) x509cert).getSubjectDN(), + "serial number: 0x" + serialno.toString(16)} + ); + + if (statsSub != null) { + statsSub.endTiming("recovery"); + } + + return true; + } + + public boolean verifyKeyPair(byte publicKeyData[], byte privateKeyData[]) + { + try { + DerValue publicKeyVal = new DerValue(publicKeyData); + DerInputStream publicKeyIn = publicKeyVal.data; + publicKeyIn.getSequence(0); + DerValue publicKeyDer = new DerValue(publicKeyIn.getBitString()); + DerInputStream publicKeyDerIn = publicKeyDer.data; + BigInt publicKeyModulus = publicKeyDerIn.getInteger(); + BigInt publicKeyExponent = publicKeyDerIn.getInteger(); + + DerValue privateKeyVal = new DerValue(privateKeyData); + if (privateKeyVal.tag != DerValue.tag_Sequence) + return false; + DerInputStream privateKeyIn = privateKeyVal.data; + privateKeyIn.getInteger(); + privateKeyIn.getSequence(0); + DerValue privateKeyDer = new DerValue(privateKeyIn.getOctetString()); + DerInputStream privateKeyDerIn = privateKeyDer.data; + BigInt privateKeyVersion = privateKeyDerIn.getInteger(); + BigInt privateKeyModulus = privateKeyDerIn.getInteger(); + BigInt privateKeyExponent = privateKeyDerIn.getInteger(); + + if (!publicKeyModulus.equals(privateKeyModulus)) { + CMS.debug("verifyKeyPair modulus mismatch publicKeyModulus=" + publicKeyModulus + " privateKeyModulus=" + privateKeyModulus); + return false; + } + + if (!publicKeyExponent.equals(privateKeyExponent)) { + CMS.debug("verifyKeyPair exponent mismatch publicKeyExponent=" + publicKeyExponent + " privateKeyExponent=" + privateKeyExponent); + return false; + } + + return true; + } catch (Exception e) { + CMS.debug("verifyKeyPair error " + e); + return false; + } + } + + /** + * Recovers key. + */ + public synchronized byte[] recoverKey(Hashtable request, KeyRecord keyRecord) + throws EBaseException { + if (CMS.getConfigStore().getBoolean("kra.keySplitting")) { + Credential creds[] = (Credential[]) + request.get(ATTR_AGENT_CREDENTIALS); + + mStorageUnit.login(creds); + } + mKRA.log(ILogger.LL_INFO, "KRA decrypts internal private"); + byte privateKeyData[] = + mStorageUnit.decryptInternalPrivate( + keyRecord.getPrivateKeyData()); + + if (CMS.getConfigStore().getBoolean("kra.keySplitting")) { + mStorageUnit.logout(); + } + if (privateKeyData == null) { + mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_PRIVATE_KEY_NOT_FOUND")); + throw new EKRAException(CMS.getUserMessage("CMS_KRA_RECOVERY_FAILED_1", "no private key")); + } + return privateKeyData; + } + + /** + * Creates a PFX (PKCS12) file. + * + * @param request CRMF recovery request + * @param priData decrypted private key (PrivateKeyInfo) + * @exception EBaseException failed to create P12 file + */ + public void createPFX(IRequest request, Hashtable params, + byte priData[]) throws EBaseException { + // create p12 + X509Certificate x509cert = + request.getExtDataInCert(ATTR_USER_CERT); + String pwd = (String) params.get(ATTR_TRANSPORT_PWD); + + try { + + // add certificate + mKRA.log(ILogger.LL_INFO, "KRA adds certificate to P12"); + SEQUENCE encSafeContents = new SEQUENCE(); + ASN1Value cert = new OCTET_STRING(x509cert.getEncoded()); + String nickname = request.getExtDataInString(ATTR_NICKNAME); + + if (nickname == null) { + nickname = x509cert.getSubjectDN().toString(); + } + byte localKeyId[] = createLocalKeyId(x509cert); + SET certAttrs = createBagAttrs( + nickname, localKeyId); + // attributes: user friendly name, Local Key ID + SafeBag certBag = new SafeBag(SafeBag.CERT_BAG, + new CertBag(CertBag.X509_CERT_TYPE, cert), + certAttrs); + + encSafeContents.addElement(certBag); + + // add key + mKRA.log(ILogger.LL_INFO, "KRA adds key to P12"); + org.mozilla.jss.util.Password pass = new + org.mozilla.jss.util.Password( + pwd.toCharArray()); + + SEQUENCE safeContents = new SEQUENCE(); + PasswordConverter passConverter = new + PasswordConverter(); + byte salt[] = {0x01, 0x01, 0x01, 0x01}; + PrivateKeyInfo pki = (PrivateKeyInfo) + ASN1Util.decode(PrivateKeyInfo.getTemplate(), + priData); + ASN1Value key = EncryptedPrivateKeyInfo.createPBE( + PBEAlgorithm.PBE_SHA1_DES3_CBC, + pass, salt, 1, passConverter, pki); + SET keyAttrs = createBagAttrs( + x509cert.getSubjectDN().toString(), + localKeyId); + SafeBag keyBag = new SafeBag( + SafeBag.PKCS8_SHROUDED_KEY_BAG, key, + keyAttrs); // ?? + + safeContents.addElement(keyBag); + + // build contents + AuthenticatedSafes authSafes = new + AuthenticatedSafes(); + + authSafes.addSafeContents( + safeContents + ); + authSafes.addSafeContents( + encSafeContents + ); + + // authSafes.addEncryptedSafeContents( + // authSafes.DEFAULT_KEY_GEN_ALG, + // pass, null, 1, + // encSafeContents); + PFX pfx = new PFX(authSafes); + + pfx.computeMacData(pass, null, 5); // ?? + ByteArrayOutputStream fos = new + ByteArrayOutputStream(); + + pfx.encode(fos); + pass.clear(); + + // put final PKCS12 into volatile request + params.put(ATTR_PKCS12, fos.toByteArray()); + } catch (Exception e) { + mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_CONSTRUCT_P12", e.toString())); + throw new EKRAException(CMS.getUserMessage("CMS_KRA_PKCS12_FAILED_1", e.toString())); + } + + // update request + mKRA.getRequestQueue().updateRequest(request); + } + + /** + * Creates local key identifier. + */ + public byte[] createLocalKeyId(X509Certificate cert) + throws EBaseException { + try { + // SHA1 hash of the X509Cert der encoding + byte certDer[] = cert.getEncoded(); + + // XXX - should use JSS + MessageDigest md = MessageDigest.getInstance("SHA"); + + md.update(certDer); + return md.digest(); + } catch (CertificateEncodingException e) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_CREAT_KEY_ID", e.toString())); + throw new EKRAException(CMS.getUserMessage("CMS_KRA_KEYID_FAILED_1", e.toString())); + } catch (NoSuchAlgorithmException e) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_CREAT_KEY_ID", e.toString())); + throw new EKRAException(CMS.getUserMessage("CMS_KRA_KEYID_FAILED_1", e.toString())); + } + } + + /** + * Creates bag attributes. + */ + public SET createBagAttrs(String nickName, byte localKeyId[]) + throws EBaseException { + try { + SET attrs = new SET(); + SEQUENCE nickNameAttr = new SEQUENCE(); + + nickNameAttr.addElement(SafeBag.FRIENDLY_NAME); + SET nickNameSet = new SET(); + + nickNameSet.addElement(new BMPString(nickName)); + nickNameAttr.addElement(nickNameSet); + attrs.addElement(nickNameAttr); + SEQUENCE localKeyAttr = new SEQUENCE(); + + localKeyAttr.addElement(SafeBag.LOCAL_KEY_ID); + SET localKeySet = new SET(); + + localKeySet.addElement(new OCTET_STRING(localKeyId)); + localKeyAttr.addElement(localKeySet); + attrs.addElement(localKeyAttr); + return attrs; + } catch (CharConversionException e) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_CREAT_KEY_BAG", e.toString())); + throw new EKRAException(CMS.getUserMessage("CMS_KRA_KEYBAG_FAILED_1", e.toString())); + } + } +} diff --git a/pki/base/kra/src/com/netscape/kra/StorageKeyUnit.java b/pki/base/kra/src/com/netscape/kra/StorageKeyUnit.java new file mode 100644 index 000000000..d037c9be9 --- /dev/null +++ b/pki/base/kra/src/com/netscape/kra/StorageKeyUnit.java @@ -0,0 +1,962 @@ +// --- 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 org.mozilla.jss.util.Password; +import java.util.*; +import java.io.*; +import com.netscape.certsrv.security.*; +import java.net.*; +import java.security.*; +import java.security.cert.*; +import org.mozilla.jss.crypto.X509Certificate; +import org.mozilla.jss.crypto.TokenCertificate; +import netscape.security.x509.*; +import netscape.security.util.*; +import com.netscape.cmscore.util.*; +//import com.netscape.cmscore.kra.*; +import com.netscape.certsrv.dbs.keydb.*; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.security.*; +import com.netscape.certsrv.kra.*; +import org.mozilla.jss.crypto.PrivateKey; +import org.mozilla.jss.*; +import org.mozilla.jss.asn1.INTEGER; +import org.mozilla.jss.util.*; +import org.mozilla.jss.util.Password; +import org.mozilla.jss.crypto.*; +import com.netscape.cmscore.cert.*; + + +/** + * A class represents a storage key unit. Currently, this + * is implemented with cryptix, the final implementation + * should be built on JSS/HCL. + * + * @author thomask + * @version $Revision: 14563 $, $Date: 2007-05-01 10:35:23 -0700 (Tue, 01 May 2007) $ + */ +public class StorageKeyUnit extends EncryptionUnit implements + ISubsystem, IStorageKeyUnit { + + private IConfigStore mConfig = null; + + // private RSAPublicKey mPublicKey = null; + // private RSAPrivateKey mPrivateKey = null; + + private IConfigStore mStorageConfig = null; + private IKeyRecoveryAuthority mKRA = null; + private String mTokenFile = null; + private X509Certificate mCert = null; + private CryptoManager mManager = null; + private CryptoToken mToken = null; + private PrivateKey mPrivateKey = null; + private byte mPrivateKeyData[] = null; + private boolean mKeySplitting = false; + + + private static final String PROP_N = "n"; + private static final String PROP_M = "m"; + private static final String PROP_UID = "uid"; + private static final String PROP_SHARE = "share"; + private static final String PROP_HARDWARE = "hardware"; + private static final String PROP_LOGOUT = "logout"; + public static final String PROP_NICKNAME = "nickName"; + public static final String PROP_KEYDB = "keydb"; + public static final String PROP_CERTDB = "certdb"; + public static final String PROP_MN = "mn"; + + /** + * Constructs this token. + */ + public StorageKeyUnit() { + super(); + } + + /** + * Retrieves subsystem identifier. + */ + public String getId() { + return "storageKeyUnit"; + } + + /** + * Sets subsystem identifier. Once the system is + * loaded, system identifier cannot be changed + * dynamically. + */ + public void setId(String id) throws EBaseException { + throw new EBaseException(CMS.getUserMessage("CMS_INVALID_OPERATION")); + } + + /** + * return true if byte arrays are equal, false otherwise + */ + private boolean byteArraysMatch(byte a[], byte b[]) { + if (a==null || b==null) { return false; } + if (a.length != b.length) { return false; } + for (int i=0; i<a.length; i++) { + if (a[i] != b[i]) { return false; } + } + return true; + } + + + /** + * Initializes this subsystem. + */ + public void init(ISubsystem owner, IConfigStore config) + throws EBaseException { + mKRA = (IKeyRecoveryAuthority) owner; + mConfig = config; + + mKeySplitting = owner.getConfigStore().getBoolean("keySplitting", false); + + try { + mManager = CryptoManager.getInstance(); + mToken = getToken(); + } catch (org.mozilla.jss.CryptoManager.NotInitializedException e) { + mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_STORAGE_INIT", e.toString())); + throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", e.toString())); + } + + if (mConfig.getString(PROP_HARDWARE, null) != null) { + System.setProperty("cms.skip_token", mConfig.getString(PROP_HARDWARE)); + +// The strategy here is to read all the certs in the token +// and cycle through them until we find one that matches the +// kra-cert.db file + + if (mKeySplitting) { + + byte certFileData[] = null; + try { + File certFile = new File( + mConfig.getString(PROP_CERTDB)); + + certFileData = new byte[ + (Long.valueOf(certFile.length())).intValue()]; + FileInputStream fi = new FileInputStream(certFile); + + fi.read(certFileData); + fi.close(); + + // pick up cert by nickName + + } catch (IOException e) { + mKRA.log(ILogger.LL_INFO, + CMS.getLogMessage("CMSCORE_KRA_STORAGE_READ_CERT", e.toString())); + throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", e.toString())); + } + + try { + X509Certificate certs[] = + getToken().getCryptoStore().getCertificates(); + for (int i=0;i <certs.length;i++) { + if (byteArraysMatch(certs[i].getEncoded(),certFileData)) { + mCert = certs[i]; + } + } + if (mCert == null) { + mKRA.log(ILogger.LL_FAILURE, "Storage Cert could not be initialized. No cert in token matched kra-cert file"); + throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", "mCert == null")); + } else { + mKRA.log(ILogger.LL_INFO, "Using Storage Cert "+mCert.getSubjectDN()); + } + } catch (CertificateEncodingException e) { + mKRA.log(ILogger.LL_FAILURE, "Error encoding cert "); + throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", e.toString())); + } catch (TokenException e) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_STORAGE_READ_CERT", e.toString())); + throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", e.toString())); + } + } + + } else { + + // read certificate from file + byte certData[] = null; + + try { + if (mKeySplitting) { + File certFile = new File( + mConfig.getString(PROP_CERTDB)); + + certData = new byte[ + (Long.valueOf(certFile.length())).intValue()]; + FileInputStream fi = new FileInputStream(certFile); + + fi.read(certData); + fi.close(); + + // pick up cert by nickName + mCert = mManager.findCertByNickname( + config.getString(PROP_NICKNAME)); + + } else { + mCert = mManager.findCertByNickname( + config.getString(PROP_NICKNAME)); + } + } catch (IOException e) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_STORAGE_READ_CERT", e.toString())); + throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", e.toString())); + } catch (TokenException e) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_STORAGE_READ_CERT", e.toString())); + throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", e.toString())); + } catch (ObjectNotFoundException e) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_STORAGE_READ_CERT", e.toString())); + // XXX - this import wont work + try { + mCert = mManager.importCertPackage(certData, + "kraStorageCert"); + } catch (Exception ex) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_STORAGE_IMPORT_CERT", e.toString())); + throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", ex.toString())); + } + } + + if (mKeySplitting) { + // read private key from the file + try { + File priFile = new File(mConfig.getString(PROP_KEYDB)); + + mPrivateKeyData = new byte[ + (Long.valueOf(priFile.length())).intValue()]; + FileInputStream fi = new FileInputStream(priFile); + + fi.read(mPrivateKeyData); + fi.close(); + } catch (IOException e) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_STORAGE_READ_PRIVATE", e.toString())); + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", e.toString())); + } + } + + } + + if (mKeySplitting) { + // open internal data storage configuration + mTokenFile = mConfig.getString(PROP_MN); + try { + // read m, n and no of identifier + mStorageConfig = CMS.createFileConfigStore(mTokenFile); + } catch (EBaseException e) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_STORAGE_READ_MN", + e.toString())); + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_OPERATION")); + + } + } + + try { + if (mCert == null) { + CMS.debug("mCert is null...retrieving "+ config.getString(PROP_NICKNAME)); + mCert = mManager.findCertByNickname( + config.getString(PROP_NICKNAME)); + CMS.debug("mCert = "+mCert); + } + } catch (Exception e) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_STORAGE_READ_CERT", e.toString())); + throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", e.toString())); + } + + } + + /** + * Starts up this subsystem. + */ + public void startup() throws EBaseException { + } + + /** + * Shutdowns this subsystem. + */ + public void shutdown() { + } + + /** + * Returns the configuration store of this token. + */ + public IConfigStore getConfigStore() { + return mConfig; + } + + public static SymmetricKey buildSymmetricKeyWithInternalStorage( + String pin) throws EBaseException { + try { + return buildSymmetricKey(CryptoManager.getInstance().getInternalKeyStorageToken(), pin); + } catch (Exception e) { + return null; + } + } + + /** + * Builds symmetric key from the given password. + */ + public static SymmetricKey buildSymmetricKey(CryptoToken token, + String pin) throws EBaseException { + try { + + Password pass = new Password(pin.toCharArray()); + KeyGenerator kg = null; + + kg = token.getKeyGenerator( + PBEAlgorithm.PBE_SHA1_DES3_CBC); + byte salt[] = {0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01}; + PBEKeyGenParams kgp = new PBEKeyGenParams(pass, + salt, 5); + + pass.clear(); + kg.initialize(kgp); + return kg.generate(); + } catch (TokenException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", + "buildSymmetricKey:" + + e.toString())); + } catch (NoSuchAlgorithmException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", + "buildSymmetricKey:" + + e.toString())); + } catch (InvalidAlgorithmParameterException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", + "buildSymmetricKey:" + + e.toString())); + } catch (CharConversionException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", + "buildSymmetricKey:" + + e.toString())); + } + } + + /** + * Unwraps the storage key with the given symmetric key. + */ + public static PrivateKey unwrapStorageKey(CryptoToken token, + SymmetricKey sk, byte wrapped[], + PublicKey pubKey) + throws EBaseException { + try { + + KeyWrapper wrapper = token.getKeyWrapper( + KeyWrapAlgorithm.DES3_CBC_PAD); + byte iv[] = {0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1}; +/* + org.mozilla.jss.pkcs11.PK11SecureRandom random = + new org.mozilla.jss.pkcs11.PK11SecureRandom(); + random.nextBytes(iv); +*/ + + + wrapper.initUnwrap(sk, new IVParameterSpec(iv)); + + // XXX - it does not like the public key that is + // not a crypto X509Certificate + PrivateKey pk = wrapper.unwrapTemporaryPrivate(wrapped, + PrivateKey.RSA, pubKey); + + return pk; + } catch (TokenException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", + "unwrapStorageKey:" + + e.toString())); + } catch (NoSuchAlgorithmException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", + "unwrapStorageKey:" + + e.toString())); + } catch (InvalidKeyException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", + "unwrapStorageKey:" + + e.toString())); + } catch (InvalidAlgorithmParameterException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", + "unwrapStorageKey:" + + e.toString())); + } + } + + /** + * Used by config-cert. + */ + public static byte[] wrapStorageKey(CryptoToken token, + SymmetricKey sk, PrivateKey pri) + throws EBaseException { + try { + // move public & private to config/storage.dat + // delete private key + KeyWrapper wrapper = token.getKeyWrapper( + KeyWrapAlgorithm.DES3_CBC_PAD); + + // next to randomly generate a symmetric + // password + byte iv[] = {0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1}; + + wrapper.initWrap(sk, new IVParameterSpec(iv)); + return wrapper.wrap(pri); + } catch (TokenException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", + "wrapStorageKey:" + + e.toString())); + } catch (NoSuchAlgorithmException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", + "wrapStorageKey:" + + e.toString())); + } catch (InvalidKeyException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", + "wrapStorageKey:" + + e.toString())); + } catch (InvalidAlgorithmParameterException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", + "wrapStorageKey:" + + e.toString())); + } + } + + /** + * Logins to this token. + */ + public void login(String pin) throws EBaseException { + if (mConfig.getString(PROP_HARDWARE, null) != null) { + try { + getToken().login(new Password(pin.toCharArray())); + PrivateKey pk[] = getToken().getCryptoStore().getPrivateKeys(); + + for (int i = 0; i < pk.length; i++) { + if (arraysEqual(pk[i].getUniqueID(), + ((TokenCertificate) mCert).getUniqueID())) { + mPrivateKey = pk[i]; + } + } + } catch (Exception e) { + mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_STORAGE_LOGIN", e.toString())); + } + + } else { + try { + SymmetricKey sk = buildSymmetricKey(mToken, pin); + + mPrivateKey = unwrapStorageKey(mToken, sk, + mPrivateKeyData, getPublicKey()); + } catch (Exception e) { + mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_STORAGE_LOGIN", e.toString())); + } + if (mPrivateKey == null) { + mPrivateKey = getPrivateKey(); + } + } + } + + /** + * Logins to this token. + */ + public void login(Credential creds[]) + throws EBaseException { + String pwd = constructPassword(creds); + + login(pwd); + } + + /** + * Logout from this token. + */ + public void logout() { + try { + if (mConfig.getString(PROP_HARDWARE, null) != null) { + if (mConfig.getBoolean(PROP_LOGOUT, false)) { + getToken().logout(); + } + } + } catch (Exception e) { + mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_STORAGE_LOGOUT", e.toString())); + + } + mPrivateKey = null; + } + + /** + * Returns a list of recovery agent identifiers. + */ + public Enumeration getAgentIdentifiers() { + Vector v = new Vector(); + + for (int i = 0;; i++) { + try { + String uid = + mStorageConfig.getString(PROP_UID + i); + + if (uid == null) + break; + v.addElement(uid); + } catch (EBaseException e) { + break; + } + } + return v.elements(); + } + + /** + * Changes agent password. + */ + public boolean changeAgentPassword(String id, String oldpwd, + String newpwd) throws EBaseException { + // locate the id(s) + for (int i = 0;; i++) { + try { + String uid = + mStorageConfig.getString(PROP_UID + i); + + if (uid == null) + break; + if (id.equals(uid)) { + byte share[] = decryptShareWithInternalStorage(mStorageConfig.getString(PROP_SHARE + i), oldpwd); + + mStorageConfig.putString(PROP_SHARE + i, + encryptShareWithInternalStorage( + share, newpwd)); + mStorageConfig.commit(false); + return true; + } + } catch (Exception e) { + break; + } + } + return false; + } + + /** + * Changes the m out of n recovery schema. + */ + public boolean changeAgentMN(int new_n, int new_m, + Credential oldcreds[], + Credential newcreds[]) + throws EBaseException { + + if (new_n != newcreds.length) { + throw new EKRAException(CMS.getUserMessage("CMS_KRA_INVALID_N")); + } + + // XXX - verify and construct original password + String secret = constructPassword(oldcreds); + + // XXX - remove extra configuration + for (int j = new_n; j < getNoOfAgents(); j++) { + mStorageConfig.remove(PROP_UID + j); + mStorageConfig.remove(PROP_SHARE + j); + } + + // XXX - split pwd into n pieces + byte shares[][] = new byte[newcreds.length][]; + + IShare s = null; + try { + String className = mConfig.getString("share_class", + "com.netscape.cms.shares.OldShare"); + s = (IShare)Class.forName(className).newInstance(); + } catch (Exception e) { + CMS.debug("Loading Shares error " + e); + } + if (s == null) { + CMS.debug("Share plugin is not found"); + return false; + } + + try { + s.initialize(secret.getBytes(), new_m); + } catch (Exception e) { + CMS.debug("Failed to initialize Share plugin"); + return false; + } + + for (int i = 0; i < newcreds.length; i++) { + byte share[] = s.createShare(i + 1); + + shares[i] = share; + } + + // store the new shares into configuration + mStorageConfig.putInteger(PROP_N, new_n); + mStorageConfig.putInteger(PROP_M, new_m); + for (int i = 0; i < newcreds.length; i++) { + mStorageConfig.putString(PROP_UID + i, + newcreds[i].getIdentifier()); + // use password to encrypt shares... + mStorageConfig.putString(PROP_SHARE + i, + encryptShareWithInternalStorage(shares[i], + newcreds[i].getPassword())); + } + + try { + mStorageConfig.commit(false); + return true; + } catch (EBaseException e) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_STORAGE_CHANGE_MN", e.toString())); + } + return false; + } + + /** + * Returns number of recovery agents. + */ + public int getNoOfAgents() throws EBaseException { + return mStorageConfig.getInteger(PROP_N); + } + + /** + * Returns number of recovery agents required for + * recovery operation. + */ + public int getNoOfRequiredAgents() throws EBaseException { + return mStorageConfig.getInteger(PROP_M); + } + + public CryptoToken getInternalToken() { + try { + return CryptoManager.getInstance().getInternalKeyStorageToken(); + } catch (Exception e) { + return null; + } + } + + public CryptoToken getToken() { + try { + if (mConfig.getString(PROP_HARDWARE, null) != null) { + return mManager.getTokenByName(mConfig.getString(PROP_HARDWARE)); + } else { + return CryptoManager.getInstance().getInternalKeyStorageToken(); + } + } catch (Exception e) { + return null; + } + } + + /** + * Returns the certificate blob. + */ + public PublicKey getPublicKey() { + // NEED to move this key into internal storage token. + return mCert.getPublicKey(); + } + + public PrivateKey getPrivateKey() { + + if (!mKeySplitting) { + try { + PrivateKey pk[] = getToken().getCryptoStore().getPrivateKeys(); + for (int i = 0; i < pk.length; i++) { + if (arraysEqual(pk[i].getUniqueID(), + ((TokenCertificate) mCert).getUniqueID())) { + return pk[i]; + } + } + } catch (TokenException e) { + } + return null; + } else { + return mPrivateKey; + } + } + + /** + * Verifies the integrity of the given key pairs. + */ + public void verify(byte publicKey[], PrivateKey privateKey) + throws EBaseException { + // XXX + } + + public static String encryptShareWithInternalStorage( + byte share[], String pwd) + throws EBaseException { + try { + return encryptShare(CryptoManager.getInstance().getInternalKeyStorageToken(), share, pwd); + } catch (Exception e) { + return null; + } + } + + /** + * Protectes the share with the given password. + */ + public static String encryptShare(CryptoToken token, + byte share[], String pwd) + throws EBaseException { + try { + Cipher cipher = token.getCipherContext( + EncryptionAlgorithm.DES3_CBC_PAD); + SymmetricKey sk = StorageKeyUnit.buildSymmetricKey(token, pwd); + byte iv[] = {0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01}; + + cipher.initEncrypt(sk, new IVParameterSpec(iv)); + byte prev[] = preVerify(share); + byte enc[] = cipher.doFinal(prev); + + // #615387 - cannot use CMS.BtoA because CMS is not present during + // configuration + return com.netscape.osutil.OSUtil.BtoA(enc).trim(); + } catch (NoSuchAlgorithmException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", + e.toString())); + } catch (TokenException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", + e.toString())); + } catch (InvalidKeyException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", + e.toString())); + } catch (InvalidAlgorithmParameterException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", + e.toString())); + } catch (BadPaddingException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", + e.toString())); + } catch (IllegalBlockSizeException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", + e.toString())); + } + } + + public static byte[] preVerify(byte share[]) { + byte data[] = new byte[share.length + 2]; + + data[0] = 0; + data[1] = 0; + for (int i = 0; i < share.length; i++) { + data[2 + i] = share[i]; + } + return data; + } + + public static boolean verifyShare(byte share[]) { + if (share[0] == 0 && share[1] == 0) { + return true; + } else { + return false; + } + } + + public static byte[] postVerify(byte share[]) { + byte data[] = new byte[share.length - 2]; + + for (int i = 2; i < share.length; i++) { + data[i - 2] = share[i]; + } + return data; + } + + public void checkPassword(String userid, String pwd) throws EBaseException { + for (int i = 0;; i++) { + String uid = null; + + try { + uid = mStorageConfig.getString(PROP_UID + i); + if (uid == null) + break; + } catch (Exception e) { + break; + } + if (uid.equals(userid)) { + byte data[] = decryptShareWithInternalStorage( + mStorageConfig.getString(PROP_SHARE + i), + pwd); + if (data == null) { + throw new EBaseException(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); + } + return; + } + } + throw new EBaseException(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); + + } + + public static byte[] decryptShareWithInternalStorage( + String encoding, String pwd) + throws EBaseException { + try { + return decryptShare(CryptoManager.getInstance().getInternalKeyStorageToken(), encoding, pwd); + } catch (Exception e) { + return null; + } + } + + /** + * Decrypts shares with the given password. + */ + public static byte[] decryptShare(CryptoToken token, + String encoding, String pwd) + throws EBaseException { + try { + byte share[] = CMS.AtoB(encoding); + Cipher cipher = token.getCipherContext( + EncryptionAlgorithm.DES3_CBC_PAD); + SymmetricKey sk = StorageKeyUnit.buildSymmetricKey( + token, pwd); + byte iv[] = {0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01}; + + cipher.initDecrypt(sk, new IVParameterSpec(iv)); + byte dec[] = cipher.doFinal(share); + + if (dec == null || !verifyShare(dec)) { + // invalid passwod + throw new EBaseException(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); + } + return postVerify(dec); + } catch (OutOfMemoryError e) { + // XXX - this happens in cipher.doFinal when + // the given share is not valid (the password + // given from the agent is not correct). + // Actulla, cipher.doFinal should return + // something better than this! + // + // e.printStackTrace(); + // + throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_PASSWORD", + e.toString())); + } catch (TokenException e) { + throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_PASSWORD", + e.toString())); + } catch (NoSuchAlgorithmException e) { + throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_PASSWORD", + e.toString())); + } catch (InvalidKeyException e) { + throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_PASSWORD", + e.toString())); + } catch (InvalidAlgorithmParameterException e) { + throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_PASSWORD", + e.toString())); + } catch (IllegalBlockSizeException e) { + throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_PASSWORD", + e.toString())); + } catch (BadPaddingException e) { + throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_PASSWORD", + e.toString())); + } + } + + /** + * Reconstructs password from recovery agents. + */ + private String constructPassword(Credential creds[]) + throws EBaseException { + // sort the credential according to the order in + // configuration file + Hashtable v = new Hashtable(); + + for (int i = 0;; i++) { + String uid = null; + + try { + uid = mStorageConfig.getString(PROP_UID + i); + if (uid == null) + break; + } catch (Exception e) { + break; + } + for (int j = 0; j < creds.length; j++) { + if (uid.equals(creds[j].getIdentifier())) { + byte pwd[] = decryptShareWithInternalStorage( + mStorageConfig.getString( + PROP_SHARE + i), + creds[j].getPassword()); + if (pwd == null) { + throw new EBaseException(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); + } + v.put(Integer.toString(i), pwd); + break; + } + } + } + + if (v.size() < 0) { + throw new EBaseException(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); + } + + if (v.size() != creds.length) { + throw new EBaseException(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); + } + + IJoinShares j = null; + try { + String className = mConfig.getString("joinshares_class", + "com.netscape.cms.shares.OldJoinShares"); + j = (IJoinShares)Class.forName(className).newInstance(); + } catch (Exception e) { + CMS.debug("JoinShares error " + e); + } + if (j == null) { + CMS.debug("JoinShares plugin is not found"); + throw new EBaseException(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); + } + + try { + j.initialize(v.size()); + } catch (Exception e) { + CMS.debug("Failed to initialize JoinShares"); + throw new EBaseException(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); + } + Enumeration e = v.keys(); + + while (e.hasMoreElements()) { + String next = (String) e.nextElement(); + + j.addShare(Integer.parseInt(next) + 1, + (byte[]) v.get(next)); + } + try { + byte secret[] = j.recoverSecret(); + String pwd = new String(secret); + + return pwd; + } catch (Exception ee) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_STORAGE_RECONSTRUCT", e.toString())); + throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_PASSWORD", + ee.toString())); + } + } + + public static boolean arraysEqual(byte[] bytes, byte[] ints) { + if (bytes == null || ints == null) { + return false; + } + + if (bytes.length != ints.length) { + return false; + } + + for (int i = 0; i < bytes.length; i++) { + if (bytes[i] != ints[i]) { + return false; + } + } + return true; + } + +} diff --git a/pki/base/kra/src/com/netscape/kra/TokenKeyRecoveryService.java b/pki/base/kra/src/com/netscape/kra/TokenKeyRecoveryService.java new file mode 100644 index 000000000..daafb8b7f --- /dev/null +++ b/pki/base/kra/src/com/netscape/kra/TokenKeyRecoveryService.java @@ -0,0 +1,532 @@ +// --- 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.util.*; +import java.io.*; +import java.net.*; +import java.math.*; +import java.security.*; +import java.security.cert.*; +import java.security.KeyPair; +import netscape.security.util.*; +import netscape.security.pkcs.*; +import netscape.security.x509.*; +import netscape.security.x509.X500Name; + +import com.netscape.cmscore.util.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.base.*; + +import com.netscape.certsrv.dbs.*; +import com.netscape.certsrv.security.*; +import com.netscape.certsrv.kra.*; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.dbs.repository.*; +import com.netscape.certsrv.dbs.keydb.*; +import com.netscape.cmscore.cert.*; +import com.netscape.cmscore.dbs.*; +import com.netscape.cmscore.dbs.*; +import com.netscape.certsrv.request.*; +import com.netscape.certsrv.authentication.*; +import com.netscape.cmsutil.util.*; + +import org.mozilla.jss.*; +import org.mozilla.jss.crypto.*; +import org.mozilla.jss.util.*; +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.asn1.*; +import org.mozilla.jss.crypto.PBEAlgorithm; +import org.mozilla.jss.pkix.primitive.*; +import org.mozilla.jss.pkcs11.*; + + +/** + * A class represents recovery request processor. + * @author Christina Fu (cfu) + * @version $Revision: 14563 $, $Date: 2007-05-01 10:35:23 -0700 (Tue, 01 May 2007) $ + */ +public class TokenKeyRecoveryService implements IService { + + public static final String ATTR_NICKNAME = "nickname"; + public static final String ATTR_OWNER_NAME = "ownerName"; + public static final String ATTR_PUBLIC_KEY_DATA = "publicKeyData"; + public static final String ATTR_PRIVATE_KEY_DATA = "privateKeyData"; + public static final String ATTR_TRANSPORT_CERT = "transportCert"; + public static final String ATTR_TRANSPORT_PWD = "transportPwd"; + public static final String ATTR_SIGNING_CERT = "signingCert"; + public static final String ATTR_PKCS12 = "pkcs12"; + public static final String ATTR_ENCRYPTION_CERTS = + "encryptionCerts"; + public static final String ATTR_AGENT_CREDENTIALS = + "agentCredentials"; + // same as encryption certs + public static final String ATTR_USER_CERT = "cert"; + public static final String ATTR_DELIVERY = "delivery"; + + private IKeyRecoveryAuthority mKRA = null; + private IKeyRepository mStorage = null; + private IStorageKeyUnit mStorageUnit = null; + private ITransportKeyUnit mTransportUnit = null; + + private final static String + LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST = + "LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_4"; + + private final static String + LOGGING_SIGNED_AUDIT_KEY_RECOVERY_PROCESSED = + "LOGGING_SIGNED_AUDIT_KEY_RECOVERY_PROCESSED_4"; + private ILogger mSignedAuditLogger = CMS.getSignedAuditLogger(); + + /** + * Constructs request processor. + */ + public TokenKeyRecoveryService(IKeyRecoveryAuthority kra) { + mKRA = kra; + mStorage = mKRA.getKeyRepository(); + mStorageUnit = mKRA.getStorageKeyUnit(); + mTransportUnit = kra.getTransportKeyUnit(); + } + + /** + * Process the HTTP request. + * + * @param s The URL to decode + */ + protected String URLdecode(String s) { + if (s == null) + return null; + ByteArrayOutputStream out = new ByteArrayOutputStream(s.length()); + + for (int i = 0; i < s.length(); i++) { + int c = (int) s.charAt(i); + + if (c == '+') { + out.write(' '); + } else if (c == '%') { + int c1 = Character.digit(s.charAt(++i), 16); + int c2 = Character.digit(s.charAt(++i), 16); + + out.write((char) (c1 * 16 + c2)); + } else { + out.write(c); + } + } // end for + return out.toString(); + } + + public static String normalizeCertStr(String s) { + String val = ""; + + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '\\') { + i++; + continue; + } else if (s.charAt(i) == '\\') { + i++; + continue; + } else if (s.charAt(i) == '"') { + continue; + } else if (s.charAt(i) == ' ') { + continue; + } + val += s.charAt(i); + } + return val; + } + + private static String base64Encode(byte[] bytes) throws IOException { + // All this streaming is lame, but Base64OutputStream needs a + // PrintStream + ByteArrayOutputStream output = new ByteArrayOutputStream(); + Base64OutputStream b64 = new Base64OutputStream(new + PrintStream(new + FilterOutputStream(output) + ) + ); + + b64.write(bytes); + b64.flush(); + + // This is internationally safe because Base64 chars are + // contained within 8859_1 + return output.toString("8859_1"); + } + + // this encrypts bytes with a symmetric key + public byte[] encryptIt(byte[] toBeEncrypted, SymmetricKey symKey, CryptoToken token, + IVParameterSpec IV) + { + try { + Cipher cipher = token.getCipherContext( + EncryptionAlgorithm.DES3_CBC_PAD); + + cipher.initEncrypt(symKey, IV); + byte pri[] = cipher.doFinal(toBeEncrypted); + return pri; + } catch (Exception e) { + CMS.debug("initEncrypt() threw exception: "+e.toString()); + return null; + } + + } + + + /** + * Processes a recovery request. The method reads + * the key record from the database, and tries to recover the + * key with the storage key unit. Once recovered, it wraps it + * with desKey + * In the params + * - cert is used for recovery record search + * - cuid may be used for additional validation check + * - userid may be used for additional validation check + * - wrappedDesKey is used for wrapping recovered private key + * + * @param request recovery request + * @return operation success or not + * @exception EBaseException failed to serve + */ + public boolean serviceRequest(IRequest request) throws EBaseException { + String auditMessage = null; + String auditSubjectID = null; + String auditRequesterID = "TPSagent"; + String auditRecoveryID = ILogger.UNIDENTIFIED; + String auditPublicKey = ILogger.UNIDENTIFIED; + + CMS.debug("KRA services token key recovery request"); + + byte[] wrapped_des_key; + + byte iv[] = {0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1}; +/* + org.mozilla.jss.pkcs11.PK11SecureRandom random = + new org.mozilla.jss.pkcs11.PK11SecureRandom(); + random.nextBytes(iv); +*/ + String id = request.getRequestId().toString(); + if (id != null) { + auditRecoveryID = id.trim(); + } + SessionContext sContext = SessionContext.getContext(); + String agentId=""; + if (sContext != null) { + agentId = + (String) sContext.get(SessionContext.USER_ID); + } + + Hashtable params = mKRA.getVolatileRequest( + request.getRequestId()); + + + if (params == null) { + // possibly we are in recovery mode + CMS.debug("getVolatileRequest params null"); + // return true; + } + + wrapped_des_key = null; + + PK11SymKey sk= null; + + String rCUID = request.getExtDataInString(IRequest.NETKEY_ATTR_CUID); + String rUserid = request.getExtDataInString(IRequest.NETKEY_ATTR_USERID); + String rWrappedDesKeyString = request.getExtDataInString(IRequest.NETKEY_ATTR_DRMTRANS_DES_KEY); + auditSubjectID=rCUID+":"+rUserid; + + CMS.debug("TokenKeyRecoveryService: received DRM-trans-wrapped des key ="+rWrappedDesKeyString); + wrapped_des_key = com.netscape.cmsutil.util.Utils.SpecialDecode(rWrappedDesKeyString); + CMS.debug("TokenKeyRecoveryService: wrapped_des_key specialDecoded"); + + if ((wrapped_des_key != null) && + (wrapped_des_key.length > 0)) { + + // unwrap the des key + sk = (PK11SymKey) mTransportUnit.unwrap_encrypt_sym(wrapped_des_key); + + if (sk == null) { + CMS.debug("TokenKeyRecoveryService: no des key"); + request.setExtData(IRequest.RESULT, Integer.valueOf(4)); + } else { + CMS.debug("TokenKeyRecoveryService: received des key"); + } + } else { + CMS.debug("TokenKeyRecoveryService: not receive des key"); + request.setExtData(IRequest.RESULT, Integer.valueOf(4)); + return false; + } + + // retrieve based on Certificate + String cert_s = request.getExtDataInString(ATTR_USER_CERT); + if (cert_s == null) { + CMS.debug("TokenKeyRecoveryService: not receive cert"); + request.setExtData(IRequest.RESULT, Integer.valueOf(3)); + return false; + } + + String cert = normalizeCertStr(cert_s); + java.security.cert.X509Certificate x509cert = null; + try { + x509cert= (java.security.cert.X509Certificate) Cert.mapCert(cert); + if (x509cert == null) { + CMS.debug("cert mapping failed"); + request.setExtData(IRequest.RESULT, Integer.valueOf(5)); + return false; + } + } catch (IOException e) { + CMS.debug("TokenKeyRecoveryService: mapCert failed"); + request.setExtData(IRequest.RESULT, Integer.valueOf(6)); + return false; + } + + try { + /* + CryptoToken internalToken = + CryptoManager.getInstance().getInternalKeyStorageToken(); + */ + CryptoToken token = mStorageUnit.getToken(); + CMS.debug("NetkeyKeygenService: got token slot:"+token.getName()); + IVParameterSpec algParam = new IVParameterSpec(iv); + + Cipher cipher = token.getCipherContext(EncryptionAlgorithm.DES3_CBC_PAD); + + + KeyRecord keyRecord = null; + CMS.debug( "KRA reading key record"); + try { + keyRecord = (KeyRecord) mStorage.readKeyRecord(cert); + if (keyRecord != null) + CMS.debug("read key record"); + else { + CMS.debug("key record not found"); + request.setExtData(IRequest.RESULT, Integer.valueOf(8)); + return false; + } + }catch (Exception e) { + com.netscape.cmscore.util.Debug.printStackTrace(e); + request.setExtData(IRequest.RESULT, Integer.valueOf(9)); + return false; + } + + // see if the owner name matches (cuid:userid) -XXX need make this optional + String owner = keyRecord.getOwnerName(); + CMS.debug("TokenKeyRecoveryService: owner name on record =" +owner); + CMS.debug("TokenKeyRecoveryService: owner name from TPS =" +rCUID+":"+rUserid); + if (owner != null) { + if (owner.equals(rCUID+":"+rUserid)) { + CMS.debug("TokenKeyRecoveryService: owner name matches"); + } else { + CMS.debug("TokenKeyRecoveryService: owner name mismatches"); + } + } + + // see if the certificate matches the key + byte pubData[] = keyRecord.getPublicKeyData(); + byte inputPubData[] = x509cert.getPublicKey().getEncoded(); + + if (inputPubData.length != pubData.length) { + mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_PUBLIC_KEY_LEN")); + throw new EKRAException( + CMS.getUserMessage("CMS_KRA_PUBLIC_KEY_NOT_MATCHED")); + } + + for (int i = 0; i < pubData.length; i++) { + if (pubData[i] != inputPubData[i]) { + mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_PUBLIC_KEY_LEN")); + throw new EKRAException( + CMS.getUserMessage("CMS_KRA_PUBLIC_KEY_NOT_MATCHED")); + } + } + + // Unwrap the archived private key + byte privateKeyData[] = null; + privateKeyData = recoverKey(params, keyRecord); + if (privateKeyData == null) { + request.setExtData(IRequest.RESULT, Integer.valueOf(4)); + CMS.debug("NetkeyKeygenService: failed getting private key"); + return false; + } + CMS.debug("NetkeyKeygenService: got private key...about to verify"); + + /* LunaSA returns data with padding which we need to remove */ + ByteArrayInputStream dis = new ByteArrayInputStream(privateKeyData); + DerValue dv = new DerValue(dis); + byte p[] = dv.toByteArray(); + int l = p.length; + CMS.debug("length different data length=" + l + + " real length=" + privateKeyData.length ); + if (l != privateKeyData.length) { + privateKeyData = p; + } + + if (verifyKeyPair(pubData, privateKeyData) == false) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_PUBLIC_NOT_FOUND")); + throw new EKRAException( + CMS.getUserMessage("CMS_KRA_INVALID_PUBLIC_KEY")); + } else { + CMS.debug("NetkeyKeygenService: private key verified with public key"); + } + + //encrypt and put in private key + cipher.initEncrypt(sk, algParam); + byte wrapped[] = cipher.doFinal(privateKeyData); + + String wrappedPrivKeyString = + com.netscape.cmsutil.util.Utils.SpecialEncode(wrapped); + if (wrappedPrivKeyString == null) { + request.setExtData(IRequest.RESULT, Integer.valueOf(4)); + CMS.debug("NetkeyKeygenService: failed generating wrapped private key"); + return false; + } else { + CMS.debug("NetkeyKeygenService: got private key data wrapped"); + request.setExtData("wrappedUserPrivate", + wrappedPrivKeyString); + request.setExtData(IRequest.RESULT, Integer.valueOf(1)); + CMS.debug( "NetkeyKeygenService: key for " +rCUID+":"+rUserid +" recovered"); + } + + //convert and put in the public key + String b64PKey = base64Encode(pubData); + + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST, + auditSubjectID, + ILogger.SUCCESS, + auditRecoveryID, + b64PKey); + + audit(auditMessage); + + if (b64PKey == null) { + request.setExtData(IRequest.RESULT, Integer.valueOf(4)); + CMS.debug("NetkeyKeygenService: failed getting publickey encoded"); + return false; + } else { + CMS.debug("NetkeyKeygenService: got publicKeyData b64 = "+ + b64PKey); + } + request.setExtData("public_key", b64PKey); + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_KEY_RECOVERY_PROCESSED, + auditSubjectID, + ILogger.SUCCESS, + auditRecoveryID, + agentId); + + audit(auditMessage); + + return true; + + } catch (Exception e) { + CMS.debug("TokenKeyRecoveryService: " + e.toString()); + request.setExtData(IRequest.RESULT, Integer.valueOf(4)); + } + + return true; + } + + public boolean verifyKeyPair(byte publicKeyData[], byte privateKeyData[]) + { + try { + DerValue publicKeyVal = new DerValue(publicKeyData); + DerInputStream publicKeyIn = publicKeyVal.data; + publicKeyIn.getSequence(0); + DerValue publicKeyDer = new DerValue(publicKeyIn.getBitString()); + DerInputStream publicKeyDerIn = publicKeyDer.data; + BigInt publicKeyModulus = publicKeyDerIn.getInteger(); + BigInt publicKeyExponent = publicKeyDerIn.getInteger(); + + DerValue privateKeyVal = new DerValue(privateKeyData); + if (privateKeyVal.tag != DerValue.tag_Sequence) + return false; + DerInputStream privateKeyIn = privateKeyVal.data; + privateKeyIn.getInteger(); + privateKeyIn.getSequence(0); + DerValue privateKeyDer = new DerValue(privateKeyIn.getOctetString()); + DerInputStream privateKeyDerIn = privateKeyDer.data; + BigInt privateKeyVersion = privateKeyDerIn.getInteger(); + BigInt privateKeyModulus = privateKeyDerIn.getInteger(); + BigInt privateKeyExponent = privateKeyDerIn.getInteger(); + + if (!publicKeyModulus.equals(privateKeyModulus)) { + CMS.debug("verifyKeyPair modulus mismatch publicKeyModulus=" + publicKeyModulus + " privateKeyModulus=" + privateKeyModulus); + return false; + } + + if (!publicKeyExponent.equals(privateKeyExponent)) { + CMS.debug("verifyKeyPair exponent mismatch publicKeyExponent=" + publicKeyExponent + " privateKeyExponent=" + privateKeyExponent); + return false; + } + + return true; + } catch (Exception e) { + CMS.debug("verifyKeyPair error " + e); + return false; + } + } + + /** + * Recovers key. + */ + public synchronized byte[] recoverKey(Hashtable request, KeyRecord keyRecord) + throws EBaseException { + /* + Credential creds[] = (Credential[]) + request.get(ATTR_AGENT_CREDENTIALS); + + mStorageUnit.login(creds); + */ + CMS.debug( "KRA decrypts internal private"); + byte privateKeyData[] = + mStorageUnit.decryptInternalPrivate( + keyRecord.getPrivateKeyData()); + /* + mStorageUnit.logout(); + */ + if (privateKeyData == null) { + mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_PRIVATE_KEY_NOT_FOUND")); + throw new EKRAException(CMS.getUserMessage("CMS_KRA_RECOVERY_FAILED_1", "no private key")); + } + return privateKeyData; + } + /** + * Signed Audit Log + *y + * This method is called to store messages to the signed audit log. + * <P> + * + * @param msg signed audit log message + */ + private void audit(String msg) { + // in this case, do NOT strip preceding/trailing whitespace + // from passed-in String parameters + + if (mSignedAuditLogger == null) { + return; + } + + mSignedAuditLogger.log(ILogger.EV_SIGNED_AUDIT, + null, + ILogger.S_SIGNED_AUDIT, + ILogger.LL_SECURITY, + msg); + } + +} diff --git a/pki/base/kra/src/com/netscape/kra/TransportKeyUnit.java b/pki/base/kra/src/com/netscape/kra/TransportKeyUnit.java new file mode 100644 index 000000000..2b852a0ca --- /dev/null +++ b/pki/base/kra/src/com/netscape/kra/TransportKeyUnit.java @@ -0,0 +1,201 @@ +// --- 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 org.mozilla.jss.crypto.Signature; + +import java.util.*; +import com.netscape.certsrv.security.*; +import java.io.*; +import java.net.*; +import java.security.*; +import java.security.cert.*; +import java.security.cert.X509Certificate; +import com.netscape.cmscore.cert.*; +import netscape.security.x509.*; +import netscape.security.provider.*; +import netscape.security.util.*; +import com.netscape.cmscore.util.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.kra.*; +import com.netscape.certsrv.apps.CMS; +import org.mozilla.jss.util.*; +import org.mozilla.jss.crypto.*; +import org.mozilla.jss.*; +import org.mozilla.jss.crypto.PrivateKey; + + +/** + * A class represents the transport key pair. This key pair + * is used to protected EE's private key in transit. + * + * @author thomask + * @version $Revision: 14563 $, $Date: 2007-05-01 10:35:23 -0700 (Tue, 01 May 2007) $ + */ +public class TransportKeyUnit extends EncryptionUnit implements + ISubsystem, ITransportKeyUnit { + + public static final String PROP_NICKNAME = "nickName"; + private byte iv[] = {0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1}; + private IVParameterSpec IV = null; + + // private RSAPublicKey mPublicKey = null; + // private RSAPrivateKey mPrivateKey = null; + private IConfigStore mConfig = null; + private org.mozilla.jss.crypto.X509Certificate mCert = null; + private CryptoManager mManager = null; + + /** + * Constructs this token. + */ + public TransportKeyUnit() { + super(); +/* + org.mozilla.jss.pkcs11.PK11SecureRandom random = + new org.mozilla.jss.pkcs11.PK11SecureRandom(); + random.nextBytes(iv); +*/ + IV = new IVParameterSpec(iv); + } + + /** + * Retrieves subsystem identifier. + */ + public String getId() { + return "transportKeyUnit"; + } + + /** + * Sets subsystem identifier. + */ + public void setId(String id) throws EBaseException { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_OPERATION")); + } + + /** + * Initializes this subsystem. + */ + public void init(ISubsystem owner, IConfigStore config) + throws EBaseException { + mConfig = config; + try { + mManager = CryptoManager.getInstance(); + mCert = mManager.findCertByNickname(getNickName()); + + // #613795 - initialize this; otherwise JSS is not happy + CryptoToken token = getToken(); + SignatureAlgorithm sigalg = + SignatureAlgorithm.RSASignatureWithMD5Digest; + Signature signer = token.getSignatureContext(sigalg); + signer.initSign(getPrivateKey()); + + + } catch (org.mozilla.jss.CryptoManager.NotInitializedException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INTERNAL_ERROR", e.toString())); + + } catch (TokenException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INTERNAL_ERROR", e.toString())); + } catch (ObjectNotFoundException e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INTERNAL_ERROR", e.toString())); + } catch (Exception e) { + throw new EBaseException(CMS.getUserMessage("CMS_BASE_INTERNAL_ERROR", e.toString())); + } + } + + public CryptoToken getInternalToken() { + try { + return CryptoManager.getInstance().getInternalKeyStorageToken(); + } catch (Exception e) { + return null; + } + } + + public CryptoToken getToken() { + // 390148: returning the token that owns the private + // key. + return getPrivateKey().getOwningToken(); + } + + /** + * Starts up this subsystem. + */ + public void startup() throws EBaseException { + } + + /** + * Shutdowns this subsystem. + */ + public void shutdown() { + } + + /** + * Returns the configuration store of this token. + */ + public IConfigStore getConfigStore() { + return mConfig; + } + + public String getNickName() throws EBaseException { + return mConfig.getString(PROP_NICKNAME); + } + + public void setNickName(String str) throws EBaseException { + mConfig.putString(PROP_NICKNAME, str); + } + + /** + * Logins to this token. + */ + public void login(String pin) throws EBaseException { + } + + /** + * Logout from this token. + */ + public void logout() { + } + + /** + * Retrieves public key. + */ + public org.mozilla.jss.crypto.X509Certificate getCertificate() { + return mCert; + } + + public PublicKey getPublicKey() { + return mCert.getPublicKey(); + } + + public PrivateKey getPrivateKey() { + try { + return mManager.findPrivKeyByCert(mCert); + } catch (TokenException e) { + return null; + } catch (ObjectNotFoundException e) { + return null; + } + } + + /** + * Verifies the integrity of the given key pair. + */ + public void verify(byte publicKey[], PrivateKey privateKey) + throws EBaseException { + // XXX + } +} |