diff options
Diffstat (limited to 'prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util')
13 files changed, 2883 insertions, 0 deletions
diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BCPBEKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BCPBEKey.java new file mode 100644 index 00000000..e8eac7e1 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BCPBEKey.java @@ -0,0 +1,155 @@ +package org.spongycastle.jcajce.provider.symmetric.util; + +import javax.crypto.interfaces.PBEKey; +import javax.crypto.spec.PBEKeySpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.PBEParametersGenerator; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithIV; + +public class BCPBEKey + implements PBEKey +{ + String algorithm; + ASN1ObjectIdentifier oid; + int type; + int digest; + int keySize; + int ivSize; + CipherParameters param; + PBEKeySpec pbeKeySpec; + boolean tryWrong = false; + + /** + * @param param + */ + public BCPBEKey( + String algorithm, + ASN1ObjectIdentifier oid, + int type, + int digest, + int keySize, + int ivSize, + PBEKeySpec pbeKeySpec, + CipherParameters param) + { + this.algorithm = algorithm; + this.oid = oid; + this.type = type; + this.digest = digest; + this.keySize = keySize; + this.ivSize = ivSize; + this.pbeKeySpec = pbeKeySpec; + this.param = param; + } + + public String getAlgorithm() + { + return algorithm; + } + + public String getFormat() + { + return "RAW"; + } + + public byte[] getEncoded() + { + if (param != null) + { + KeyParameter kParam; + + if (param instanceof ParametersWithIV) + { + kParam = (KeyParameter)((ParametersWithIV)param).getParameters(); + } + else + { + kParam = (KeyParameter)param; + } + + return kParam.getKey(); + } + else + { + if (type == PBE.PKCS12) + { + return PBEParametersGenerator.PKCS12PasswordToBytes(pbeKeySpec.getPassword()); + } + else if (type == PBE.PKCS5S2_UTF8) + { + return PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(pbeKeySpec.getPassword()); + } + else + { + return PBEParametersGenerator.PKCS5PasswordToBytes(pbeKeySpec.getPassword()); + } + } + } + + int getType() + { + return type; + } + + int getDigest() + { + return digest; + } + + int getKeySize() + { + return keySize; + } + + public int getIvSize() + { + return ivSize; + } + + public CipherParameters getParam() + { + return param; + } + + /* (non-Javadoc) + * @see javax.crypto.interfaces.PBEKey#getPassword() + */ + public char[] getPassword() + { + return pbeKeySpec.getPassword(); + } + + /* (non-Javadoc) + * @see javax.crypto.interfaces.PBEKey#getSalt() + */ + public byte[] getSalt() + { + return pbeKeySpec.getSalt(); + } + + /* (non-Javadoc) + * @see javax.crypto.interfaces.PBEKey#getIterationCount() + */ + public int getIterationCount() + { + return pbeKeySpec.getIterationCount(); + } + + public ASN1ObjectIdentifier getOID() + { + return oid; + } + + public void setTryWrongPKCS12Zero(boolean tryWrong) + { + this.tryWrong = tryWrong; + } + + boolean shouldTryWrongPKCS12() + { + return tryWrong; + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseAlgorithmParameterGenerator.java b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseAlgorithmParameterGenerator.java new file mode 100644 index 00000000..532c88ce --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseAlgorithmParameterGenerator.java @@ -0,0 +1,19 @@ +package org.spongycastle.jcajce.provider.symmetric.util; + +import java.security.AlgorithmParameterGeneratorSpi; +import java.security.SecureRandom; + +public abstract class BaseAlgorithmParameterGenerator + extends AlgorithmParameterGeneratorSpi +{ + protected SecureRandom random; + protected int strength = 1024; + + protected void engineInit( + int strength, + SecureRandom random) + { + this.strength = strength; + this.random = random; + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseAlgorithmParameters.java b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseAlgorithmParameters.java new file mode 100644 index 00000000..2bba41f1 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseAlgorithmParameters.java @@ -0,0 +1,29 @@ +package org.spongycastle.jcajce.provider.symmetric.util; + +import java.security.AlgorithmParametersSpi; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; + +public abstract class BaseAlgorithmParameters + extends AlgorithmParametersSpi +{ + protected boolean isASN1FormatString(String format) + { + return format == null || format.equals("ASN.1"); + } + + protected AlgorithmParameterSpec engineGetParameterSpec( + Class paramSpec) + throws InvalidParameterSpecException + { + if (paramSpec == null) + { + throw new NullPointerException("argument to getParameterSpec must not be null"); + } + + return localEngineGetParameterSpec(paramSpec); + } + + protected abstract AlgorithmParameterSpec localEngineGetParameterSpec(Class paramSpec) + throws InvalidParameterSpecException; +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java new file mode 100644 index 00000000..8d7bb13c --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java @@ -0,0 +1,1095 @@ +package org.spongycastle.jcajce.provider.symmetric.util; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEParameterSpec; +import javax.crypto.spec.RC2ParameterSpec; +import javax.crypto.spec.RC5ParameterSpec; + +import org.spongycastle.asn1.cms.GCMParameters; +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.BufferedBlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.OutputLengthException; +import org.spongycastle.crypto.modes.AEADBlockCipher; +import org.spongycastle.crypto.modes.CBCBlockCipher; +import org.spongycastle.crypto.modes.CCMBlockCipher; +import org.spongycastle.crypto.modes.CFBBlockCipher; +import org.spongycastle.crypto.modes.CTSBlockCipher; +import org.spongycastle.crypto.modes.EAXBlockCipher; +import org.spongycastle.crypto.modes.GCFBBlockCipher; +import org.spongycastle.crypto.modes.GCMBlockCipher; +import org.spongycastle.crypto.modes.GOFBBlockCipher; +import org.spongycastle.crypto.modes.OCBBlockCipher; +import org.spongycastle.crypto.modes.OFBBlockCipher; +import org.spongycastle.crypto.modes.OpenPGPCFBBlockCipher; +import org.spongycastle.crypto.modes.PGPCFBBlockCipher; +import org.spongycastle.crypto.modes.SICBlockCipher; +import org.spongycastle.crypto.paddings.BlockCipherPadding; +import org.spongycastle.crypto.paddings.ISO10126d2Padding; +import org.spongycastle.crypto.paddings.ISO7816d4Padding; +import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher; +import org.spongycastle.crypto.paddings.TBCPadding; +import org.spongycastle.crypto.paddings.X923Padding; +import org.spongycastle.crypto.paddings.ZeroBytePadding; +import org.spongycastle.crypto.params.AEADParameters; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithIV; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.crypto.params.ParametersWithSBox; +import org.spongycastle.crypto.params.RC2Parameters; +import org.spongycastle.crypto.params.RC5Parameters; +import org.spongycastle.jcajce.spec.GOST28147ParameterSpec; +import org.spongycastle.jcajce.spec.RepeatedSecretKeySpec; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.util.Strings; + +public class BaseBlockCipher + extends BaseWrapCipher + implements PBE +{ + private static final Class gcmSpecClass = lookup("javax.crypto.spec.GCMParameterSpec"); + + // + // specs we can handle. + // + private Class[] availableSpecs = + { + RC2ParameterSpec.class, + RC5ParameterSpec.class, + IvParameterSpec.class, + PBEParameterSpec.class, + GOST28147ParameterSpec.class, + gcmSpecClass + }; + + private BlockCipher baseEngine; + private BlockCipherProvider engineProvider; + private GenericBlockCipher cipher; + private ParametersWithIV ivParam; + private AEADParameters aeadParams; + + private int ivLength = 0; + + private boolean padded; + + private PBEParameterSpec pbeSpec = null; + private String pbeAlgorithm = null; + + private String modeName = null; + + private static Class lookup(String className) + { + try + { + Class def = BaseBlockCipher.class.getClassLoader().loadClass(className); + + return def; + } + catch (Exception e) + { + return null; + } + } + + protected BaseBlockCipher( + BlockCipher engine) + { + baseEngine = engine; + + cipher = new BufferedGenericBlockCipher(engine); + } + + protected BaseBlockCipher( + BlockCipherProvider provider) + { + baseEngine = provider.get(); + engineProvider = provider; + + cipher = new BufferedGenericBlockCipher(provider.get()); + } + + protected BaseBlockCipher( + AEADBlockCipher engine) + { + baseEngine = engine.getUnderlyingCipher(); + ivLength = baseEngine.getBlockSize(); + cipher = new AEADGenericBlockCipher(engine); + } + + protected BaseBlockCipher( + org.spongycastle.crypto.BlockCipher engine, + int ivLength) + { + baseEngine = engine; + + this.cipher = new BufferedGenericBlockCipher(engine); + this.ivLength = ivLength / 8; + } + + protected BaseBlockCipher( + BufferedBlockCipher engine, + int ivLength) + { + baseEngine = engine.getUnderlyingCipher(); + + this.cipher = new BufferedGenericBlockCipher(engine); + this.ivLength = ivLength / 8; + } + + protected int engineGetBlockSize() + { + return baseEngine.getBlockSize(); + } + + protected byte[] engineGetIV() + { + if (aeadParams != null) + { + return aeadParams.getNonce(); + } + + return (ivParam != null) ? ivParam.getIV() : null; + } + + protected int engineGetKeySize( + Key key) + { + return key.getEncoded().length * 8; + } + + protected int engineGetOutputSize( + int inputLen) + { + return cipher.getOutputSize(inputLen); + } + + protected AlgorithmParameters engineGetParameters() + { + if (engineParams == null) + { + if (pbeSpec != null) + { + try + { + engineParams = AlgorithmParameters.getInstance(pbeAlgorithm, BouncyCastleProvider.PROVIDER_NAME); + engineParams.init(pbeSpec); + } + catch (Exception e) + { + return null; + } + } + else if (ivParam != null) + { + String name = cipher.getUnderlyingCipher().getAlgorithmName(); + + if (name.indexOf('/') >= 0) + { + name = name.substring(0, name.indexOf('/')); + } + + try + { + engineParams = AlgorithmParameters.getInstance(name, BouncyCastleProvider.PROVIDER_NAME); + engineParams.init(ivParam.getIV()); + } + catch (Exception e) + { + throw new RuntimeException(e.toString()); + } + } + else if (aeadParams != null) + { + try + { + engineParams = AlgorithmParameters.getInstance("GCM", BouncyCastleProvider.PROVIDER_NAME); + engineParams.init(new GCMParameters(aeadParams.getNonce(), aeadParams.getMacSize()).getEncoded()); + } + catch (Exception e) + { + throw new RuntimeException(e.toString()); + } + } + } + + return engineParams; + } + + protected void engineSetMode( + String mode) + throws NoSuchAlgorithmException + { + modeName = Strings.toUpperCase(mode); + + if (modeName.equals("ECB")) + { + ivLength = 0; + cipher = new BufferedGenericBlockCipher(baseEngine); + } + else if (modeName.equals("CBC")) + { + ivLength = baseEngine.getBlockSize(); + cipher = new BufferedGenericBlockCipher( + new CBCBlockCipher(baseEngine)); + } + else if (modeName.startsWith("OFB")) + { + ivLength = baseEngine.getBlockSize(); + if (modeName.length() != 3) + { + int wordSize = Integer.parseInt(modeName.substring(3)); + + cipher = new BufferedGenericBlockCipher( + new OFBBlockCipher(baseEngine, wordSize)); + } + else + { + cipher = new BufferedGenericBlockCipher( + new OFBBlockCipher(baseEngine, 8 * baseEngine.getBlockSize())); + } + } + else if (modeName.startsWith("CFB")) + { + ivLength = baseEngine.getBlockSize(); + if (modeName.length() != 3) + { + int wordSize = Integer.parseInt(modeName.substring(3)); + + cipher = new BufferedGenericBlockCipher( + new CFBBlockCipher(baseEngine, wordSize)); + } + else + { + cipher = new BufferedGenericBlockCipher( + new CFBBlockCipher(baseEngine, 8 * baseEngine.getBlockSize())); + } + } + else if (modeName.startsWith("PGP")) + { + boolean inlineIV = modeName.equalsIgnoreCase("PGPCFBwithIV"); + + ivLength = baseEngine.getBlockSize(); + cipher = new BufferedGenericBlockCipher( + new PGPCFBBlockCipher(baseEngine, inlineIV)); + } + else if (modeName.equalsIgnoreCase("OpenPGPCFB")) + { + ivLength = 0; + cipher = new BufferedGenericBlockCipher( + new OpenPGPCFBBlockCipher(baseEngine)); + } + else if (modeName.startsWith("SIC")) + { + ivLength = baseEngine.getBlockSize(); + if (ivLength < 16) + { + throw new IllegalArgumentException("Warning: SIC-Mode can become a twotime-pad if the blocksize of the cipher is too small. Use a cipher with a block size of at least 128 bits (e.g. AES)"); + } + cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher( + new SICBlockCipher(baseEngine))); + } + else if (modeName.startsWith("CTR")) + { + ivLength = baseEngine.getBlockSize(); + cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher( + new SICBlockCipher(baseEngine))); + } + else if (modeName.startsWith("GOFB")) + { + ivLength = baseEngine.getBlockSize(); + cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher( + new GOFBBlockCipher(baseEngine))); + } + else if (modeName.startsWith("GCFB")) + { + ivLength = baseEngine.getBlockSize(); + cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher( + new GCFBBlockCipher(baseEngine))); + } + else if (modeName.startsWith("CTS")) + { + ivLength = baseEngine.getBlockSize(); + cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(new CBCBlockCipher(baseEngine))); + } + else if (modeName.startsWith("CCM")) + { + ivLength = 13; // CCM nonce 7..13 bytes + cipher = new AEADGenericBlockCipher(new CCMBlockCipher(baseEngine)); + } + else if (modeName.startsWith("OCB")) + { + if (engineProvider != null) + { + /* + * RFC 7253 4.2. Nonce is a string of no more than 120 bits + */ + ivLength = 15; + cipher = new AEADGenericBlockCipher(new OCBBlockCipher(baseEngine, engineProvider.get())); + } + else + { + throw new NoSuchAlgorithmException("can't support mode " + mode); + } + } + else if (modeName.startsWith("EAX")) + { + ivLength = baseEngine.getBlockSize(); + cipher = new AEADGenericBlockCipher(new EAXBlockCipher(baseEngine)); + } + else if (modeName.startsWith("GCM")) + { + ivLength = baseEngine.getBlockSize(); + cipher = new AEADGenericBlockCipher(new GCMBlockCipher(baseEngine)); + } + else + { + throw new NoSuchAlgorithmException("can't support mode " + mode); + } + } + + protected void engineSetPadding( + String padding) + throws NoSuchPaddingException + { + String paddingName = Strings.toUpperCase(padding); + + if (paddingName.equals("NOPADDING")) + { + if (cipher.wrapOnNoPadding()) + { + cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(cipher.getUnderlyingCipher())); + } + } + else if (paddingName.equals("WITHCTS")) + { + cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(cipher.getUnderlyingCipher())); + } + else + { + padded = true; + + if (isAEADModeName(modeName)) + { + throw new NoSuchPaddingException("Only NoPadding can be used with AEAD modes."); + } + else if (paddingName.equals("PKCS5PADDING") || paddingName.equals("PKCS7PADDING")) + { + cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher()); + } + else if (paddingName.equals("ZEROBYTEPADDING")) + { + cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new ZeroBytePadding()); + } + else if (paddingName.equals("ISO10126PADDING") || paddingName.equals("ISO10126-2PADDING")) + { + cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new ISO10126d2Padding()); + } + else if (paddingName.equals("X9.23PADDING") || paddingName.equals("X923PADDING")) + { + cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new X923Padding()); + } + else if (paddingName.equals("ISO7816-4PADDING") || paddingName.equals("ISO9797-1PADDING")) + { + cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new ISO7816d4Padding()); + } + else if (paddingName.equals("TBCPADDING")) + { + cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new TBCPadding()); + } + else + { + throw new NoSuchPaddingException("Padding " + padding + " unknown."); + } + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + CipherParameters param; + + this.pbeSpec = null; + this.pbeAlgorithm = null; + this.engineParams = null; + this.aeadParams = null; + + // + // basic key check + // + if (!(key instanceof SecretKey)) + { + throw new InvalidKeyException("Key for algorithm " + key.getAlgorithm() + " not suitable for symmetric enryption."); + } + + // + // for RC5-64 we must have some default parameters + // + if (params == null && baseEngine.getAlgorithmName().startsWith("RC5-64")) + { + throw new InvalidAlgorithmParameterException("RC5 requires an RC5ParametersSpec to be passed in."); + } + + // + // a note on iv's - if ivLength is zero the IV gets ignored (we don't use it). + // + if (key instanceof BCPBEKey) + { + BCPBEKey k = (BCPBEKey)key; + + if (k.getOID() != null) + { + pbeAlgorithm = k.getOID().getId(); + } + else + { + pbeAlgorithm = k.getAlgorithm(); + } + + if (k.getParam() != null) + { + param = k.getParam(); + if (params instanceof IvParameterSpec) + { + IvParameterSpec iv = (IvParameterSpec)params; + + param = new ParametersWithIV(param, iv.getIV()); + } + else if (params instanceof GOST28147ParameterSpec) + { + // need to pick up IV and SBox. + GOST28147ParameterSpec gost28147Param = (GOST28147ParameterSpec)params; + + param = new ParametersWithSBox(param, gost28147Param.getSbox()); + + if (gost28147Param.getIV() != null && ivLength != 0) + { + param = new ParametersWithIV(param, gost28147Param.getIV()); + } + } + } + else if (params instanceof PBEParameterSpec) + { + pbeSpec = (PBEParameterSpec)params; + param = PBE.Util.makePBEParameters(k, params, cipher.getUnderlyingCipher().getAlgorithmName()); + } + else + { + throw new InvalidAlgorithmParameterException("PBE requires PBE parameters to be set."); + } + + if (param instanceof ParametersWithIV) + { + ivParam = (ParametersWithIV)param; + } + } + else if (params == null) + { + param = new KeyParameter(key.getEncoded()); + } + else if (params instanceof IvParameterSpec) + { + if (ivLength != 0) + { + IvParameterSpec p = (IvParameterSpec)params; + + if (p.getIV().length != ivLength && !isAEADModeName(modeName)) + { + throw new InvalidAlgorithmParameterException("IV must be " + ivLength + " bytes long."); + } + + if (key instanceof RepeatedSecretKeySpec) + { + param = new ParametersWithIV(null, p.getIV()); + ivParam = (ParametersWithIV)param; + } + else + { + param = new ParametersWithIV(new KeyParameter(key.getEncoded()), p.getIV()); + ivParam = (ParametersWithIV)param; + } + } + else + { + if (modeName != null && modeName.equals("ECB")) + { + throw new InvalidAlgorithmParameterException("ECB mode does not use an IV"); + } + + param = new KeyParameter(key.getEncoded()); + } + } + else if (params instanceof GOST28147ParameterSpec) + { + GOST28147ParameterSpec gost28147Param = (GOST28147ParameterSpec)params; + + param = new ParametersWithSBox( + new KeyParameter(key.getEncoded()), ((GOST28147ParameterSpec)params).getSbox()); + + if (gost28147Param.getIV() != null && ivLength != 0) + { + param = new ParametersWithIV(param, gost28147Param.getIV()); + ivParam = (ParametersWithIV)param; + } + } + else if (params instanceof RC2ParameterSpec) + { + RC2ParameterSpec rc2Param = (RC2ParameterSpec)params; + + param = new RC2Parameters(key.getEncoded(), ((RC2ParameterSpec)params).getEffectiveKeyBits()); + + if (rc2Param.getIV() != null && ivLength != 0) + { + param = new ParametersWithIV(param, rc2Param.getIV()); + ivParam = (ParametersWithIV)param; + } + } + else if (params instanceof RC5ParameterSpec) + { + RC5ParameterSpec rc5Param = (RC5ParameterSpec)params; + + param = new RC5Parameters(key.getEncoded(), ((RC5ParameterSpec)params).getRounds()); + if (baseEngine.getAlgorithmName().startsWith("RC5")) + { + if (baseEngine.getAlgorithmName().equals("RC5-32")) + { + if (rc5Param.getWordSize() != 32) + { + throw new InvalidAlgorithmParameterException("RC5 already set up for a word size of 32 not " + rc5Param.getWordSize() + "."); + } + } + else if (baseEngine.getAlgorithmName().equals("RC5-64")) + { + if (rc5Param.getWordSize() != 64) + { + throw new InvalidAlgorithmParameterException("RC5 already set up for a word size of 64 not " + rc5Param.getWordSize() + "."); + } + } + } + else + { + throw new InvalidAlgorithmParameterException("RC5 parameters passed to a cipher that is not RC5."); + } + if ((rc5Param.getIV() != null) && (ivLength != 0)) + { + param = new ParametersWithIV(param, rc5Param.getIV()); + ivParam = (ParametersWithIV)param; + } + } + else if (gcmSpecClass != null && gcmSpecClass.isInstance(params)) + { + if (!isAEADModeName(modeName) && !(cipher instanceof AEADGenericBlockCipher)) + { + throw new InvalidAlgorithmParameterException("GCMParameterSpec can only be used with AEAD modes."); + } + + try + { + Method tLen = gcmSpecClass.getDeclaredMethod("getTLen", new Class[0]); + Method iv= gcmSpecClass.getDeclaredMethod("getIV", new Class[0]); + + if (key instanceof RepeatedSecretKeySpec) + { + param = aeadParams = new AEADParameters(null, ((Integer)tLen.invoke(params, new Object[0])).intValue(), (byte[])iv.invoke(params, new Object[0])); + } + else + { + param = aeadParams = new AEADParameters(new KeyParameter(key.getEncoded()), ((Integer)tLen.invoke(params, new Object[0])).intValue(), (byte[])iv.invoke(params, new Object[0])); + } + } + catch (Exception e) + { + throw new InvalidAlgorithmParameterException("Cannot process GCMParameterSpec."); + } + } + else + { + throw new InvalidAlgorithmParameterException("unknown parameter type."); + } + + if ((ivLength != 0) && !(param instanceof ParametersWithIV) && !(param instanceof AEADParameters)) + { + SecureRandom ivRandom = random; + + if (ivRandom == null) + { + ivRandom = new SecureRandom(); + } + + if ((opmode == Cipher.ENCRYPT_MODE) || (opmode == Cipher.WRAP_MODE)) + { + byte[] iv = new byte[ivLength]; + + ivRandom.nextBytes(iv); + param = new ParametersWithIV(param, iv); + ivParam = (ParametersWithIV)param; + } + else if (cipher.getUnderlyingCipher().getAlgorithmName().indexOf("PGPCFB") < 0) + { + throw new InvalidAlgorithmParameterException("no IV set when one expected"); + } + } + + if (random != null && padded) + { + param = new ParametersWithRandom(param, random); + } + + try + { + switch (opmode) + { + case Cipher.ENCRYPT_MODE: + case Cipher.WRAP_MODE: + cipher.init(true, param); + break; + case Cipher.DECRYPT_MODE: + case Cipher.UNWRAP_MODE: + cipher.init(false, param); + break; + default: + throw new InvalidParameterException("unknown opmode " + opmode + " passed"); + } + } + catch (Exception e) + { + throw new InvalidKeyException(e.getMessage()); + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + AlgorithmParameterSpec paramSpec = null; + + if (params != null) + { + for (int i = 0; i != availableSpecs.length; i++) + { + if (availableSpecs[i] == null) + { + continue; + } + + try + { + paramSpec = params.getParameterSpec(availableSpecs[i]); + break; + } + catch (Exception e) + { + // try again if possible + } + } + + if (paramSpec == null) + { + throw new InvalidAlgorithmParameterException("can't handle parameter " + params.toString()); + } + } + + engineInit(opmode, key, paramSpec, random); + + engineParams = params; + } + + protected void engineInit( + int opmode, + Key key, + SecureRandom random) + throws InvalidKeyException + { + try + { + engineInit(opmode, key, (AlgorithmParameterSpec)null, random); + } + catch (InvalidAlgorithmParameterException e) + { + throw new InvalidKeyException(e.getMessage()); + } + } + + protected void engineUpdateAAD(byte[] input, int offset, int length) + { + cipher.updateAAD(input, offset, length); + } + + protected void engineUpdateAAD(ByteBuffer bytebuffer) + { + int offset = bytebuffer.arrayOffset() + bytebuffer.position(); + int length = bytebuffer.limit() - bytebuffer.position(); + engineUpdateAAD(bytebuffer.array(), offset, length); + } + + protected byte[] engineUpdate( + byte[] input, + int inputOffset, + int inputLen) + { + int length = cipher.getUpdateOutputSize(inputLen); + + if (length > 0) + { + byte[] out = new byte[length]; + + int len = cipher.processBytes(input, inputOffset, inputLen, out, 0); + + if (len == 0) + { + return null; + } + else if (len != out.length) + { + byte[] tmp = new byte[len]; + + System.arraycopy(out, 0, tmp, 0, len); + + return tmp; + } + + return out; + } + + cipher.processBytes(input, inputOffset, inputLen, null, 0); + + return null; + } + + protected int engineUpdate( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + throws ShortBufferException + { + try + { + return cipher.processBytes(input, inputOffset, inputLen, output, outputOffset); + } + catch (DataLengthException e) + { + throw new ShortBufferException(e.getMessage()); + } + } + + protected byte[] engineDoFinal( + byte[] input, + int inputOffset, + int inputLen) + throws IllegalBlockSizeException, BadPaddingException + { + int len = 0; + byte[] tmp = new byte[engineGetOutputSize(inputLen)]; + + if (inputLen != 0) + { + len = cipher.processBytes(input, inputOffset, inputLen, tmp, 0); + } + + try + { + len += cipher.doFinal(tmp, len); + } + catch (DataLengthException e) + { + throw new IllegalBlockSizeException(e.getMessage()); + } + + if (len == tmp.length) + { + return tmp; + } + + byte[] out = new byte[len]; + + System.arraycopy(tmp, 0, out, 0, len); + + return out; + } + + protected int engineDoFinal( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + throws IllegalBlockSizeException, BadPaddingException, ShortBufferException + { + try + { + int len = 0; + + if (inputLen != 0) + { + len = cipher.processBytes(input, inputOffset, inputLen, output, outputOffset); + } + + return (len + cipher.doFinal(output, outputOffset + len)); + } + catch (OutputLengthException e) + { + throw new ShortBufferException(e.getMessage()); + } + catch (DataLengthException e) + { + throw new IllegalBlockSizeException(e.getMessage()); + } + } + + private boolean isAEADModeName( + String modeName) + { + return "CCM".equals(modeName) || "EAX".equals(modeName) || "GCM".equals(modeName) || "OCB".equals(modeName); + } + + /* + * The ciphers that inherit from us. + */ + + static private interface GenericBlockCipher + { + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException; + + public boolean wrapOnNoPadding(); + + public String getAlgorithmName(); + + public org.spongycastle.crypto.BlockCipher getUnderlyingCipher(); + + public int getOutputSize(int len); + + public int getUpdateOutputSize(int len); + + public void updateAAD(byte[] input, int offset, int length); + + public int processByte(byte in, byte[] out, int outOff) + throws DataLengthException; + + public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) + throws DataLengthException; + + public int doFinal(byte[] out, int outOff) + throws IllegalStateException, + BadPaddingException; + } + + private static class BufferedGenericBlockCipher + implements GenericBlockCipher + { + private BufferedBlockCipher cipher; + + BufferedGenericBlockCipher(BufferedBlockCipher cipher) + { + this.cipher = cipher; + } + + BufferedGenericBlockCipher(org.spongycastle.crypto.BlockCipher cipher) + { + this.cipher = new PaddedBufferedBlockCipher(cipher); + } + + BufferedGenericBlockCipher(org.spongycastle.crypto.BlockCipher cipher, BlockCipherPadding padding) + { + this.cipher = new PaddedBufferedBlockCipher(cipher, padding); + } + + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + cipher.init(forEncryption, params); + } + + public boolean wrapOnNoPadding() + { + return !(cipher instanceof CTSBlockCipher); + } + + public String getAlgorithmName() + { + return cipher.getUnderlyingCipher().getAlgorithmName(); + } + + public org.spongycastle.crypto.BlockCipher getUnderlyingCipher() + { + return cipher.getUnderlyingCipher(); + } + + public int getOutputSize(int len) + { + return cipher.getOutputSize(len); + } + + public int getUpdateOutputSize(int len) + { + return cipher.getUpdateOutputSize(len); + } + + public void updateAAD(byte[] input, int offset, int length) + { + throw new UnsupportedOperationException("AAD is not supported in the current mode."); + } + + public int processByte(byte in, byte[] out, int outOff) throws DataLengthException + { + return cipher.processByte(in, out, outOff); + } + + public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) throws DataLengthException + { + return cipher.processBytes(in, inOff, len, out, outOff); + } + + public int doFinal(byte[] out, int outOff) throws IllegalStateException, BadPaddingException + { + try + { + return cipher.doFinal(out, outOff); + } + catch (InvalidCipherTextException e) + { + throw new BadPaddingException(e.getMessage()); + } + } + } + + private static class AEADGenericBlockCipher + implements GenericBlockCipher + { + private static final Constructor aeadBadTagConstructor; + + static { + Class aeadBadTagClass = lookup("javax.crypto.AEADBadTagException"); + if (aeadBadTagClass != null) + { + aeadBadTagConstructor = findExceptionConstructor(aeadBadTagClass); + } + else + { + aeadBadTagConstructor = null; + } + } + + private static Constructor findExceptionConstructor(Class clazz) + { + try + { + return clazz.getConstructor(new Class[]{String.class}); + } + catch (Exception e) + { + return null; + } + } + + private AEADBlockCipher cipher; + + AEADGenericBlockCipher(AEADBlockCipher cipher) + { + this.cipher = cipher; + } + + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + cipher.init(forEncryption, params); + } + + public String getAlgorithmName() + { + return cipher.getUnderlyingCipher().getAlgorithmName(); + } + + public boolean wrapOnNoPadding() + { + return false; + } + + public org.spongycastle.crypto.BlockCipher getUnderlyingCipher() + { + return cipher.getUnderlyingCipher(); + } + + public int getOutputSize(int len) + { + return cipher.getOutputSize(len); + } + + public int getUpdateOutputSize(int len) + { + return cipher.getUpdateOutputSize(len); + } + + public void updateAAD(byte[] input, int offset, int length) + { + cipher.processAADBytes(input, offset, length); + } + + public int processByte(byte in, byte[] out, int outOff) throws DataLengthException + { + return cipher.processByte(in, out, outOff); + } + + public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) throws DataLengthException + { + return cipher.processBytes(in, inOff, len, out, outOff); + } + + public int doFinal(byte[] out, int outOff) throws IllegalStateException, BadPaddingException + { + try + { + return cipher.doFinal(out, outOff); + } + catch (InvalidCipherTextException e) + { + if (aeadBadTagConstructor != null) + { + BadPaddingException aeadBadTag = null; + try + { + aeadBadTag = (BadPaddingException)aeadBadTagConstructor + .newInstance(new Object[]{e.getMessage()}); + } + catch (Exception i) + { + // Shouldn't happen, but fall through to BadPaddingException + } + if (aeadBadTag != null) + { + throw aeadBadTag; + } + } + throw new BadPaddingException(e.getMessage()); + } + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseKeyGenerator.java b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseKeyGenerator.java new file mode 100644 index 00000000..460d14a9 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseKeyGenerator.java @@ -0,0 +1,82 @@ +package org.spongycastle.jcajce.provider.symmetric.util; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidParameterException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.KeyGeneratorSpi; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import org.spongycastle.crypto.CipherKeyGenerator; +import org.spongycastle.crypto.KeyGenerationParameters; + +public class BaseKeyGenerator + extends KeyGeneratorSpi +{ + protected String algName; + protected int keySize; + protected int defaultKeySize; + protected CipherKeyGenerator engine; + + protected boolean uninitialised = true; + + protected BaseKeyGenerator( + String algName, + int defaultKeySize, + CipherKeyGenerator engine) + { + this.algName = algName; + this.keySize = this.defaultKeySize = defaultKeySize; + this.engine = engine; + } + + protected void engineInit( + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + throw new InvalidAlgorithmParameterException("Not Implemented"); + } + + protected void engineInit( + SecureRandom random) + { + if (random != null) + { + engine.init(new KeyGenerationParameters(random, defaultKeySize)); + uninitialised = false; + } + } + + protected void engineInit( + int keySize, + SecureRandom random) + { + try + { + if (random == null) + { + random = new SecureRandom(); + } + engine.init(new KeyGenerationParameters(random, keySize)); + uninitialised = false; + } + catch (IllegalArgumentException e) + { + throw new InvalidParameterException(e.getMessage()); + } + } + + protected SecretKey engineGenerateKey() + { + if (uninitialised) + { + engine.init(new KeyGenerationParameters(new SecureRandom(), defaultKeySize)); + uninitialised = false; + } + + return new SecretKeySpec(engine.generateKey(), algName); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseMac.java b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseMac.java new file mode 100644 index 00000000..a711dae7 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseMac.java @@ -0,0 +1,144 @@ +package org.spongycastle.jcajce.provider.symmetric.util; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; + +import javax.crypto.MacSpi; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEParameterSpec; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.Mac; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithIV; +import org.spongycastle.crypto.params.SkeinParameters; +import org.spongycastle.jcajce.spec.SkeinParameterSpec; + +public class BaseMac + extends MacSpi implements PBE +{ + private Mac macEngine; + + private int pbeType = PKCS12; + private int pbeHash = SHA1; + private int keySize = 160; + + protected BaseMac( + Mac macEngine) + { + this.macEngine = macEngine; + } + + protected BaseMac( + Mac macEngine, + int pbeType, + int pbeHash, + int keySize) + { + this.macEngine = macEngine; + this.pbeType = pbeType; + this.pbeHash = pbeHash; + this.keySize = keySize; + } + + protected void engineInit( + Key key, + AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + CipherParameters param; + + if (key == null) + { + throw new InvalidKeyException("key is null"); + } + + if (key instanceof BCPBEKey) + { + BCPBEKey k = (BCPBEKey)key; + + if (k.getParam() != null) + { + param = k.getParam(); + } + else if (params instanceof PBEParameterSpec) + { + param = PBE.Util.makePBEMacParameters(k, params); + } + else + { + throw new InvalidAlgorithmParameterException("PBE requires PBE parameters to be set."); + } + } + else if (params instanceof IvParameterSpec) + { + param = new ParametersWithIV(new KeyParameter(key.getEncoded()), ((IvParameterSpec)params).getIV()); + } + else if (params instanceof SkeinParameterSpec) + { + param = new SkeinParameters.Builder(copyMap(((SkeinParameterSpec)params).getParameters())).setKey(key.getEncoded()).build(); + } + else if (params == null) + { + param = new KeyParameter(key.getEncoded()); + } + else + { + throw new InvalidAlgorithmParameterException("unknown parameter type."); + } + + macEngine.init(param); + } + + protected int engineGetMacLength() + { + return macEngine.getMacSize(); + } + + protected void engineReset() + { + macEngine.reset(); + } + + protected void engineUpdate( + byte input) + { + macEngine.update(input); + } + + protected void engineUpdate( + byte[] input, + int offset, + int len) + { + macEngine.update(input, offset, len); + } + + protected byte[] engineDoFinal() + { + byte[] out = new byte[engineGetMacLength()]; + + macEngine.doFinal(out, 0); + + return out; + } + + private static Hashtable copyMap(Map paramsMap) + { + Hashtable newTable = new Hashtable(); + + Iterator keys = paramsMap.keySet().iterator(); + while (keys.hasNext()) + { + Object key = keys.next(); + newTable.put(key, paramsMap.get(key)); + } + + return newTable; + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseSecretKeyFactory.java b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseSecretKeyFactory.java new file mode 100644 index 00000000..1ce00a3c --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseSecretKeyFactory.java @@ -0,0 +1,93 @@ +package org.spongycastle.jcajce.provider.symmetric.util; + +import java.lang.reflect.Constructor; +import java.security.InvalidKeyException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactorySpi; +import javax.crypto.spec.SecretKeySpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; + +public class BaseSecretKeyFactory + extends SecretKeyFactorySpi + implements PBE +{ + protected String algName; + protected ASN1ObjectIdentifier algOid; + + protected BaseSecretKeyFactory( + String algName, + ASN1ObjectIdentifier algOid) + { + this.algName = algName; + this.algOid = algOid; + } + + protected SecretKey engineGenerateSecret( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof SecretKeySpec) + { + return (SecretKey)keySpec; + } + + throw new InvalidKeySpecException("Invalid KeySpec"); + } + + protected KeySpec engineGetKeySpec( + SecretKey key, + Class keySpec) + throws InvalidKeySpecException + { + if (keySpec == null) + { + throw new InvalidKeySpecException("keySpec parameter is null"); + } + if (key == null) + { + throw new InvalidKeySpecException("key parameter is null"); + } + + if (SecretKeySpec.class.isAssignableFrom(keySpec)) + { + return new SecretKeySpec(key.getEncoded(), algName); + } + + try + { + Class[] parameters = { byte[].class }; + + Constructor c = keySpec.getConstructor(parameters); + Object[] p = new Object[1]; + + p[0] = key.getEncoded(); + + return (KeySpec)c.newInstance(p); + } + catch (Exception e) + { + throw new InvalidKeySpecException(e.toString()); + } + } + + protected SecretKey engineTranslateKey( + SecretKey key) + throws InvalidKeyException + { + if (key == null) + { + throw new InvalidKeyException("key parameter is null"); + } + + if (!key.getAlgorithm().equalsIgnoreCase(algName)) + { + throw new InvalidKeyException("Key not of type " + algName + "."); + } + + return new SecretKeySpec(key.getEncoded(), algName); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseStreamCipher.java b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseStreamCipher.java new file mode 100644 index 00000000..e362f383 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseStreamCipher.java @@ -0,0 +1,359 @@ +package org.spongycastle.jcajce.provider.symmetric.util; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.Key; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEParameterSpec; +import javax.crypto.spec.RC2ParameterSpec; +import javax.crypto.spec.RC5ParameterSpec; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.StreamCipher; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithIV; +import org.spongycastle.jce.provider.BouncyCastleProvider; + +public class BaseStreamCipher + extends BaseWrapCipher + implements PBE +{ + // + // specs we can handle. + // + private Class[] availableSpecs = + { + RC2ParameterSpec.class, + RC5ParameterSpec.class, + IvParameterSpec.class, + PBEParameterSpec.class + }; + + private StreamCipher cipher; + private ParametersWithIV ivParam; + + private int ivLength = 0; + + private PBEParameterSpec pbeSpec = null; + private String pbeAlgorithm = null; + + protected BaseStreamCipher( + StreamCipher engine, + int ivLength) + { + cipher = engine; + this.ivLength = ivLength; + } + + protected int engineGetBlockSize() + { + return 0; + } + + protected byte[] engineGetIV() + { + return (ivParam != null) ? ivParam.getIV() : null; + } + + protected int engineGetKeySize( + Key key) + { + return key.getEncoded().length * 8; + } + + protected int engineGetOutputSize( + int inputLen) + { + return inputLen; + } + + protected AlgorithmParameters engineGetParameters() + { + if (engineParams == null) + { + if (pbeSpec != null) + { + try + { + AlgorithmParameters engineParams = AlgorithmParameters.getInstance(pbeAlgorithm, BouncyCastleProvider.PROVIDER_NAME); + engineParams.init(pbeSpec); + + return engineParams; + } + catch (Exception e) + { + return null; + } + } + } + + return engineParams; + } + + /** + * should never be called. + */ + protected void engineSetMode( + String mode) + { + if (!mode.equalsIgnoreCase("ECB")) + { + throw new IllegalArgumentException("can't support mode " + mode); + } + } + + /** + * should never be called. + */ + protected void engineSetPadding( + String padding) + throws NoSuchPaddingException + { + if (!padding.equalsIgnoreCase("NoPadding")) + { + throw new NoSuchPaddingException("Padding " + padding + " unknown."); + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + CipherParameters param; + + this.pbeSpec = null; + this.pbeAlgorithm = null; + + this.engineParams = null; + + // + // basic key check + // + if (!(key instanceof SecretKey)) + { + throw new InvalidKeyException("Key for algorithm " + key.getAlgorithm() + " not suitable for symmetric enryption."); + } + + if (key instanceof BCPBEKey) + { + BCPBEKey k = (BCPBEKey)key; + + if (k.getOID() != null) + { + pbeAlgorithm = k.getOID().getId(); + } + else + { + pbeAlgorithm = k.getAlgorithm(); + } + + if (k.getParam() != null) + { + param = k.getParam(); + pbeSpec = new PBEParameterSpec(k.getSalt(), k.getIterationCount()); + } + else if (params instanceof PBEParameterSpec) + { + param = PBE.Util.makePBEParameters(k, params, cipher.getAlgorithmName()); + pbeSpec = (PBEParameterSpec)params; + } + else + { + throw new InvalidAlgorithmParameterException("PBE requires PBE parameters to be set."); + } + + if (k.getIvSize() != 0) + { + ivParam = (ParametersWithIV)param; + } + } + else if (params == null) + { + param = new KeyParameter(key.getEncoded()); + } + else if (params instanceof IvParameterSpec) + { + param = new ParametersWithIV(new KeyParameter(key.getEncoded()), ((IvParameterSpec)params).getIV()); + ivParam = (ParametersWithIV)param; + } + else + { + throw new InvalidAlgorithmParameterException("unknown parameter type."); + } + + if ((ivLength != 0) && !(param instanceof ParametersWithIV)) + { + SecureRandom ivRandom = random; + + if (ivRandom == null) + { + ivRandom = new SecureRandom(); + } + + if ((opmode == Cipher.ENCRYPT_MODE) || (opmode == Cipher.WRAP_MODE)) + { + byte[] iv = new byte[ivLength]; + + ivRandom.nextBytes(iv); + param = new ParametersWithIV(param, iv); + ivParam = (ParametersWithIV)param; + } + else + { + throw new InvalidAlgorithmParameterException("no IV set when one expected"); + } + } + + try + { + switch (opmode) + { + case Cipher.ENCRYPT_MODE: + case Cipher.WRAP_MODE: + cipher.init(true, param); + break; + case Cipher.DECRYPT_MODE: + case Cipher.UNWRAP_MODE: + cipher.init(false, param); + break; + default: + throw new InvalidParameterException("unknown opmode " + opmode + " passed"); + } + } + catch (Exception e) + { + throw new InvalidKeyException(e.getMessage()); + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + AlgorithmParameterSpec paramSpec = null; + + if (params != null) + { + for (int i = 0; i != availableSpecs.length; i++) + { + try + { + paramSpec = params.getParameterSpec(availableSpecs[i]); + break; + } + catch (Exception e) + { + continue; + } + } + + if (paramSpec == null) + { + throw new InvalidAlgorithmParameterException("can't handle parameter " + params.toString()); + } + } + + engineInit(opmode, key, paramSpec, random); + engineParams = params; + } + + protected void engineInit( + int opmode, + Key key, + SecureRandom random) + throws InvalidKeyException + { + try + { + engineInit(opmode, key, (AlgorithmParameterSpec)null, random); + } + catch (InvalidAlgorithmParameterException e) + { + throw new InvalidKeyException(e.getMessage()); + } + } + + protected byte[] engineUpdate( + byte[] input, + int inputOffset, + int inputLen) + { + byte[] out = new byte[inputLen]; + + cipher.processBytes(input, inputOffset, inputLen, out, 0); + + return out; + } + + protected int engineUpdate( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + throws ShortBufferException + { + try + { + cipher.processBytes(input, inputOffset, inputLen, output, outputOffset); + + return inputLen; + } + catch (DataLengthException e) + { + throw new ShortBufferException(e.getMessage()); + } + } + + protected byte[] engineDoFinal( + byte[] input, + int inputOffset, + int inputLen) + { + if (inputLen != 0) + { + byte[] out = engineUpdate(input, inputOffset, inputLen); + + cipher.reset(); + + return out; + } + + cipher.reset(); + + return new byte[0]; + } + + protected int engineDoFinal( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + { + if (inputLen != 0) + { + cipher.processBytes(input, inputOffset, inputLen, output, outputOffset); + } + + cipher.reset(); + + return inputLen; + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseWrapCipher.java b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseWrapCipher.java new file mode 100644 index 00000000..ce1909c0 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BaseWrapCipher.java @@ -0,0 +1,394 @@ +package org.spongycastle.jcajce.provider.symmetric.util; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.CipherSpi; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEParameterSpec; +import javax.crypto.spec.RC2ParameterSpec; +import javax.crypto.spec.RC5ParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.Wrapper; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithIV; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.jce.provider.BouncyCastleProvider; + +public abstract class BaseWrapCipher + extends CipherSpi + implements PBE +{ + // + // specs we can handle. + // + private Class[] availableSpecs = + { + IvParameterSpec.class, + PBEParameterSpec.class, + RC2ParameterSpec.class, + RC5ParameterSpec.class + }; + + protected int pbeType = PKCS12; + protected int pbeHash = SHA1; + protected int pbeKeySize; + protected int pbeIvSize; + + protected AlgorithmParameters engineParams = null; + + protected Wrapper wrapEngine = null; + + private int ivSize; + private byte[] iv; + + protected BaseWrapCipher() + { + } + + protected BaseWrapCipher( + Wrapper wrapEngine) + { + this(wrapEngine, 0); + } + + protected BaseWrapCipher( + Wrapper wrapEngine, + int ivSize) + { + this.wrapEngine = wrapEngine; + this.ivSize = ivSize; + } + + protected int engineGetBlockSize() + { + return 0; + } + + protected byte[] engineGetIV() + { + return (byte[])iv.clone(); + } + + protected int engineGetKeySize( + Key key) + { + return key.getEncoded().length; + } + + protected int engineGetOutputSize( + int inputLen) + { + return -1; + } + + protected AlgorithmParameters engineGetParameters() + { + return null; + } + + protected void engineSetMode( + String mode) + throws NoSuchAlgorithmException + { + throw new NoSuchAlgorithmException("can't support mode " + mode); + } + + protected void engineSetPadding( + String padding) + throws NoSuchPaddingException + { + throw new NoSuchPaddingException("Padding " + padding + " unknown."); + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + CipherParameters param; + + if (key instanceof BCPBEKey) + { + BCPBEKey k = (BCPBEKey)key; + + if (params instanceof PBEParameterSpec) + { + param = PBE.Util.makePBEParameters(k, params, wrapEngine.getAlgorithmName()); + } + else if (k.getParam() != null) + { + param = k.getParam(); + } + else + { + throw new InvalidAlgorithmParameterException("PBE requires PBE parameters to be set."); + } + } + else + { + param = new KeyParameter(key.getEncoded()); + } + + if (params instanceof IvParameterSpec) + { + IvParameterSpec iv = (IvParameterSpec) params; + param = new ParametersWithIV(param, iv.getIV()); + } + + if (param instanceof KeyParameter && ivSize != 0) + { + iv = new byte[ivSize]; + random.nextBytes(iv); + param = new ParametersWithIV(param, iv); + } + + if (random != null) + { + param = new ParametersWithRandom(param, random); + } + + switch (opmode) + { + case Cipher.WRAP_MODE: + wrapEngine.init(true, param); + break; + case Cipher.UNWRAP_MODE: + wrapEngine.init(false, param); + break; + case Cipher.ENCRYPT_MODE: + case Cipher.DECRYPT_MODE: + throw new IllegalArgumentException("engine only valid for wrapping"); + default: + System.out.println("eeek!"); + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + AlgorithmParameterSpec paramSpec = null; + + if (params != null) + { + for (int i = 0; i != availableSpecs.length; i++) + { + try + { + paramSpec = params.getParameterSpec(availableSpecs[i]); + break; + } + catch (Exception e) + { + // try next spec + } + } + + if (paramSpec == null) + { + throw new InvalidAlgorithmParameterException("can't handle parameter " + params.toString()); + } + } + + engineParams = params; + engineInit(opmode, key, paramSpec, random); + } + + protected void engineInit( + int opmode, + Key key, + SecureRandom random) + throws InvalidKeyException + { + try + { + engineInit(opmode, key, (AlgorithmParameterSpec)null, random); + } + catch (InvalidAlgorithmParameterException e) + { + throw new IllegalArgumentException(e.getMessage()); + } + } + + protected byte[] engineUpdate( + byte[] input, + int inputOffset, + int inputLen) + { + throw new RuntimeException("not supported for wrapping"); + } + + protected int engineUpdate( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + throws ShortBufferException + { + throw new RuntimeException("not supported for wrapping"); + } + + protected byte[] engineDoFinal( + byte[] input, + int inputOffset, + int inputLen) + throws IllegalBlockSizeException, BadPaddingException + { + return null; + } + + protected int engineDoFinal( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + throws IllegalBlockSizeException, BadPaddingException, ShortBufferException + { + return 0; + } + + protected byte[] engineWrap( + Key key) + throws IllegalBlockSizeException, InvalidKeyException + { + byte[] encoded = key.getEncoded(); + if (encoded == null) + { + throw new InvalidKeyException("Cannot wrap key, null encoding."); + } + + try + { + if (wrapEngine == null) + { + return engineDoFinal(encoded, 0, encoded.length); + } + else + { + return wrapEngine.wrap(encoded, 0, encoded.length); + } + } + catch (BadPaddingException e) + { + throw new IllegalBlockSizeException(e.getMessage()); + } + } + + protected Key engineUnwrap( + byte[] wrappedKey, + String wrappedKeyAlgorithm, + int wrappedKeyType) + throws InvalidKeyException, NoSuchAlgorithmException + { + byte[] encoded; + try + { + if (wrapEngine == null) + { + encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length); + } + else + { + encoded = wrapEngine.unwrap(wrappedKey, 0, wrappedKey.length); + } + } + catch (InvalidCipherTextException e) + { + throw new InvalidKeyException(e.getMessage()); + } + catch (BadPaddingException e) + { + throw new InvalidKeyException(e.getMessage()); + } + catch (IllegalBlockSizeException e2) + { + throw new InvalidKeyException(e2.getMessage()); + } + + if (wrappedKeyType == Cipher.SECRET_KEY) + { + return new SecretKeySpec(encoded, wrappedKeyAlgorithm); + } + else if (wrappedKeyAlgorithm.equals("") && wrappedKeyType == Cipher.PRIVATE_KEY) + { + /* + * The caller doesn't know the algorithm as it is part of + * the encrypted data. + */ + try + { + PrivateKeyInfo in = PrivateKeyInfo.getInstance(encoded); + + PrivateKey privKey = BouncyCastleProvider.getPrivateKey(in); + + if (privKey != null) + { + return privKey; + } + else + { + throw new InvalidKeyException("algorithm " + in.getPrivateKeyAlgorithm().getAlgorithm() + " not supported"); + } + } + catch (Exception e) + { + throw new InvalidKeyException("Invalid key encoding."); + } + } + else + { + try + { + KeyFactory kf = KeyFactory.getInstance(wrappedKeyAlgorithm, BouncyCastleProvider.PROVIDER_NAME); + + if (wrappedKeyType == Cipher.PUBLIC_KEY) + { + return kf.generatePublic(new X509EncodedKeySpec(encoded)); + } + else if (wrappedKeyType == Cipher.PRIVATE_KEY) + { + return kf.generatePrivate(new PKCS8EncodedKeySpec(encoded)); + } + } + catch (NoSuchProviderException e) + { + throw new InvalidKeyException("Unknown key type " + e.getMessage()); + } + catch (InvalidKeySpecException e2) + { + throw new InvalidKeyException("Unknown key type " + e2.getMessage()); + } + + throw new InvalidKeyException("Unknown key type " + wrappedKeyType); + } + } + +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BlockCipherProvider.java b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BlockCipherProvider.java new file mode 100644 index 00000000..b4a55c10 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/BlockCipherProvider.java @@ -0,0 +1,8 @@ +package org.spongycastle.jcajce.provider.symmetric.util; + +import org.spongycastle.crypto.BlockCipher; + +public interface BlockCipherProvider +{ + BlockCipher get(); +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/IvAlgorithmParameters.java b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/IvAlgorithmParameters.java new file mode 100644 index 00000000..1c1ae189 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/IvAlgorithmParameters.java @@ -0,0 +1,118 @@ +package org.spongycastle.jcajce.provider.symmetric.util; + +import java.io.IOException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; + +import javax.crypto.spec.IvParameterSpec; + +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.util.Arrays; + +public class IvAlgorithmParameters + extends BaseAlgorithmParameters +{ + private byte[] iv; + + protected byte[] engineGetEncoded() + throws IOException + { + return engineGetEncoded("ASN.1"); + } + + protected byte[] engineGetEncoded( + String format) + throws IOException + { + if (isASN1FormatString(format)) + { + return new DEROctetString(engineGetEncoded("RAW")).getEncoded(); + } + + if (format.equals("RAW")) + { + return Arrays.clone(iv); + } + + return null; + } + + protected AlgorithmParameterSpec localEngineGetParameterSpec( + Class paramSpec) + throws InvalidParameterSpecException + { + if (paramSpec == IvParameterSpec.class) + { + return new IvParameterSpec(iv); + } + + throw new InvalidParameterSpecException("unknown parameter spec passed to IV parameters object."); + } + + protected void engineInit( + AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException + { + if (!(paramSpec instanceof IvParameterSpec)) + { + throw new InvalidParameterSpecException("IvParameterSpec required to initialise a IV parameters algorithm parameters object"); + } + + this.iv = ((IvParameterSpec)paramSpec).getIV(); + } + + protected void engineInit( + byte[] params) + throws IOException + { + // + // check that we don't have a DER encoded octet string + // + if ((params.length % 8) != 0 + && params[0] == 0x04 && params[1] == params.length - 2) + { + ASN1OctetString oct = (ASN1OctetString)ASN1Primitive.fromByteArray(params); + + params = oct.getOctets(); + } + + this.iv = Arrays.clone(params); + } + + protected void engineInit( + byte[] params, + String format) + throws IOException + { + if (isASN1FormatString(format)) + { + try + { + ASN1OctetString oct = (ASN1OctetString)ASN1Primitive.fromByteArray(params); + + engineInit(oct.getOctets()); + } + catch (Exception e) + { + throw new IOException("Exception decoding: " + e); + } + + return; + } + + if (format.equals("RAW")) + { + engineInit(params); + return; + } + + throw new IOException("Unknown parameters format in IV parameters object"); + } + + protected String engineToString() + { + return "IV Parameters"; + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/PBE.java b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/PBE.java new file mode 100644 index 00000000..a0a4721e --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/PBE.java @@ -0,0 +1,319 @@ +package org.spongycastle.jcajce.provider.symmetric.util; + +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.PBEParametersGenerator; +import org.spongycastle.crypto.digests.GOST3411Digest; +import org.spongycastle.crypto.digests.MD2Digest; +import org.spongycastle.crypto.digests.MD5Digest; +import org.spongycastle.crypto.digests.RIPEMD160Digest; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.digests.SHA256Digest; +import org.spongycastle.crypto.digests.TigerDigest; +import org.spongycastle.crypto.generators.OpenSSLPBEParametersGenerator; +import org.spongycastle.crypto.generators.PKCS12ParametersGenerator; +import org.spongycastle.crypto.generators.PKCS5S1ParametersGenerator; +import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator; +import org.spongycastle.crypto.params.DESParameters; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithIV; + +public interface PBE +{ + // + // PBE Based encryption constants - by default we do PKCS12 with SHA-1 + // + static final int MD5 = 0; + static final int SHA1 = 1; + static final int RIPEMD160 = 2; + static final int TIGER = 3; + static final int SHA256 = 4; + static final int MD2 = 5; + static final int GOST3411 = 6; + + static final int PKCS5S1 = 0; + static final int PKCS5S2 = 1; + static final int PKCS12 = 2; + static final int OPENSSL = 3; + static final int PKCS5S1_UTF8 = 4; + static final int PKCS5S2_UTF8 = 5; + + /** + * uses the appropriate mixer to generate the key and IV if necessary. + */ + static class Util + { + static private PBEParametersGenerator makePBEGenerator( + int type, + int hash) + { + PBEParametersGenerator generator; + + if (type == PKCS5S1 || type == PKCS5S1_UTF8) + { + switch (hash) + { + case MD2: + generator = new PKCS5S1ParametersGenerator(new MD2Digest()); + break; + case MD5: + generator = new PKCS5S1ParametersGenerator(new MD5Digest()); + break; + case SHA1: + generator = new PKCS5S1ParametersGenerator(new SHA1Digest()); + break; + default: + throw new IllegalStateException("PKCS5 scheme 1 only supports MD2, MD5 and SHA1."); + } + } + else if (type == PKCS5S2 || type == PKCS5S2_UTF8) + { + switch (hash) + { + case MD2: + generator = new PKCS5S2ParametersGenerator(new MD2Digest()); + break; + case MD5: + generator = new PKCS5S2ParametersGenerator(new MD5Digest()); + break; + case SHA1: + generator = new PKCS5S2ParametersGenerator(new SHA1Digest()); + break; + case RIPEMD160: + generator = new PKCS5S2ParametersGenerator(new RIPEMD160Digest()); + break; + case TIGER: + generator = new PKCS5S2ParametersGenerator(new TigerDigest()); + break; + case SHA256: + generator = new PKCS5S2ParametersGenerator(new SHA256Digest()); + break; + case GOST3411: + generator = new PKCS5S2ParametersGenerator(new GOST3411Digest()); + break; + default: + throw new IllegalStateException("unknown digest scheme for PBE PKCS5S2 encryption."); + } + } + else if (type == PKCS12) + { + switch (hash) + { + case MD2: + generator = new PKCS12ParametersGenerator(new MD2Digest()); + break; + case MD5: + generator = new PKCS12ParametersGenerator(new MD5Digest()); + break; + case SHA1: + generator = new PKCS12ParametersGenerator(new SHA1Digest()); + break; + case RIPEMD160: + generator = new PKCS12ParametersGenerator(new RIPEMD160Digest()); + break; + case TIGER: + generator = new PKCS12ParametersGenerator(new TigerDigest()); + break; + case SHA256: + generator = new PKCS12ParametersGenerator(new SHA256Digest()); + break; + case GOST3411: + generator = new PKCS12ParametersGenerator(new GOST3411Digest()); + break; + default: + throw new IllegalStateException("unknown digest scheme for PBE encryption."); + } + } + else + { + generator = new OpenSSLPBEParametersGenerator(); + } + + return generator; + } + + /** + * construct a key and iv (if necessary) suitable for use with a + * Cipher. + */ + public static CipherParameters makePBEParameters( + BCPBEKey pbeKey, + AlgorithmParameterSpec spec, + String targetAlgorithm) + { + if ((spec == null) || !(spec instanceof PBEParameterSpec)) + { + throw new IllegalArgumentException("Need a PBEParameter spec with a PBE key."); + } + + PBEParameterSpec pbeParam = (PBEParameterSpec)spec; + PBEParametersGenerator generator = makePBEGenerator(pbeKey.getType(), pbeKey.getDigest()); + byte[] key = pbeKey.getEncoded(); + CipherParameters param; + + if (pbeKey.shouldTryWrongPKCS12()) + { + key = new byte[2]; + } + + generator.init(key, pbeParam.getSalt(), pbeParam.getIterationCount()); + + if (pbeKey.getIvSize() != 0) + { + param = generator.generateDerivedParameters(pbeKey.getKeySize(), pbeKey.getIvSize()); + } + else + { + param = generator.generateDerivedParameters(pbeKey.getKeySize()); + } + + if (targetAlgorithm.startsWith("DES")) + { + if (param instanceof ParametersWithIV) + { + KeyParameter kParam = (KeyParameter)((ParametersWithIV)param).getParameters(); + + DESParameters.setOddParity(kParam.getKey()); + } + else + { + KeyParameter kParam = (KeyParameter)param; + + DESParameters.setOddParity(kParam.getKey()); + } + } + + for (int i = 0; i != key.length; i++) + { + key[i] = 0; + } + + return param; + } + + /** + * generate a PBE based key suitable for a MAC algorithm, the + * key size is chosen according the MAC size, or the hashing algorithm, + * whichever is greater. + */ + public static CipherParameters makePBEMacParameters( + BCPBEKey pbeKey, + AlgorithmParameterSpec spec) + { + if ((spec == null) || !(spec instanceof PBEParameterSpec)) + { + throw new IllegalArgumentException("Need a PBEParameter spec with a PBE key."); + } + + PBEParameterSpec pbeParam = (PBEParameterSpec)spec; + PBEParametersGenerator generator = makePBEGenerator(pbeKey.getType(), pbeKey.getDigest()); + byte[] key = pbeKey.getEncoded(); + CipherParameters param; + + if (pbeKey.shouldTryWrongPKCS12()) + { + key = new byte[2]; + } + + generator.init(key, pbeParam.getSalt(), pbeParam.getIterationCount()); + + param = generator.generateDerivedMacParameters(pbeKey.getKeySize()); + + for (int i = 0; i != key.length; i++) + { + key[i] = 0; + } + + return param; + } + + /** + * construct a key and iv (if necessary) suitable for use with a + * Cipher. + */ + public static CipherParameters makePBEParameters( + PBEKeySpec keySpec, + int type, + int hash, + int keySize, + int ivSize) + { + PBEParametersGenerator generator = makePBEGenerator(type, hash); + byte[] key; + CipherParameters param; + + key = convertPassword(type, keySpec); + + generator.init(key, keySpec.getSalt(), keySpec.getIterationCount()); + + if (ivSize != 0) + { + param = generator.generateDerivedParameters(keySize, ivSize); + } + else + { + param = generator.generateDerivedParameters(keySize); + } + + for (int i = 0; i != key.length; i++) + { + key[i] = 0; + } + + return param; + } + + + /** + * generate a PBE based key suitable for a MAC algorithm, the + * key size is chosen according the MAC size, or the hashing algorithm, + * whichever is greater. + */ + public static CipherParameters makePBEMacParameters( + PBEKeySpec keySpec, + int type, + int hash, + int keySize) + { + PBEParametersGenerator generator = makePBEGenerator(type, hash); + byte[] key; + CipherParameters param; + + key = convertPassword(type, keySpec); + + generator.init(key, keySpec.getSalt(), keySpec.getIterationCount()); + + param = generator.generateDerivedMacParameters(keySize); + + for (int i = 0; i != key.length; i++) + { + key[i] = 0; + } + + return param; + } + + private static byte[] convertPassword(int type, PBEKeySpec keySpec) + { + byte[] key; + + if (type == PKCS12) + { + key = PBEParametersGenerator.PKCS12PasswordToBytes(keySpec.getPassword()); + } + else if (type == PKCS5S2_UTF8 || type == PKCS5S1_UTF8) + { + key = PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(keySpec.getPassword()); + } + else + { + key = PBEParametersGenerator.PKCS5PasswordToBytes(keySpec.getPassword()); + } + return key; + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/PBESecretKeyFactory.java b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/PBESecretKeyFactory.java new file mode 100644 index 00000000..0e4db8ec --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/symmetric/util/PBESecretKeyFactory.java @@ -0,0 +1,68 @@ +package org.spongycastle.jcajce.provider.symmetric.util; + +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +import javax.crypto.SecretKey; +import javax.crypto.spec.PBEKeySpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.crypto.CipherParameters; + +public class PBESecretKeyFactory + extends BaseSecretKeyFactory + implements PBE +{ + private boolean forCipher; + private int scheme; + private int digest; + private int keySize; + private int ivSize; + + public PBESecretKeyFactory( + String algorithm, + ASN1ObjectIdentifier oid, + boolean forCipher, + int scheme, + int digest, + int keySize, + int ivSize) + { + super(algorithm, oid); + + this.forCipher = forCipher; + this.scheme = scheme; + this.digest = digest; + this.keySize = keySize; + this.ivSize = ivSize; + } + + protected SecretKey engineGenerateSecret( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof PBEKeySpec) + { + PBEKeySpec pbeSpec = (PBEKeySpec)keySpec; + CipherParameters param; + + if (pbeSpec.getSalt() == null) + { + return new BCPBEKey(this.algName, this.algOid, scheme, digest, keySize, ivSize, pbeSpec, null); + } + + if (forCipher) + { + param = PBE.Util.makePBEParameters(pbeSpec, scheme, digest, keySize, ivSize); + } + else + { + param = PBE.Util.makePBEMacParameters(pbeSpec, scheme, digest, keySize); + } + + return new BCPBEKey(this.algName, this.algOid, scheme, digest, keySize, ivSize, pbeSpec, param); + } + + throw new InvalidKeySpecException("Invalid KeySpec"); + } +} |