blob: 7a978045a1436814302ef3a34a64ef1b83c8b2a7 [file] [log] [blame]
package org.bouncycastle.openssl.jcajce;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.RC2ParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.jcajce.util.JcaJceHelper;
import org.bouncycastle.openssl.EncryptionException;
import org.bouncycastle.openssl.PEMException;
import org.bouncycastle.util.Integers;
class PEMUtilities
{
private static final Map KEYSIZES = new HashMap();
private static final Set PKCS5_SCHEME_1 = new HashSet();
private static final Set PKCS5_SCHEME_2 = new HashSet();
private static final Map PRFS = new HashMap();
private static final Map PRFS_SALT = new HashMap();
static
{
PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC);
PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC);
PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC);
PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC);
PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC);
PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC);
PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.id_PBES2);
PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.des_EDE3_CBC);
PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes128_CBC);
PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes192_CBC);
PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes256_CBC);
KEYSIZES.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), Integers.valueOf(192));
KEYSIZES.put(NISTObjectIdentifiers.id_aes128_CBC.getId(), Integers.valueOf(128));
KEYSIZES.put(NISTObjectIdentifiers.id_aes192_CBC.getId(), Integers.valueOf(192));
KEYSIZES.put(NISTObjectIdentifiers.id_aes256_CBC.getId(), Integers.valueOf(256));
KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4.getId(), Integers.valueOf(128));
KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC4, Integers.valueOf(40));
KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd2_KeyTripleDES_CBC, Integers.valueOf(128));
KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC, Integers.valueOf(192));
KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC, Integers.valueOf(128));
KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC, Integers.valueOf(40));
PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA1, "PBKDF2withHMACSHA1");
PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA256, "PBKDF2withHMACSHA256");
PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA512, "PBKDF2withHMACSHA512");
PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA224, "PBKDF2withHMACSHA224");
PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA384, "PBKDF2withHMACSHA384");
PRFS.put(NISTObjectIdentifiers.id_hmacWithSHA3_224, "PBKDF2withHMACSHA3-224");
PRFS.put(NISTObjectIdentifiers.id_hmacWithSHA3_256, "PBKDF2withHMACSHA3-256");
PRFS.put(NISTObjectIdentifiers.id_hmacWithSHA3_384, "PBKDF2withHMACSHA3-384");
PRFS.put(NISTObjectIdentifiers.id_hmacWithSHA3_512, "PBKDF2withHMACSHA3-512");
PRFS.put(CryptoProObjectIdentifiers.gostR3411Hmac, "PBKDF2withHMACGOST3411");
PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA1, Integers.valueOf(20));
PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA256, Integers.valueOf(32));
PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA512, Integers.valueOf(64));
PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA224, Integers.valueOf(28));
PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA384, Integers.valueOf(48));
PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_224, Integers.valueOf(28));
PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_256, Integers.valueOf(32));
PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_384, Integers.valueOf(48));
PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_512, Integers.valueOf(64));
PRFS_SALT.put(CryptoProObjectIdentifiers.gostR3411Hmac, Integers.valueOf(32));
}
static int getKeySize(String algorithm)
{
if (!KEYSIZES.containsKey(algorithm))
{
throw new IllegalStateException("no key size for algorithm: " + algorithm);
}
return ((Integer)KEYSIZES.get(algorithm)).intValue();
}
static int getSaltSize(ASN1ObjectIdentifier algorithm)
{
if (!PRFS_SALT.containsKey(algorithm))
{
throw new IllegalStateException("no salt size for algorithm: " + algorithm);
}
return ((Integer)PRFS_SALT.get(algorithm)).intValue();
}
static boolean isHmacSHA1(AlgorithmIdentifier prf)
{
return prf == null || prf.getAlgorithm().equals(PKCSObjectIdentifiers.id_hmacWithSHA1);
}
static boolean isPKCS5Scheme1(ASN1ObjectIdentifier algOid)
{
return PKCS5_SCHEME_1.contains(algOid);
}
static boolean isPKCS5Scheme2(ASN1ObjectIdentifier algOid)
{
return PKCS5_SCHEME_2.contains(algOid);
}
public static boolean isPKCS12(ASN1ObjectIdentifier algOid)
{
return algOid.getId().startsWith(PKCSObjectIdentifiers.pkcs_12PbeIds.getId());
}
public static SecretKey generateSecretKeyForPKCS5Scheme2(JcaJceHelper helper, String algorithm, char[] password, byte[] salt, int iterationCount)
throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException
{
SecretKeyFactory keyGen = helper.createSecretKeyFactory("PBKDF2with8BIT");
SecretKey sKey = keyGen.generateSecret(new PBEKeySpec(password, salt, iterationCount, PEMUtilities.getKeySize(algorithm)));
return new SecretKeySpec(sKey.getEncoded(), algorithm);
}
public static SecretKey generateSecretKeyForPKCS5Scheme2(JcaJceHelper helper, String algorithm, char[] password, byte[] salt, int iterationCount, AlgorithmIdentifier prf)
throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException
{
String prfName = (String)PRFS.get(prf.getAlgorithm());
if (prfName == null)
{
throw new NoSuchAlgorithmException("unknown PRF in PKCS#2: " + prf.getAlgorithm());
}
SecretKeyFactory keyGen = helper.createSecretKeyFactory(prfName);
SecretKey sKey = keyGen.generateSecret(new PBEKeySpec(password, salt, iterationCount, PEMUtilities.getKeySize(algorithm)));
return new SecretKeySpec(sKey.getEncoded(), algorithm);
}
static byte[] crypt(
boolean encrypt,
JcaJceHelper helper,
byte[] bytes,
char[] password,
String dekAlgName,
byte[] iv)
throws PEMException
{
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
String alg;
String blockMode = "CBC";
String padding = "PKCS5Padding";
Key sKey;
// Figure out block mode and padding.
if (dekAlgName.endsWith("-CFB"))
{
blockMode = "CFB";
padding = "NoPadding";
}
if (dekAlgName.endsWith("-ECB") ||
"DES-EDE".equals(dekAlgName) ||
"DES-EDE3".equals(dekAlgName))
{
// ECB is actually the default (though seldom used) when OpenSSL
// uses DES-EDE (des2) or DES-EDE3 (des3).
blockMode = "ECB";
paramSpec = null;
}
if (dekAlgName.endsWith("-OFB"))
{
blockMode = "OFB";
padding = "NoPadding";
}
// Figure out algorithm and key size.
if (dekAlgName.startsWith("DES-EDE"))
{
alg = "DESede";
// "DES-EDE" is actually des2 in OpenSSL-speak!
// "DES-EDE3" is des3.
boolean des2 = !dekAlgName.startsWith("DES-EDE3");
sKey = getKey(helper, password, alg, 24, iv, des2);
}
else if (dekAlgName.startsWith("DES-"))
{
alg = "DES";
sKey = getKey(helper, password, alg, 8, iv);
}
else if (dekAlgName.startsWith("BF-"))
{
alg = "Blowfish";
sKey = getKey(helper, password, alg, 16, iv);
}
else if (dekAlgName.startsWith("RC2-"))
{
alg = "RC2";
int keyBits = 128;
if (dekAlgName.startsWith("RC2-40-"))
{
keyBits = 40;
}
else if (dekAlgName.startsWith("RC2-64-"))
{
keyBits = 64;
}
sKey = getKey(helper, password, alg, keyBits / 8, iv);
if (paramSpec == null) // ECB block mode
{
paramSpec = new RC2ParameterSpec(keyBits);
}
else
{
paramSpec = new RC2ParameterSpec(keyBits, iv);
}
}
else if (dekAlgName.startsWith("AES-"))
{
alg = "AES";
byte[] salt = iv;
if (salt.length > 8)
{
salt = new byte[8];
System.arraycopy(iv, 0, salt, 0, 8);
}
int keyBits;
if (dekAlgName.startsWith("AES-128-"))
{
keyBits = 128;
}
else if (dekAlgName.startsWith("AES-192-"))
{
keyBits = 192;
}
else if (dekAlgName.startsWith("AES-256-"))
{
keyBits = 256;
}
else
{
throw new EncryptionException("unknown AES encryption with private key");
}
sKey = getKey(helper, password, "AES", keyBits / 8, salt);
}
else
{
throw new EncryptionException("unknown encryption with private key");
}
String transformation = alg + "/" + blockMode + "/" + padding;
try
{
Cipher c = helper.createCipher(transformation);
int mode = encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE;
if (paramSpec == null) // ECB block mode
{
c.init(mode, sKey);
}
else
{
c.init(mode, sKey, paramSpec);
}
return c.doFinal(bytes);
}
catch (Exception e)
{
throw new EncryptionException("exception using cipher - please check password and data.", e);
}
}
private static SecretKey getKey(
JcaJceHelper helper,
char[] password,
String algorithm,
int keyLength,
byte[] salt)
throws PEMException
{
return getKey(helper, password, algorithm, keyLength, salt, false);
}
private static SecretKey getKey(
JcaJceHelper helper,
char[] password,
String algorithm,
int keyLength,
byte[] salt,
boolean des2)
throws PEMException
{
try
{
PBEKeySpec spec = new PBEKeySpec(password, salt, 1, keyLength * 8);
SecretKeyFactory keyFactory = helper.createSecretKeyFactory("PBKDF-OpenSSL");
byte[] key = keyFactory.generateSecret(spec).getEncoded();
if (des2 && key.length >= 24)
{
// For DES2, we must copy first 8 bytes into the last 8 bytes.
System.arraycopy(key, 0, key, 16, 8);
}
return new SecretKeySpec(key, algorithm);
}
catch (GeneralSecurityException e)
{
throw new PEMException("Unable to create OpenSSL PBDKF: " + e.getMessage(), e);
}
}
}