diff options
Diffstat (limited to 'bcpkix/src/main/java/org/bouncycastle/operator/jcajce')
10 files changed, 1506 insertions, 0 deletions
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java new file mode 100644 index 00000000..04885c0a --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java @@ -0,0 +1,160 @@ +package org.bouncycastle.operator.jcajce; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.GeneralSecurityException; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.SignatureException; + +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.jcajce.DefaultJcaJceHelper; +import org.bouncycastle.jcajce.NamedJcaJceHelper; +import org.bouncycastle.jcajce.ProviderJcaJceHelper; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.OperatorStreamException; +import org.bouncycastle.operator.RuntimeOperatorException; + +public class JcaContentSignerBuilder +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + private SecureRandom random; + private String signatureAlgorithm; + private AlgorithmIdentifier sigAlgId; + + public JcaContentSignerBuilder(String signatureAlgorithm) + { + this.signatureAlgorithm = signatureAlgorithm; + this.sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgorithm); + } + + public JcaContentSignerBuilder setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + return this; + } + + public JcaContentSignerBuilder setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + return this; + } + + public JcaContentSignerBuilder setSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + public ContentSigner build(PrivateKey privateKey) + throws OperatorCreationException + { + try + { + final Signature sig = helper.createSignature(sigAlgId); + + if (random != null) + { + sig.initSign(privateKey, random); + } + else + { + sig.initSign(privateKey); + } + + return new ContentSigner() + { + private SignatureOutputStream stream = new SignatureOutputStream(sig); + + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return sigAlgId; + } + + public OutputStream getOutputStream() + { + return stream; + } + + public byte[] getSignature() + { + try + { + return stream.getSignature(); + } + catch (SignatureException e) + { + throw new RuntimeOperatorException("exception obtaining signature: " + e.getMessage(), e); + } + } + }; + } + catch (GeneralSecurityException e) + { + throw new OperatorCreationException("cannot create signer: " + e.getMessage(), e); + } + } + + private class SignatureOutputStream + extends OutputStream + { + private Signature sig; + + SignatureOutputStream(Signature sig) + { + this.sig = sig; + } + + public void write(byte[] bytes, int off, int len) + throws IOException + { + try + { + sig.update(bytes, off, len); + } + catch (SignatureException e) + { + throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e); + } + } + + public void write(byte[] bytes) + throws IOException + { + try + { + sig.update(bytes); + } + catch (SignatureException e) + { + throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e); + } + } + + public void write(int b) + throws IOException + { + try + { + sig.update((byte)b); + } + catch (SignatureException e) + { + throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e); + } + } + + byte[] getSignature() + throws SignatureException + { + return sig.sign(); + } + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java new file mode 100644 index 00000000..56c37718 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java @@ -0,0 +1,305 @@ +package org.bouncycastle.operator.jcajce; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.GeneralSecurityException; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; +import org.bouncycastle.jcajce.DefaultJcaJceHelper; +import org.bouncycastle.jcajce.NamedJcaJceHelper; +import org.bouncycastle.jcajce.ProviderJcaJceHelper; +import org.bouncycastle.operator.ContentVerifier; +import org.bouncycastle.operator.ContentVerifierProvider; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.OperatorStreamException; +import org.bouncycastle.operator.RawContentVerifier; +import org.bouncycastle.operator.RuntimeOperatorException; + +public class JcaContentVerifierProviderBuilder +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + + public JcaContentVerifierProviderBuilder() + { + } + + public JcaContentVerifierProviderBuilder setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + return this; + } + + public JcaContentVerifierProviderBuilder setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + return this; + } + + public ContentVerifierProvider build(X509CertificateHolder certHolder) + throws OperatorCreationException, CertificateException + { + return build(helper.convertCertificate(certHolder)); + } + + public ContentVerifierProvider build(final X509Certificate certificate) + throws OperatorCreationException + { + final X509CertificateHolder certHolder; + + try + { + certHolder = new JcaX509CertificateHolder(certificate); + } + catch (CertificateEncodingException e) + { + throw new OperatorCreationException("cannot process certificate: " + e.getMessage(), e); + } + + return new ContentVerifierProvider() + { + private SignatureOutputStream stream; + + public boolean hasAssociatedCertificate() + { + return true; + } + + public X509CertificateHolder getAssociatedCertificate() + { + return certHolder; + } + + public ContentVerifier get(AlgorithmIdentifier algorithm) + throws OperatorCreationException + { + try + { + Signature sig = helper.createSignature(algorithm); + + sig.initVerify(certificate.getPublicKey()); + + stream = new SignatureOutputStream(sig); + } + catch (GeneralSecurityException e) + { + throw new OperatorCreationException("exception on setup: " + e, e); + } + + Signature rawSig = createRawSig(algorithm, certificate.getPublicKey()); + + if (rawSig != null) + { + return new RawSigVerifier(algorithm, stream, rawSig); + } + else + { + return new SigVerifier(algorithm, stream); + } + } + }; + } + + public ContentVerifierProvider build(final PublicKey publicKey) + throws OperatorCreationException + { + return new ContentVerifierProvider() + { + public boolean hasAssociatedCertificate() + { + return false; + } + + public X509CertificateHolder getAssociatedCertificate() + { + return null; + } + + public ContentVerifier get(AlgorithmIdentifier algorithm) + throws OperatorCreationException + { + SignatureOutputStream stream = createSignatureStream(algorithm, publicKey); + + Signature rawSig = createRawSig(algorithm, publicKey); + + if (rawSig != null) + { + return new RawSigVerifier(algorithm, stream, rawSig); + } + else + { + return new SigVerifier(algorithm, stream); + } + } + }; + } + + private SignatureOutputStream createSignatureStream(AlgorithmIdentifier algorithm, PublicKey publicKey) + throws OperatorCreationException + { + try + { + Signature sig = helper.createSignature(algorithm); + + sig.initVerify(publicKey); + + return new SignatureOutputStream(sig); + } + catch (GeneralSecurityException e) + { + throw new OperatorCreationException("exception on setup: " + e, e); + } + } + + private Signature createRawSig(AlgorithmIdentifier algorithm, PublicKey publicKey) + { + Signature rawSig; + try + { + rawSig = helper.createRawSignature(algorithm); + + if (rawSig != null) + { + rawSig.initVerify(publicKey); + } + } + catch (Exception e) + { + rawSig = null; + } + return rawSig; + } + + private class SigVerifier + implements ContentVerifier + { + private SignatureOutputStream stream; + private AlgorithmIdentifier algorithm; + + SigVerifier(AlgorithmIdentifier algorithm, SignatureOutputStream stream) + { + this.algorithm = algorithm; + this.stream = stream; + } + + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return algorithm; + } + + public OutputStream getOutputStream() + { + if (stream == null) + { + throw new IllegalStateException("verifier not initialised"); + } + + return stream; + } + + public boolean verify(byte[] expected) + { + try + { + return stream.verify(expected); + } + catch (SignatureException e) + { + throw new RuntimeOperatorException("exception obtaining signature: " + e.getMessage(), e); + } + } + } + + private class RawSigVerifier + extends SigVerifier + implements RawContentVerifier + { + private Signature rawSignature; + + RawSigVerifier(AlgorithmIdentifier algorithm, SignatureOutputStream stream, Signature rawSignature) + { + super(algorithm, stream); + this.rawSignature = rawSignature; + } + + public boolean verify(byte[] digest, byte[] expected) + { + try + { + rawSignature.update(digest); + + return rawSignature.verify(expected); + } + catch (SignatureException e) + { + throw new RuntimeOperatorException("exception obtaining raw signature: " + e.getMessage(), e); + } + } + } + + private class SignatureOutputStream + extends OutputStream + { + private Signature sig; + + SignatureOutputStream(Signature sig) + { + this.sig = sig; + } + + public void write(byte[] bytes, int off, int len) + throws IOException + { + try + { + sig.update(bytes, off, len); + } + catch (SignatureException e) + { + throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e); + } + } + + public void write(byte[] bytes) + throws IOException + { + try + { + sig.update(bytes); + } + catch (SignatureException e) + { + throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e); + } + } + + public void write(int b) + throws IOException + { + try + { + sig.update((byte)b); + } + catch (SignatureException e) + { + throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e); + } + } + + boolean verify(byte[] expected) + throws SignatureException + { + return sig.verify(expected); + } + } +}
\ No newline at end of file diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaDigestCalculatorProviderBuilder.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaDigestCalculatorProviderBuilder.java new file mode 100644 index 00000000..6f59cd08 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaDigestCalculatorProviderBuilder.java @@ -0,0 +1,114 @@ +package org.bouncycastle.operator.jcajce; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.GeneralSecurityException; +import java.security.MessageDigest; +import java.security.Provider; + +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.jcajce.DefaultJcaJceHelper; +import org.bouncycastle.jcajce.NamedJcaJceHelper; +import org.bouncycastle.jcajce.ProviderJcaJceHelper; +import org.bouncycastle.operator.DigestCalculator; +import org.bouncycastle.operator.DigestCalculatorProvider; +import org.bouncycastle.operator.OperatorCreationException; + +public class JcaDigestCalculatorProviderBuilder +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + + public JcaDigestCalculatorProviderBuilder() + { + } + + public JcaDigestCalculatorProviderBuilder setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + return this; + } + + public JcaDigestCalculatorProviderBuilder setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + return this; + } + + public DigestCalculatorProvider build() + throws OperatorCreationException + { + return new DigestCalculatorProvider() + { + public DigestCalculator get(final AlgorithmIdentifier algorithm) + throws OperatorCreationException + { + final DigestOutputStream stream; + + try + { + MessageDigest dig = helper.createDigest(algorithm); + + stream = new DigestOutputStream(dig); + } + catch (GeneralSecurityException e) + { + throw new OperatorCreationException("exception on setup: " + e, e); + } + + return new DigestCalculator() + { + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return algorithm; + } + + public OutputStream getOutputStream() + { + return stream; + } + + public byte[] getDigest() + { + return stream.getDigest(); + } + }; + } + }; + } + + private class DigestOutputStream + extends OutputStream + { + private MessageDigest dig; + + DigestOutputStream(MessageDigest dig) + { + this.dig = dig; + } + + public void write(byte[] bytes, int off, int len) + throws IOException + { + dig.update(bytes, off, len); + } + + public void write(byte[] bytes) + throws IOException + { + dig.update(bytes); + } + + public void write(int b) + throws IOException + { + dig.update((byte)b); + } + + byte[] getDigest() + { + return dig.digest(); + } + } +}
\ No newline at end of file diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyUnwrapper.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyUnwrapper.java new file mode 100644 index 00000000..9413f967 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyUnwrapper.java @@ -0,0 +1,124 @@ +package org.bouncycastle.operator.jcajce; + +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.ProviderException; +import java.util.HashMap; +import java.util.Map; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.spec.SecretKeySpec; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.jcajce.DefaultJcaJceHelper; +import org.bouncycastle.jcajce.NamedJcaJceHelper; +import org.bouncycastle.jcajce.ProviderJcaJceHelper; +import org.bouncycastle.operator.AsymmetricKeyUnwrapper; +import org.bouncycastle.operator.GenericKey; +import org.bouncycastle.operator.OperatorException; + +public class JceAsymmetricKeyUnwrapper + extends AsymmetricKeyUnwrapper +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + private Map extraMappings = new HashMap(); + private PrivateKey privKey; + + public JceAsymmetricKeyUnwrapper(AlgorithmIdentifier algorithmIdentifier, PrivateKey privKey) + { + super(algorithmIdentifier); + + this.privKey = privKey; + } + + public JceAsymmetricKeyUnwrapper setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + return this; + } + + public JceAsymmetricKeyUnwrapper setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + return this; + } + + /** + * Internally algorithm ids are converted into cipher names using a lookup table. For some providers + * the standard lookup table won't work. Use this method to establish a specific mapping from an + * algorithm identifier to a specific algorithm. + * <p> + * For example: + * <pre> + * unwrapper.setAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption, "RSA"); + * </pre> + * </p> + * @param algorithm OID of algorithm in recipient. + * @param algorithmName JCE algorithm name to use. + * @return the current Unwrapper. + */ + public JceAsymmetricKeyUnwrapper setAlgorithmMapping(ASN1ObjectIdentifier algorithm, String algorithmName) + { + extraMappings.put(algorithm, algorithmName); + + return this; + } + + public GenericKey generateUnwrappedKey(AlgorithmIdentifier encryptedKeyAlgorithm, byte[] encryptedKey) + throws OperatorException + { + try + { + Key sKey = null; + + Cipher keyCipher = helper.createAsymmetricWrapper(this.getAlgorithmIdentifier().getAlgorithm(), extraMappings); + + try + { + keyCipher.init(Cipher.UNWRAP_MODE, privKey); + sKey = keyCipher.unwrap(encryptedKey, helper.getKeyAlgorithmName(encryptedKeyAlgorithm.getAlgorithm()), Cipher.SECRET_KEY); + } + catch (GeneralSecurityException e) + { + } + catch (IllegalStateException e) + { + } + catch (UnsupportedOperationException e) + { + } + catch (ProviderException e) + { + } + + // some providers do not support UNWRAP (this appears to be only for asymmetric algorithms) + if (sKey == null) + { + keyCipher.init(Cipher.DECRYPT_MODE, privKey); + sKey = new SecretKeySpec(keyCipher.doFinal(encryptedKey), encryptedKeyAlgorithm.getAlgorithm().getId()); + } + + return new JceGenericKey(encryptedKeyAlgorithm, sKey); + } + catch (InvalidKeyException e) + { + throw new OperatorException("key invalid: " + e.getMessage(), e); + } + catch (IllegalBlockSizeException e) + { + throw new OperatorException("illegal blocksize: " + e.getMessage(), e); + } + catch (BadPaddingException e) + { + throw new OperatorException("bad padding: " + e.getMessage(), e); + } + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyWrapper.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyWrapper.java new file mode 100644 index 00000000..4a2ffaea --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyWrapper.java @@ -0,0 +1,125 @@ +package org.bouncycastle.operator.jcajce; + +import java.security.GeneralSecurityException; +import java.security.Provider; +import java.security.ProviderException; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.Map; + +import javax.crypto.Cipher; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.jcajce.DefaultJcaJceHelper; +import org.bouncycastle.jcajce.NamedJcaJceHelper; +import org.bouncycastle.jcajce.ProviderJcaJceHelper; +import org.bouncycastle.operator.AsymmetricKeyWrapper; +import org.bouncycastle.operator.GenericKey; +import org.bouncycastle.operator.OperatorException; + +public class JceAsymmetricKeyWrapper + extends AsymmetricKeyWrapper +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + private Map extraMappings = new HashMap(); + private PublicKey publicKey; + private SecureRandom random; + + public JceAsymmetricKeyWrapper(PublicKey publicKey) + { + super(SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()).getAlgorithm()); + + this.publicKey = publicKey; + } + + public JceAsymmetricKeyWrapper(X509Certificate certificate) + { + this(certificate.getPublicKey()); + } + + public JceAsymmetricKeyWrapper setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + return this; + } + + public JceAsymmetricKeyWrapper setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + return this; + } + + public JceAsymmetricKeyWrapper setSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + /** + * Internally algorithm ids are converted into cipher names using a lookup table. For some providers + * the standard lookup table won't work. Use this method to establish a specific mapping from an + * algorithm identifier to a specific algorithm. + * <p> + * For example: + * <pre> + * unwrapper.setAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption, "RSA"); + * </pre> + * </p> + * @param algorithm OID of algorithm in recipient. + * @param algorithmName JCE algorithm name to use. + * @return the current Wrapper. + */ + public JceAsymmetricKeyWrapper setAlgorithmMapping(ASN1ObjectIdentifier algorithm, String algorithmName) + { + extraMappings.put(algorithm, algorithmName); + + return this; + } + + public byte[] generateWrappedKey(GenericKey encryptionKey) + throws OperatorException + { + Cipher keyEncryptionCipher = helper.createAsymmetricWrapper(getAlgorithmIdentifier().getAlgorithm(), extraMappings); + byte[] encryptedKeyBytes = null; + + try + { + keyEncryptionCipher.init(Cipher.WRAP_MODE, publicKey, random); + encryptedKeyBytes = keyEncryptionCipher.wrap(OperatorUtils.getJceKey(encryptionKey)); + } + catch (GeneralSecurityException e) + { + } + catch (IllegalStateException e) + { + } + catch (UnsupportedOperationException e) + { + } + catch (ProviderException e) + { + } + + // some providers do not support WRAP (this appears to be only for asymmetric algorithms) + if (encryptedKeyBytes == null) + { + try + { + keyEncryptionCipher.init(Cipher.ENCRYPT_MODE, publicKey, random); + encryptedKeyBytes = keyEncryptionCipher.doFinal(OperatorUtils.getJceKey(encryptionKey).getEncoded()); + } + catch (GeneralSecurityException e) + { + throw new OperatorException("unable to encrypt contents key", e); + } + } + + return encryptedKeyBytes; + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceGenericKey.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceGenericKey.java new file mode 100644 index 00000000..efcbc3dc --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceGenericKey.java @@ -0,0 +1,33 @@ +package org.bouncycastle.operator.jcajce; + +import java.security.Key; + +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.operator.GenericKey; + +public class JceGenericKey + extends GenericKey +{ + /** + * Attempt to simplify the key representation if possible. + * + * @param key a provider based key + * @return the byte encoding if one exists, key object otherwise. + */ + private static Object getRepresentation(Key key) + { + byte[] keyBytes = key.getEncoded(); + + if (keyBytes != null) + { + return keyBytes; + } + + return key; + } + + public JceGenericKey(AlgorithmIdentifier algorithmIdentifier, Key representation) + { + super(algorithmIdentifier, getRepresentation(representation)); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceSymmetricKeyUnwrapper.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceSymmetricKeyUnwrapper.java new file mode 100644 index 00000000..2c4c1b6f --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceSymmetricKeyUnwrapper.java @@ -0,0 +1,65 @@ +package org.bouncycastle.operator.jcajce; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; + +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.jcajce.DefaultJcaJceHelper; +import org.bouncycastle.jcajce.NamedJcaJceHelper; +import org.bouncycastle.jcajce.ProviderJcaJceHelper; +import org.bouncycastle.operator.GenericKey; +import org.bouncycastle.operator.OperatorException; +import org.bouncycastle.operator.SymmetricKeyUnwrapper; + +public class JceSymmetricKeyUnwrapper + extends SymmetricKeyUnwrapper +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + private SecretKey secretKey; + + public JceSymmetricKeyUnwrapper(AlgorithmIdentifier algorithmIdentifier, SecretKey secretKey) + { + super(algorithmIdentifier); + + this.secretKey = secretKey; + } + + public JceSymmetricKeyUnwrapper setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + return this; + } + + public JceSymmetricKeyUnwrapper setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + return this; + } + + public GenericKey generateUnwrappedKey(AlgorithmIdentifier encryptedKeyAlgorithm, byte[] encryptedKey) + throws OperatorException + { + try + { + Cipher keyCipher = helper.createSymmetricWrapper(this.getAlgorithmIdentifier().getAlgorithm()); + + keyCipher.init(Cipher.UNWRAP_MODE, secretKey); + + return new JceGenericKey(encryptedKeyAlgorithm, keyCipher.unwrap(encryptedKey, helper.getKeyAlgorithmName(encryptedKeyAlgorithm.getAlgorithm()), Cipher.SECRET_KEY)); + } + catch (InvalidKeyException e) + { + throw new OperatorException("key invalid in message.", e); + } + catch (NoSuchAlgorithmException e) + { + throw new OperatorException("can't find algorithm.", e); + } + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceSymmetricKeyWrapper.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceSymmetricKeyWrapper.java new file mode 100644 index 00000000..008085df --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceSymmetricKeyWrapper.java @@ -0,0 +1,154 @@ +package org.bouncycastle.operator.jcajce; + +import java.security.GeneralSecurityException; +import java.security.Key; +import java.security.Provider; +import java.security.SecureRandom; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; + +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers; +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.jcajce.DefaultJcaJceHelper; +import org.bouncycastle.jcajce.NamedJcaJceHelper; +import org.bouncycastle.jcajce.ProviderJcaJceHelper; +import org.bouncycastle.operator.GenericKey; +import org.bouncycastle.operator.OperatorException; +import org.bouncycastle.operator.SymmetricKeyWrapper; + +public class JceSymmetricKeyWrapper + extends SymmetricKeyWrapper +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + private SecureRandom random; + private SecretKey wrappingKey; + + public JceSymmetricKeyWrapper(SecretKey wrappingKey) + { + super(determineKeyEncAlg(wrappingKey)); + + this.wrappingKey = wrappingKey; + } + + public JceSymmetricKeyWrapper setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + return this; + } + + public JceSymmetricKeyWrapper setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + return this; + } + + public JceSymmetricKeyWrapper setSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + public byte[] generateWrappedKey(GenericKey encryptionKey) + throws OperatorException + { + Key contentEncryptionKeySpec = OperatorUtils.getJceKey(encryptionKey); + + Cipher keyEncryptionCipher = helper.createSymmetricWrapper(this.getAlgorithmIdentifier().getAlgorithm()); + + try + { + keyEncryptionCipher.init(Cipher.WRAP_MODE, wrappingKey, random); + + return keyEncryptionCipher.wrap(contentEncryptionKeySpec); + } + catch (GeneralSecurityException e) + { + throw new OperatorException("cannot wrap key: " + e.getMessage(), e); + } + } + + private static AlgorithmIdentifier determineKeyEncAlg(SecretKey key) + { + String algorithm = key.getAlgorithm(); + + if (algorithm.startsWith("DES")) + { + return new AlgorithmIdentifier(new ASN1ObjectIdentifier( + "1.2.840.113549.1.9.16.3.6"), DERNull.INSTANCE); + } + else if (algorithm.startsWith("RC2")) + { + return new AlgorithmIdentifier(new ASN1ObjectIdentifier( + "1.2.840.113549.1.9.16.3.7"), new ASN1Integer(58)); + } + else if (algorithm.startsWith("AES")) + { + int length = key.getEncoded().length * 8; + ASN1ObjectIdentifier wrapOid; + + if (length == 128) + { + wrapOid = NISTObjectIdentifiers.id_aes128_wrap; + } + else if (length == 192) + { + wrapOid = NISTObjectIdentifiers.id_aes192_wrap; + } + else if (length == 256) + { + wrapOid = NISTObjectIdentifiers.id_aes256_wrap; + } + else + { + throw new IllegalArgumentException("illegal keysize in AES"); + } + + return new AlgorithmIdentifier(wrapOid); // parameters absent + } + else if (algorithm.startsWith("SEED")) + { + // parameters absent + return new AlgorithmIdentifier( + KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap); + } + else if (algorithm.startsWith("Camellia")) + { + int length = key.getEncoded().length * 8; + ASN1ObjectIdentifier wrapOid; + + if (length == 128) + { + wrapOid = NTTObjectIdentifiers.id_camellia128_wrap; + } + else if (length == 192) + { + wrapOid = NTTObjectIdentifiers.id_camellia192_wrap; + } + else if (length == 256) + { + wrapOid = NTTObjectIdentifiers.id_camellia256_wrap; + } + else + { + throw new IllegalArgumentException( + "illegal keysize in Camellia"); + } + + return new AlgorithmIdentifier(wrapOid); // parameters must be + // absent + } + else + { + throw new IllegalArgumentException("unknown algorithm"); + } + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java new file mode 100644 index 00000000..bdffa533 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java @@ -0,0 +1,401 @@ +package org.bouncycastle.operator.jcajce; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Signature; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.PSSParameterSpec; +import java.util.HashMap; +import java.util.Map; + +import javax.crypto.Cipher; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers; +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers; +import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.RSASSAPSSparams; +import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.jcajce.JcaJceHelper; +import org.bouncycastle.operator.OperatorCreationException; + +class OperatorHelper +{ + private static final Map oids = new HashMap(); + private static final Map asymmetricWrapperAlgNames = new HashMap(); + private static final Map symmetricWrapperAlgNames = new HashMap(); + private static final Map symmetricKeyAlgNames = new HashMap(); + + static + { + // + // reverse mappings + // + oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.5"), "SHA1WITHRSA"); + oids.put(PKCSObjectIdentifiers.sha224WithRSAEncryption, "SHA224WITHRSA"); + oids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256WITHRSA"); + oids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384WITHRSA"); + oids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512WITHRSA"); + oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3411WITHGOST3410"); + oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "GOST3411WITHECGOST3410"); + + oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.4"), "MD5WITHRSA"); + oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.2"), "MD2WITHRSA"); + oids.put(new ASN1ObjectIdentifier("1.2.840.10040.4.3"), "SHA1WITHDSA"); + oids.put(X9ObjectIdentifiers.ecdsa_with_SHA1, "SHA1WITHECDSA"); + oids.put(X9ObjectIdentifiers.ecdsa_with_SHA224, "SHA224WITHECDSA"); + oids.put(X9ObjectIdentifiers.ecdsa_with_SHA256, "SHA256WITHECDSA"); + oids.put(X9ObjectIdentifiers.ecdsa_with_SHA384, "SHA384WITHECDSA"); + oids.put(X9ObjectIdentifiers.ecdsa_with_SHA512, "SHA512WITHECDSA"); + oids.put(OIWObjectIdentifiers.sha1WithRSA, "SHA1WITHRSA"); + oids.put(OIWObjectIdentifiers.dsaWithSHA1, "SHA1WITHDSA"); + oids.put(NISTObjectIdentifiers.dsa_with_sha224, "SHA224WITHDSA"); + oids.put(NISTObjectIdentifiers.dsa_with_sha256, "SHA256WITHDSA"); + + oids.put(OIWObjectIdentifiers.idSHA1, "SHA-1"); + oids.put(NISTObjectIdentifiers.id_sha224, "SHA-224"); + oids.put(NISTObjectIdentifiers.id_sha256, "SHA-256"); + oids.put(NISTObjectIdentifiers.id_sha384, "SHA-384"); + oids.put(NISTObjectIdentifiers.id_sha512, "SHA-512"); + oids.put(TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD-128"); + oids.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD-160"); + oids.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD-256"); + + asymmetricWrapperAlgNames.put(PKCSObjectIdentifiers.rsaEncryption, "RSA/ECB/PKCS1Padding"); + + symmetricWrapperAlgNames.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap, "DESEDEWrap"); + symmetricWrapperAlgNames.put(PKCSObjectIdentifiers.id_alg_CMSRC2wrap, "RC2Wrap"); + symmetricWrapperAlgNames.put(NISTObjectIdentifiers.id_aes128_wrap, "AESWrap"); + symmetricWrapperAlgNames.put(NISTObjectIdentifiers.id_aes192_wrap, "AESWrap"); + symmetricWrapperAlgNames.put(NISTObjectIdentifiers.id_aes256_wrap, "AESWrap"); + symmetricWrapperAlgNames.put(NTTObjectIdentifiers.id_camellia128_wrap, "CamelliaWrap"); + symmetricWrapperAlgNames.put(NTTObjectIdentifiers.id_camellia192_wrap, "CamelliaWrap"); + symmetricWrapperAlgNames.put(NTTObjectIdentifiers.id_camellia256_wrap, "CamelliaWrap"); + symmetricWrapperAlgNames.put(KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap, "SEEDWrap"); + symmetricWrapperAlgNames.put(PKCSObjectIdentifiers.des_EDE3_CBC, "DESede"); + + symmetricKeyAlgNames.put(NISTObjectIdentifiers.aes, "AES"); + symmetricKeyAlgNames.put(NISTObjectIdentifiers.id_aes128_CBC, "AES"); + symmetricKeyAlgNames.put(NISTObjectIdentifiers.id_aes192_CBC, "AES"); + symmetricKeyAlgNames.put(NISTObjectIdentifiers.id_aes256_CBC, "AES"); + symmetricKeyAlgNames.put(PKCSObjectIdentifiers.des_EDE3_CBC, "DESede"); + symmetricKeyAlgNames.put(PKCSObjectIdentifiers.RC2_CBC, "RC2"); + } + + private JcaJceHelper helper; + + OperatorHelper(JcaJceHelper helper) + { + this.helper = helper; + } + + Cipher createAsymmetricWrapper(ASN1ObjectIdentifier algorithm, Map extraAlgNames) + throws OperatorCreationException + { + try + { + String cipherName = null; + + if (!extraAlgNames.isEmpty()) + { + cipherName = (String)extraAlgNames.get(algorithm); + } + + if (cipherName == null) + { + cipherName = (String)asymmetricWrapperAlgNames.get(algorithm); + } + + if (cipherName != null) + { + try + { + // this is reversed as the Sun policy files now allow unlimited strength RSA + return helper.createCipher(cipherName); + } + catch (NoSuchAlgorithmException e) + { + // try alternate for RSA + if (cipherName.equals("RSA/ECB/PKCS1Padding")) + { + try + { + return helper.createCipher("RSA/NONE/PKCS1Padding"); + } + catch (NoSuchAlgorithmException ex) + { + // Ignore + } + } + // Ignore + } + } + + return helper.createCipher(algorithm.getId()); + } + catch (GeneralSecurityException e) + { + throw new OperatorCreationException("cannot create cipher: " + e.getMessage(), e); + } + } + + Cipher createSymmetricWrapper(ASN1ObjectIdentifier algorithm) + throws OperatorCreationException + { + try + { + String cipherName = (String)symmetricWrapperAlgNames.get(algorithm); + + if (cipherName != null) + { + try + { + // this is reversed as the Sun policy files now allow unlimited strength RSA + return helper.createCipher(cipherName); + } + catch (NoSuchAlgorithmException e) + { + // Ignore + } + } + return helper.createCipher(algorithm.getId()); + } + catch (GeneralSecurityException e) + { + throw new OperatorCreationException("cannot create cipher: " + e.getMessage(), e); + } + } + + MessageDigest createDigest(AlgorithmIdentifier digAlgId) + throws GeneralSecurityException + { + MessageDigest dig; + + try + { + dig = helper.createDigest(getDigestAlgName(digAlgId.getAlgorithm())); + } + catch (NoSuchAlgorithmException e) + { + // + // try an alternate + // + if (oids.get(digAlgId.getAlgorithm()) != null) + { + String digestAlgorithm = (String)oids.get(digAlgId.getAlgorithm()); + + dig = helper.createDigest(digestAlgorithm); + } + else + { + throw e; + } + } + + return dig; + } + + Signature createSignature(AlgorithmIdentifier sigAlgId) + throws GeneralSecurityException + { + Signature sig; + + try + { + sig = helper.createSignature(getSignatureName(sigAlgId)); + } + catch (NoSuchAlgorithmException e) + { + // + // try an alternate + // + if (oids.get(sigAlgId.getAlgorithm()) != null) + { + String signatureAlgorithm = (String)oids.get(sigAlgId.getAlgorithm()); + + sig = helper.createSignature(signatureAlgorithm); + } + else + { + throw e; + } + } + + return sig; + } + + public Signature createRawSignature(AlgorithmIdentifier algorithm) + { + Signature sig; + + try + { + String algName = getSignatureName(algorithm); + + algName = "NONE" + algName.substring(algName.indexOf("WITH")); + + sig = helper.createSignature(algName); + + // RFC 4056 + // When the id-RSASSA-PSS algorithm identifier is used for a signature, + // the AlgorithmIdentifier parameters field MUST contain RSASSA-PSS-params. + if (algorithm.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS)) + { + AlgorithmParameters params = helper.createAlgorithmParameters(algName); + + params.init(algorithm.getParameters().toASN1Primitive().getEncoded(), "ASN.1"); + + PSSParameterSpec spec = (PSSParameterSpec)params.getParameterSpec(PSSParameterSpec.class); + sig.setParameter(spec); + } + } + catch (Exception e) + { + return null; + } + + return sig; + } + + private static String getSignatureName( + AlgorithmIdentifier sigAlgId) + { + ASN1Encodable params = sigAlgId.getParameters(); + + if (params != null && !DERNull.INSTANCE.equals(params)) + { + if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS)) + { + RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params); + return getDigestAlgName(rsaParams.getHashAlgorithm().getAlgorithm()) + "WITHRSAANDMGF1"; + } + } + + if (oids.containsKey(sigAlgId.getAlgorithm())) + { + return (String)oids.get(sigAlgId.getAlgorithm()); + } + + return sigAlgId.getAlgorithm().getId(); + } + + private static String getDigestAlgName( + ASN1ObjectIdentifier digestAlgOID) + { + if (PKCSObjectIdentifiers.md5.equals(digestAlgOID)) + { + return "MD5"; + } + else if (OIWObjectIdentifiers.idSHA1.equals(digestAlgOID)) + { + return "SHA1"; + } + else if (NISTObjectIdentifiers.id_sha224.equals(digestAlgOID)) + { + return "SHA224"; + } + else if (NISTObjectIdentifiers.id_sha256.equals(digestAlgOID)) + { + return "SHA256"; + } + else if (NISTObjectIdentifiers.id_sha384.equals(digestAlgOID)) + { + return "SHA384"; + } + else if (NISTObjectIdentifiers.id_sha512.equals(digestAlgOID)) + { + return "SHA512"; + } + else if (TeleTrusTObjectIdentifiers.ripemd128.equals(digestAlgOID)) + { + return "RIPEMD128"; + } + else if (TeleTrusTObjectIdentifiers.ripemd160.equals(digestAlgOID)) + { + return "RIPEMD160"; + } + else if (TeleTrusTObjectIdentifiers.ripemd256.equals(digestAlgOID)) + { + return "RIPEMD256"; + } + else if (CryptoProObjectIdentifiers.gostR3411.equals(digestAlgOID)) + { + return "GOST3411"; + } + else + { + return digestAlgOID.getId(); + } + } + + public X509Certificate convertCertificate(X509CertificateHolder certHolder) + throws CertificateException + { + + try + { + CertificateFactory certFact = helper.createCertificateFactory("X.509"); + + return (X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(certHolder.getEncoded())); + } + catch (IOException e) + { + throw new OpCertificateException("cannot get encoded form of certificate: " + e.getMessage(), e); + } + catch (NoSuchAlgorithmException e) + { + throw new OpCertificateException("cannot create certificate factory: " + e.getMessage(), e); + } + catch (NoSuchProviderException e) + { + throw new OpCertificateException("cannot find factory provider: " + e.getMessage(), e); + } + } + + // TODO: put somewhere public so cause easily accessed + private static class OpCertificateException + extends CertificateException + { + private Throwable cause; + + public OpCertificateException(String msg, Throwable cause) + { + super(msg); + + this.cause = cause; + } + + public Throwable getCause() + { + return cause; + } + } + + String getKeyAlgorithmName(ASN1ObjectIdentifier oid) + { + + String name = (String)symmetricKeyAlgNames.get(oid); + + if (name != null) + { + return name; + } + + return oid.getId(); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorUtils.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorUtils.java new file mode 100644 index 00000000..6c41d960 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorUtils.java @@ -0,0 +1,25 @@ +package org.bouncycastle.operator.jcajce; + +import java.security.Key; + +import javax.crypto.spec.SecretKeySpec; + +import org.bouncycastle.operator.GenericKey; + +class OperatorUtils +{ + static Key getJceKey(GenericKey key) + { + if (key.getRepresentation() instanceof Key) + { + return (Key)key.getRepresentation(); + } + + if (key.getRepresentation() instanceof byte[]) + { + return new SecretKeySpec((byte[])key.getRepresentation(), "ENC"); + } + + throw new IllegalArgumentException("unknown generic key type"); + } +}
\ No newline at end of file |