package org.keyczar;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.interfaces.PBEKey;
import javax.crypto.spec.PBEKeySpec;
import org.keyczar.enums.KeyPurpose;
import org.keyczar.enums.KeyStatus;
import org.keyczar.exceptions.KeyczarException;
import org.keyczar.interfaces.KeyczarReader;
import org.keyczar.util.Base64Coder;
import org.keyczar.util.Util;
import com.google.common.base.Charsets;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
public class KeyczarUtils {
public static AesKey unpack(byte[] data) throws KeyczarException {
AesKey aesKey = AesKey.fromPackedKey(data);
return aesKey;
}
public static KeyczarReader asReader(AesKey aesKey) {
ImportedKeyReader importedKeyReader = new ImportedKeyReader(aesKey);
return importedKeyReader;
}
public static byte[] pack(AesKey key) {
return key.getEncoded();
}
@Deprecated
public static SecretKey getKey(AesKey key) {
return key.getJceKey();
}
public static byte[] generate(KeyMetadata kmd) throws KeyczarException {
KeyczarKey key = createKey(kmd);
byte[] packed = KeyczarUtils.pack((AesKey) key);
return packed;
}
public static KeyczarKey createKey(KeyMetadata kmd) {
try {
MemoryKeyczarStore store = new MemoryKeyczarStore();
GenericKeyczar keyczar = GenericKeyczar.create(store, kmd);
keyczar.addVersion(KeyStatus.PRIMARY);
KeyczarKey key = keyczar.getPrimaryKey();
return key;
} catch (KeyczarException e) {
throw new IllegalStateException("Error generating key", e);
}
}
public static AesKey generateSymmetricKey() {
KeyMetadata kmd = new KeyMetadata("key", KeyPurpose.DECRYPT_AND_ENCRYPT, DefaultKeyType.AES);
return (AesKey) createKey(kmd);
}
public static byte[] encrypt(AesKey key, byte[] plaintext) {
try {
Crypter crypter = buildCrypter(key);
return crypter.encrypt(plaintext);
} catch (KeyczarException e) {
throw new IllegalStateException("Error encrypting data", e);
}
}
public static Crypter buildCrypter(AesKey key) {
try {
KeyczarReader importedKeyReader = asReader(key);
Crypter crypter = new Crypter(importedKeyReader);
return crypter;
} catch (KeyczarException e) {
throw new IllegalStateException("Error wrapping key", e);
}
}
public static AesKey deriveKey(int iterations, byte[] salt, String password) {
int keyLength = 256;
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, iterations, keyLength);
SecretKeyFactory factory;
try {
factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("Unable to get PBKDF2 provider", e);
}
PBEKey pbeKey;
try {
pbeKey = (PBEKey) factory.generateSecret(pbeKeySpec);
} catch (InvalidKeySpecException e) {
throw new IllegalStateException("Error generating secret", e);
}
byte[] aesBytes = pbeKey.getEncoded();
if (aesBytes.length != (256 / 8)) {
throw new IllegalStateException();
}
try {
HmacKey hmacKey = deriveHmac(aesBytes, salt, password);
return new AesKey(aesBytes, hmacKey);
} catch (KeyczarException e) {
throw new IllegalStateException("Error building key", e);
}
}
public static HmacKey deriveHmac(byte[] aesBytes, byte[] salt, String password) {
byte[] hmacBytes;
{
// We need something that can be consistent
// Some people say it's OK to just use the hash of the key,
// but obviously there's no proof of that. We add a little
// complexity
// to be safer.
Hasher hasher = Hashing.sha256().newHasher();
byte[] passwordBytes = password.getBytes(Charsets.UTF_8);
hasher.putBytes(passwordBytes);
hasher.putBytes(aesBytes);
hasher.putBytes(passwordBytes);
hasher.putBytes(salt);
hasher.putBytes(passwordBytes);
hmacBytes = hasher.hash().asBytes();
}
if (hmacBytes.length != (256 / 8)) {
throw new IllegalStateException();
}
try {
HmacKey hmacKey = new HmacKey(hmacBytes);
return hmacKey;
} catch (KeyczarException e) {
throw new IllegalStateException("Error building key", e);
}
}
public static byte[] decrypt(AesKey newKey, byte[] ciphertext) throws KeyczarException {
Crypter crypter = buildCrypter(newKey);
return crypter.decrypt(ciphertext);
}
public static RsaPrivateKey readRsaPrivateKey(String data) throws KeyczarException {
return RsaPrivateKey.read(data);
}
public static RsaPublicKey readRsaPublicKey(String data) throws KeyczarException {
return RsaPublicKey.read(data);
}
public static byte[] generateSecureRandom(int len) {
return org.keyczar.util.Util.rand(len);
}
public static KeyczarPublicKey getPublicKey(KeyczarKey keypair) {
return ((KeyczarPrivateKey) keypair).getPublic();
}
public static RsaPrivateKey getPrivateKey(KeyczarKey keypair) {
return ((RsaPrivateKey) keypair);
}
public static PublicKey getJce(KeyczarPublicKey keyczarKey) {
return (PublicKey) keyczarKey.getJceKey();
}
public static PrivateKey getJce(RsaPrivateKey keyczarPrivateKey) {
return keyczarPrivateKey.getJceKey();
}
private static final String PEM_FOOTER_BEGIN = "-----END ";
private static final String PEM_LINE_ENDING = "-----\n";
private static final String PEM_HEADER_BEGIN = "-----BEGIN ";
/**
* Because sometimes we don't want a password...
*/
public static String toPem(RsaPrivateKey keyczarPrivateKey) {
RSAPrivateCrtKey jceKey = keyczarPrivateKey.getJceKey();
byte[] keyData = jceKey.getEncoded();
String pemType = jceKey.getAlgorithm() + " PRIVATE KEY";
String base64Key = Base64Coder.encodeMime(keyData, true);
StringBuffer result = new StringBuffer();
result.append(PEM_HEADER_BEGIN);
result.append(pemType);
result.append(PEM_LINE_ENDING);
for (String line : Util.split(base64Key, 64)) {
result.append(line);
result.append('\n');
}
result.append(PEM_FOOTER_BEGIN);
result.append(pemType);
result.append(PEM_LINE_ENDING);
return result.toString();
}
}