/* This code is part of Freenet. It is distributed under the GNU General * Public License, version 2 (or at your option any later version). See * http://www.gnu.org/ for further details of the GPL. */ package freenet.crypt; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import freenet.node.NodeStarter; import freenet.support.Fields; /** * KeyGenUtils offers a set of methods to easily generate Keys and KeyPairs for * specific algorithms as well as for generating IVs and nonces. It will also take * keys stored in byte arrays and put them in SecretKey or KeyPair instances. * @author unixninja92 * */ public final class KeyGenUtils { /** * Generates a public/private key pair formatted for the algorithm specified * and stores the keys in a KeyPair. Can not handle DSA keys. * @param type The algorithm format that the key pair should be generated for. * @return Returns the generated key pair */ public static KeyPair genKeyPair(KeyPairType type) { if(type.equals(KeyPairType.DSA)){ throw new UnsupportedTypeException(type); } try { KeyPairGenerator kg = KeyPairGenerator.getInstance(type.alg); kg.initialize(type.spec); return kg.generateKeyPair(); } catch (NoSuchAlgorithmException e) { throw new Error(e); // Impossible? } catch (InvalidAlgorithmParameterException e) { throw new Error(e); // Impossible? } } /** * Converts a specified key for a specified algorithm to a PublicKey. Can not handle DSA keys. * @param type The type of key being passed in * @param pub Public key as byte[] * @return Public key as PublicKey */ public static PublicKey getPublicKey(KeyPairType type, byte[] pub){ if(type.equals(KeyPairType.DSA)){ throw new UnsupportedTypeException(type); } try { KeyFactory kf = KeyFactory.getInstance(type.alg); X509EncodedKeySpec xks = new X509EncodedKeySpec(pub); return kf.generatePublic(xks); } catch (NoSuchAlgorithmException e) { throw new Error(e); // Impossible? } catch (InvalidKeySpecException e) { throw new IllegalArgumentException(e); // Indicates passed in key is bogus } } /** * Converts a specified key for a specified algorithm to a PublicKey. Can not handle DSA keys. * @param type The type of key being passed in * @param pub Public key as ByteBuffer * @return Public key as PublicKey */ public static PublicKey getPublicKey(KeyPairType type, ByteBuffer pub){ return getPublicKey(type, Fields.copyToArray(pub)); } /** * Converts a specified key for a specified algorithm to a PublicKey which is then stored in * a KeyPair. The private key of the KeyPair is null. Can not handle DSA keys. * @param type The type of key being passed in * @param pub Public key as byte[] * @return Public key as KeyPair with a null private key */ public static KeyPair getPublicKeyPair(KeyPairType type, byte[] pub) { return getKeyPair(getPublicKey(type, pub), null); } /** * Converts a specified key for a specified algorithm to a PublicKey which is then stored in * a KeyPair. The private key of the KeyPair is null. Can not handle DSA keys. * @param type The type of key being passed in * @param pub Public key as ByteBuffer * @return Public key as KeyPair with a null private key */ public static KeyPair getPublicKeyPair(KeyPairType type, ByteBuffer pub) { return getPublicKeyPair(type, Fields.copyToArray(pub)); } /** * Converts the specified keys for a specified algorithm to PrivateKey and PublicKey * respectively. These are then placed in a KeyPair. Can not handle DSA keys. * @param type The type of key being passed in * @param pub Public key as byte[] * @param pri Private key as byte[] * @return The public key and private key in a KeyPair */ public static KeyPair getKeyPair(KeyPairType type, byte[] pub, byte[] pri) { if(type.equals(KeyPairType.DSA)){ throw new UnsupportedTypeException(type); } try { KeyFactory kf = KeyFactory.getInstance(type.alg); PublicKey pubK = getPublicKey(type, pub); PKCS8EncodedKeySpec pks = new PKCS8EncodedKeySpec(pri); PrivateKey privK = kf.generatePrivate(pks); // FIXME verify that the keys are consistent if assertions/logging enabled?? return getKeyPair(pubK, privK); } catch (UnsupportedTypeException e) { throw new Error(e); // Should be impossible } catch (NoSuchAlgorithmException e) { throw new Error(e); // Should be impossible } catch (InvalidKeySpecException e) { throw new IllegalArgumentException(e); } } /** * Converts the specified keys for a specified algorithm to PrivateKey and PublicKey * respectively. These are then placed in a KeyPair. Can not handle DSA keys. * @param type The type of key being passed in * @param pub Public key as ByteBuffer * @param pri Private key as ByteBuffer * @return The public key and private key in a KeyPair */ public static KeyPair getKeyPair(KeyPairType type, ByteBuffer pub, ByteBuffer pri) { return getKeyPair(type, Fields.copyToArray(pub), Fields.copyToArray(pri)); } /** * Takes the PublicKey and PrivateKey and stores them in a KeyPair * @param pubK Public key as PublicKey * @param privK Private key as PrivateKey * @return The public key and private key in a KeyPair */ public static KeyPair getKeyPair(PublicKey pubK, PrivateKey privK){ return new KeyPair(pubK, privK); } /** * Generates a secret key for the specified symmetric algorithm * @param type Type of key to generate * @return Generated key */ public static SecretKey genSecretKey(KeyType type){ try{ KeyGenerator kg = KeyGenerator.getInstance(type.alg); kg.init(type.keySize); return kg.generateKey(); } catch (NoSuchAlgorithmException e) { throw new Error(e); // Impossible? } } /** * Converts the specified key into a SecretKey for the specified algorithm. Checks the length of * the key to make sure it is correct. HMAC does not have a set key length, so any key is * acceptable when using a key of this type. * @param key The byte[] of the key * @param type Type of key * @return The key as a SecretKey */ public static SecretKey getSecretKey(KeyType type, byte[] key){ if(!type.name().startsWith("HMAC") && key.length != type.keySize >> 3){ throw new IllegalArgumentException("Key size does not match KeyType"); } return new SecretKeySpec(key, type.alg); } /** * Converts the specified key into a SecretKey for the specified algorithm * @param key The ByteBuffer of the key * @param type Type of key * @return The key as a SecretKey */ public static SecretKey getSecretKey(KeyType type, ByteBuffer key){ return getSecretKey(type, Fields.copyToArray(key)); } /** * Generates a random byte[] of a specified length * @param length How long the byte[] should be * @return The randomly generated byte[] */ private static byte[] genRandomBytes(int length){ byte[] randBytes = new byte[length]; NodeStarter.getGlobalSecureRandom().nextBytes(randBytes); return randBytes; } /** * Generates a random nonce of a specified length * @param length How long the nonce should be * @return The randomly generated nonce */ public static ByteBuffer genNonce(int length){ return ByteBuffer.wrap(genRandomBytes(length)); } /** * Generates a random iv of a specified length * @param length How long the iv should be in bytes * @return The randomly generated iv */ public static IvParameterSpec genIV(int length){ return new IvParameterSpec(genRandomBytes(length)); } /** * Converts an iv in a specified portion of a byte[] and places it in a IvParameterSpec. * @param iv The byte[] containing the iv * @param offset Where the iv begins * @param length How long the iv is * @return Returns an IvParameterSpec containing the iv. */ public static IvParameterSpec getIvParameterSpec(byte[] iv, int offset, int length){ return new IvParameterSpec(iv, offset, length); } /** * Converts an iv in a ByteBuffer and places it in a IvParameterSpec. * @param iv The ByteBuffer containing the iv * @return Returns an IvParameterSpec containing the iv. */ public static IvParameterSpec getIvParameterSpec(ByteBuffer iv){ return new IvParameterSpec(Fields.copyToArray(iv)); } /** * Derives a ByteBuffer that is 512 bits (32 bytes) long from the given key using the provided * class name and kdfString * @param kdfKey The key to derive from * @param c Class name to use in derivation * @param kdfString Sting to use in derivation * @return A 512 long ByteBuffer * @throws InvalidKeyException */ private static ByteBuffer deriveBytes(SecretKey kdfKey, Class<?> c, String kdfString) throws InvalidKeyException{ if(kdfString == null){ throw new NullPointerException(); } MessageAuthCode kdf = new MessageAuthCode(MACType.HMACSHA512, kdfKey); try { return kdf.genMac((c.getName()+kdfString).getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { throw new Error(e); // Impossible } } /** * Derives a ByteBuffer of the specified length from the given key using the provided class name * and kdfString * @param kdfKey The key to derive from * @param c Class name to use in derivation * @param kdfString String to use in derivation * @param len How long the new ByteBuffer should be. * @return A ByteBuffer of the specified length * @throws InvalidKeyException */ private static ByteBuffer deriveBytesTruncated(SecretKey kdfKey, Class<?> c, String kdfString, int len) throws InvalidKeyException{ byte[] key = new byte[len]; deriveBytes(kdfKey, c, kdfString).get(key); return ByteBuffer.wrap(key); } /** * Derives a SecretKey of the specified type from the given key using the provided class name * and kdfString * @param kdfKey The key to derive from * @param c Class name to use in derivation * @param kdfString String to use in derivation * @param type The type of key to derive * @return The derived key as a SecretKey * @throws InvalidKeyException */ public static SecretKey deriveSecretKey(SecretKey kdfKey, Class<?> c, String kdfString, KeyType type) throws InvalidKeyException{ return getSecretKey(type, deriveBytesTruncated(kdfKey, c, kdfString, type.keySize >> 3)); } /** * Derives a IvParameterSpec of the specified type from the given key using the provided class * name and kdfString * @param kdfKey The key to derive from * @param c Class name to use in derivation * @param kdfString String to use in derivation * @param ivType The type of IV to derive * @return The derived IV as an IvParameterSpec * @throws InvalidKeyException */ public static IvParameterSpec deriveIvParameterSpec(SecretKey kdfKey, Class<?> c, String kdfString, KeyType ivType) throws InvalidKeyException{ return getIvParameterSpec(deriveBytesTruncated(kdfKey, c, kdfString, ivType.ivSize >> 3)); } }