diff options
author | David Hook <dgh@cryptoworkshop.com> | 2013-09-13 10:27:50 +0400 |
---|---|---|
committer | David Hook <dgh@cryptoworkshop.com> | 2013-09-13 10:27:50 +0400 |
commit | 1c67cdcb2d9d30487efee98979ee1646a89553de (patch) | |
tree | 6b2630bc78c2cf73ae9b8c857988330fba9e5d45 | |
parent | 972c20edfaa73bf197b1665eb3c4818c01ac3934 (diff) |
added support for GOST in conjunction with PBKDF2.
added support for keyMeshing with GOST block cipher (see GCFBBlockCipher).
added correct encoding/decoding for GOST private keys.
26 files changed, 972 insertions, 244 deletions
diff --git a/core/src/main/j2me/java/util/AbstractList.java b/core/src/main/j2me/java/util/AbstractList.java index 42dda96e..dbb1dffc 100644 --- a/core/src/main/j2me/java/util/AbstractList.java +++ b/core/src/main/j2me/java/util/AbstractList.java @@ -57,7 +57,7 @@ public abstract class AbstractList { int index = li.nextIndex(); e = li.next(); - System.out.println(e); + if (o == null) { if (e == null) @@ -217,14 +217,13 @@ public abstract class AbstractList protected void removeRange(int fromIndex, int toIndex) { - System.out.println("breakpoint 1"); if (fromIndex == toIndex) { return; } - System.out.println("breakpoint 2"); + ListIterator li = listIterator(fromIndex); - System.out.println("breakpoint 3"); + int i = fromIndex; do { diff --git a/core/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java index 108270e5..c6e8e0d1 100644 --- a/core/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java +++ b/core/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java @@ -19,7 +19,7 @@ public interface CryptoProObjectIdentifiers static final ASN1ObjectIdentifier gostR3411Hmac = GOST_id.branch("10"); /** Gost R28147 OID: 1.2.643.2.2.21 */ - static final ASN1ObjectIdentifier gostR28147_cbc = GOST_id.branch("21"); + static final ASN1ObjectIdentifier gostR28147_gcfb = GOST_id.branch("21"); /** Gost R28147-89-CryotoPro-A-ParamSet OID: 1.2.643.2.2.31.1 */ static final ASN1ObjectIdentifier id_Gost28147_89_CryptoPro_A_ParamSet = GOST_id.branch("31.1"); diff --git a/core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java b/core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java index 0307f50f..45d78147 100644 --- a/core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java +++ b/core/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java @@ -57,6 +57,9 @@ public class GOST3410PublicKeyAlgParameters this.encryptionParamSet = encryptionParamSet; } + /** + * @deprecated use getInstance() + */ public GOST3410PublicKeyAlgParameters( ASN1Sequence seq) { diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java index c8e92dc9..92c4e8f1 100644 --- a/core/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java +++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java @@ -89,6 +89,42 @@ public class PBKDF2Params this.keyLength = new ASN1Integer(keyLength); } + /** + * Create a PBKDF2Params with the specified salt, iteration count, keyLength, and a defined prf. + * + * @param salt input salt. + * @param iterationCount input iteration count. + * @param keyLength intended key length to be produced. + * @param prf the pseudo-random function to use. + */ + public PBKDF2Params( + byte[] salt, + int iterationCount, + int keyLength, + AlgorithmIdentifier prf) + { + this(salt, iterationCount); + + this.keyLength = new ASN1Integer(keyLength); + this.prf = prf; + } + + /** + * Create a PBKDF2Params with the specified salt, iteration count, and a defined prf. + * + * @param salt input salt. + * @param iterationCount input iteration count. + * @param prf the pseudo-random function to use. + */ + public PBKDF2Params( + byte[] salt, + int iterationCount, + AlgorithmIdentifier prf) + { + this(salt, iterationCount); + this.prf = prf; + } + private PBKDF2Params( ASN1Sequence seq) { diff --git a/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java index bdb694d4..dd056aca 100644 --- a/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java @@ -54,7 +54,7 @@ public class BufferedBlockCipher } else { - partialBlockOkay = (idx > 0 && (name.startsWith("CFB", idx) || name.startsWith("OFB", idx) || name.startsWith("OpenPGP", idx) || name.startsWith("SIC", idx) || name.startsWith("GCTR", idx))); + partialBlockOkay = (idx > 0 && (name.startsWith("CFB", idx) || name.startsWith("GCFB", idx) ||name.startsWith("OFB", idx) || name.startsWith("OpenPGP", idx) || name.startsWith("SIC", idx) || name.startsWith("GCTR", idx))); } } diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java index 640ead46..0954d481 100644 --- a/core/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java @@ -58,7 +58,7 @@ public class PKCS5S2ParametersGenerator hMac.doFinal(state, 0); System.arraycopy(state, 0, out, outOff, state.length); - + for (int count = 1; count < c; count++) { hMac.update(state, 0, state.length); diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java index d0fb9bb6..a8851690 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java @@ -4,6 +4,7 @@ import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; /** * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher. @@ -246,6 +247,16 @@ public class CFBBlockCipher } /** + * Return the current state of the initialisation vector. + * + * @return current IV + */ + public byte[] getCurrentIV() + { + return Arrays.clone(cfbV); + } + + /** * reset the chaining vector back to the IV and reset the underlying * cipher. */ diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/GCFBBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/GCFBBlockCipher.java index f441b89f..887c1697 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/GCFBBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/GCFBBlockCipher.java @@ -3,38 +3,107 @@ package org.bouncycastle.crypto.modes; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.crypto.params.ParametersWithSBox; +/** + * An implementation of the GOST CFB mode with CryptoPro key meshing as described in RFC 4357. + */ public class GCFBBlockCipher implements BlockCipher { + private static final byte[] C = + { + 0x69, 0x00, 0x72, 0x22, 0x64, (byte)0xC9, 0x04, 0x23, + (byte)0x8D, 0x3A, (byte)0xDB, (byte)0x96, 0x46, (byte)0xE9, 0x2A, (byte)0xC4, + 0x18, (byte)0xFE, (byte)0xAC, (byte)0x94, 0x00, (byte)0xED, 0x07, 0x12, + (byte)0xC0, (byte)0x86, (byte)0xDC, (byte)0xC2, (byte)0xEF, 0x4C, (byte)0xA9, 0x2B + }; + + private final CFBBlockCipher cfbEngine; + + private KeyParameter key; + private long counter = 0; + private boolean forEncryption; + public GCFBBlockCipher(BlockCipher engine) { - + this.cfbEngine = new CFBBlockCipher(engine, engine.getBlockSize() * 8); } + public void init(boolean forEncryption, CipherParameters params) throws IllegalArgumentException { - //To change body of implemented methods use File | Settings | File Templates. + counter = 0; + cfbEngine.init(forEncryption, params); + + this.forEncryption = forEncryption; + + if (params instanceof ParametersWithIV) + { + params = ((ParametersWithIV)params).getParameters(); + } + + if (params instanceof ParametersWithRandom) + { + params = ((ParametersWithRandom)params).getParameters(); + } + + if (params instanceof ParametersWithSBox) + { + params = ((ParametersWithSBox)params).getParameters(); + } + + key = (KeyParameter)params; } public String getAlgorithmName() { - return null; //To change body of implemented methods use File | Settings | File Templates. + return "G" + cfbEngine.getAlgorithmName(); } public int getBlockSize() { - return 0; //To change body of implemented methods use File | Settings | File Templates. + return cfbEngine.getBlockSize(); } public int processBlock(byte[] in, int inOff, byte[] out, int outOff) throws DataLengthException, IllegalStateException { - return 0; //To change body of implemented methods use File | Settings | File Templates. + if (counter > 0 && counter % 1024 == 0) + { + BlockCipher base = cfbEngine.getUnderlyingCipher(); + + base.init(false, key); + + byte[] nextKey = new byte[32]; + + base.processBlock(C, 0, nextKey, 0); + base.processBlock(C, 8, nextKey, 8); + base.processBlock(C, 16, nextKey, 16); + base.processBlock(C, 24, nextKey, 24); + + key = new KeyParameter(nextKey); + + byte[] iv = new byte[8]; + + base.init(true, key); + + base.processBlock(cfbEngine.getCurrentIV(), 0, iv, 0); + + cfbEngine.init(forEncryption, new ParametersWithIV(key, iv)); + } + + counter += cfbEngine.getBlockSize(); + + return cfbEngine.processBlock(in, inOff, out, outOff); } public void reset() { - //To change body of implemented methods use File | Settings | File Templates. + counter = 0; + cfbEngine.reset(); } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/GOST3411DigestTest.java b/core/src/test/java/org/bouncycastle/crypto/test/GOST3411DigestTest.java index 9fb81a6c..7bf5be72 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/GOST3411DigestTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/GOST3411DigestTest.java @@ -2,6 +2,12 @@ package org.bouncycastle.crypto.test; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.GOST3411Digest; +import org.bouncycastle.crypto.engines.GOST28147Engine; +import org.bouncycastle.crypto.generators.PKCS5S1ParametersGenerator; +import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; +import org.bouncycastle.crypto.macs.HMac; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.util.encoders.Hex; public class GOST3411DigestTest extends DigestTest @@ -45,6 +51,19 @@ public class GOST3411DigestTest super.performTest(); millionATest(million_a_digest); + + HMac gMac = new HMac(new GOST3411Digest()); + + gMac.init(new KeyParameter(PKCS5S1ParametersGenerator.PKCS5PasswordToUTF8Bytes("1".toCharArray()))); + + byte[] data = "fred".getBytes(); + + gMac.update(data, 0, data.length); + byte[] mac = new byte[gMac.getMacSize()]; + + gMac.doFinal(mac, 0); + System.err.println("e080de3bde792327a6cccfa5dfd51e72b6829baa88d8130ed1a48822873fc7f6"); + System.err.println(new String(Hex.encode(mac))); } protected Digest cloneDigest(Digest digest) @@ -55,6 +74,34 @@ public class GOST3411DigestTest public static void main( String[] args) { + HMac gMac = new HMac(new GOST3411Digest(GOST28147Engine.getSBox("D-Test"))); + + gMac.init(new KeyParameter(PKCS5S1ParametersGenerator.PKCS5PasswordToUTF8Bytes("Boss".toCharArray()))); + byte[] iBuf = new byte[4]; + byte[] data = Hex.decode("b5d78fa546ba645c"); + + gMac.update(data, 0, data.length); + byte[] mac = new byte[gMac.getMacSize()]; + + int pos = 3; + while (++iBuf[pos] == 0) + { + --pos; + } + gMac.update(iBuf, 0, iBuf.length); + + gMac.doFinal(mac, 0); + + System.err.println(mac.length + " " + new String(Hex.encode(mac))); + + PKCS5S2ParametersGenerator pGen = new PKCS5S2ParametersGenerator(new GOST3411Digest()); + + pGen.init(PKCS5S1ParametersGenerator.PKCS5PasswordToUTF8Bytes("1".toCharArray()), data, 2048); + + KeyParameter kp = (KeyParameter)pGen.generateDerivedMacParameters(256); + + System.err.println(kp.getKey().length + " " + new String(Hex.encode(kp.getKey()))); + runTest(new GOST3411DigestTest()); } } diff --git a/pkix/src/main/java/org/bouncycastle/operator/DefaultSecretKeyProvider.java b/pkix/src/main/java/org/bouncycastle/operator/DefaultSecretKeyProvider.java index 234c38b1..8c419808 100644 --- a/pkix/src/main/java/org/bouncycastle/operator/DefaultSecretKeyProvider.java +++ b/pkix/src/main/java/org/bouncycastle/operator/DefaultSecretKeyProvider.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.Map; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; @@ -34,6 +35,8 @@ public class DefaultSecretKeyProvider keySizes.put(NTTObjectIdentifiers.id_camellia192_cbc, Integers.valueOf(192)); keySizes.put(NTTObjectIdentifiers.id_camellia256_cbc, Integers.valueOf(256)); + keySizes.put(CryptoProObjectIdentifiers.gostR28147_gcfb, Integers.valueOf(256)); + KEY_SIZES = Collections.unmodifiableMap(keySizes); } diff --git a/pkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java b/pkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java index 79ab492f..eaddab4d 100644 --- a/pkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java +++ b/pkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java @@ -11,8 +11,10 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; +import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.cryptopro.GOST28147Parameters; import org.bouncycastle.asn1.pkcs.PBES2Parameters; import org.bouncycastle.asn1.pkcs.PBKDF2Params; import org.bouncycastle.asn1.pkcs.PKCS12PBEParams; @@ -23,13 +25,13 @@ import org.bouncycastle.jcajce.JcaJceHelper; import org.bouncycastle.jcajce.NamedJcaJceHelper; import org.bouncycastle.jcajce.ProviderJcaJceHelper; import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey; +import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec; +import org.bouncycastle.jcajce.spec.PBKDF2KeySpec; import org.bouncycastle.operator.DefaultSecretKeyProvider; -import org.bouncycastle.operator.GenericKey; import org.bouncycastle.operator.InputDecryptor; import org.bouncycastle.operator.InputDecryptorProvider; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.SecretKeySizeProvider; -import org.bouncycastle.operator.jcajce.JceGenericKey; public class JcePKCSPBEInputDecryptorProviderBuilder { @@ -125,13 +127,31 @@ public class JcePKCSPBEInputDecryptorProviderBuilder SecretKeyFactory keyFact = helper.createSecretKeyFactory(alg.getKeyDerivationFunc().getAlgorithm().getId()); - key = keyFact.generateSecret(new PBEKeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme))); + if (func.isDefaultPrf()) + { + key = keyFact.generateSecret(new PBEKeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme))); + } + else + { + key = keyFact.generateSecret(new PBKDF2KeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme), func.getPrf())); + } cipher = helper.createCipher(alg.getEncryptionScheme().getAlgorithm().getId()); encryptionAlg = AlgorithmIdentifier.getInstance(alg.getEncryptionScheme()); - cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ASN1OctetString.getInstance(alg.getEncryptionScheme().getParameters()).getOctets())); + ASN1Encodable encParams = alg.getEncryptionScheme().getParameters(); + if (encParams instanceof ASN1OctetString) + { + cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ASN1OctetString.getInstance(encParams).getOctets())); + } + else + { + // TODO: at the moment it's just GOST, but... + GOST28147Parameters gParams = GOST28147Parameters.getInstance(encParams); + + cipher.init(Cipher.DECRYPT_MODE, key, new GOST28147ParameterSpec(gParams.getEncryptionParamSet(), gParams.getIV())); + } } } catch (Exception e) @@ -150,11 +170,6 @@ public class JcePKCSPBEInputDecryptorProviderBuilder { return new CipherInputStream(input, cipher); } - - public GenericKey getKey() - { - return new JceGenericKey(encryptionAlg, key); - } }; } }; diff --git a/pkix/src/test/java/org/bouncycastle/pkcs/test/PfxPduTest.java b/pkix/src/test/java/org/bouncycastle/pkcs/test/PfxPduTest.java index 9c4d138c..2bbf9eaa 100644 --- a/pkix/src/test/java/org/bouncycastle/pkcs/test/PfxPduTest.java +++ b/pkix/src/test/java/org/bouncycastle/pkcs/test/PfxPduTest.java @@ -19,6 +19,7 @@ import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.DERBMPString; import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; import org.bouncycastle.asn1.pkcs.Attribute; import org.bouncycastle.asn1.pkcs.ContentInfo; @@ -455,6 +456,74 @@ public class PfxPduTest + "LgBvAHIAZzAxMCEwCQYFKw4DAhoFAAQUc8hyg5aq/58lH3whwo66zJkWY28E" + "CKHZUIQsQX9hAgIIAA=="); + private byte[] gostPfx = Base64.decode( + "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" + + "BAgY5sDtkrVeBQICCAA="); + + private byte[] gostPfxFoo123 = Base64.decode( + "MIID6gIBAzCCA6MGCSqGSIb3DQEHAaCCA5QEggOQMIIDjDCCApQGCSqGSIb3" + + "DQEHBqCCAoUwggKBAgEAMIICegYJKoZIhvcNAQcBMFUGCSqGSIb3DQEFDTBI" + + "MCcGCSqGSIb3DQEFDDAaBAhIVrbUVNoQ2wICCAAwCgYGKoUDAgIKBQAwHQYG" + + "KoUDAgIVMBMECBLmAh+XCCYhBgcqhQMCAh8BgIICFP9hQLgDq5SORy2npOdo" + + "1bvoGl9Qdga1kV9s2c1/Y1kTGpuiYKfm5Il+PurzYdE5t/Wi2+SxoePm/AKA" + + "x1Ep5btK/002wnyRbUKdjgF1r7fMXRrd5Ioy8lYxB1v6qhHmzE5fz7FxY+iV" + + "Z70dSRS0JkTasI8MRsFLkJJfDb9twgoch8lYGFfYirHLcVy4xxA3JO9VSHm2" + + "8nuSWSnsmGN0ufPX14UpV2RFe3Rt0gZ0Jc8u2h2Mo0sIoVU6HVwdXzoe6LN7" + + "1NPZdRuhVtjxEvjDAvNJ8WHXQnBQMai2nVAj87uNr6OHLRs+foEccEY9WpPQ" + + "GPt4XbPt4MtmVctT2+Gsvf6Ws2UCx6hD4k8i28a6xS8lhTVam2g/2Z5cxtUV" + + "HxYt7j13HjuQVsuSNdgtrUnw3l43LnBxRZhlFz0r2zrvTB04ynenS+lGdVuG" + + "0TauIH+rdP1ubujw6lFdG9RNgUxWvS5IdwbFGX73a+ZrWiYJeERX11N/6r3g" + + "0EqVFNH9t/ROsdAtCCe2FycQoOSb+VxPU6I+SHjwe7Oa7R8Xxazh/eWTsV59" + + "QzPuLriUMbyYdQIf4xdclgcJoxFElopgl4orRfzH3XQsVbtTxN33lwjkE0j/" + + "686VtcO+b+dU7+BEB7O5yDcx1tupgre0ha/0KOlYfPvmbogGdDf0r6MOwrS7" + + "QFXxKlHfp8vn4mNwoy7pjrzjmjclkbkwgfEGCSqGSIb3DQEHAaCB4wSB4DCB" + + "3TCB2gYLKoZIhvcNAQwKAQKggaMwgaAwVQYJKoZIhvcNAQUNMEgwJwYJKoZI" + + "hvcNAQUMMBoECLD6Ld7TqurqAgIIADAKBgYqhQMCAgoFADAdBgYqhQMCAhUw" + + "EwQIoYXV7LETOEAGByqFAwICHwEERyBQK9LuYnOO0ELrge+a6JFeAVwPL85V" + + "ip2Kj/GfD3nzZR4tPzCwAt79RriKQklNqa3uCc9o0C9Zk5Qcj36SqiXxD1tz" + + "Ea63MSUwIwYJKoZIhvcNAQkVMRYEFKjg5gKM+i+vFhSwaga8YGaZ5thVMD4w" + + "LjAKBgYqhQMCAgkFAAQgIwD0CRCwva2Bjdlv5g970H2bCB1aafBNr/hxJLZE" + + "Ey4ECAW3VYXJzJpYAgIIAA=="); + /** * we generate the CA's certificate */ @@ -1042,6 +1111,90 @@ public class PfxPduTest } } + public void testGOST1() + throws Exception + { + char[] password = "1".toCharArray(); + + InputDecryptorProvider inputDecryptorProvider = new JcePKCSPBEInputDecryptorProviderBuilder() + .setProvider("BC").build(password); + PKCS12PfxPdu pfx = new PKCS12PfxPdu(gostPfx); + + assertTrue(pfx.hasMac()); + assertTrue(pfx.isMacValid(new JcePKCS12MacCalculatorBuilderProvider().setProvider("BC"), password)); + + ContentInfo[] infos = pfx.getContentInfos(); + + for (int i = 0; i != infos.length; i++) + { + if (infos[i].getContentType().equals(PKCSObjectIdentifiers.encryptedData)) + { + PKCS12SafeBagFactory dataFact = new PKCS12SafeBagFactory(infos[i], inputDecryptorProvider); + + PKCS12SafeBag[] bags = dataFact.getSafeBags(); + + // TODO: finish! +// assertEquals(3, bags.length); +// assertEquals(PKCSObjectIdentifiers.certBag, bags[0].getType()); + } + else + { + PKCS12SafeBagFactory dataFact = new PKCS12SafeBagFactory(infos[i]); + + PKCS12SafeBag[] bags = dataFact.getSafeBags(); + + assertEquals(1, bags.length); + assertEquals(PKCSObjectIdentifiers.pkcs8ShroudedKeyBag, bags[0].getType()); + + PKCS8EncryptedPrivateKeyInfo encInfo = (PKCS8EncryptedPrivateKeyInfo)bags[0].getBagValue(); + PrivateKeyInfo info = encInfo.decryptPrivateKeyInfo(inputDecryptorProvider); + assertEquals(CryptoProObjectIdentifiers.gostR3410_2001, info.getPrivateKeyAlgorithm().getAlgorithm()); + } + } + } + + public void testGOST2() + throws Exception + { + char[] password = "foo123".toCharArray(); + + InputDecryptorProvider inputDecryptorProvider = new JcePKCSPBEInputDecryptorProviderBuilder() + .setProvider("BC").build(password); + PKCS12PfxPdu pfx = new PKCS12PfxPdu(gostPfxFoo123); + + assertTrue(pfx.hasMac()); + assertTrue(pfx.isMacValid(new JcePKCS12MacCalculatorBuilderProvider().setProvider("BC"), password)); + + ContentInfo[] infos = pfx.getContentInfos(); + + for (int i = 0; i != infos.length; i++) + { + if (infos[i].getContentType().equals(PKCSObjectIdentifiers.encryptedData)) + { + PKCS12SafeBagFactory dataFact = new PKCS12SafeBagFactory(infos[i], inputDecryptorProvider); + + PKCS12SafeBag[] bags = dataFact.getSafeBags(); + + // TODO: finish! +// assertEquals(3, bags.length); +// assertEquals(PKCSObjectIdentifiers.certBag, bags[0].getType()); + } + else + { + PKCS12SafeBagFactory dataFact = new PKCS12SafeBagFactory(infos[i]); + + PKCS12SafeBag[] bags = dataFact.getSafeBags(); + + assertEquals(1, bags.length); + assertEquals(PKCSObjectIdentifiers.pkcs8ShroudedKeyBag, bags[0].getType()); + + PKCS8EncryptedPrivateKeyInfo encInfo = (PKCS8EncryptedPrivateKeyInfo)bags[0].getBagValue(); + PrivateKeyInfo info = encInfo.decryptPrivateKeyInfo(inputDecryptorProvider); + assertEquals(CryptoProObjectIdentifiers.gostR3410_2001, info.getPrivateKeyAlgorithm().getAlgorithm()); + } + } + } + private X509Certificate[] createCertChain(KeyFactory fact, PublicKey pubKey) throws Exception { diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java index 88d81c0a..02bf4533 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java @@ -14,27 +14,32 @@ import java.util.Enumeration; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERBitString; import org.bouncycastle.asn1.DERInteger; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.DERObjectIdentifier; +import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves; +import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.asn1.x9.X962Parameters; import org.bouncycastle.asn1.x9.X9ECParameters; -import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util; import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl; +import org.bouncycastle.jce.ECGOST3410NamedCurveTable; import org.bouncycastle.jce.interfaces.ECPointEncoder; import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; import org.bouncycastle.jce.spec.ECNamedCurveSpec; import org.bouncycastle.math.ec.ECCurve; @@ -43,10 +48,11 @@ public class BCECGOST3410PrivateKey { static final long serialVersionUID = 7245981689601667138L; - private String algorithm = "ECGOST3410"; - private boolean withCompression; + private String algorithm = "ECGOST3410"; + private boolean withCompression; - private transient BigInteger d; + private transient GOST3410PublicKeyAlgParameters gostParams; + private transient BigInteger d; private transient ECParameterSpec ecSpec; private transient DERBitString publicKey; private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl(); @@ -99,6 +105,7 @@ public class BCECGOST3410PrivateKey this.withCompression = key.withCompression; this.attrCarrier = key.attrCarrier; this.publicKey = key.publicKey; + this.gostParams = key.gostParams; } public BCECGOST3410PrivateKey( @@ -107,7 +114,7 @@ public class BCECGOST3410PrivateKey BCECGOST3410PublicKey pubKey, ECParameterSpec spec) { - ECDomainParameters dp = params.getParameters(); + ECDomainParameters dp = params.getParameters(); this.algorithm = algorithm; this.d = params.getD(); @@ -117,18 +124,20 @@ public class BCECGOST3410PrivateKey EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); this.ecSpec = new ECParameterSpec( - ellipticCurve, - new ECPoint( - dp.getG().getX().toBigInteger(), - dp.getG().getY().toBigInteger()), - dp.getN(), - dp.getH().intValue()); + ellipticCurve, + new ECPoint( + dp.getG().getX().toBigInteger(), + dp.getG().getY().toBigInteger()), + dp.getN(), + dp.getH().intValue()); } else { this.ecSpec = spec; } + this.gostParams = pubKey.getGostParams(); + publicKey = getPublicKeyDetails(pubKey); } @@ -138,7 +147,7 @@ public class BCECGOST3410PrivateKey BCECGOST3410PublicKey pubKey, org.bouncycastle.jce.spec.ECParameterSpec spec) { - ECDomainParameters dp = params.getParameters(); + ECDomainParameters dp = params.getParameters(); this.algorithm = algorithm; this.d = params.getD(); @@ -148,26 +157,28 @@ public class BCECGOST3410PrivateKey EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); this.ecSpec = new ECParameterSpec( - ellipticCurve, - new ECPoint( - dp.getG().getX().toBigInteger(), - dp.getG().getY().toBigInteger()), - dp.getN(), - dp.getH().intValue()); + ellipticCurve, + new ECPoint( + dp.getG().getX().toBigInteger(), + dp.getG().getY().toBigInteger()), + dp.getN(), + dp.getH().intValue()); } else { EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed()); - + this.ecSpec = new ECParameterSpec( - ellipticCurve, - new ECPoint( - spec.getG().getX().toBigInteger(), - spec.getG().getY().toBigInteger()), - spec.getN(), - spec.getH().intValue()); + ellipticCurve, + new ECPoint( + spec.getG().getX().toBigInteger(), + spec.getG().getY().toBigInteger()), + spec.getN(), + spec.getH().intValue()); } + this.gostParams = pubKey.getGostParams(); + publicKey = getPublicKeyDetails(pubKey); } @@ -190,72 +201,107 @@ public class BCECGOST3410PrivateKey private void populateFromPrivKeyInfo(PrivateKeyInfo info) throws IOException { - X962Parameters params = new X962Parameters((ASN1Primitive)info.getPrivateKeyAlgorithm().getParameters()); + ASN1Primitive p = info.getPrivateKeyAlgorithm().getParameters().toASN1Primitive(); + + if (p instanceof ASN1Sequence && (ASN1Sequence.getInstance(p).size() == 2 || ASN1Sequence.getInstance(p).size() == 3)) + { + gostParams = GOST3410PublicKeyAlgParameters.getInstance(info.getPrivateKeyAlgorithm().getParameters()); + + ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet())); - if (params.isNamedCurve()) + ECCurve curve = spec.getCurve(); + EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed()); + + ecSpec = new ECNamedCurveSpec( + ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()), + ellipticCurve, + new ECPoint( + spec.getG().getX().toBigInteger(), + spec.getG().getY().toBigInteger()), + spec.getN(), spec.getH()); + + ASN1Encodable privKey = info.parsePrivateKey(); + + byte[] encVal = ASN1OctetString.getInstance(privKey).getOctets(); + byte[] dVal = new byte[encVal.length]; + + for (int i = 0; i != encVal.length; i++) + { + dVal[i] = encVal[encVal.length - 1 - i]; + } + + this.d = new BigInteger(1, dVal); + } + else { - ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters()); - X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid); + // for backwards compatibility + X962Parameters params = X962Parameters.getInstance(info.getPrivateKeyAlgorithm().getParameters()); - if (ecP == null) // GOST Curve + if (params.isNamedCurve()) { - ECDomainParameters gParam = ECGOST3410NamedCurves.getByOID(oid); - EllipticCurve ellipticCurve = EC5Util.convertCurve(gParam.getCurve(), gParam.getSeed()); + ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters()); + X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid); - ecSpec = new ECNamedCurveSpec( + if (ecP == null) // GOST Curve + { + ECDomainParameters gParam = ECGOST3410NamedCurves.getByOID(oid); + EllipticCurve ellipticCurve = EC5Util.convertCurve(gParam.getCurve(), gParam.getSeed()); + + ecSpec = new ECNamedCurveSpec( ECGOST3410NamedCurves.getName(oid), ellipticCurve, new ECPoint( - gParam.getG().getX().toBigInteger(), - gParam.getG().getY().toBigInteger()), + gParam.getG().getX().toBigInteger(), + gParam.getG().getY().toBigInteger()), gParam.getN(), gParam.getH()); - } - else - { - EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed()); + } + else + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed()); - ecSpec = new ECNamedCurveSpec( + ecSpec = new ECNamedCurveSpec( ECUtil.getCurveName(oid), ellipticCurve, new ECPoint( - ecP.getG().getX().toBigInteger(), - ecP.getG().getY().toBigInteger()), + ecP.getG().getX().toBigInteger(), + ecP.getG().getY().toBigInteger()), ecP.getN(), ecP.getH()); + } } - } - else if (params.isImplicitlyCA()) - { - ecSpec = null; - } - else - { - X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters()); - EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed()); + else if (params.isImplicitlyCA()) + { + ecSpec = null; + } + else + { + X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters()); + EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed()); - this.ecSpec = new ECParameterSpec( - ellipticCurve, - new ECPoint( + this.ecSpec = new ECParameterSpec( + ellipticCurve, + new ECPoint( ecP.getG().getX().toBigInteger(), ecP.getG().getY().toBigInteger()), - ecP.getN(), - ecP.getH().intValue()); - } + ecP.getN(), + ecP.getH().intValue()); + } - ASN1Encodable privKey = info.parsePrivateKey(); - if (privKey instanceof DERInteger) - { - DERInteger derD = DERInteger.getInstance(privKey); + ASN1Encodable privKey = info.parsePrivateKey(); + if (privKey instanceof DERInteger) + { + DERInteger derD = DERInteger.getInstance(privKey); - this.d = derD.getValue(); - } - else - { - org.bouncycastle.asn1.sec.ECPrivateKey ec = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(privKey); + this.d = derD.getValue(); + } + else + { + org.bouncycastle.asn1.sec.ECPrivateKey ec = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(privKey); - this.d = ec.getKey(); - this.publicKey = ec.getPublicKey(); + this.d = ec.getKey(); + this.publicKey = ec.getPublicKey(); + } } } @@ -282,64 +328,92 @@ public class BCECGOST3410PrivateKey */ public byte[] getEncoded() { - X962Parameters params; - - if (ecSpec instanceof ECNamedCurveSpec) + if (gostParams != null) { - DERObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName()); - if (curveOid == null) // guess it's the OID + byte[] encKey = new byte[32]; + + extractBytes(encKey, 0, this.getS()); + + try { - curveOid = new DERObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName()); + PrivateKeyInfo info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, gostParams), new DEROctetString(encKey)); + + return info.getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + return null; } - params = new X962Parameters(curveOid); - } - else if (ecSpec == null) - { - params = new X962Parameters(DERNull.INSTANCE); } else { - ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve()); + X962Parameters params; - X9ECParameters ecP = new X9ECParameters( - curve, - EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression), - ecSpec.getOrder(), - BigInteger.valueOf(ecSpec.getCofactor()), - ecSpec.getCurve().getSeed()); + if (ecSpec instanceof ECNamedCurveSpec) + { + DERObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName()); + if (curveOid == null) // guess it's the OID + { + curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName()); + } + params = new X962Parameters(curveOid); + } + else if (ecSpec == null) + { + params = new X962Parameters(DERNull.INSTANCE); + } + else + { + ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve()); - params = new X962Parameters(ecP); - } - - PrivateKeyInfo info; - org.bouncycastle.asn1.sec.ECPrivateKey keyStructure; + X9ECParameters ecP = new X9ECParameters( + curve, + EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression), + ecSpec.getOrder(), + BigInteger.valueOf(ecSpec.getCofactor()), + ecSpec.getCurve().getSeed()); - if (publicKey != null) - { - keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(this.getS(), publicKey, params); - } - else - { - keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(this.getS(), params); - } + params = new X962Parameters(ecP); + } - try - { - if (algorithm.equals("ECGOST3410")) + PrivateKeyInfo info; + org.bouncycastle.asn1.sec.ECPrivateKey keyStructure; + + if (publicKey != null) { - info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params.toASN1Primitive()), keyStructure.toASN1Primitive()); + keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(this.getS(), publicKey, params); } else { + keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(this.getS(), params); + } + + try + { + info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params.toASN1Primitive()), keyStructure.toASN1Primitive()); - info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params.toASN1Primitive()), keyStructure.toASN1Primitive()); + return info.getEncoded(ASN1Encoding.DER); } + catch (IOException e) + { + return null; + } + } + } - return info.getEncoded(ASN1Encoding.DER); + private void extractBytes(byte[] encKey, int offSet, BigInteger bI) + { + byte[] val = bI.toByteArray(); + if (val.length < 32) + { + byte[] tmp = new byte[32]; + System.arraycopy(val, 0, tmp, tmp.length - val.length, val.length); + val = tmp; } - catch (IOException e) + + for (int i = 0; i != 32; i++) { - return null; + encKey[offSet + i] = val[val.length - 1 - i]; } } @@ -354,7 +428,7 @@ public class BCECGOST3410PrivateKey { return null; } - + return EC5Util.convertSpec(ecSpec, withCompression); } @@ -377,10 +451,10 @@ public class BCECGOST3410PrivateKey { return d; } - + public void setBagAttribute( ASN1ObjectIdentifier oid, - ASN1Encodable attribute) + ASN1Encodable attribute) { attrCarrier.setBagAttribute(oid, attribute); } @@ -398,7 +472,7 @@ public class BCECGOST3410PrivateKey public void setPointFormat(String style) { - withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style)); + withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style)); } public boolean equals(Object o) @@ -420,8 +494,8 @@ public class BCECGOST3410PrivateKey public String toString() { - StringBuffer buf = new StringBuffer(); - String nl = System.getProperty("line.separator"); + StringBuffer buf = new StringBuffer(); + String nl = System.getProperty("line.separator"); buf.append("EC Private Key").append(nl); buf.append(" S: ").append(this.d.toString(16)).append(nl); diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java index 8d3ef79b..7fa9673f 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java @@ -13,7 +13,6 @@ import java.security.spec.EllipticCurve; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Primitive; -import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERBitString; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; @@ -80,7 +79,7 @@ public class BCECGOST3410PublicKey { org.bouncycastle.jce.spec.ECParameterSpec s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); - q = s.getCurve().createPoint(q.getX().toBigInteger(), q.getY().toBigInteger(), false); + q = s.getCurve().createPoint(q.getX().toBigInteger(), q.getY().toBigInteger()); } this.ecSpec = null; } @@ -198,14 +197,14 @@ public class BCECGOST3410PublicKey y[i] = keyEnc[64 - 1 - i]; } - gostParams = new GOST3410PublicKeyAlgParameters((ASN1Sequence)info.getAlgorithm().getParameters()); + gostParams = GOST3410PublicKeyAlgParameters.getInstance(info.getAlgorithm().getParameters()); ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet())); ECCurve curve = spec.getCurve(); EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed()); - this.q = curve.createPoint(new BigInteger(1, x), new BigInteger(1, y), false); + this.q = curve.createPoint(new BigInteger(1, x), new BigInteger(1, y)); ecSpec = new ECNamedCurveSpec( ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()), @@ -398,4 +397,9 @@ public class BCECGOST3410PublicKey out.writeObject(this.getEncoded()); } + + public GOST3410PublicKeyAlgParameters getGostParams() + { + return gostParams; + } } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/digest/GOST3411.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/digest/GOST3411.java index 7ff57d32..2112673e 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/digest/GOST3411.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/digest/GOST3411.java @@ -7,6 +7,7 @@ import org.bouncycastle.crypto.macs.HMac; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; +import org.bouncycastle.jcajce.provider.symmetric.util.PBESecretKeyFactory; public class GOST3411 { @@ -46,6 +47,18 @@ public class GOST3411 } } + /** + * PBEWithHmacGOST3411 + */ + public static class PBEWithMacKeyFactory + extends PBESecretKeyFactory + { + public PBEWithMacKeyFactory() + { + super("PBEwithHmacGOST3411", null, false, PKCS12, GOST3411, 256, 0); + } + } + public static class KeyGenerator extends BaseKeyGenerator { @@ -71,6 +84,9 @@ public class GOST3411 provider.addAlgorithm("Alg.Alias.MessageDigest.GOST-3411", "GOST3411"); provider.addAlgorithm("Alg.Alias.MessageDigest." + CryptoProObjectIdentifiers.gostR3411, "GOST3411"); + provider.addAlgorithm("SecretKeyFactory.PBEWITHHMACGOST3411", PREFIX + "$PBEWithMacKeyFactory"); + provider.addAlgorithm("Alg.Alias.SecretKeyFactory." + CryptoProObjectIdentifiers.gostR3411, "PBEWITHHMACGOST3411"); + addHMACAlgorithm(provider, "GOST3411", PREFIX + "$HashMac", PREFIX + "$KeyGenerator"); addHMACAlias(provider, "GOST3411", CryptoProObjectIdentifiers.gostR3411); } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA1.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA1.java index df5d41aa..c7502c77 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA1.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA1.java @@ -193,7 +193,6 @@ public class SHA1 provider.addAlgorithm("SecretKeyFactory.PBEWITHHMACSHA1", PREFIX + "$PBEWithMacKeyFactory"); provider.addAlgorithm("SecretKeyFactory.PBKDF2WithHmacSHA1", PREFIX + "$PBKDF2WithHmacSHA1UTF8"); - provider.addAlgorithm("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.id_PBKDF2, "PBKDF2WithHmacSHA1"); provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBKDF2WithHmacSHA1AndUTF8", "PBKDF2WithHmacSHA1"); provider.addAlgorithm("SecretKeyFactory.PBKDF2WithHmacSHA1And8BIT", PREFIX + "$PBKDF2WithHmacSHA18BIT"); } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java index c2550027..9a62c98e 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java @@ -6,6 +6,8 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; import java.security.Key; import java.security.KeyStore; import java.security.KeyStore.LoadStoreParameter; @@ -24,13 +26,18 @@ import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.util.Collections; import java.util.Date; import java.util.Enumeration; +import java.util.HashMap; import java.util.Hashtable; +import java.util.Map; import java.util.Vector; import javax.crypto.Cipher; import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.IvParameterSpec; @@ -54,6 +61,10 @@ import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DEROutputStream; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERSet; +import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.bouncycastle.asn1.cryptopro.GOST28147Parameters; +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers; import org.bouncycastle.asn1.pkcs.AuthenticatedSafe; import org.bouncycastle.asn1.pkcs.CertBag; import org.bouncycastle.asn1.pkcs.ContentInfo; @@ -75,12 +86,14 @@ import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; import org.bouncycastle.jcajce.provider.config.PKCS12StoreParameter; import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey; -import org.bouncycastle.jcajce.provider.util.SecretKeyUtil; +import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec; +import org.bouncycastle.jcajce.spec.PBKDF2KeySpec; import org.bouncycastle.jce.interfaces.BCKeyStore; import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.JDKPKCS12StoreParameter; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Integers; import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; @@ -92,6 +105,7 @@ public class PKCS12KeyStoreSpi private static final int MIN_ITERATIONS = 1024; private static final Provider bcProvider = new BouncyCastleProvider(); + private static final DefaultSecretKeyProvider keySizeProvider = new DefaultSecretKeyProvider(); private IgnoresCaseHashtable keys = new IgnoresCaseHashtable(); private Hashtable localIds = new Hashtable(); @@ -593,16 +607,8 @@ public class PKCS12KeyStoreSpi } else if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2)) { - PBES2Parameters alg = PBES2Parameters.getInstance(algId.getParameters()); - PBKDF2Params func = PBKDF2Params.getInstance(alg.getKeyDerivationFunc().getParameters()); - SecretKeyFactory keyFact = SecretKeyFactory.getInstance(alg.getKeyDerivationFunc().getAlgorithm().getId(), bcProvider); - - SecretKey k = keyFact.generateSecret(new PBEKeySpec(password, func.getSalt(), func.getIterationCount().intValue(), SecretKeyUtil.getKeySize(alg.getEncryptionScheme().getAlgorithm()))); - - Cipher cipher = Cipher.getInstance(alg.getEncryptionScheme().getAlgorithm().getId(), bcProvider); - - cipher.init(Cipher.UNWRAP_MODE, k, new IvParameterSpec(ASN1OctetString.getInstance(alg.getEncryptionScheme().getParameters()).getOctets())); + Cipher cipher = createCipher(Cipher.UNWRAP_MODE, password, algId); // we pass "" as the key algorithm type as it is unknown at this point return (PrivateKey)cipher.unwrap(data, "", Cipher.PRIVATE_KEY); @@ -656,29 +662,88 @@ public class PKCS12KeyStoreSpi byte[] data) throws IOException { - String algorithm = algId.getAlgorithm().getId(); - PKCS12PBEParams pbeParams = PKCS12PBEParams.getInstance(algId.getParameters()); - PBEKeySpec pbeSpec = new PBEKeySpec(password); + ASN1ObjectIdentifier algorithm = algId.getAlgorithm(); - try + if (algorithm.on(PKCSObjectIdentifiers.pkcs_12PbeIds)) { - SecretKeyFactory keyFact = SecretKeyFactory.getInstance(algorithm, bcProvider); - PBEParameterSpec defParams = new PBEParameterSpec( - pbeParams.getIV(), - pbeParams.getIterations().intValue()); - BCPBEKey key = (BCPBEKey)keyFact.generateSecret(pbeSpec); + PKCS12PBEParams pbeParams = PKCS12PBEParams.getInstance(algId.getParameters()); + PBEKeySpec pbeSpec = new PBEKeySpec(password); - key.setTryWrongPKCS12Zero(wrongPKCS12Zero); + try + { + SecretKeyFactory keyFact = SecretKeyFactory.getInstance(algorithm.getId(), bcProvider); + PBEParameterSpec defParams = new PBEParameterSpec( + pbeParams.getIV(), + pbeParams.getIterations().intValue()); + BCPBEKey key = (BCPBEKey)keyFact.generateSecret(pbeSpec); - Cipher cipher = Cipher.getInstance(algorithm, bcProvider); - int mode = forEncryption ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE; - cipher.init(mode, key, defParams); - return cipher.doFinal(data); + key.setTryWrongPKCS12Zero(wrongPKCS12Zero); + + Cipher cipher = Cipher.getInstance(algorithm.getId(), bcProvider); + int mode = forEncryption ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE; + cipher.init(mode, key, defParams); + return cipher.doFinal(data); + } + catch (Exception e) + { + throw new IOException("exception decrypting data - " + e.toString()); + } } - catch (Exception e) + else if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2)) + { + try + { + Cipher cipher = createCipher(Cipher.DECRYPT_MODE, password, algId); + + return cipher.doFinal(data); + } + catch (Exception e) + { + throw new IOException("exception decrypting data - " + e.toString()); + } + } + else + { + throw new IOException("unknown PBE algorithm: " + algorithm); + } + } + + private Cipher createCipher(int mode, char[] password, AlgorithmIdentifier algId) + throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException + { + PBES2Parameters alg = PBES2Parameters.getInstance(algId.getParameters()); + PBKDF2Params func = PBKDF2Params.getInstance(alg.getKeyDerivationFunc().getParameters()); + AlgorithmIdentifier encScheme = AlgorithmIdentifier.getInstance(alg.getEncryptionScheme()); + + SecretKeyFactory keyFact = SecretKeyFactory.getInstance(alg.getKeyDerivationFunc().getAlgorithm().getId(), bcProvider); + SecretKey key; + + if (func.isDefaultPrf()) + { + key = keyFact.generateSecret(new PBEKeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme))); + } + else + { + key = keyFact.generateSecret(new PBKDF2KeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme), func.getPrf())); + } + + Cipher cipher = Cipher.getInstance(alg.getEncryptionScheme().getAlgorithm().getId()); + + AlgorithmIdentifier encryptionAlg = AlgorithmIdentifier.getInstance(alg.getEncryptionScheme()); + + ASN1Encodable encParams = alg.getEncryptionScheme().getParameters(); + if (encParams instanceof ASN1OctetString) { - throw new IOException("exception decrypting data - " + e.toString()); + cipher.init(mode, key, new IvParameterSpec(ASN1OctetString.getInstance(encParams).getOctets())); } + else + { + // TODO: at the moment it's just GOST, but... + GOST28147Parameters gParams = GOST28147Parameters.getInstance(encParams); + + cipher.init(mode, key, new GOST28147ParameterSpec(gParams.getEncryptionParamSet(), gParams.getIV())); + } + return cipher; } public void engineLoad( @@ -1671,4 +1736,43 @@ public class PKCS12KeyStoreSpi return orig.elements(); } } + + private static class DefaultSecretKeyProvider + { + private final Map KEY_SIZES; + + DefaultSecretKeyProvider() + { + Map keySizes = new HashMap(); + + keySizes.put(new ASN1ObjectIdentifier("1.2.840.113533.7.66.10"), Integers.valueOf(128)); + + keySizes.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), Integers.valueOf(192)); + + keySizes.put(NISTObjectIdentifiers.id_aes128_CBC, Integers.valueOf(128)); + keySizes.put(NISTObjectIdentifiers.id_aes192_CBC, Integers.valueOf(192)); + keySizes.put(NISTObjectIdentifiers.id_aes256_CBC, Integers.valueOf(256)); + + keySizes.put(NTTObjectIdentifiers.id_camellia128_cbc, Integers.valueOf(128)); + keySizes.put(NTTObjectIdentifiers.id_camellia192_cbc, Integers.valueOf(192)); + keySizes.put(NTTObjectIdentifiers.id_camellia256_cbc, Integers.valueOf(256)); + + keySizes.put(CryptoProObjectIdentifiers.gostR28147_gcfb, Integers.valueOf(256)); + + KEY_SIZES = Collections.unmodifiableMap(keySizes); + } + + public int getKeySize(AlgorithmIdentifier algorithmIdentifier) + { + // TODO: not all ciphers/oid relationships are this simple. + Integer keySize = (Integer)KEY_SIZES.get(algorithmIdentifier.getAlgorithm()); + + if (keySize != null) + { + return keySize.intValue(); + } + + return -1; + } + } } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GOST28147.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GOST28147.java index 389b79a5..b3ff96b3 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GOST28147.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GOST28147.java @@ -8,10 +8,12 @@ import java.security.spec.AlgorithmParameterSpec; import javax.crypto.spec.IvParameterSpec; import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.GOST28147Engine; import org.bouncycastle.crypto.macs.GOST28147Mac; import org.bouncycastle.crypto.modes.CBCBlockCipher; +import org.bouncycastle.crypto.modes.GCFBBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator; import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; @@ -45,6 +47,15 @@ public final class GOST28147 } } + public static class GCFB + extends BaseBlockCipher + { + public GCFB() + { + super(new BufferedBlockCipher(new GCFBBlockCipher(new GOST28147Engine())), 64); + } + } + /** * GOST28147 */ @@ -132,12 +143,12 @@ public final class GOST28147 provider.addAlgorithm("Cipher.GOST28147", PREFIX + "$ECB"); provider.addAlgorithm("Alg.Alias.Cipher.GOST", "GOST28147"); provider.addAlgorithm("Alg.Alias.Cipher.GOST-28147", "GOST28147"); - provider.addAlgorithm("Cipher." + CryptoProObjectIdentifiers.gostR28147_cbc, PREFIX + "$CBC"); + provider.addAlgorithm("Cipher." + CryptoProObjectIdentifiers.gostR28147_gcfb, PREFIX + "$GCFB"); provider.addAlgorithm("KeyGenerator.GOST28147", PREFIX + "$KeyGen"); provider.addAlgorithm("Alg.Alias.KeyGenerator.GOST", "GOST28147"); provider.addAlgorithm("Alg.Alias.KeyGenerator.GOST-28147", "GOST28147"); - provider.addAlgorithm("Alg.Alias.KeyGenerator." + CryptoProObjectIdentifiers.gostR28147_cbc, "GOST28147"); + provider.addAlgorithm("Alg.Alias.KeyGenerator." + CryptoProObjectIdentifiers.gostR28147_gcfb, "GOST28147"); provider.addAlgorithm("Mac.GOST28147MAC", PREFIX + "$Mac"); provider.addAlgorithm("Alg.Alias.Mac.GOST28147", "GOST28147MAC"); diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java index ee3cac9e..4b0d8b97 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java @@ -2,17 +2,28 @@ package org.bouncycastle.jcajce.provider.symmetric; import java.io.IOException; import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidParameterSpecException; +import java.security.spec.KeySpec; +import javax.crypto.SecretKey; +import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PBKDF2Params; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; +import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey; import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameters; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseSecretKeyFactory; +import org.bouncycastle.jcajce.provider.symmetric.util.PBE; import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; +import org.bouncycastle.jcajce.spec.PBKDF2KeySpec; public class PBEPBKDF2 { @@ -104,6 +115,99 @@ public class PBEPBKDF2 } } + public static class BasePBKDF2 + extends BaseSecretKeyFactory + { + private int scheme; + + public BasePBKDF2(String name, int scheme) + { + super(name, PKCSObjectIdentifiers.id_PBKDF2); + + this.scheme = scheme; + } + + protected SecretKey engineGenerateSecret( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof PBEKeySpec) + { + PBEKeySpec pbeSpec = (PBEKeySpec)keySpec; + + if (pbeSpec.getSalt() == null) + { + throw new InvalidKeySpecException("missing required salt"); + } + + if (pbeSpec.getIterationCount() <= 0) + { + throw new InvalidKeySpecException("positive iteration count required: " + + pbeSpec.getIterationCount()); + } + + if (pbeSpec.getKeyLength() <= 0) + { + throw new InvalidKeySpecException("positive key length required: " + + pbeSpec.getKeyLength()); + } + + if (pbeSpec.getPassword().length == 0) + { + throw new IllegalArgumentException("password empty"); + } + + if (pbeSpec instanceof PBKDF2KeySpec) + { + PBKDF2KeySpec spec = (PBKDF2KeySpec)pbeSpec; + + int digest = getDigestCode(spec.getPrf().getAlgorithm()); + int keySize = pbeSpec.getKeyLength(); + int ivSize = -1; // JDK 1,2 and earlier does not understand simplified version. + CipherParameters param = PBE.Util.makePBEMacParameters(pbeSpec, scheme, digest, keySize); + + return new BCPBEKey(this.algName, this.algOid, scheme, digest, keySize, ivSize, pbeSpec, param); + } + else + { + int digest = SHA1; + int keySize = pbeSpec.getKeyLength(); + int ivSize = -1; // JDK 1,2 and earlier does not understand simplified version. + CipherParameters 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"); + } + + + private int getDigestCode(ASN1ObjectIdentifier algorithm) + throws InvalidKeySpecException + { + if (algorithm.equals(CryptoProObjectIdentifiers.gostR3411Hmac)) + { + return GOST3411; + } + else if (algorithm.equals(PKCSObjectIdentifiers.id_hmacWithSHA1)) + { + return SHA1; + } + + throw new InvalidKeySpecException("Invalid KeySpec: unknown PRF algorithm " + algorithm); + } + } + + public static class PBKDF2withUTF8 + extends BasePBKDF2 + { + public PBKDF2withUTF8() + { + super("PBKDF2", PKCS5S2_UTF8); + } + } + public static class Mappings extends AlgorithmProvider { @@ -117,6 +221,8 @@ public class PBEPBKDF2 { provider.addAlgorithm("AlgorithmParameters.PBKDF2", PREFIX + "$AlgParams"); provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + PKCSObjectIdentifiers.id_PBKDF2, "PBKDF2"); + provider.addAlgorithm("SecretKeyFactory.PBKDF2", PREFIX + "$PBKDF2withUTF8"); + provider.addAlgorithm("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.id_PBKDF2, "PBKDF2"); } } } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java index 8379b55a..f73997fc 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java @@ -32,6 +32,7 @@ import org.bouncycastle.crypto.modes.CCMBlockCipher; import org.bouncycastle.crypto.modes.CFBBlockCipher; import org.bouncycastle.crypto.modes.CTSBlockCipher; import org.bouncycastle.crypto.modes.EAXBlockCipher; +import org.bouncycastle.crypto.modes.GCFBBlockCipher; import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.crypto.modes.GOFBBlockCipher; import org.bouncycastle.crypto.modes.OCBBlockCipher; @@ -52,9 +53,9 @@ import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.crypto.params.ParametersWithSBox; import org.bouncycastle.crypto.params.RC2Parameters; import org.bouncycastle.crypto.params.RC5Parameters; +import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec; +import org.bouncycastle.jcajce.spec.RepeatedSecretKeySpec; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.jce.spec.GOST28147ParameterSpec; -import org.bouncycastle.jce.spec.RepeatedSecretKeySpec; import org.bouncycastle.util.Strings; public class BaseBlockCipher @@ -271,6 +272,12 @@ public class BaseBlockCipher 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(); @@ -420,6 +427,18 @@ public class BaseBlockCipher 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) { diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java index f16de3ca..fac3ead0 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java @@ -72,7 +72,32 @@ public interface PBE } else if (type == PKCS5S2 || type == PKCS5S2_UTF8) { - generator = new PKCS5S2ParametersGenerator(); + 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) { @@ -261,9 +286,9 @@ public interface PBE 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; diff --git a/prov/src/main/java/org/bouncycastle/jce/spec/GOST28147ParameterSpec.java b/prov/src/main/java/org/bouncycastle/jce/spec/GOST28147ParameterSpec.java index 384d871c..d03fbfe7 100644 --- a/prov/src/main/java/org/bouncycastle/jce/spec/GOST28147ParameterSpec.java +++ b/prov/src/main/java/org/bouncycastle/jce/spec/GOST28147ParameterSpec.java @@ -1,73 +1,48 @@ -package org.bouncycastle.jce.spec; - -import java.security.spec.AlgorithmParameterSpec; - -import org.bouncycastle.crypto.engines.GOST28147Engine; +package org.bouncycastle.jce.spec; /** * A parameter spec for the GOST-28147 cipher. + * @deprecated use org.bouncycastle.jcajce.spec.GOST28147ParameterSpec */ public class GOST28147ParameterSpec - implements AlgorithmParameterSpec + extends org.bouncycastle.jcajce.spec.GOST28147ParameterSpec { - private byte[] iv = null; - private byte[] sBox = null; - + /** + * @deprecated + */ public GOST28147ParameterSpec( byte[] sBox) { - this.sBox = new byte[sBox.length]; - - System.arraycopy(sBox, 0, this.sBox, 0, sBox.length); + super(sBox); } + /** + * @deprecated + */ public GOST28147ParameterSpec( byte[] sBox, byte[] iv) { - this(sBox); - this.iv = new byte[iv.length]; - - System.arraycopy(iv, 0, this.iv, 0, iv.length); + super(sBox, iv); + } - + + /** + * @deprecated + */ public GOST28147ParameterSpec( String sBoxName) { - this.sBox = GOST28147Engine.getSBox(sBoxName); + super(sBoxName); } + /** + * @deprecated + */ public GOST28147ParameterSpec( String sBoxName, byte[] iv) { - this(sBoxName); - this.iv = new byte[iv.length]; - - System.arraycopy(iv, 0, this.iv, 0, iv.length); - } - - public byte[] getSbox() - { - return sBox; - } - - /** - * Returns the IV or null if this parameter set does not contain an IV. - * - * @return the IV or null if this parameter set does not contain an IV. - */ - public byte[] getIV() - { - if (iv == null) - { - return null; - } - - byte[] tmp = new byte[iv.length]; - - System.arraycopy(iv, 0, tmp, 0, tmp.length); - - return tmp; + super(sBoxName, iv); } }
\ No newline at end of file diff --git a/prov/src/main/java/org/bouncycastle/jce/spec/RepeatedSecretKeySpec.java b/prov/src/main/java/org/bouncycastle/jce/spec/RepeatedSecretKeySpec.java index 2a7ceb53..41110728 100644 --- a/prov/src/main/java/org/bouncycastle/jce/spec/RepeatedSecretKeySpec.java +++ b/prov/src/main/java/org/bouncycastle/jce/spec/RepeatedSecretKeySpec.java @@ -1,34 +1,17 @@ package org.bouncycastle.jce.spec; - -import javax.crypto.SecretKey; - /** * A simple object to indicate that a symmetric cipher should reuse the * last key provided. + * @deprecated use super class org.bouncycastle.jcajce.spec.RepeatedSecretKeySpec */ public class RepeatedSecretKeySpec - implements SecretKey + extends org.bouncycastle.jcajce.spec.RepeatedSecretKeySpec { private String algorithm; public RepeatedSecretKeySpec(String algorithm) { - this.algorithm = algorithm; - } - - public String getAlgorithm() - { - return algorithm; - } - - public String getFormat() - { - return null; - } - - public byte[] getEncoded() - { - return null; + super(algorithm); } } diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/AESSICTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/AESSICTest.java index 61f79959..ae6d7bce 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/AESSICTest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/AESSICTest.java @@ -7,8 +7,8 @@ import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; +import org.bouncycastle.jcajce.spec.RepeatedSecretKeySpec; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.jce.spec.RepeatedSecretKeySpec; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/GOST28147Test.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/GOST28147Test.java index b7fecd09..93e3ad74 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/GOST28147Test.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/GOST28147Test.java @@ -11,8 +11,8 @@ import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; import org.bouncycastle.jce.provider.BouncyCastleProvider; @@ -182,11 +182,11 @@ public class GOST28147Test private void oidTest() { String[] oids = { - CryptoProObjectIdentifiers.gostR28147_cbc.getId(), + CryptoProObjectIdentifiers.gostR28147_gcfb.getId(), }; String[] names = { - "GOST28147/CBC/PKCS7Padding" + "GOST28147/GCFB/NoPadding" }; try diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java index c35c5b88..0828440b 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java @@ -11,6 +11,7 @@ import java.security.KeyStore; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Security; +import java.security.Signature; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; @@ -426,6 +427,49 @@ public class PKCS12StoreTest + "AHoAeQB0AGsAbwB3AG4AaQBrAGEwMTAhMAkGBSsOAwIaBQAEFKJpUOIj0OtI" + "j2CPp38YIFBEqvjsBAi8G+yhJe3A/wICCAA="); + private byte[] gostPfx = Base64.decode( + "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" + + "BAgY5sDtkrVeBQICCAA="); + /** * we generate a self signed certificate for the sake of testing - RSA */ @@ -482,6 +526,38 @@ public class PKCS12StoreTest return certGen.generate(privKey); } + private void testGOSTStore() + throws Exception + { + byte[] data = Hex.decode("deadbeef"); + + KeyStore pkcs12 = KeyStore.getInstance("PKCS12", "BC"); + + pkcs12.load(new ByteArrayInputStream(gostPfx), "1".toCharArray()); + + PrivateKey pk = (PrivateKey)pkcs12.getKey("cp_exported", null); + Certificate[] pubCerts = pkcs12.getCertificateChain("cp_exported"); + + Signature sig = Signature.getInstance("ECGOST3410", "BC"); + + sig.initSign(pk); + + sig.update(data); + + byte[] signature = sig.sign(); + + sig = Signature.getInstance("ECGOST3410", "BC"); + + sig.initVerify(pubCerts[0].getPublicKey()); + + sig.update(data); + + if (!sig.verify(signature)) + { + fail("key test failed in GOST store"); + } + } + public void testPKCS12Store() throws Exception { @@ -1080,7 +1156,7 @@ public class PKCS12StoreTest throws Exception { testPKCS12Store(); - + testGOSTStore(); // converter tests |