/* * Part of the CCNx Java Library. * * Copyright (C) 2008, 2009, 2010 Palo Alto Research Center, Inc. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. * This library 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 * Lesser General Public License for more details. You should have received * a copy of the GNU Lesser General Public License along with this library; * if not, write to the Free Software Foundation, Inc., 51 Franklin Street, * Fifth Floor, Boston, MA 02110-1301 USA. */ package org.ccnx.ccn.io.content; import java.io.IOException; import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import org.ccnx.ccn.CCNHandle; import org.ccnx.ccn.impl.CCNFlowControl.SaveType; import org.ccnx.ccn.impl.encoding.CCNProtocolDTags; import org.ccnx.ccn.impl.encoding.GenericXMLEncodable; import org.ccnx.ccn.impl.encoding.XMLDecoder; import org.ccnx.ccn.impl.encoding.XMLEncodable; import org.ccnx.ccn.impl.encoding.XMLEncoder; import org.ccnx.ccn.impl.security.crypto.CCNDigestHelper; import org.ccnx.ccn.impl.security.crypto.SignatureLocks; import org.ccnx.ccn.impl.security.crypto.jce.AESWrapWithPad; import org.ccnx.ccn.impl.support.DataUtils; import org.ccnx.ccn.impl.support.Log; import org.ccnx.ccn.io.ErrorStateException; import org.ccnx.ccn.protocol.ContentName; import org.ccnx.ccn.protocol.ContentObject; import org.ccnx.ccn.protocol.KeyLocator; import org.ccnx.ccn.protocol.PublisherPublicKeyDigest; /** * A representation of wrapped (encrypted) keys for strorage in CCN. These are used * to transfer symmetric and private keys between users, store them for backup, do * key distribution for access control, and so on. They use standard key wrapping * algorithms, and try to be fairly general. You can use them to wrap almost any * type of key in any other type of key (as long as the latter is capable of * encryption), though use of this class with some key types or key lengths * may require installation of the Java unlimited strength cryptography policy * files to succeed. * * This class automatically handles generation of interstitial nonce keys for wrapping keys * of incompatible lengths -- if you want to wrap a private key in another private key, * it will generate a nonce key, wrap the first key in that nonce key, and that nonce key * in the second private key. * * For now, we have a very loose definition of default -- the default wrap algorithm * depends on the type of key being used to wrap; similarly the default key algorithm * depends on the type of the key being wrapped. We assume that the wrapper and unwrapper * usually know the type of the wrapping key, and can derive the wrapAlgorithm. The * keyAlgorithm is more often necessary to determine how to decode the key once unwrapped * so it is more frequently present. Both are optional. * * If the caller does not specify a wrapping algorithm, a standards-based default is * selected, see documentation for details. * * If the caller specifies values they will be encoded on the wire and decoded on * the other end; defaults will not currently be enforced automatically. This means * equals behavior should be watched closely. */ public class WrappedKey extends GenericXMLEncodable implements XMLEncodable { protected static final String NONCE_KEY_ALGORITHM = "AES"; protected static final int NONCE_KEY_LENGTH = 128; /** * A CCNNetworkObject wrapper around WrappedKey, used for easily saving and retrieving * versioned WrappedKeys to CCN. A typical pattern for using network objects to save * objects that happen to be encodable or serializable is to incorporate such a static * member wrapper class subclassing CCNEncodableObject, CCNSerializableObject, or * CCNNetworkObject itself inside the main class definition. */ public static class WrappedKeyObject extends CCNEncodableObject<WrappedKey> { public WrappedKeyObject(ContentName name, WrappedKey data, SaveType saveType, CCNHandle handle) throws IOException { super(WrappedKey.class, true, name, data, saveType, handle); } public WrappedKeyObject(ContentName name, WrappedKey data, SaveType saveType, PublisherPublicKeyDigest publisher, KeyLocator locator, CCNHandle handle) throws IOException { super(WrappedKey.class, true, name, data, saveType, publisher, locator, handle); } public WrappedKeyObject(ContentName name, CCNHandle handle) throws ContentDecodingException, IOException { super(WrappedKey.class, true, name, (PublisherPublicKeyDigest)null, handle); } public WrappedKeyObject(ContentName name, PublisherPublicKeyDigest publisher, CCNHandle handle) throws ContentDecodingException, IOException { super(WrappedKey.class, true, name, publisher, handle); } public WrappedKeyObject(ContentObject firstBlock, CCNHandle handle) throws ContentDecodingException, IOException { super(WrappedKey.class, true, firstBlock, handle); } public WrappedKey wrappedKey() throws ContentNotReadyException, ContentGoneException, ErrorStateException { return data(); } } private static final Map<String,String> _WrapAlgorithmMap = new HashMap<String,String>(); byte [] _wrappingKeyIdentifier; WrappingKeyName _wrappingKeyName; String _wrapAlgorithm; String _keyAlgorithm; String _label; byte [] _encryptedNonceKey; byte [] _encryptedKey; static { // In Java 1.5, many of these require BouncyCastle. They are typically built in in 1.6. _WrapAlgorithmMap.put("AES", "AESWRAPWITHPAD"); _WrapAlgorithmMap.put("RSA", "RSA/NONE/OAEPWithSHA256AndMGF1Padding"); } /* * Factory methods to build wrapped keys out of their components, wrapping * in the process. For the moment, these use only the default wrapping algorithms. */ /** * Wraps a {symmetric, private} key in another {symmetric, public} key, using standard wrap * algorithm. Does not include an identifier of the wrapping key; that * can be added if necessary using setWrappingKeyIdentifier(Key). * Default wrap algorithm if wrapping key is AES is AESWrap (RFC3394, NIST standard); * this is available in Java 1.6, or BouncyCastle. * * @param keyToBeWrapped The key to wrap, can be symmetric, private, or public. * @param keyAlgorithm optional algorithm to associate with the wrapped key; if null * we use key.getAlgorithm(). * @param keyLabel a friendly name for the key. * @param wrappingKey The key to use for wrapping. This can be a symmetric key or a public key * (as noted above, some public key algorithms may require Java's unlimited strength policy * files). If the wrapping key is a public key, and the wrapped key is a private key (which * may extend past the block length of the public key) we will automatically generate a nonce (random) * AES key, wrap the private key in that, and then wrap that nonce key in the public key. * We derive the wrapping algorithm to use as a function of this key's algorithm. Eventually may * want to allow it to be passed in (merely encrypting with the key as usual may not be the * best key wrap algorithm; keys are high entropy and often require specialized padding schemes). * @throws NoSuchPaddingException * @throws NoSuchAlgorithmException * @throws InvalidKeyException * @throws IllegalBlockSizeException */ public static WrappedKey wrapKey(Key keyToBeWrapped, String keyAlgorithm, String keyLabel, Key wrappingKey) throws InvalidKeyException { String wrappingAlgorithm = wrapAlgorithmForKey(wrappingKey.getAlgorithm()); if (null == wrappingAlgorithm) { // Try this, may not work... wrappingAlgorithm = wrappingKey.getAlgorithm(); } byte [] wrappedNonceKey = null; byte [] wrappedKey = null; if (Log.isLoggable(Level.FINER)) { Log.finer("wrapKey: wrapping key with id {0} under key with id {1} using label {2}", DataUtils.printHexBytes(wrappingKeyIdentifier(keyToBeWrapped)), DataUtils.printHexBytes(wrappingKeyIdentifier(wrappingKey)), keyLabel); } if (wrappingAlgorithm.equalsIgnoreCase("AESWrapWithPad")) { try { wrappedKey = AESWrapWithPad(wrappingKey, keyToBeWrapped); } catch (IllegalBlockSizeException e) { Log.warning("Unexpected IllegalBlockSizeException attempting to instantiate wrapping algorithm."); throw new InvalidKeyException("Unexpected IllegalBlockSizeException attempting to instantiate wrapping algorithm"); } } else { Cipher wrapCipher = null; try { wrapCipher = Cipher.getInstance(wrappingAlgorithm); if (Log.isLoggable(Level.INFO)) { Log.info("Wrap cipher {0}, provider {1}.", wrapCipher.getAlgorithm(), wrapCipher.getProvider()); } } catch (NoSuchAlgorithmException e) { Log.warning("Unexpected NoSuchAlgorithmException attempting to instantiate wrapping algorithm: "+ wrappingAlgorithm); throw new InvalidKeyException("Unexpected NoSuchAlgorithmException attempting to instantiate wrapping algorithm: "+ wrappingAlgorithm); } catch (NoSuchPaddingException e) { Log.warning("Unexpected NoSuchPaddingException attempting to instantiate wrapping algorithm: "+ wrappingAlgorithm); throw new InvalidKeyException("Unexpected NoSuchPaddingException attempting to instantiate wrapping algorithm: "+ wrappingAlgorithm); } wrapCipher.init(Cipher.WRAP_MODE, wrappingKey); // If we are dealing with a short-block cipher, like RSA, we need to // interpose a nonce key. Key nonceKey = null; try { int wrappedKeyType = getCipherType(keyToBeWrapped.getAlgorithm()); // If we're wrapping a private key in a public key, need to handle multi-block // keys. Probably don't want to do that with ECB mode. ECIES already acts // as a hybrid cipher, so we don't need to do this for that. if (((Cipher.PRIVATE_KEY == wrappedKeyType) || (Cipher.PUBLIC_KEY == wrappedKeyType)) && (wrappingKey instanceof PublicKey) && (!wrappingKey.getAlgorithm().equals("ECIES"))) { nonceKey = generateNonceKey(); //try { // We know the nonce key is an AES key. Use standard wrap algorithm. // DKS -- fix when have provider. //Cipher nonceCipher = Cipher.getInstance(wrapAlgorithmForKey(nonceKey.getAlgorithm())); //nonceCipher.init(Cipher.WRAP_MODE, nonceKey); //wrappedKey = nonceCipher.wrap(keyToBeWrapped); wrappedKey = AESWrapWithPad(nonceKey, keyToBeWrapped); //} catch (NoSuchAlgorithmException nsex) { // Log.warning("Configuration error: Unknown default nonce key algorithm: " + NONCE_KEY_ALGORITHM); // Log.warningStackTrace(nsex); // throw new RuntimeException("Configuration error: Unknown default nonce key algorithm: " + NONCE_KEY_ALGORITHM); //} wrappedNonceKey = wrapCipher.wrap(nonceKey); } else { wrappedKey = wrapCipher.wrap(keyToBeWrapped); } } catch (IllegalBlockSizeException ex) { Log.warning("IllegalBlockSizeException " + ex.getMessage() + " in wrap key -- unexpected, we should have compensated for this. Key to be wrapped algorithm? " + keyToBeWrapped.getAlgorithm() + ". Using nonce key? " + (null == nonceKey)); throw new InvalidKeyException("IllegalBlockSizeException " + ex.getMessage() + " in wrap key -- unexpected, we should have compensated for this. Key to be wrapped algorithm? " + keyToBeWrapped.getAlgorithm() + ". Using nonce key? " + (null == nonceKey)); } } // Default wrapping algorithm is being used, don't need to include it. WrappedKey wk = new WrappedKey(null, null, ((null == keyAlgorithm) ? keyToBeWrapped.getAlgorithm() : keyAlgorithm), keyLabel, wrappedNonceKey, wrappedKey); if (Log.isLoggable(Level.FINER)) { Log.finer("wrapKey: got {0} by wrapping {1} with {2}", wk, DataUtils.printHexBytes(keyToBeWrapped.getEncoded()), DataUtils.printHexBytes(wrappingKey.getEncoded())); } return wk; } /** * Represent an already-wrapped key as a WrappedKey. * @param wrappingKeyIdentifier a byte ID for this key, usually the digest of the encoded key. * @param encryptedKey the wrapped, encoded key. */ public WrappedKey(byte [] wrappingKeyIdentifier, byte [] encryptedKey) { this(wrappingKeyIdentifier, null, null, null, null, encryptedKey); } /** * Represent an already-wrapped key as a WrappedKey. * @param wrappingKeyIdentifier a byte ID for this key, usually the digest of the encoded key. * @param label a friendly name for the key. * @param encryptedKey the wrapped, encoded key. */ public WrappedKey(byte [] wrappingKeyIdentifier, String label, byte [] encryptedKey) { this(wrappingKeyIdentifier, null, null, label, null, encryptedKey); } /** * Represent an already-wrapped key as a WrappedKey. * @param wrappingKeyIdentifier a byte ID for this key, usually the digest of the encoded key. * @param wrapAlgorithm the algorithm used to wrap this key, if null the default wrap algorithm for * the decryption key (specified to unwrapKey()) is used * @param keyAlgorithm the algorithm of the wrapped (encrypted, encoded) key. Necessary to decode * the key back into a Key object as part of unwrapping. Can be specified at unwrapping time if * not stored here. * @param label a friendly name for the key. * @param encryptedKey the wrapped, encoded key. */ public WrappedKey(byte [] wrappingKeyIdentifier, String wrapAlgorithm, String keyAlgorithm, String label, byte [] encryptedKey) { this(wrappingKeyIdentifier, wrapAlgorithm, keyAlgorithm, label, null, encryptedKey); } /** * Represent an already-wrapped key as a WrappedKey. * @param wrappingKeyIdentifier a byte ID for this key, usually the digest of the encoded key. * @param wrapAlgorithm the algorithm used to wrap this key, if null the default wrap algorithm for * the decryption key (specified to unwrapKey()) is used * @param keyAlgorithm the algorithm of the wrapped (encrypted, encoded) key. Necessary to decode * the key back into a Key object as part of unwrapping. Can be specified at unwrapping time if * not stored here. * @param label a friendly name for the key. * @param encryptedNonceKey if the key is a private or public key wrapped by a public key, this defines * an encrypted interposed nonce key where the nonce key is used to wrap the private or public * key and the wrapping key is used to wrap the nonce key. * @param encryptedKey the wrapped, encoded key. */ public WrappedKey(byte [] wrappingKeyIdentifier, String wrapAlgorithm, String keyAlgorithm, String label, byte [] encryptedNonceKey, byte [] encryptedKey) { _wrappingKeyIdentifier = wrappingKeyIdentifier; _wrapAlgorithm = wrapAlgorithm; _keyAlgorithm = keyAlgorithm; _label = label; _encryptedNonceKey = encryptedNonceKey; _encryptedKey = encryptedKey; } /** * Empty constructor for decoding. */ public WrappedKey() { } /** * Unwraps an encrypted key, and decodes it into a Key of an algorithm type * specified by keyAlgorithm(). See unwrapKey(Key, String) for details. * @throws NoSuchAlgorithmException * @throws * @see unwrapKey(Key, String) **/ public Key unwrapKey(Key unwrapKey) throws InvalidKeyException, NoSuchAlgorithmException { if (null == keyAlgorithm()) { throw new NoSuchAlgorithmException("Null algorithm specified for key to be unwrapped!"); } byte [] wki = wrappingKeyIdentifier(unwrapKey); if (Log.isLoggable(Level.INFO)) { Log.info("WrappedKey: unwrapping key wrapped with wrapping key ID {0}, incoming wrapping key digest {1} match? {2}", DataUtils.printHexBytes(wrappingKeyIdentifier()), DataUtils.printHexBytes(wki), Arrays.equals(wki, wrappingKeyIdentifier())); } return unwrapKey(unwrapKey, keyAlgorithm()); } /** * Unwraps an encrypted key, and decodes it into a Key of an algorithm type * specified by wrappedKeyAlgorithm. * @param unwrapKey the key to use to decrypt this wrapped key. * @param wrappedKeyAlgorithm the algorithm of the wrapped key, used in decoding it. * @return the decrypted key if successful. * @throws InvalidKeyException if we encounter an error using the unwrapKey to decrypt. * @throws NoSuchAlgorithmException if we do not recognize the wrappedKeyAlgorithm. **/ public Key unwrapKey(Key unwrapKey, String wrappedKeyAlgorithm) throws InvalidKeyException, NoSuchAlgorithmException { Key unwrappedKey; if (Log.isLoggable(Level.INFO)) { Log.info("wrap algorithm: " + wrapAlgorithm() + " wa for key " + wrapAlgorithmForKey(unwrapKey.getAlgorithm())); Log.info("unwrapKey: unwrapping {0} with {1}", this, DataUtils.printHexBytes(wrappingKeyIdentifier(unwrapKey))); } if (((null != wrapAlgorithm()) && (wrapAlgorithm().equalsIgnoreCase("AESWrapWithPad"))) || wrapAlgorithmForKey(unwrapKey.getAlgorithm()).equalsIgnoreCase("AESWrapWithPad")) { unwrappedKey = AESUnwrapWithPad(unwrapKey, wrappedKeyAlgorithm, encryptedKey(), 0, encryptedKey().length); } else { Cipher unwrapCipher = null; try { if (null != wrapAlgorithm()) { unwrapCipher = Cipher.getInstance(wrapAlgorithm()); } else { unwrapCipher = Cipher.getInstance(wrapAlgorithmForKey(unwrapKey.getAlgorithm())); } } catch (NoSuchAlgorithmException e) { Log.warning("Unexpected NoSuchAlgorithmException attempting to instantiate wrapping algorithm."); throw new InvalidKeyException("Unexpected NoSuchAlgorithmException attempting to instantiate wrapping algorithm."); } catch (NoSuchPaddingException e) { Log.warning("Unexpected NoSuchPaddingException attempting to instantiate wrapping algorithm."); throw new InvalidKeyException("Unexpected NoSuchPaddingException attempting to instantiate wrapping algorithm"); } unwrapCipher.init(Cipher.UNWRAP_MODE, unwrapKey); int keyType = getCipherType(wrappedKeyAlgorithm); if (null != encryptedNonceKey()) { try { Key nonceKey = null; // Cope with GC memory corruption SignatureLocks.unwrapLock(); try { nonceKey = unwrapCipher.unwrap(encryptedNonceKey(), NONCE_KEY_ALGORITHM, Cipher.SECRET_KEY); } finally { SignatureLocks.unwrapUnock(); } //Cipher nonceKeyCipher = Cipher.getInstance(wrapAlgorithmForKey(NONCE_KEY_ALGORITHM)); //nonceKeyCipher.init(Cipher.UNWRAP_MODE, nonceKey); //unwrappedKey = nonceKeyCipher.unwrap(encryptedKey(), wrappedKeyAlgorithm, keyType); unwrappedKey = AESUnwrapWithPad(nonceKey, wrappedKeyAlgorithm, encryptedKey(), 0, encryptedKey().length); } catch(NoSuchAlgorithmException nsex) { Log.warning("Configuration error: Unknown default nonce key algorithm: " + NONCE_KEY_ALGORITHM); Log.warningStackTrace(nsex); throw new RuntimeException("Configuration error: Unknown default nonce key algorithm: " + NONCE_KEY_ALGORITHM); } } else { // Cope with GC memory corruption SignatureLocks.unwrapLock(); try { unwrappedKey = unwrapCipher.unwrap(encryptedKey(), wrappedKeyAlgorithm, keyType); } finally { SignatureLocks.unwrapUnock(); } } } return unwrappedKey; } /** * * @return the wrappingKeyIdentfier for this object */ public byte [] wrappingKeyIdentifier() { return _wrappingKeyIdentifier; } /** * Sets the wrappingKeyIdentifier * @param wrappingKeyIdentifier new identifier */ public void setWrappingKeyIdentifier(byte [] wrappingKeyIdentifier) { _wrappingKeyIdentifier = wrappingKeyIdentifier; } /** * Sets the wrappingKeyIdentifier * @param wrappingKey key from which to generate the new identifier */ public void setWrappingKeyIdentifier(Key wrappingKey) { setWrappingKeyIdentifier(wrappingKeyIdentifier(wrappingKey)); } /** * Calculate the wrappingKeyIdentifier corresponding to this key * @param wrappingKey the key * @return the identifier */ public static byte [] wrappingKeyIdentifier(Key wrappingKey) { return CCNDigestHelper.digest(wrappingKey.getEncoded()); } /** * @return the wrappingKeyName if specified */ public ContentName wrappingKeyName() { return _wrappingKeyName; } /** * Set the wrappingKeyName * @param keyName the new name */ public void setWrappingKeyName(ContentName keyName) { _wrappingKeyName = new WrappingKeyName(keyName); } /** * Returns the wrapping algorithm identifier, if specified * @return the wrap algorithm */ public String wrapAlgorithm() { return _wrapAlgorithm; } /** * Returns the key algorithm identifier, if specified * @return the key algorithm */ public String keyAlgorithm() { return _keyAlgorithm; } /** * Returns the label if we have one * @return the label */ public String label() { return _label; } /** * Returns the encrypted nonce key if we have one. * @return the encryptedNonceKey, if one is present */ public byte [] encryptedNonceKey() { return _encryptedNonceKey; } /** * Get the encrypted key * @return the encrypted key */ public byte [] encryptedKey() { return _encryptedKey; } @Override public void decode(XMLDecoder decoder) throws ContentDecodingException { decoder.readStartElement(getElementLabel()); if (decoder.peekStartElement(CCNProtocolDTags.WrappingKeyIdentifier)) { _wrappingKeyIdentifier = decoder.readBinaryElement(CCNProtocolDTags.WrappingKeyIdentifier); } if (decoder.peekStartElement(CCNProtocolDTags.WrappingKeyName)) { _wrappingKeyName = new WrappingKeyName(); _wrappingKeyName.decode(decoder); } if (decoder.peekStartElement(CCNProtocolDTags.WrapAlgorithm)) { _wrapAlgorithm = decoder.readUTF8Element(CCNProtocolDTags.WrapAlgorithm); } if (decoder.peekStartElement(CCNProtocolDTags.KeyAlgorithm)) { _keyAlgorithm = decoder.readUTF8Element(CCNProtocolDTags.KeyAlgorithm); } if (decoder.peekStartElement(CCNProtocolDTags.Label)) { _label = decoder.readUTF8Element(CCNProtocolDTags.Label); } if (decoder.peekStartElement(CCNProtocolDTags.EncryptedNonceKey)) { _encryptedNonceKey = decoder.readBinaryElement(CCNProtocolDTags.EncryptedNonceKey); } _encryptedKey = decoder.readBinaryElement(CCNProtocolDTags.EncryptedKey); decoder.readEndElement(); } @Override public void encode(XMLEncoder encoder) throws ContentEncodingException { if (!validate()) { throw new ContentEncodingException("Cannot encode " + this.getClass().getName() + ": field values missing."); } encoder.writeStartElement(getElementLabel()); if (null != wrappingKeyIdentifier()) { // needs to handle null WKI encoder.writeElement(CCNProtocolDTags.WrappingKeyIdentifier, wrappingKeyIdentifier()); } if (null != wrappingKeyName()) { wrappingKeyName().encode(encoder); } if (null != wrapAlgorithm()) { //String wrapOID = OIDLookup.getCipherOID(wrapAlgorithm()); encoder.writeElement(CCNProtocolDTags.WrapAlgorithm, wrapAlgorithm()); } if (null != keyAlgorithm()) { //String keyOID = OIDLookup.getCipherOID(keyAlgorithm()); encoder.writeElement(CCNProtocolDTags.KeyAlgorithm, keyAlgorithm()); } if (null != label()) { encoder.writeElement(CCNProtocolDTags.Label, label()); } if (null != encryptedNonceKey()) { encoder.writeElement(CCNProtocolDTags.EncryptedNonceKey, encryptedNonceKey()); } encoder.writeElement(CCNProtocolDTags.EncryptedKey, encryptedKey()); encoder.writeEndElement(); } @Override public long getElementLabel() { return CCNProtocolDTags.WrappedKey; } @Override public boolean validate() { // Only mandatory component is the encrypted key. return ((null != _encryptedKey) && (_encryptedKey.length > 0)); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + Arrays.hashCode(_encryptedKey); result = prime * result + Arrays.hashCode(_encryptedNonceKey); result = prime * result + ((_keyAlgorithm == null) ? 0 : _keyAlgorithm.hashCode()); result = prime * result + ((_label == null) ? 0 : _label.hashCode()); result = prime * result + ((_wrapAlgorithm == null) ? 0 : _wrapAlgorithm.hashCode()); result = prime * result + Arrays.hashCode(_wrappingKeyIdentifier); result = prime * result + ((_wrappingKeyName == null) ? 0 : _wrappingKeyName.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; WrappedKey other = (WrappedKey) obj; if (!Arrays.equals(_encryptedKey, other._encryptedKey)) return false; if (!Arrays.equals(_encryptedNonceKey, other._encryptedNonceKey)) return false; if (_keyAlgorithm == null) { if (other._keyAlgorithm != null) return false; } else if (!_keyAlgorithm.equals(other._keyAlgorithm)) return false; if (_label == null) { if (other._label != null) return false; } else if (!_label.equals(other._label)) return false; if (_wrapAlgorithm == null) { if (other._wrapAlgorithm != null) return false; } else if (!_wrapAlgorithm.equals(other._wrapAlgorithm)) return false; if (!Arrays .equals(_wrappingKeyIdentifier, other._wrappingKeyIdentifier)) return false; if (_wrappingKeyName == null) { if (other._wrappingKeyName != null) return false; } else if (!_wrappingKeyName.equals(other._wrappingKeyName)) return false; return true; } @Override public String toString() { return "WrappedKey: wrapping key ID: " + DataUtils.printHexBytes(_wrappingKeyIdentifier) + " wrapping key name: " + _wrappingKeyName + " wrap algorithm: " + _wrapAlgorithm + " key algorithm: " + _keyAlgorithm + " label: " + _label + " has nonce key? " + (null != _encryptedNonceKey); } public static int getCipherType(String cipherAlgorithm) { if (cipherAlgorithm.equalsIgnoreCase("ECIES") || cipherAlgorithm.equalsIgnoreCase("EC") || cipherAlgorithm.equalsIgnoreCase("RSA") || cipherAlgorithm.equalsIgnoreCase("ElGamal") || cipherAlgorithm.equalsIgnoreCase("DH") || cipherAlgorithm.equalsIgnoreCase("DSA")) { return Cipher.PRIVATE_KEY; // right now assume we don't wrap public keys } return Cipher.SECRET_KEY; } /** * Convert a given wrapping key algorithm to the default wrap algorithm for * using that key. * @param keyAlgorithm * @return */ public static String wrapAlgorithmForKey(String keyAlgorithm) { String wrapAlgorithm = _WrapAlgorithmMap.get(keyAlgorithm); if (null == wrapAlgorithm) { // punt return keyAlgorithm; } return wrapAlgorithm; } public static Key generateNonceKey() { KeyGenerator kg; try { kg = KeyGenerator.getInstance(NONCE_KEY_ALGORITHM); kg.init(NONCE_KEY_LENGTH); Key nk = kg.generateKey(); return nk; } catch (NoSuchAlgorithmException e) { Log.warning("Configuration error: Unknown default nonce key algorithm: " + NONCE_KEY_ALGORITHM); Log.warningStackTrace(e); throw new RuntimeException("Configuration error: Unknown default nonce key algorithm: " + NONCE_KEY_ALGORITHM); } } /** * Wrap using AES. Do not use standard Cipher interface, as we need to use an alternate algorithm * (see AESWrapWithPadEngine) that is not currently included in any signed provider. Once it * is, we will drop this special-case code. * @param wrappingKey key to use to encrypt * @param keyToBeWrapped key to encrypt * @return encrypted key. * @throws IllegalBlockSizeException * @throws InvalidKeyException */ protected static byte [] AESWrapWithPad(Key wrappingKey, Key keyToBeWrapped) throws InvalidKeyException, IllegalBlockSizeException { if (! wrappingKey.getAlgorithm().equals("AES")) { throw new IllegalArgumentException("AES wrap must wrap with with an AES key."); } AESWrapWithPad engine = new AESWrapWithPad(); return engine.wrap(wrappingKey, keyToBeWrapped); } /** * Unwrap using AES. Do not use standard Cipher interface, as we need to use an alternate algorithm * (see AESWrapWithPadEngine) that is not currently included in any signed provider. Once it * is, we will drop this special-case code. * @param unwrappingKey key to use to decrypt * @param wrappedKeyAlgorithm algorithm to use to decode key once decrypted. * @param input encrypted key to decrypt and decode * @param offset offset into encrypted data buffer * @param length length of data to decrypt. * @return decrypted, decoded key. */ protected static Key AESUnwrapWithPad(Key unwrappingKey, String wrappedKeyAlgorithm, byte [] input, int offset, int length) throws InvalidKeyException { if (! unwrappingKey.getAlgorithm().equals("AES")) { throw new IllegalArgumentException("AES wrap must unwrap with with an AES key."); } AESWrapWithPad engine = new AESWrapWithPad(); if ((offset != 0) || (length != input.length)) { byte [] tmpbuf = new byte[length]; System.arraycopy(input, offset, tmpbuf, 0, length); input = tmpbuf; } try { return engine.unwrap(unwrappingKey, input, wrappedKeyAlgorithm); } catch (NoSuchAlgorithmException e) { // engine.unwrap only throws NoSuchAlgorithmException in older versions of BouncyCastle. // Newer versions do exactly what we're doing here; add it here manually to allow // compatibility with multiple BC versions. throw new InvalidKeyException("Unknown key type " + e.getMessage()); } } }