diff options
Diffstat (limited to 'pkix/src/main/java/org/spongycastle/pkcs/jcajce')
8 files changed, 799 insertions, 0 deletions
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS10CertificationRequest.java b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS10CertificationRequest.java new file mode 100644 index 00000000..8f0e20cf --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS10CertificationRequest.java @@ -0,0 +1,115 @@ +package org.spongycastle.pkcs.jcajce; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.Hashtable; + +import org.spongycastle.asn1.pkcs.CertificationRequest; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.jcajce.util.DefaultJcaJceHelper; +import org.spongycastle.jcajce.util.JcaJceHelper; +import org.spongycastle.jcajce.util.NamedJcaJceHelper; +import org.spongycastle.jcajce.util.ProviderJcaJceHelper; +import org.spongycastle.pkcs.PKCS10CertificationRequest; + +public class JcaPKCS10CertificationRequest + extends PKCS10CertificationRequest +{ + private static Hashtable keyAlgorithms = new Hashtable(); + + static + { + // + // key types + // + keyAlgorithms.put(PKCSObjectIdentifiers.rsaEncryption, "RSA"); + keyAlgorithms.put(X9ObjectIdentifiers.id_dsa, "DSA"); + } + + private JcaJceHelper helper = new DefaultJcaJceHelper(); + + public JcaPKCS10CertificationRequest(CertificationRequest certificationRequest) + { + super(certificationRequest); + } + + public JcaPKCS10CertificationRequest(byte[] encoding) + throws IOException + { + super(encoding); + } + + public JcaPKCS10CertificationRequest(PKCS10CertificationRequest requestHolder) + { + super(requestHolder.toASN1Structure()); + } + + public JcaPKCS10CertificationRequest setProvider(String providerName) + { + helper = new NamedJcaJceHelper(providerName); + + return this; + } + + public JcaPKCS10CertificationRequest setProvider(Provider provider) + { + helper = new ProviderJcaJceHelper(provider); + + return this; + } + + public PublicKey getPublicKey() + throws InvalidKeyException, NoSuchAlgorithmException + { + try + { + SubjectPublicKeyInfo keyInfo = this.getSubjectPublicKeyInfo(); + X509EncodedKeySpec xspec = new X509EncodedKeySpec(keyInfo.getEncoded()); + KeyFactory kFact; + + try + { + kFact = helper.createKeyFactory(keyInfo.getAlgorithm().getAlgorithm().getId()); + } + catch (NoSuchAlgorithmException e) + { + // + // try an alternate + // + if (keyAlgorithms.get(keyInfo.getAlgorithm().getAlgorithm()) != null) + { + String keyAlgorithm = (String)keyAlgorithms.get(keyInfo.getAlgorithm().getAlgorithm()); + + kFact = helper.createKeyFactory(keyAlgorithm); + } + else + { + throw e; + } + } + + return kFact.generatePublic(xspec); + } + catch (InvalidKeySpecException e) + { + throw new InvalidKeyException("error decoding public key"); + } + catch (IOException e) + { + throw new InvalidKeyException("error extracting key encoding"); + } + catch (NoSuchProviderException e) + { + throw new NoSuchAlgorithmException("cannot find provider: " + e.getMessage()); + } + } +} diff --git a/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS10CertificationRequestBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS10CertificationRequestBuilder.java new file mode 100644 index 00000000..0efa5fa2 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS10CertificationRequestBuilder.java @@ -0,0 +1,38 @@ +package org.spongycastle.pkcs.jcajce; + +import java.security.PublicKey; + +import javax.security.auth.x500.X500Principal; + +import org.spongycastle.asn1.x500.X500Name; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.pkcs.PKCS10CertificationRequestBuilder; + +/** + * Extension of the PKCS#10 builder to support PublicKey and X500Principal objects. + */ +public class JcaPKCS10CertificationRequestBuilder + extends PKCS10CertificationRequestBuilder +{ + /** + * Create a PKCS#10 builder for the passed in subject and JCA public key. + * + * @param subject an X500Name containing the subject associated with the request we are building. + * @param publicKey a JCA public key that is to be associated with the request we are building. + */ + public JcaPKCS10CertificationRequestBuilder(X500Name subject, PublicKey publicKey) + { + super(subject, SubjectPublicKeyInfo.getInstance(publicKey.getEncoded())); + } + + /** + * Create a PKCS#10 builder for the passed in subject and JCA public key. + * + * @param subject an X500Principal containing the subject associated with the request we are building. + * @param publicKey a JCA public key that is to be associated with the request we are building. + */ + public JcaPKCS10CertificationRequestBuilder(X500Principal subject, PublicKey publicKey) + { + super(X500Name.getInstance(subject.getEncoded()), SubjectPublicKeyInfo.getInstance(publicKey.getEncoded())); + } +} diff --git a/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS12SafeBagBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS12SafeBagBuilder.java new file mode 100644 index 00000000..f8c06f7c --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS12SafeBagBuilder.java @@ -0,0 +1,45 @@ +package org.spongycastle.pkcs.jcajce; + +import java.io.IOException; +import java.security.PrivateKey; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; + +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.Certificate; +import org.spongycastle.operator.OutputEncryptor; +import org.spongycastle.pkcs.PKCS12SafeBagBuilder; +import org.spongycastle.pkcs.PKCSIOException; + +public class JcaPKCS12SafeBagBuilder + extends PKCS12SafeBagBuilder +{ + public JcaPKCS12SafeBagBuilder(X509Certificate certificate) + throws IOException + { + super(convertCert(certificate)); + } + + private static Certificate convertCert(X509Certificate certificate) + throws IOException + { + try + { + return Certificate.getInstance(certificate.getEncoded()); + } + catch (CertificateEncodingException e) + { + throw new PKCSIOException("cannot encode certificate: " + e.getMessage(), e); + } + } + + public JcaPKCS12SafeBagBuilder(PrivateKey privateKey, OutputEncryptor encryptor) + { + super(PrivateKeyInfo.getInstance(privateKey.getEncoded()), encryptor); + } + + public JcaPKCS12SafeBagBuilder(PrivateKey privateKey) + { + super(PrivateKeyInfo.getInstance(privateKey.getEncoded())); + } +} diff --git a/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS8EncryptedPrivateKeyInfoBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS8EncryptedPrivateKeyInfoBuilder.java new file mode 100644 index 00000000..f8a5856c --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS8EncryptedPrivateKeyInfoBuilder.java @@ -0,0 +1,15 @@ +package org.spongycastle.pkcs.jcajce; + +import java.security.PrivateKey; + +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.pkcs.PKCS8EncryptedPrivateKeyInfoBuilder; + +public class JcaPKCS8EncryptedPrivateKeyInfoBuilder + extends PKCS8EncryptedPrivateKeyInfoBuilder +{ + public JcaPKCS8EncryptedPrivateKeyInfoBuilder(PrivateKey privateKey) + { + super(PrivateKeyInfo.getInstance(privateKey.getEncoded())); + } +} diff --git a/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCS12MacCalculatorBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCS12MacCalculatorBuilder.java new file mode 100644 index 00000000..25b8da06 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCS12MacCalculatorBuilder.java @@ -0,0 +1,122 @@ +package org.spongycastle.pkcs.jcajce; + +import java.io.OutputStream; +import java.security.Provider; +import java.security.SecureRandom; + +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.oiw.OIWObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PKCS12PBEParams; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.ExtendedDigest; +import org.spongycastle.crypto.generators.PKCS12ParametersGenerator; +import org.spongycastle.jcajce.io.MacOutputStream; +import org.spongycastle.jcajce.util.DefaultJcaJceHelper; +import org.spongycastle.jcajce.util.JcaJceHelper; +import org.spongycastle.jcajce.util.NamedJcaJceHelper; +import org.spongycastle.jcajce.util.ProviderJcaJceHelper; +import org.spongycastle.operator.GenericKey; +import org.spongycastle.operator.MacCalculator; +import org.spongycastle.operator.OperatorCreationException; +import org.spongycastle.pkcs.PKCS12MacCalculatorBuilder; + +public class JcePKCS12MacCalculatorBuilder + implements PKCS12MacCalculatorBuilder +{ + private JcaJceHelper helper = new DefaultJcaJceHelper(); + private ExtendedDigest digest; + private ASN1ObjectIdentifier algorithm; + + private SecureRandom random; + private int saltLength; + private int iterationCount = 1024; + + public JcePKCS12MacCalculatorBuilder() + { + this(OIWObjectIdentifiers.idSHA1); + } + + public JcePKCS12MacCalculatorBuilder(ASN1ObjectIdentifier hashAlgorithm) + { + this.algorithm = hashAlgorithm; + } + + public JcePKCS12MacCalculatorBuilder setProvider(Provider provider) + { + this.helper = new ProviderJcaJceHelper(provider); + + return this; + } + + public JcePKCS12MacCalculatorBuilder setProvider(String providerName) + { + this.helper = new NamedJcaJceHelper(providerName); + + return this; + } + + public AlgorithmIdentifier getDigestAlgorithmIdentifier() + { + return new AlgorithmIdentifier(algorithm, DERNull.INSTANCE); + } + + public MacCalculator build(final char[] password) + throws OperatorCreationException + { + if (random == null) + { + random = new SecureRandom(); + } + + try + { + final Mac mac = helper.createMac(algorithm.getId()); + + saltLength = mac.getMacLength(); + final byte[] salt = new byte[saltLength]; + + random.nextBytes(salt); + + SecretKeyFactory keyFact = helper.createSecretKeyFactory(algorithm.getId()); + PBEParameterSpec defParams = new PBEParameterSpec(salt, iterationCount); + PBEKeySpec pbeSpec = new PBEKeySpec(password); + SecretKey key = keyFact.generateSecret(pbeSpec); + + mac.init(key, defParams); + + return new MacCalculator() + { + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return new AlgorithmIdentifier(algorithm, new PKCS12PBEParams(salt, iterationCount)); + } + + public OutputStream getOutputStream() + { + return new MacOutputStream(mac); + } + + public byte[] getMac() + { + return mac.doFinal(); + } + + public GenericKey getKey() + { + return new GenericKey(getAlgorithmIdentifier(), PKCS12ParametersGenerator.PKCS12PasswordToBytes(password)); + } + }; + } + catch (Exception e) + { + throw new OperatorCreationException("unable to create MAC calculator: " + e.getMessage(), e); + } + } +} diff --git a/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCS12MacCalculatorBuilderProvider.java b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCS12MacCalculatorBuilderProvider.java new file mode 100644 index 00000000..6c9c3023 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCS12MacCalculatorBuilderProvider.java @@ -0,0 +1,108 @@ +package org.spongycastle.pkcs.jcajce; + +import java.io.OutputStream; +import java.security.Provider; + +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.pkcs.PKCS12PBEParams; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.generators.PKCS12ParametersGenerator; +import org.spongycastle.jcajce.io.MacOutputStream; +import org.spongycastle.jcajce.util.DefaultJcaJceHelper; +import org.spongycastle.jcajce.util.JcaJceHelper; +import org.spongycastle.jcajce.util.NamedJcaJceHelper; +import org.spongycastle.jcajce.util.ProviderJcaJceHelper; +import org.spongycastle.operator.GenericKey; +import org.spongycastle.operator.MacCalculator; +import org.spongycastle.operator.OperatorCreationException; +import org.spongycastle.pkcs.PKCS12MacCalculatorBuilder; +import org.spongycastle.pkcs.PKCS12MacCalculatorBuilderProvider; + +public class JcePKCS12MacCalculatorBuilderProvider + implements PKCS12MacCalculatorBuilderProvider +{ + private JcaJceHelper helper = new DefaultJcaJceHelper(); + + public JcePKCS12MacCalculatorBuilderProvider() + { + } + + public JcePKCS12MacCalculatorBuilderProvider setProvider(Provider provider) + { + this.helper = new ProviderJcaJceHelper(provider); + + return this; + } + + public JcePKCS12MacCalculatorBuilderProvider setProvider(String providerName) + { + this.helper = new NamedJcaJceHelper(providerName); + + return this; + } + + public PKCS12MacCalculatorBuilder get(final AlgorithmIdentifier algorithmIdentifier) + { + return new PKCS12MacCalculatorBuilder() + { + public MacCalculator build(final char[] password) + throws OperatorCreationException + { + final PKCS12PBEParams pbeParams = PKCS12PBEParams.getInstance(algorithmIdentifier.getParameters()); + + try + { + final ASN1ObjectIdentifier algorithm = algorithmIdentifier.getAlgorithm(); + + final Mac mac = helper.createMac(algorithm.getId()); + + SecretKeyFactory keyFact = helper.createSecretKeyFactory(algorithm.getId()); + PBEParameterSpec defParams = new PBEParameterSpec(pbeParams.getIV(), pbeParams.getIterations().intValue()); + PBEKeySpec pbeSpec = new PBEKeySpec(password); + SecretKey key = keyFact.generateSecret(pbeSpec); + + mac.init(key, defParams); + + return new MacCalculator() + { + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return new AlgorithmIdentifier(algorithm, pbeParams); + } + + public OutputStream getOutputStream() + { + return new MacOutputStream(mac); + } + + public byte[] getMac() + { + return mac.doFinal(); + } + + public GenericKey getKey() + { + return new GenericKey(getAlgorithmIdentifier(), PKCS12ParametersGenerator.PKCS12PasswordToBytes(password)); + } + }; + } + catch (Exception e) + { + throw new OperatorCreationException("unable to create MAC calculator: " + e.getMessage(), e); + } + } + + public AlgorithmIdentifier getDigestAlgorithmIdentifier() + { + return new AlgorithmIdentifier(algorithmIdentifier.getAlgorithm(), DERNull.INSTANCE); + } + }; + } +} diff --git a/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java new file mode 100644 index 00000000..5f5413ca --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java @@ -0,0 +1,177 @@ +package org.spongycastle.pkcs.jcajce; + +import java.io.InputStream; +import java.security.Provider; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.cryptopro.GOST28147Parameters; +import org.spongycastle.asn1.pkcs.PBES2Parameters; +import org.spongycastle.asn1.pkcs.PBKDF2Params; +import org.spongycastle.asn1.pkcs.PKCS12PBEParams; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.jcajce.provider.symmetric.util.BCPBEKey; +import org.spongycastle.jcajce.spec.GOST28147ParameterSpec; +import org.spongycastle.jcajce.spec.PBKDF2KeySpec; +import org.spongycastle.jcajce.util.DefaultJcaJceHelper; +import org.spongycastle.jcajce.util.JcaJceHelper; +import org.spongycastle.jcajce.util.NamedJcaJceHelper; +import org.spongycastle.jcajce.util.ProviderJcaJceHelper; +import org.spongycastle.operator.DefaultSecretKeySizeProvider; +import org.spongycastle.operator.InputDecryptor; +import org.spongycastle.operator.InputDecryptorProvider; +import org.spongycastle.operator.OperatorCreationException; +import org.spongycastle.operator.SecretKeySizeProvider; + +public class JcePKCSPBEInputDecryptorProviderBuilder +{ + private JcaJceHelper helper = new DefaultJcaJceHelper(); + private boolean wrongPKCS12Zero = false; + private SecretKeySizeProvider keySizeProvider = DefaultSecretKeySizeProvider.INSTANCE; + + public JcePKCSPBEInputDecryptorProviderBuilder() + { + } + + public JcePKCSPBEInputDecryptorProviderBuilder setProvider(Provider provider) + { + this.helper = new ProviderJcaJceHelper(provider); + + return this; + } + + public JcePKCSPBEInputDecryptorProviderBuilder setProvider(String providerName) + { + this.helper = new NamedJcaJceHelper(providerName); + + return this; + } + + public JcePKCSPBEInputDecryptorProviderBuilder setTryWrongPKCS12Zero(boolean tryWrong) + { + this.wrongPKCS12Zero = tryWrong; + + return this; + } + + /** + * Set the lookup provider of AlgorithmIdentifier returning key_size_in_bits used to + * handle PKCS5 decryption. + * + * @param keySizeProvider a provider of integer secret key sizes. + * + * @return the current builder. + */ + public JcePKCSPBEInputDecryptorProviderBuilder setKeySizeProvider(SecretKeySizeProvider keySizeProvider) + { + this.keySizeProvider = keySizeProvider; + + return this; + } + + public InputDecryptorProvider build(final char[] password) + { + return new InputDecryptorProvider() + { + private Cipher cipher; + private SecretKey key; + private AlgorithmIdentifier encryptionAlg; + + public InputDecryptor get(final AlgorithmIdentifier algorithmIdentifier) + throws OperatorCreationException + { + ASN1ObjectIdentifier algorithm = algorithmIdentifier.getAlgorithm(); + + try + { + if (algorithm.on(PKCSObjectIdentifiers.pkcs_12PbeIds)) + { + PKCS12PBEParams pbeParams = PKCS12PBEParams.getInstance(algorithmIdentifier.getParameters()); + + PBEKeySpec pbeSpec = new PBEKeySpec(password); + + SecretKeyFactory keyFact = helper.createSecretKeyFactory(algorithm.getId()); + + PBEParameterSpec defParams = new PBEParameterSpec( + pbeParams.getIV(), + pbeParams.getIterations().intValue()); + + key = keyFact.generateSecret(pbeSpec); + + if (key instanceof BCPBEKey) + { + ((BCPBEKey)key).setTryWrongPKCS12Zero(wrongPKCS12Zero); + } + + cipher = helper.createCipher(algorithm.getId()); + + cipher.init(Cipher.DECRYPT_MODE, key, defParams); + + encryptionAlg = algorithmIdentifier; + } + else if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2)) + { + PBES2Parameters alg = PBES2Parameters.getInstance(algorithmIdentifier.getParameters()); + PBKDF2Params func = PBKDF2Params.getInstance(alg.getKeyDerivationFunc().getParameters()); + AlgorithmIdentifier encScheme = AlgorithmIdentifier.getInstance(alg.getEncryptionScheme()); + + SecretKeyFactory keyFact = helper.createSecretKeyFactory(alg.getKeyDerivationFunc().getAlgorithm().getId()); + + 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()); + + 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) + { + throw new OperatorCreationException("unable to create InputDecryptor: " + e.getMessage(), e); + } + + return new InputDecryptor() + { + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return encryptionAlg; + } + + public InputStream getInputStream(InputStream input) + { + return new CipherInputStream(input, cipher); + } + }; + } + }; + } +} diff --git a/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCSPBEOutputEncryptorBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCSPBEOutputEncryptorBuilder.java new file mode 100644 index 00000000..9ab806ef --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCSPBEOutputEncryptorBuilder.java @@ -0,0 +1,179 @@ +package org.spongycastle.pkcs.jcajce; + +import java.io.OutputStream; +import java.security.Provider; +import java.security.SecureRandom; + +import javax.crypto.Cipher; +import javax.crypto.CipherOutputStream; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.bc.BCObjectIdentifiers; +import org.spongycastle.asn1.pkcs.EncryptionScheme; +import org.spongycastle.asn1.pkcs.KeyDerivationFunc; +import org.spongycastle.asn1.pkcs.PBES2Parameters; +import org.spongycastle.asn1.pkcs.PBKDF2Params; +import org.spongycastle.asn1.pkcs.PKCS12PBEParams; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.PBEParametersGenerator; +import org.spongycastle.jcajce.util.DefaultJcaJceHelper; +import org.spongycastle.jcajce.util.JcaJceHelper; +import org.spongycastle.jcajce.util.NamedJcaJceHelper; +import org.spongycastle.jcajce.util.ProviderJcaJceHelper; +import org.spongycastle.operator.DefaultSecretKeySizeProvider; +import org.spongycastle.operator.GenericKey; +import org.spongycastle.operator.OperatorCreationException; +import org.spongycastle.operator.OutputEncryptor; +import org.spongycastle.operator.SecretKeySizeProvider; + +public class JcePKCSPBEOutputEncryptorBuilder +{ + private JcaJceHelper helper = new DefaultJcaJceHelper(); + private ASN1ObjectIdentifier algorithm; + private ASN1ObjectIdentifier keyEncAlgorithm; + private SecureRandom random; + private SecretKeySizeProvider keySizeProvider = DefaultSecretKeySizeProvider.INSTANCE; + + public JcePKCSPBEOutputEncryptorBuilder(ASN1ObjectIdentifier algorithm) + { + if (isPKCS12(algorithm)) + { + this.algorithm = algorithm; + this.keyEncAlgorithm = algorithm; + } + else + { + this.algorithm = PKCSObjectIdentifiers.id_PBES2; + this.keyEncAlgorithm = algorithm; + } + } + + public JcePKCSPBEOutputEncryptorBuilder setProvider(Provider provider) + { + this.helper = new ProviderJcaJceHelper(provider); + + return this; + } + + public JcePKCSPBEOutputEncryptorBuilder setProvider(String providerName) + { + this.helper = new NamedJcaJceHelper(providerName); + + return this; + } + + /** + * Set the lookup provider of AlgorithmIdentifier returning key_size_in_bits used to + * handle PKCS5 decryption. + * + * @param keySizeProvider a provider of integer secret key sizes. + * + * @return the current builder. + */ + public JcePKCSPBEOutputEncryptorBuilder setKeySizeProvider(SecretKeySizeProvider keySizeProvider) + { + this.keySizeProvider = keySizeProvider; + + return this; + } + + public OutputEncryptor build(final char[] password) + throws OperatorCreationException + { + final Cipher cipher; + SecretKey key; + + if (random == null) + { + random = new SecureRandom(); + } + + final AlgorithmIdentifier encryptionAlg; + final byte[] salt = new byte[20]; + final int iterationCount = 1024; + + random.nextBytes(salt); + + try + { + if (algorithm.on(PKCSObjectIdentifiers.pkcs_12PbeIds)) + { + PBEKeySpec pbeSpec = new PBEKeySpec(password); + + SecretKeyFactory keyFact = helper.createSecretKeyFactory(algorithm.getId()); + + PBEParameterSpec defParams = new PBEParameterSpec(salt, iterationCount); + + key = keyFact.generateSecret(pbeSpec); + + cipher = helper.createCipher(algorithm.getId()); + + cipher.init(Cipher.ENCRYPT_MODE, key, defParams); + + encryptionAlg = new AlgorithmIdentifier(algorithm, new PKCS12PBEParams(salt, iterationCount)); + } + else if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2)) + { + SecretKeyFactory keyFact = helper.createSecretKeyFactory(PKCSObjectIdentifiers.id_PBKDF2.getId()); + + key = keyFact.generateSecret(new PBEKeySpec(password, salt, iterationCount, keySizeProvider.getKeySize(new AlgorithmIdentifier(keyEncAlgorithm)))); + + cipher = helper.createCipher(keyEncAlgorithm.getId()); + + cipher.init(Cipher.ENCRYPT_MODE, key, random); + + PBES2Parameters algParams = new PBES2Parameters( + new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params(salt, iterationCount)), + new EncryptionScheme(keyEncAlgorithm, ASN1Primitive.fromByteArray(cipher.getParameters().getEncoded()))); + + encryptionAlg = new AlgorithmIdentifier(algorithm, algParams); + } + else + { + throw new OperatorCreationException("unrecognised algorithm"); + } + + return new OutputEncryptor() + { + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return encryptionAlg; + } + + public OutputStream getOutputStream(OutputStream out) + { + return new CipherOutputStream(out, cipher); + } + + public GenericKey getKey() + { + if (isPKCS12(encryptionAlg.getAlgorithm())) + { + return new GenericKey(encryptionAlg, PBEParametersGenerator.PKCS5PasswordToBytes(password)); + } + else + { + return new GenericKey(encryptionAlg, PBEParametersGenerator.PKCS12PasswordToBytes(password)); + } + } + }; + } + catch (Exception e) + { + throw new OperatorCreationException("unable to create OutputEncryptor: " + e.getMessage(), e); + } + } + + private boolean isPKCS12(ASN1ObjectIdentifier algorithm) + { + return algorithm.on(PKCSObjectIdentifiers.pkcs_12PbeIds) + || algorithm.on(BCObjectIdentifiers.bc_pbe_sha1_pkcs12) + || algorithm.on(BCObjectIdentifiers.bc_pbe_sha256_pkcs12); + } +} |