diff options
Diffstat (limited to 'pkix/src/main/java/org/spongycastle/operator')
65 files changed, 3770 insertions, 0 deletions
diff --git a/pkix/src/main/java/org/spongycastle/operator/AsymmetricKeyUnwrapper.java b/pkix/src/main/java/org/spongycastle/operator/AsymmetricKeyUnwrapper.java new file mode 100644 index 00000000..2ed2f145 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/AsymmetricKeyUnwrapper.java @@ -0,0 +1,19 @@ +package org.spongycastle.operator; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public abstract class AsymmetricKeyUnwrapper + implements KeyUnwrapper +{ + private AlgorithmIdentifier algorithmId; + + protected AsymmetricKeyUnwrapper(AlgorithmIdentifier algorithmId) + { + this.algorithmId = algorithmId; + } + + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return algorithmId; + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/AsymmetricKeyWrapper.java b/pkix/src/main/java/org/spongycastle/operator/AsymmetricKeyWrapper.java new file mode 100644 index 00000000..3de80226 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/AsymmetricKeyWrapper.java @@ -0,0 +1,19 @@ +package org.spongycastle.operator; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public abstract class AsymmetricKeyWrapper + implements KeyWrapper +{ + private AlgorithmIdentifier algorithmId; + + protected AsymmetricKeyWrapper(AlgorithmIdentifier algorithmId) + { + this.algorithmId = algorithmId; + } + + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return algorithmId; + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/BufferingContentSigner.java b/pkix/src/main/java/org/spongycastle/operator/BufferingContentSigner.java new file mode 100644 index 00000000..e96a906c --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/BufferingContentSigner.java @@ -0,0 +1,70 @@ +package org.spongycastle.operator; + +import java.io.OutputStream; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.util.io.BufferingOutputStream; + +/** + * A class that explicitly buffers the data to be signed, sending it in one + * block when ready for signing. + */ +public class BufferingContentSigner + implements ContentSigner +{ + private final ContentSigner contentSigner; + private final OutputStream output; + + /** + * Base constructor. + * + * @param contentSigner the content signer to be wrapped. + */ + public BufferingContentSigner(ContentSigner contentSigner) + { + this.contentSigner = contentSigner; + this.output = new BufferingOutputStream(contentSigner.getOutputStream()); + } + + /** + * Base constructor. + * + * @param contentSigner the content signer to be wrapped. + * @param bufferSize the size of the internal buffer to use. + */ + public BufferingContentSigner(ContentSigner contentSigner, int bufferSize) + { + this.contentSigner = contentSigner; + this.output = new BufferingOutputStream(contentSigner.getOutputStream(), bufferSize); + } + + /** + * Return the algorithm identifier supported by this signer. + * + * @return algorithm identifier for the signature generated. + */ + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return contentSigner.getAlgorithmIdentifier(); + } + + /** + * Return the buffering stream. + * + * @return the output stream used to accumulate the data. + */ + public OutputStream getOutputStream() + { + return output; + } + + /** + * Generate signature from internally buffered data. + * + * @return the signature calculated from the bytes written to the buffering stream. + */ + public byte[] getSignature() + { + return contentSigner.getSignature(); + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/ContentSigner.java b/pkix/src/main/java/org/spongycastle/operator/ContentSigner.java new file mode 100644 index 00000000..fcdeefc4 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/ContentSigner.java @@ -0,0 +1,27 @@ +package org.spongycastle.operator; + +import java.io.OutputStream; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public interface ContentSigner +{ + AlgorithmIdentifier getAlgorithmIdentifier(); + + /** + * Returns a stream that will accept data for the purpose of calculating + * a signature. Use org.spongycastle.util.io.TeeOutputStream if you want to accumulate + * the data on the fly as well. + * + * @return an OutputStream + */ + OutputStream getOutputStream(); + + /** + * Returns a signature based on the current data written to the stream, since the + * start or the last call to getSignature(). + * + * @return bytes representing the signature. + */ + byte[] getSignature(); +} diff --git a/pkix/src/main/java/org/spongycastle/operator/ContentVerifier.java b/pkix/src/main/java/org/spongycastle/operator/ContentVerifier.java new file mode 100644 index 00000000..a139ebb2 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/ContentVerifier.java @@ -0,0 +1,31 @@ +package org.spongycastle.operator; + +import java.io.OutputStream; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public interface ContentVerifier +{ + /** + * Return the algorithm identifier describing the signature + * algorithm and parameters this expander supports. + * + * @return algorithm oid and parameters. + */ + AlgorithmIdentifier getAlgorithmIdentifier(); + + /** + * Returns a stream that will accept data for the purpose of calculating + * a signature for later verification. Use org.spongycastle.util.io.TeeOutputStream if you want to accumulate + * the data on the fly as well. + * + * @return an OutputStream + */ + OutputStream getOutputStream(); + + /** + * @param expected expected value of the signature on the data. + * @return true if the signature verifies, false otherwise + */ + boolean verify(byte[] expected); +}
\ No newline at end of file diff --git a/pkix/src/main/java/org/spongycastle/operator/ContentVerifierProvider.java b/pkix/src/main/java/org/spongycastle/operator/ContentVerifierProvider.java new file mode 100644 index 00000000..9d91304a --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/ContentVerifierProvider.java @@ -0,0 +1,34 @@ +package org.spongycastle.operator; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.cert.X509CertificateHolder; + +/** + * General interface for providers of ContentVerifier objects. + */ +public interface ContentVerifierProvider +{ + /** + * Return whether or not this verifier has a certificate associated with it. + * + * @return true if there is an associated certificate, false otherwise. + */ + boolean hasAssociatedCertificate(); + + /** + * Return the associated certificate if there is one. + * + * @return a holder containing the associated certificate if there is one, null if there is not. + */ + X509CertificateHolder getAssociatedCertificate(); + + /** + * Return a ContentVerifier that matches the passed in algorithm identifier, + * + * @param verifierAlgorithmIdentifier the algorithm and parameters required. + * @return a matching ContentVerifier + * @throws OperatorCreationException if the required ContentVerifier cannot be created. + */ + ContentVerifier get(AlgorithmIdentifier verifierAlgorithmIdentifier) + throws OperatorCreationException; +} diff --git a/pkix/src/main/java/org/spongycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java b/pkix/src/main/java/org/spongycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java new file mode 100644 index 00000000..42d6665e --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java @@ -0,0 +1,97 @@ +package org.spongycastle.operator; + +import java.util.HashMap; +import java.util.Map; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.spongycastle.asn1.nist.NISTObjectIdentifiers; +import org.spongycastle.asn1.oiw.OIWObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.RSASSAPSSparams; +import org.spongycastle.asn1.teletrust.TeleTrusTObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; + +public class DefaultDigestAlgorithmIdentifierFinder + implements DigestAlgorithmIdentifierFinder +{ + private static Map digestOids = new HashMap(); + private static Map digestNameToOids = new HashMap(); + + static + { + // + // digests + // + digestOids.put(OIWObjectIdentifiers.md4WithRSAEncryption, PKCSObjectIdentifiers.md4); + digestOids.put(OIWObjectIdentifiers.md4WithRSA, PKCSObjectIdentifiers.md4); + digestOids.put(OIWObjectIdentifiers.sha1WithRSA, OIWObjectIdentifiers.idSHA1); + + digestOids.put(PKCSObjectIdentifiers.sha224WithRSAEncryption, NISTObjectIdentifiers.id_sha224); + digestOids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, NISTObjectIdentifiers.id_sha256); + digestOids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, NISTObjectIdentifiers.id_sha384); + digestOids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, NISTObjectIdentifiers.id_sha512); + digestOids.put(PKCSObjectIdentifiers.md2WithRSAEncryption, PKCSObjectIdentifiers.md2); + digestOids.put(PKCSObjectIdentifiers.md4WithRSAEncryption, PKCSObjectIdentifiers.md4); + digestOids.put(PKCSObjectIdentifiers.md5WithRSAEncryption, PKCSObjectIdentifiers.md5); + digestOids.put(PKCSObjectIdentifiers.sha1WithRSAEncryption, OIWObjectIdentifiers.idSHA1); + + digestOids.put(X9ObjectIdentifiers.ecdsa_with_SHA1, OIWObjectIdentifiers.idSHA1); + digestOids.put(X9ObjectIdentifiers.ecdsa_with_SHA224, NISTObjectIdentifiers.id_sha224); + digestOids.put(X9ObjectIdentifiers.ecdsa_with_SHA256, NISTObjectIdentifiers.id_sha256); + digestOids.put(X9ObjectIdentifiers.ecdsa_with_SHA384, NISTObjectIdentifiers.id_sha384); + digestOids.put(X9ObjectIdentifiers.ecdsa_with_SHA512, NISTObjectIdentifiers.id_sha512); + digestOids.put(X9ObjectIdentifiers.id_dsa_with_sha1, OIWObjectIdentifiers.idSHA1); + + digestOids.put(NISTObjectIdentifiers.dsa_with_sha224, NISTObjectIdentifiers.id_sha224); + digestOids.put(NISTObjectIdentifiers.dsa_with_sha256, NISTObjectIdentifiers.id_sha256); + digestOids.put(NISTObjectIdentifiers.dsa_with_sha384, NISTObjectIdentifiers.id_sha384); + digestOids.put(NISTObjectIdentifiers.dsa_with_sha512, NISTObjectIdentifiers.id_sha512); + + digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128, TeleTrusTObjectIdentifiers.ripemd128); + digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160, TeleTrusTObjectIdentifiers.ripemd160); + digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256, TeleTrusTObjectIdentifiers.ripemd256); + + digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, CryptoProObjectIdentifiers.gostR3411); + digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, CryptoProObjectIdentifiers.gostR3411); + + digestNameToOids.put("SHA-1", OIWObjectIdentifiers.idSHA1); + digestNameToOids.put("SHA-224", NISTObjectIdentifiers.id_sha224); + digestNameToOids.put("SHA-256", NISTObjectIdentifiers.id_sha256); + digestNameToOids.put("SHA-384", NISTObjectIdentifiers.id_sha384); + digestNameToOids.put("SHA-512", NISTObjectIdentifiers.id_sha512); + + digestNameToOids.put("GOST3411", CryptoProObjectIdentifiers.gostR3411); + + digestNameToOids.put("MD2", PKCSObjectIdentifiers.md2); + digestNameToOids.put("MD4", PKCSObjectIdentifiers.md4); + digestNameToOids.put("MD5", PKCSObjectIdentifiers.md5); + + digestNameToOids.put("RIPEMD128", TeleTrusTObjectIdentifiers.ripemd128); + digestNameToOids.put("RIPEMD160", TeleTrusTObjectIdentifiers.ripemd160); + digestNameToOids.put("RIPEMD256", TeleTrusTObjectIdentifiers.ripemd256); + } + + public AlgorithmIdentifier find(AlgorithmIdentifier sigAlgId) + { + AlgorithmIdentifier digAlgId; + + if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS)) + { + digAlgId = RSASSAPSSparams.getInstance(sigAlgId.getParameters()).getHashAlgorithm(); + } + else + { + digAlgId = new AlgorithmIdentifier((ASN1ObjectIdentifier)digestOids.get(sigAlgId.getAlgorithm()), DERNull.INSTANCE); + } + + return digAlgId; + } + + public AlgorithmIdentifier find(String digAlgName) + { + return new AlgorithmIdentifier((ASN1ObjectIdentifier)digestNameToOids.get(digAlgName), DERNull.INSTANCE); + } +}
\ No newline at end of file diff --git a/pkix/src/main/java/org/spongycastle/operator/DefaultSecretKeySizeProvider.java b/pkix/src/main/java/org/spongycastle/operator/DefaultSecretKeySizeProvider.java new file mode 100644 index 00000000..d830e5cc --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/DefaultSecretKeySizeProvider.java @@ -0,0 +1,69 @@ +package org.spongycastle.operator; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.spongycastle.asn1.nist.NISTObjectIdentifiers; +import org.spongycastle.asn1.ntt.NTTObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.util.Integers; + +public class DefaultSecretKeySizeProvider + implements SecretKeySizeProvider +{ + public static final SecretKeySizeProvider INSTANCE = new DefaultSecretKeySizeProvider(); + + private static final Map KEY_SIZES; + + static + { + Map keySizes = new HashMap(); + + keySizes.put(new ASN1ObjectIdentifier("1.2.840.113533.7.66.10"), Integers.valueOf(128)); + + keySizes.put(PKCSObjectIdentifiers.des_EDE3_CBC, 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) + { + int keySize = getKeySize(algorithmIdentifier.getAlgorithm()); + + // just need the OID + if (keySize > 0) + { + return keySize; + } + + // TODO: support OID/Parameter key sizes (e.g. RC2). + + return -1; + } + + public int getKeySize(ASN1ObjectIdentifier algorithm) + { + Integer keySize = (Integer)KEY_SIZES.get(algorithm); + + if (keySize != null) + { + return keySize.intValue(); + } + + return -1; + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java b/pkix/src/main/java/org/spongycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java new file mode 100644 index 00000000..58608c8b --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java @@ -0,0 +1,224 @@ +package org.spongycastle.operator; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.bsi.BSIObjectIdentifiers; +import org.spongycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.spongycastle.asn1.eac.EACObjectIdentifiers; +import org.spongycastle.asn1.nist.NISTObjectIdentifiers; +import org.spongycastle.asn1.oiw.OIWObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.RSASSAPSSparams; +import org.spongycastle.asn1.teletrust.TeleTrusTObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.util.Strings; + +public class DefaultSignatureAlgorithmIdentifierFinder + implements SignatureAlgorithmIdentifierFinder +{ + private static Map algorithms = new HashMap(); + private static Set noParams = new HashSet(); + private static Map params = new HashMap(); + private static Set pkcs15RsaEncryption = new HashSet(); + private static Map digestOids = new HashMap(); + + private static final ASN1ObjectIdentifier ENCRYPTION_RSA = PKCSObjectIdentifiers.rsaEncryption; + private static final ASN1ObjectIdentifier ENCRYPTION_DSA = X9ObjectIdentifiers.id_dsa_with_sha1; + private static final ASN1ObjectIdentifier ENCRYPTION_ECDSA = X9ObjectIdentifiers.ecdsa_with_SHA1; + private static final ASN1ObjectIdentifier ENCRYPTION_RSA_PSS = PKCSObjectIdentifiers.id_RSASSA_PSS; + private static final ASN1ObjectIdentifier ENCRYPTION_GOST3410 = CryptoProObjectIdentifiers.gostR3410_94; + private static final ASN1ObjectIdentifier ENCRYPTION_ECGOST3410 = CryptoProObjectIdentifiers.gostR3410_2001; + + static + { + algorithms.put("MD2WITHRSAENCRYPTION", PKCSObjectIdentifiers.md2WithRSAEncryption); + algorithms.put("MD2WITHRSA", PKCSObjectIdentifiers.md2WithRSAEncryption); + algorithms.put("MD5WITHRSAENCRYPTION", PKCSObjectIdentifiers.md5WithRSAEncryption); + algorithms.put("MD5WITHRSA", PKCSObjectIdentifiers.md5WithRSAEncryption); + algorithms.put("SHA1WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha1WithRSAEncryption); + algorithms.put("SHA1WITHRSA", PKCSObjectIdentifiers.sha1WithRSAEncryption); + algorithms.put("SHA224WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha224WithRSAEncryption); + algorithms.put("SHA224WITHRSA", PKCSObjectIdentifiers.sha224WithRSAEncryption); + algorithms.put("SHA256WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha256WithRSAEncryption); + algorithms.put("SHA256WITHRSA", PKCSObjectIdentifiers.sha256WithRSAEncryption); + algorithms.put("SHA384WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha384WithRSAEncryption); + algorithms.put("SHA384WITHRSA", PKCSObjectIdentifiers.sha384WithRSAEncryption); + algorithms.put("SHA512WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha512WithRSAEncryption); + algorithms.put("SHA512WITHRSA", PKCSObjectIdentifiers.sha512WithRSAEncryption); + algorithms.put("SHA1WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS); + algorithms.put("SHA224WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS); + algorithms.put("SHA256WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS); + algorithms.put("SHA384WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS); + algorithms.put("SHA512WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS); + algorithms.put("RIPEMD160WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160); + algorithms.put("RIPEMD160WITHRSA", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160); + algorithms.put("RIPEMD128WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128); + algorithms.put("RIPEMD128WITHRSA", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128); + algorithms.put("RIPEMD256WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256); + algorithms.put("RIPEMD256WITHRSA", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256); + algorithms.put("SHA1WITHDSA", X9ObjectIdentifiers.id_dsa_with_sha1); + algorithms.put("DSAWITHSHA1", X9ObjectIdentifiers.id_dsa_with_sha1); + algorithms.put("SHA224WITHDSA", NISTObjectIdentifiers.dsa_with_sha224); + algorithms.put("SHA256WITHDSA", NISTObjectIdentifiers.dsa_with_sha256); + algorithms.put("SHA384WITHDSA", NISTObjectIdentifiers.dsa_with_sha384); + algorithms.put("SHA512WITHDSA", NISTObjectIdentifiers.dsa_with_sha512); + algorithms.put("SHA1WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA1); + algorithms.put("ECDSAWITHSHA1", X9ObjectIdentifiers.ecdsa_with_SHA1); + algorithms.put("SHA224WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA224); + algorithms.put("SHA256WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA256); + algorithms.put("SHA384WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA384); + algorithms.put("SHA512WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA512); + algorithms.put("GOST3411WITHGOST3410", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94); + algorithms.put("GOST3411WITHGOST3410-94", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94); + algorithms.put("GOST3411WITHECGOST3410", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001); + algorithms.put("GOST3411WITHECGOST3410-2001", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001); + algorithms.put("GOST3411WITHGOST3410-2001", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001); + algorithms.put("SHA1WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA1); + algorithms.put("SHA224WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA224); + algorithms.put("SHA256WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA256); + algorithms.put("SHA384WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA384); + algorithms.put("SHA512WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA512); + algorithms.put("RIPEMD160WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_RIPEMD160); + algorithms.put("SHA1WITHCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_1); + algorithms.put("SHA224WITHPCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_224); + algorithms.put("SHA256WITHCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_256); + algorithms.put("SHA384WITHCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_384); + algorithms.put("SHA512WITHCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_512); + // + // According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field. + // The parameters field SHALL be NULL for RSA based signature algorithms. + // + noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA1); + noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA224); + noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA256); + noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA384); + noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA512); + noParams.add(X9ObjectIdentifiers.id_dsa_with_sha1); + noParams.add(NISTObjectIdentifiers.dsa_with_sha224); + noParams.add(NISTObjectIdentifiers.dsa_with_sha256); + noParams.add(NISTObjectIdentifiers.dsa_with_sha384); + noParams.add(NISTObjectIdentifiers.dsa_with_sha512); + + // + // RFC 4491 + // + noParams.add(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94); + noParams.add(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001); + + // + // PKCS 1.5 encrypted algorithms + // + pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha1WithRSAEncryption); + pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha224WithRSAEncryption); + pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha256WithRSAEncryption); + pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha384WithRSAEncryption); + pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha512WithRSAEncryption); + pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128); + pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160); + pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256); + + // + // explicit params + // + AlgorithmIdentifier sha1AlgId = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE); + params.put("SHA1WITHRSAANDMGF1", createPSSParams(sha1AlgId, 20)); + + AlgorithmIdentifier sha224AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha224, DERNull.INSTANCE); + params.put("SHA224WITHRSAANDMGF1", createPSSParams(sha224AlgId, 28)); + + AlgorithmIdentifier sha256AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, DERNull.INSTANCE); + params.put("SHA256WITHRSAANDMGF1", createPSSParams(sha256AlgId, 32)); + + AlgorithmIdentifier sha384AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha384, DERNull.INSTANCE); + params.put("SHA384WITHRSAANDMGF1", createPSSParams(sha384AlgId, 48)); + + AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512, DERNull.INSTANCE); + params.put("SHA512WITHRSAANDMGF1", createPSSParams(sha512AlgId, 64)); + + // + // digests + // + digestOids.put(PKCSObjectIdentifiers.sha224WithRSAEncryption, NISTObjectIdentifiers.id_sha224); + digestOids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, NISTObjectIdentifiers.id_sha256); + digestOids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, NISTObjectIdentifiers.id_sha384); + digestOids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, NISTObjectIdentifiers.id_sha512); + digestOids.put(PKCSObjectIdentifiers.md2WithRSAEncryption, PKCSObjectIdentifiers.md2); + digestOids.put(PKCSObjectIdentifiers.md4WithRSAEncryption, PKCSObjectIdentifiers.md4); + digestOids.put(PKCSObjectIdentifiers.md5WithRSAEncryption, PKCSObjectIdentifiers.md5); + digestOids.put(PKCSObjectIdentifiers.sha1WithRSAEncryption, OIWObjectIdentifiers.idSHA1); + digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128, TeleTrusTObjectIdentifiers.ripemd128); + digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160, TeleTrusTObjectIdentifiers.ripemd160); + digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256, TeleTrusTObjectIdentifiers.ripemd256); + digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, CryptoProObjectIdentifiers.gostR3411); + digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, CryptoProObjectIdentifiers.gostR3411); + } + + private static AlgorithmIdentifier generate(String signatureAlgorithm) + { + AlgorithmIdentifier sigAlgId; + AlgorithmIdentifier encAlgId; + AlgorithmIdentifier digAlgId; + + String algorithmName = Strings.toUpperCase(signatureAlgorithm); + ASN1ObjectIdentifier sigOID = (ASN1ObjectIdentifier)algorithms.get(algorithmName); + if (sigOID == null) + { + throw new IllegalArgumentException("Unknown signature type requested: " + algorithmName); + } + + if (noParams.contains(sigOID)) + { + sigAlgId = new AlgorithmIdentifier(sigOID); + } + else if (params.containsKey(algorithmName)) + { + sigAlgId = new AlgorithmIdentifier(sigOID, (ASN1Encodable)params.get(algorithmName)); + } + else + { + sigAlgId = new AlgorithmIdentifier(sigOID, DERNull.INSTANCE); + } + + if (pkcs15RsaEncryption.contains(sigOID)) + { + encAlgId = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE); + } + else + { + encAlgId = sigAlgId; + } + + if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS)) + { + digAlgId = ((RSASSAPSSparams)sigAlgId.getParameters()).getHashAlgorithm(); + } + else + { + digAlgId = new AlgorithmIdentifier((ASN1ObjectIdentifier)digestOids.get(sigOID), DERNull.INSTANCE); + } + + return sigAlgId; + } + + private static RSASSAPSSparams createPSSParams(AlgorithmIdentifier hashAlgId, int saltSize) + { + return new RSASSAPSSparams( + hashAlgId, + new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, hashAlgId), + new ASN1Integer(saltSize), + new ASN1Integer(1)); + } + + public AlgorithmIdentifier find(String sigAlgName) + { + return generate(sigAlgName); + } +}
\ No newline at end of file diff --git a/pkix/src/main/java/org/spongycastle/operator/DigestAlgorithmIdentifierFinder.java b/pkix/src/main/java/org/spongycastle/operator/DigestAlgorithmIdentifierFinder.java new file mode 100644 index 00000000..1254c38e --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/DigestAlgorithmIdentifierFinder.java @@ -0,0 +1,24 @@ +package org.spongycastle.operator; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public interface DigestAlgorithmIdentifierFinder +{ + /** + * Find the digest algorithm identifier that matches with + * the passed in signature algorithm identifier. + * + * @param sigAlgId the signature algorithm of interest. + * @return an algorithm identifier for the corresponding digest. + */ + AlgorithmIdentifier find(AlgorithmIdentifier sigAlgId); + + /** + * Find the algorithm identifier that matches with + * the passed in digest name. + * + * @param digAlgName the name of the digest algorithm of interest. + * @return an algorithm identifier for the digest signature. + */ + AlgorithmIdentifier find(String digAlgName); +}
\ No newline at end of file diff --git a/pkix/src/main/java/org/spongycastle/operator/DigestCalculator.java b/pkix/src/main/java/org/spongycastle/operator/DigestCalculator.java new file mode 100644 index 00000000..0bb4712f --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/DigestCalculator.java @@ -0,0 +1,36 @@ +package org.spongycastle.operator; + +import java.io.OutputStream; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +/** + * General interface for an operator that is able to calculate a digest from + * a stream of output. + */ +public interface DigestCalculator +{ + /** + * Return the algorithm identifier representing the digest implemented by + * this calculator. + * + * @return algorithm id and parameters. + */ + AlgorithmIdentifier getAlgorithmIdentifier(); + + /** + * Returns a stream that will accept data for the purpose of calculating + * a digest. Use org.spongycastle.util.io.TeeOutputStream if you want to accumulate + * the data on the fly as well. + * + * @return an OutputStream + */ + OutputStream getOutputStream(); + + /** + * Return the digest calculated on what has been written to the calculator's output stream. + * + * @return a digest. + */ + byte[] getDigest(); +} diff --git a/pkix/src/main/java/org/spongycastle/operator/DigestCalculatorProvider.java b/pkix/src/main/java/org/spongycastle/operator/DigestCalculatorProvider.java new file mode 100644 index 00000000..55a7c143 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/DigestCalculatorProvider.java @@ -0,0 +1,9 @@ +package org.spongycastle.operator; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public interface DigestCalculatorProvider +{ + DigestCalculator get(AlgorithmIdentifier digestAlgorithmIdentifier) + throws OperatorCreationException; +} diff --git a/pkix/src/main/java/org/spongycastle/operator/GenericKey.java b/pkix/src/main/java/org/spongycastle/operator/GenericKey.java new file mode 100644 index 00000000..5446ce7b --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/GenericKey.java @@ -0,0 +1,41 @@ +package org.spongycastle.operator; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public class GenericKey +{ + private AlgorithmIdentifier algorithmIdentifier; + private Object representation; + + /** + * @deprecated provide an AlgorithmIdentifier. + * @param representation key data + */ + public GenericKey(Object representation) + { + this.algorithmIdentifier = null; + this.representation = representation; + } + + public GenericKey(AlgorithmIdentifier algorithmIdentifier, byte[] representation) + { + this.algorithmIdentifier = algorithmIdentifier; + this.representation = representation; + } + + protected GenericKey(AlgorithmIdentifier algorithmIdentifier, Object representation) + { + this.algorithmIdentifier = algorithmIdentifier; + this.representation = representation; + } + + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return algorithmIdentifier; + } + + public Object getRepresentation() + { + return representation; + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/InputDecryptor.java b/pkix/src/main/java/org/spongycastle/operator/InputDecryptor.java new file mode 100644 index 00000000..c55b3db0 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/InputDecryptor.java @@ -0,0 +1,29 @@ +package org.spongycastle.operator; + +import java.io.InputStream; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +/** + * General interface for an operator that is able to produce + * an InputStream that will decrypt a stream of encrypted data. + */ +public interface InputDecryptor +{ + /** + * Return the algorithm identifier describing the encryption + * algorithm and parameters this decryptor can process. + * + * @return algorithm oid and parameters. + */ + AlgorithmIdentifier getAlgorithmIdentifier(); + + /** + * Wrap the passed in input stream encIn, returning an input stream + * that decrypts what it reads from encIn before returning it. + * + * @param encIn InputStream containing encrypted input. + * @return an decrypting InputStream + */ + InputStream getInputStream(InputStream encIn); +} diff --git a/pkix/src/main/java/org/spongycastle/operator/InputDecryptorProvider.java b/pkix/src/main/java/org/spongycastle/operator/InputDecryptorProvider.java new file mode 100644 index 00000000..4ef7e9c0 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/InputDecryptorProvider.java @@ -0,0 +1,9 @@ +package org.spongycastle.operator; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public interface InputDecryptorProvider +{ + public InputDecryptor get(AlgorithmIdentifier algorithm) + throws OperatorCreationException; +} diff --git a/pkix/src/main/java/org/spongycastle/operator/InputExpander.java b/pkix/src/main/java/org/spongycastle/operator/InputExpander.java new file mode 100644 index 00000000..870e4807 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/InputExpander.java @@ -0,0 +1,29 @@ +package org.spongycastle.operator; + +import java.io.InputStream; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +/** + * General interface for an operator that is able to produce + * an InputStream that will produce uncompressed data. + */ +public interface InputExpander +{ + /** + * Return the algorithm identifier describing the compression + * algorithm and parameters this expander supports. + * + * @return algorithm oid and parameters. + */ + AlgorithmIdentifier getAlgorithmIdentifier(); + + /** + * Wrap the passed in input stream comIn, returning an input stream + * that expands anything read in from comIn. + * + * @param comIn the compressed input data stream.. + * @return an expanding InputStream. + */ + InputStream getInputStream(InputStream comIn); +} diff --git a/pkix/src/main/java/org/spongycastle/operator/InputExpanderProvider.java b/pkix/src/main/java/org/spongycastle/operator/InputExpanderProvider.java new file mode 100644 index 00000000..d38b813a --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/InputExpanderProvider.java @@ -0,0 +1,8 @@ +package org.spongycastle.operator; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public interface InputExpanderProvider +{ + InputExpander get(AlgorithmIdentifier algorithm); +} diff --git a/pkix/src/main/java/org/spongycastle/operator/KeyUnwrapper.java b/pkix/src/main/java/org/spongycastle/operator/KeyUnwrapper.java new file mode 100644 index 00000000..8e216230 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/KeyUnwrapper.java @@ -0,0 +1,11 @@ +package org.spongycastle.operator; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public interface KeyUnwrapper +{ + AlgorithmIdentifier getAlgorithmIdentifier(); + + GenericKey generateUnwrappedKey(AlgorithmIdentifier encryptionKeyAlgorithm, byte[] encryptedKey) + throws OperatorException; +} diff --git a/pkix/src/main/java/org/spongycastle/operator/KeyWrapper.java b/pkix/src/main/java/org/spongycastle/operator/KeyWrapper.java new file mode 100644 index 00000000..4b7986df --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/KeyWrapper.java @@ -0,0 +1,11 @@ +package org.spongycastle.operator; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public interface KeyWrapper +{ + AlgorithmIdentifier getAlgorithmIdentifier(); + + byte[] generateWrappedKey(GenericKey encryptionKey) + throws OperatorException; +} diff --git a/pkix/src/main/java/org/spongycastle/operator/MacCalculator.java b/pkix/src/main/java/org/spongycastle/operator/MacCalculator.java new file mode 100644 index 00000000..df59ed65 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/MacCalculator.java @@ -0,0 +1,34 @@ +package org.spongycastle.operator; + +import java.io.OutputStream; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public interface MacCalculator +{ + AlgorithmIdentifier getAlgorithmIdentifier(); + + /** + * Returns a stream that will accept data for the purpose of calculating + * the MAC for later verification. Use org.spongycastle.util.io.TeeOutputStream if you want to accumulate + * the data on the fly as well. + * + * @return an OutputStream + */ + OutputStream getOutputStream(); + + /** + * Return the calculated MAC based on what has been written to the stream. + * + * @return calculated MAC. + */ + byte[] getMac(); + + + /** + * Return the key used for calculating the MAC. + * + * @return the MAC key. + */ + GenericKey getKey(); +}
\ No newline at end of file diff --git a/pkix/src/main/java/org/spongycastle/operator/MacCalculatorProvider.java b/pkix/src/main/java/org/spongycastle/operator/MacCalculatorProvider.java new file mode 100644 index 00000000..a30773f2 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/MacCalculatorProvider.java @@ -0,0 +1,8 @@ +package org.spongycastle.operator; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public interface MacCalculatorProvider +{ + public MacCalculator get(AlgorithmIdentifier algorithm); +} diff --git a/pkix/src/main/java/org/spongycastle/operator/OperatorCreationException.java b/pkix/src/main/java/org/spongycastle/operator/OperatorCreationException.java new file mode 100644 index 00000000..4e7cadac --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/OperatorCreationException.java @@ -0,0 +1,15 @@ +package org.spongycastle.operator; + +public class OperatorCreationException + extends OperatorException +{ + public OperatorCreationException(String msg, Throwable cause) + { + super(msg, cause); + } + + public OperatorCreationException(String msg) + { + super(msg); + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/OperatorException.java b/pkix/src/main/java/org/spongycastle/operator/OperatorException.java new file mode 100644 index 00000000..32ce9e41 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/OperatorException.java @@ -0,0 +1,24 @@ +package org.spongycastle.operator; + +public class OperatorException + extends Exception +{ + private Throwable cause; + + public OperatorException(String msg, Throwable cause) + { + super(msg); + + this.cause = cause; + } + + public OperatorException(String msg) + { + super(msg); + } + + public Throwable getCause() + { + return cause; + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/OperatorStreamException.java b/pkix/src/main/java/org/spongycastle/operator/OperatorStreamException.java new file mode 100644 index 00000000..960d292f --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/OperatorStreamException.java @@ -0,0 +1,21 @@ +package org.spongycastle.operator; + +import java.io.IOException; + +public class OperatorStreamException + extends IOException +{ + private Throwable cause; + + public OperatorStreamException(String msg, Throwable cause) + { + super(msg); + + this.cause = cause; + } + + public Throwable getCause() + { + return cause; + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/OutputCompressor.java b/pkix/src/main/java/org/spongycastle/operator/OutputCompressor.java new file mode 100644 index 00000000..0e10df9a --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/OutputCompressor.java @@ -0,0 +1,29 @@ +package org.spongycastle.operator; + +import java.io.OutputStream; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +/** + * General interface for an operator that is able to produce + * an OutputStream that will output compressed data. + */ +public interface OutputCompressor +{ + /** + * Return the algorithm identifier describing the compression + * algorithm and parameters this compressor uses. + * + * @return algorithm oid and parameters. + */ + AlgorithmIdentifier getAlgorithmIdentifier(); + + /** + * Wrap the passed in output stream comOut, returning an output stream + * that compresses anything passed in before sending on to comOut. + * + * @param comOut output stream for compressed output. + * @return a compressing OutputStream + */ + OutputStream getOutputStream(OutputStream comOut); +} diff --git a/pkix/src/main/java/org/spongycastle/operator/OutputEncryptor.java b/pkix/src/main/java/org/spongycastle/operator/OutputEncryptor.java new file mode 100644 index 00000000..595e3b75 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/OutputEncryptor.java @@ -0,0 +1,36 @@ +package org.spongycastle.operator; + +import java.io.OutputStream; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +/** + * General interface for an operator that is able to produce + * an OutputStream that will output encrypted data. + */ +public interface OutputEncryptor +{ + /** + * Return the algorithm identifier describing the encryption + * algorithm and parameters this encryptor uses. + * + * @return algorithm oid and parameters. + */ + AlgorithmIdentifier getAlgorithmIdentifier(); + + /** + * Wrap the passed in output stream encOut, returning an output stream + * that encrypts anything passed in before sending on to encOut. + * + * @param encOut output stream for encrypted output. + * @return an encrypting OutputStream + */ + OutputStream getOutputStream(OutputStream encOut); + + /** + * Return the key used for encrypting the output. + * + * @return the encryption key. + */ + GenericKey getKey(); +} diff --git a/pkix/src/main/java/org/spongycastle/operator/RawContentVerifier.java b/pkix/src/main/java/org/spongycastle/operator/RawContentVerifier.java new file mode 100644 index 00000000..56bfb47f --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/RawContentVerifier.java @@ -0,0 +1,17 @@ +package org.spongycastle.operator; + +/** + * Interface for ContentVerifiers that also support raw signatures that can be + * verified using the digest of the calculated data. + */ +public interface RawContentVerifier +{ + /** + * Verify that the expected signature value was derived from the passed in digest. + * + * @param digest digest calculated from the content. + * @param expected expected value of the signature + * @return true if the expected signature is derived from the digest, false otherwise. + */ + boolean verify(byte[] digest, byte[] expected); +} diff --git a/pkix/src/main/java/org/spongycastle/operator/RuntimeOperatorException.java b/pkix/src/main/java/org/spongycastle/operator/RuntimeOperatorException.java new file mode 100644 index 00000000..56cab04a --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/RuntimeOperatorException.java @@ -0,0 +1,24 @@ +package org.spongycastle.operator; + +public class RuntimeOperatorException + extends RuntimeException +{ + private Throwable cause; + + public RuntimeOperatorException(String msg) + { + super(msg); + } + + public RuntimeOperatorException(String msg, Throwable cause) + { + super(msg); + + this.cause = cause; + } + + public Throwable getCause() + { + return cause; + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/SecretKeySizeProvider.java b/pkix/src/main/java/org/spongycastle/operator/SecretKeySizeProvider.java new file mode 100644 index 00000000..cb2d6561 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/SecretKeySizeProvider.java @@ -0,0 +1,17 @@ +package org.spongycastle.operator; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public interface SecretKeySizeProvider +{ + int getKeySize(AlgorithmIdentifier algorithmIdentifier); + + /** + * Return the key size implied by the OID, if one exists. + * + * @param algorithm the OID of the algorithm of interest. + * @return -1 if there is no fixed key size associated with the OID, or more information is required. + */ + int getKeySize(ASN1ObjectIdentifier algorithm); +} diff --git a/pkix/src/main/java/org/spongycastle/operator/SignatureAlgorithmIdentifierFinder.java b/pkix/src/main/java/org/spongycastle/operator/SignatureAlgorithmIdentifierFinder.java new file mode 100644 index 00000000..5c997bda --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/SignatureAlgorithmIdentifierFinder.java @@ -0,0 +1,15 @@ +package org.spongycastle.operator; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public interface SignatureAlgorithmIdentifierFinder +{ + /** + * Find the signature algorithm identifier that matches with + * the passed in signature algorithm name. + * + * @param sigAlgName the name of the signature algorithm of interest. + * @return an algorithm identifier for the corresponding signature. + */ + AlgorithmIdentifier find(String sigAlgName); +}
\ No newline at end of file diff --git a/pkix/src/main/java/org/spongycastle/operator/SymmetricKeyUnwrapper.java b/pkix/src/main/java/org/spongycastle/operator/SymmetricKeyUnwrapper.java new file mode 100644 index 00000000..705a7671 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/SymmetricKeyUnwrapper.java @@ -0,0 +1,19 @@ +package org.spongycastle.operator; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public abstract class SymmetricKeyUnwrapper + implements KeyUnwrapper +{ + private AlgorithmIdentifier algorithmId; + + protected SymmetricKeyUnwrapper(AlgorithmIdentifier algorithmId) + { + this.algorithmId = algorithmId; + } + + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return algorithmId; + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/SymmetricKeyWrapper.java b/pkix/src/main/java/org/spongycastle/operator/SymmetricKeyWrapper.java new file mode 100644 index 00000000..56ac7ef1 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/SymmetricKeyWrapper.java @@ -0,0 +1,19 @@ +package org.spongycastle.operator; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +public abstract class SymmetricKeyWrapper + implements KeyWrapper +{ + private AlgorithmIdentifier algorithmId; + + protected SymmetricKeyWrapper(AlgorithmIdentifier algorithmId) + { + this.algorithmId = algorithmId; + } + + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return algorithmId; + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/AESUtil.java b/pkix/src/main/java/org/spongycastle/operator/bc/AESUtil.java new file mode 100644 index 00000000..7abd31be --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/AESUtil.java @@ -0,0 +1,34 @@ +package org.spongycastle.operator.bc; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.nist.NISTObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.params.KeyParameter; + +class AESUtil +{ + static AlgorithmIdentifier determineKeyEncAlg(KeyParameter key) + { + int length = key.getKey().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 + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/BcAESSymmetricKeyUnwrapper.java b/pkix/src/main/java/org/spongycastle/operator/bc/BcAESSymmetricKeyUnwrapper.java new file mode 100644 index 00000000..f9b8c09b --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/BcAESSymmetricKeyUnwrapper.java @@ -0,0 +1,13 @@ +package org.spongycastle.operator.bc; + +import org.spongycastle.crypto.engines.AESWrapEngine; +import org.spongycastle.crypto.params.KeyParameter; + +public class BcAESSymmetricKeyUnwrapper + extends BcSymmetricKeyUnwrapper +{ + public BcAESSymmetricKeyUnwrapper(KeyParameter wrappingKey) + { + super(AESUtil.determineKeyEncAlg(wrappingKey), new AESWrapEngine(), wrappingKey); + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/BcAESSymmetricKeyWrapper.java b/pkix/src/main/java/org/spongycastle/operator/bc/BcAESSymmetricKeyWrapper.java new file mode 100644 index 00000000..62dc062f --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/BcAESSymmetricKeyWrapper.java @@ -0,0 +1,13 @@ +package org.spongycastle.operator.bc; + +import org.spongycastle.crypto.engines.AESWrapEngine; +import org.spongycastle.crypto.params.KeyParameter; + +public class BcAESSymmetricKeyWrapper + extends BcSymmetricKeyWrapper +{ + public BcAESSymmetricKeyWrapper(KeyParameter wrappingKey) + { + super(AESUtil.determineKeyEncAlg(wrappingKey), new AESWrapEngine(), wrappingKey); + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/BcAsymmetricKeyUnwrapper.java b/pkix/src/main/java/org/spongycastle/operator/bc/BcAsymmetricKeyUnwrapper.java new file mode 100644 index 00000000..8fed9deb --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/BcAsymmetricKeyUnwrapper.java @@ -0,0 +1,51 @@ +package org.spongycastle.operator.bc; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.operator.AsymmetricKeyUnwrapper; +import org.spongycastle.operator.GenericKey; +import org.spongycastle.operator.OperatorException; + +public abstract class BcAsymmetricKeyUnwrapper + extends AsymmetricKeyUnwrapper +{ + private AsymmetricKeyParameter privateKey; + + public BcAsymmetricKeyUnwrapper(AlgorithmIdentifier encAlgId, AsymmetricKeyParameter privateKey) + { + super(encAlgId); + + this.privateKey = privateKey; + } + + public GenericKey generateUnwrappedKey(AlgorithmIdentifier encryptedKeyAlgorithm, byte[] encryptedKey) + throws OperatorException + { + AsymmetricBlockCipher keyCipher = createAsymmetricUnwrapper(this.getAlgorithmIdentifier().getAlgorithm()); + + keyCipher.init(false, privateKey); + try + { + byte[] key = keyCipher.processBlock(encryptedKey, 0, encryptedKey.length); + + if (encryptedKeyAlgorithm.getAlgorithm().equals(PKCSObjectIdentifiers.des_EDE3_CBC)) + { + return new GenericKey(encryptedKeyAlgorithm, key); + } + else + { + return new GenericKey(encryptedKeyAlgorithm, key); + } + } + catch (InvalidCipherTextException e) + { + throw new OperatorException("unable to recover secret key: " + e.getMessage(), e); + } + } + + protected abstract AsymmetricBlockCipher createAsymmetricUnwrapper(ASN1ObjectIdentifier algorithm); +} diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/BcAsymmetricKeyWrapper.java b/pkix/src/main/java/org/spongycastle/operator/bc/BcAsymmetricKeyWrapper.java new file mode 100644 index 00000000..8b5bb3e8 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/BcAsymmetricKeyWrapper.java @@ -0,0 +1,60 @@ +package org.spongycastle.operator.bc; + +import java.security.SecureRandom; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.operator.AsymmetricKeyWrapper; +import org.spongycastle.operator.GenericKey; +import org.spongycastle.operator.OperatorException; + +public abstract class BcAsymmetricKeyWrapper + extends AsymmetricKeyWrapper +{ + private AsymmetricKeyParameter publicKey; + private SecureRandom random; + + public BcAsymmetricKeyWrapper(AlgorithmIdentifier encAlgId, AsymmetricKeyParameter publicKey) + { + super(encAlgId); + + this.publicKey = publicKey; + } + + public BcAsymmetricKeyWrapper setSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + public byte[] generateWrappedKey(GenericKey encryptionKey) + throws OperatorException + { + AsymmetricBlockCipher keyEncryptionCipher = createAsymmetricWrapper(getAlgorithmIdentifier().getAlgorithm()); + + CipherParameters params = publicKey; + if (random != null) + { + params = new ParametersWithRandom(params, random); + } + + try + { + byte[] keyEnc = OperatorUtils.getKeyBytes(encryptionKey); + keyEncryptionCipher.init(true, publicKey); + return keyEncryptionCipher.processBlock(keyEnc, 0, keyEnc.length); + } + catch (InvalidCipherTextException e) + { + throw new OperatorException("unable to encrypt contents key", e); + } + } + + protected abstract AsymmetricBlockCipher createAsymmetricWrapper(ASN1ObjectIdentifier algorithm); +} diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/BcContentSignerBuilder.java b/pkix/src/main/java/org/spongycastle/operator/bc/BcContentSignerBuilder.java new file mode 100644 index 00000000..7160adff --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/BcContentSignerBuilder.java @@ -0,0 +1,82 @@ +package org.spongycastle.operator.bc; + +import java.io.OutputStream; +import java.security.SecureRandom; +import java.util.Map; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.Signer; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.operator.ContentSigner; +import org.spongycastle.operator.OperatorCreationException; +import org.spongycastle.operator.RuntimeOperatorException; + +public abstract class BcContentSignerBuilder +{ + private SecureRandom random; + private AlgorithmIdentifier sigAlgId; + private AlgorithmIdentifier digAlgId; + + protected BcDigestProvider digestProvider; + + public BcContentSignerBuilder(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier digAlgId) + { + this.sigAlgId = sigAlgId; + this.digAlgId = digAlgId; + this.digestProvider = BcDefaultDigestProvider.INSTANCE; + } + + public BcContentSignerBuilder setSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + public ContentSigner build(AsymmetricKeyParameter privateKey) + throws OperatorCreationException + { + final Signer sig = createSigner(sigAlgId, digAlgId); + + if (random != null) + { + sig.init(true, new ParametersWithRandom(privateKey, random)); + } + else + { + sig.init(true, privateKey); + } + + return new ContentSigner() + { + private BcSignerOutputStream stream = new BcSignerOutputStream(sig); + + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return sigAlgId; + } + + public OutputStream getOutputStream() + { + return stream; + } + + public byte[] getSignature() + { + try + { + return stream.getSignature(); + } + catch (CryptoException e) + { + throw new RuntimeOperatorException("exception obtaining signature: " + e.getMessage(), e); + } + } + }; + } + + protected abstract Signer createSigner(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier algorithmIdentifier) + throws OperatorCreationException; +} diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/BcContentVerifierProviderBuilder.java b/pkix/src/main/java/org/spongycastle/operator/bc/BcContentVerifierProviderBuilder.java new file mode 100644 index 00000000..3b975e4f --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/BcContentVerifierProviderBuilder.java @@ -0,0 +1,144 @@ +package org.spongycastle.operator.bc; + +import java.io.IOException; +import java.io.OutputStream; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.cert.X509CertificateHolder; +import org.spongycastle.crypto.Signer; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.operator.ContentVerifier; +import org.spongycastle.operator.ContentVerifierProvider; +import org.spongycastle.operator.OperatorCreationException; + +public abstract class BcContentVerifierProviderBuilder +{ + protected BcDigestProvider digestProvider; + + public BcContentVerifierProviderBuilder() + { + this.digestProvider = BcDefaultDigestProvider.INSTANCE; + } + + public ContentVerifierProvider build(final X509CertificateHolder certHolder) + throws OperatorCreationException + { + return new ContentVerifierProvider() + { + public boolean hasAssociatedCertificate() + { + return true; + } + + public X509CertificateHolder getAssociatedCertificate() + { + return certHolder; + } + + public ContentVerifier get(AlgorithmIdentifier algorithm) + throws OperatorCreationException + { + try + { + AsymmetricKeyParameter publicKey = extractKeyParameters(certHolder.getSubjectPublicKeyInfo()); + BcSignerOutputStream stream = createSignatureStream(algorithm, publicKey); + + return new SigVerifier(algorithm, stream); + } + catch (IOException e) + { + throw new OperatorCreationException("exception on setup: " + e, e); + } + } + }; + } + + public ContentVerifierProvider build(final AsymmetricKeyParameter publicKey) + throws OperatorCreationException + { + return new ContentVerifierProvider() + { + public boolean hasAssociatedCertificate() + { + return false; + } + + public X509CertificateHolder getAssociatedCertificate() + { + return null; + } + + public ContentVerifier get(AlgorithmIdentifier algorithm) + throws OperatorCreationException + { + BcSignerOutputStream stream = createSignatureStream(algorithm, publicKey); + + return new SigVerifier(algorithm, stream); + } + }; + } + + private BcSignerOutputStream createSignatureStream(AlgorithmIdentifier algorithm, AsymmetricKeyParameter publicKey) + throws OperatorCreationException + { + Signer sig = createSigner(algorithm); + + sig.init(false, publicKey); + + return new BcSignerOutputStream(sig); + } + + /** + * Extract an AsymmetricKeyParameter from the passed in SubjectPublicKeyInfo structure. + * + * @param publicKeyInfo a publicKeyInfo structure describing the public key required. + * @return an AsymmetricKeyParameter object containing the appropriate public key. + * @throws IOException if the publicKeyInfo data cannot be parsed, + */ + protected abstract AsymmetricKeyParameter extractKeyParameters(SubjectPublicKeyInfo publicKeyInfo) + throws IOException; + + /** + * Create the correct signer for the algorithm identifier sigAlgId. + * + * @param sigAlgId the algorithm details for the signature we want to verify. + * @return a Signer object. + * @throws OperatorCreationException if the Signer cannot be constructed. + */ + protected abstract Signer createSigner(AlgorithmIdentifier sigAlgId) + throws OperatorCreationException; + + private class SigVerifier + implements ContentVerifier + { + private BcSignerOutputStream stream; + private AlgorithmIdentifier algorithm; + + SigVerifier(AlgorithmIdentifier algorithm, BcSignerOutputStream 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) + { + return stream.verify(expected); + } + } +}
\ No newline at end of file diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/BcDSAContentSignerBuilder.java b/pkix/src/main/java/org/spongycastle/operator/bc/BcDSAContentSignerBuilder.java new file mode 100644 index 00000000..db7b608d --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/BcDSAContentSignerBuilder.java @@ -0,0 +1,25 @@ +package org.spongycastle.operator.bc; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.Signer; +import org.spongycastle.crypto.signers.DSADigestSigner; +import org.spongycastle.crypto.signers.DSASigner; +import org.spongycastle.operator.OperatorCreationException; + +public class BcDSAContentSignerBuilder + extends BcContentSignerBuilder +{ + public BcDSAContentSignerBuilder(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier digAlgId) + { + super(sigAlgId, digAlgId); + } + + protected Signer createSigner(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier digAlgId) + throws OperatorCreationException + { + Digest dig = digestProvider.get(digAlgId); + + return new DSADigestSigner(new DSASigner(), dig); + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/BcDSAContentVerifierProviderBuilder.java b/pkix/src/main/java/org/spongycastle/operator/bc/BcDSAContentVerifierProviderBuilder.java new file mode 100644 index 00000000..aaf25f4d --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/BcDSAContentVerifierProviderBuilder.java @@ -0,0 +1,40 @@ +package org.spongycastle.operator.bc; + +import java.io.IOException; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.Signer; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.signers.DSADigestSigner; +import org.spongycastle.crypto.signers.DSASigner; +import org.spongycastle.crypto.util.PublicKeyFactory; +import org.spongycastle.operator.DigestAlgorithmIdentifierFinder; +import org.spongycastle.operator.OperatorCreationException; + +public class BcDSAContentVerifierProviderBuilder + extends BcContentVerifierProviderBuilder +{ + private DigestAlgorithmIdentifierFinder digestAlgorithmFinder; + + public BcDSAContentVerifierProviderBuilder(DigestAlgorithmIdentifierFinder digestAlgorithmFinder) + { + this.digestAlgorithmFinder = digestAlgorithmFinder; + } + + protected Signer createSigner(AlgorithmIdentifier sigAlgId) + throws OperatorCreationException + { + AlgorithmIdentifier digAlg = digestAlgorithmFinder.find(sigAlgId); + Digest dig = digestProvider.get(digAlg); + + return new DSADigestSigner(new DSASigner(), dig); + } + + protected AsymmetricKeyParameter extractKeyParameters(SubjectPublicKeyInfo publicKeyInfo) + throws IOException + { + return PublicKeyFactory.createKey(publicKeyInfo); + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/BcDefaultDigestProvider.java b/pkix/src/main/java/org/spongycastle/operator/bc/BcDefaultDigestProvider.java new file mode 100644 index 00000000..dce50a9e --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/BcDefaultDigestProvider.java @@ -0,0 +1,144 @@ +package org.spongycastle.operator.bc; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.spongycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.spongycastle.asn1.nist.NISTObjectIdentifiers; +import org.spongycastle.asn1.oiw.OIWObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.teletrust.TeleTrusTObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.ExtendedDigest; +import org.spongycastle.crypto.digests.GOST3411Digest; +import org.spongycastle.crypto.digests.MD2Digest; +import org.spongycastle.crypto.digests.MD4Digest; +import org.spongycastle.crypto.digests.MD5Digest; +import org.spongycastle.crypto.digests.RIPEMD128Digest; +import org.spongycastle.crypto.digests.RIPEMD160Digest; +import org.spongycastle.crypto.digests.RIPEMD256Digest; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.digests.SHA224Digest; +import org.spongycastle.crypto.digests.SHA256Digest; +import org.spongycastle.crypto.digests.SHA384Digest; +import org.spongycastle.crypto.digests.SHA512Digest; +import org.spongycastle.operator.OperatorCreationException; + +public class BcDefaultDigestProvider + implements BcDigestProvider +{ + private static final Map lookup = createTable(); + + private static Map createTable() + { + Map table = new HashMap(); + + table.put(OIWObjectIdentifiers.idSHA1, new BcDigestProvider() + { + public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier) + { + return new SHA1Digest(); + } + }); + table.put(NISTObjectIdentifiers.id_sha224, new BcDigestProvider() + { + public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier) + { + return new SHA224Digest(); + } + }); + table.put(NISTObjectIdentifiers.id_sha256, new BcDigestProvider() + { + public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier) + { + return new SHA256Digest(); + } + }); + table.put(NISTObjectIdentifiers.id_sha384, new BcDigestProvider() + { + public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier) + { + return new SHA384Digest(); + } + }); + table.put(NISTObjectIdentifiers.id_sha512, new BcDigestProvider() + { + public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier) + { + return new SHA512Digest(); + } + }); + table.put(PKCSObjectIdentifiers.md5, new BcDigestProvider() + { + public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier) + { + return new MD5Digest(); + } + }); + table.put(PKCSObjectIdentifiers.md4, new BcDigestProvider() + { + public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier) + { + return new MD4Digest(); + } + }); + table.put(PKCSObjectIdentifiers.md2, new BcDigestProvider() + { + public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier) + { + return new MD2Digest(); + } + }); + table.put(CryptoProObjectIdentifiers.gostR3411, new BcDigestProvider() + { + public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier) + { + return new GOST3411Digest(); + } + }); + table.put(TeleTrusTObjectIdentifiers.ripemd128, new BcDigestProvider() + { + public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier) + { + return new RIPEMD128Digest(); + } + }); + table.put(TeleTrusTObjectIdentifiers.ripemd160, new BcDigestProvider() + { + public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier) + { + return new RIPEMD160Digest(); + } + }); + table.put(TeleTrusTObjectIdentifiers.ripemd256, new BcDigestProvider() + { + public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier) + { + return new RIPEMD256Digest(); + } + }); + + return Collections.unmodifiableMap(table); + } + + public static final BcDigestProvider INSTANCE = new BcDefaultDigestProvider(); + + private BcDefaultDigestProvider() + { + + } + + public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier) + throws OperatorCreationException + { + BcDigestProvider extProv = (BcDigestProvider)lookup.get(digestAlgorithmIdentifier.getAlgorithm()); + + if (extProv == null) + { + throw new OperatorCreationException("cannot recognise digest"); + } + + return extProv.get(digestAlgorithmIdentifier); + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/BcDigestCalculatorProvider.java b/pkix/src/main/java/org/spongycastle/operator/bc/BcDigestCalculatorProvider.java new file mode 100644 index 00000000..8e0a12f6 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/BcDigestCalculatorProvider.java @@ -0,0 +1,82 @@ +package org.spongycastle.operator.bc; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.ExtendedDigest; +import org.spongycastle.operator.DigestCalculator; +import org.spongycastle.operator.DigestCalculatorProvider; +import org.spongycastle.operator.OperatorCreationException; + +public class BcDigestCalculatorProvider + implements DigestCalculatorProvider +{ + private BcDigestProvider digestProvider = BcDefaultDigestProvider.INSTANCE; + + public DigestCalculator get(final AlgorithmIdentifier algorithm) + throws OperatorCreationException + { + Digest dig = digestProvider.get(algorithm); + + final DigestOutputStream stream = new DigestOutputStream(dig); + + 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 Digest dig; + + DigestOutputStream(Digest 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, 0, bytes.length); + } + + public void write(int b) + throws IOException + { + dig.update((byte)b); + } + + byte[] getDigest() + { + byte[] d = new byte[dig.getDigestSize()]; + + dig.doFinal(d, 0); + + return d; + } + } +}
\ No newline at end of file diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/BcDigestProvider.java b/pkix/src/main/java/org/spongycastle/operator/bc/BcDigestProvider.java new file mode 100644 index 00000000..6eb930ee --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/BcDigestProvider.java @@ -0,0 +1,11 @@ +package org.spongycastle.operator.bc; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.ExtendedDigest; +import org.spongycastle.operator.OperatorCreationException; + +public interface BcDigestProvider +{ + ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier) + throws OperatorCreationException; +} diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/BcRSAAsymmetricKeyUnwrapper.java b/pkix/src/main/java/org/spongycastle/operator/bc/BcRSAAsymmetricKeyUnwrapper.java new file mode 100644 index 00000000..99762353 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/BcRSAAsymmetricKeyUnwrapper.java @@ -0,0 +1,22 @@ +package org.spongycastle.operator.bc; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.encodings.PKCS1Encoding; +import org.spongycastle.crypto.engines.RSAEngine; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; + +public class BcRSAAsymmetricKeyUnwrapper + extends BcAsymmetricKeyUnwrapper +{ + public BcRSAAsymmetricKeyUnwrapper(AlgorithmIdentifier encAlgId, AsymmetricKeyParameter privateKey) + { + super(encAlgId, privateKey); + } + + protected AsymmetricBlockCipher createAsymmetricUnwrapper(ASN1ObjectIdentifier algorithm) + { + return new PKCS1Encoding(new RSAEngine()); + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/BcRSAAsymmetricKeyWrapper.java b/pkix/src/main/java/org/spongycastle/operator/bc/BcRSAAsymmetricKeyWrapper.java new file mode 100644 index 00000000..c2153e6d --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/BcRSAAsymmetricKeyWrapper.java @@ -0,0 +1,32 @@ +package org.spongycastle.operator.bc; + +import java.io.IOException; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.encodings.PKCS1Encoding; +import org.spongycastle.crypto.engines.RSAEngine; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.util.PublicKeyFactory; + +public class BcRSAAsymmetricKeyWrapper + extends BcAsymmetricKeyWrapper +{ + public BcRSAAsymmetricKeyWrapper(AlgorithmIdentifier encAlgId, AsymmetricKeyParameter publicKey) + { + super(encAlgId, publicKey); + } + + public BcRSAAsymmetricKeyWrapper(AlgorithmIdentifier encAlgId, SubjectPublicKeyInfo publicKeyInfo) + throws IOException + { + super(encAlgId, PublicKeyFactory.createKey(publicKeyInfo)); + } + + protected AsymmetricBlockCipher createAsymmetricWrapper(ASN1ObjectIdentifier algorithm) + { + return new PKCS1Encoding(new RSAEngine()); + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/BcRSAContentSignerBuilder.java b/pkix/src/main/java/org/spongycastle/operator/bc/BcRSAContentSignerBuilder.java new file mode 100644 index 00000000..d62543b0 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/BcRSAContentSignerBuilder.java @@ -0,0 +1,24 @@ +package org.spongycastle.operator.bc; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.Signer; +import org.spongycastle.crypto.signers.RSADigestSigner; +import org.spongycastle.operator.OperatorCreationException; + +public class BcRSAContentSignerBuilder + extends BcContentSignerBuilder +{ + public BcRSAContentSignerBuilder(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier digAlgId) + { + super(sigAlgId, digAlgId); + } + + protected Signer createSigner(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier digAlgId) + throws OperatorCreationException + { + Digest dig = digestProvider.get(digAlgId); + + return new RSADigestSigner(dig); + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/BcRSAContentVerifierProviderBuilder.java b/pkix/src/main/java/org/spongycastle/operator/bc/BcRSAContentVerifierProviderBuilder.java new file mode 100644 index 00000000..e1fd6736 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/BcRSAContentVerifierProviderBuilder.java @@ -0,0 +1,39 @@ +package org.spongycastle.operator.bc; + +import java.io.IOException; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.Signer; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.signers.RSADigestSigner; +import org.spongycastle.crypto.util.PublicKeyFactory; +import org.spongycastle.operator.DigestAlgorithmIdentifierFinder; +import org.spongycastle.operator.OperatorCreationException; + +public class BcRSAContentVerifierProviderBuilder + extends BcContentVerifierProviderBuilder +{ + private DigestAlgorithmIdentifierFinder digestAlgorithmFinder; + + public BcRSAContentVerifierProviderBuilder(DigestAlgorithmIdentifierFinder digestAlgorithmFinder) + { + this.digestAlgorithmFinder = digestAlgorithmFinder; + } + + protected Signer createSigner(AlgorithmIdentifier sigAlgId) + throws OperatorCreationException + { + AlgorithmIdentifier digAlg = digestAlgorithmFinder.find(sigAlgId); + Digest dig = digestProvider.get(digAlg); + + return new RSADigestSigner(dig); + } + + protected AsymmetricKeyParameter extractKeyParameters(SubjectPublicKeyInfo publicKeyInfo) + throws IOException + { + return PublicKeyFactory.createKey(publicKeyInfo); + } +}
\ No newline at end of file diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/BcSignerOutputStream.java b/pkix/src/main/java/org/spongycastle/operator/bc/BcSignerOutputStream.java new file mode 100644 index 00000000..f4cdf62b --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/BcSignerOutputStream.java @@ -0,0 +1,47 @@ +package org.spongycastle.operator.bc; + +import java.io.IOException; +import java.io.OutputStream; + +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.Signer; + +public class BcSignerOutputStream + extends OutputStream +{ + private Signer sig; + + BcSignerOutputStream(Signer sig) + { + this.sig = sig; + } + + public void write(byte[] bytes, int off, int len) + throws IOException + { + sig.update(bytes, off, len); + } + + public void write(byte[] bytes) + throws IOException + { + sig.update(bytes, 0, bytes.length); + } + + public void write(int b) + throws IOException + { + sig.update((byte)b); + } + + byte[] getSignature() + throws CryptoException + { + return sig.generateSignature(); + } + + boolean verify(byte[] expected) + { + return sig.verifySignature(expected); + } +}
\ No newline at end of file diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/BcSymmetricKeyUnwrapper.java b/pkix/src/main/java/org/spongycastle/operator/bc/BcSymmetricKeyUnwrapper.java new file mode 100644 index 00000000..da37cf1a --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/BcSymmetricKeyUnwrapper.java @@ -0,0 +1,49 @@ +package org.spongycastle.operator.bc; + +import java.security.SecureRandom; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.Wrapper; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.operator.GenericKey; +import org.spongycastle.operator.OperatorException; +import org.spongycastle.operator.SymmetricKeyUnwrapper; + +public class BcSymmetricKeyUnwrapper + extends SymmetricKeyUnwrapper +{ + private SecureRandom random; + private Wrapper wrapper; + private KeyParameter wrappingKey; + + public BcSymmetricKeyUnwrapper(AlgorithmIdentifier wrappingAlgorithm, Wrapper wrapper, KeyParameter wrappingKey) + { + super(wrappingAlgorithm); + + this.wrapper = wrapper; + this.wrappingKey = wrappingKey; + } + + public BcSymmetricKeyUnwrapper setSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + public GenericKey generateUnwrappedKey(AlgorithmIdentifier encryptedKeyAlgorithm, byte[] encryptedKey) + throws OperatorException + { + wrapper.init(false, wrappingKey); + + try + { + return new GenericKey(encryptedKeyAlgorithm, wrapper.unwrap(encryptedKey, 0, encryptedKey.length)); + } + catch (InvalidCipherTextException e) + { + throw new OperatorException("unable to unwrap key: " + e.getMessage(), e); + } + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/BcSymmetricKeyWrapper.java b/pkix/src/main/java/org/spongycastle/operator/bc/BcSymmetricKeyWrapper.java new file mode 100644 index 00000000..a35de7d7 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/BcSymmetricKeyWrapper.java @@ -0,0 +1,51 @@ +package org.spongycastle.operator.bc; + +import java.security.SecureRandom; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.Wrapper; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.operator.GenericKey; +import org.spongycastle.operator.OperatorException; +import org.spongycastle.operator.SymmetricKeyWrapper; + +public class BcSymmetricKeyWrapper + extends SymmetricKeyWrapper +{ + private SecureRandom random; + private Wrapper wrapper; + private KeyParameter wrappingKey; + + public BcSymmetricKeyWrapper(AlgorithmIdentifier wrappingAlgorithm, Wrapper wrapper, KeyParameter wrappingKey) + { + super(wrappingAlgorithm); + + this.wrapper = wrapper; + this.wrappingKey = wrappingKey; + } + + public BcSymmetricKeyWrapper setSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + public byte[] generateWrappedKey(GenericKey encryptionKey) + throws OperatorException + { + byte[] contentEncryptionKeySpec = OperatorUtils.getKeyBytes(encryptionKey); + + if (random == null) + { + wrapper.init(true, wrappingKey); + } + else + { + wrapper.init(true, new ParametersWithRandom(wrappingKey, random)); + } + + return wrapper.wrap(contentEncryptionKeySpec, 0, contentEncryptionKeySpec.length); + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/CamelliaUtil.java b/pkix/src/main/java/org/spongycastle/operator/bc/CamelliaUtil.java new file mode 100644 index 00000000..9b7c9836 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/CamelliaUtil.java @@ -0,0 +1,36 @@ +package org.spongycastle.operator.bc; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ntt.NTTObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.params.KeyParameter; + +class CamelliaUtil +{ + static AlgorithmIdentifier determineKeyEncAlg(KeyParameter key) + { + int length = key.getKey().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 + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/OperatorUtils.java b/pkix/src/main/java/org/spongycastle/operator/bc/OperatorUtils.java new file mode 100644 index 00000000..a53c4692 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/OperatorUtils.java @@ -0,0 +1,23 @@ +package org.spongycastle.operator.bc; + +import java.security.Key; + +import org.spongycastle.operator.GenericKey; + +class OperatorUtils +{ + static byte[] getKeyBytes(GenericKey key) + { + if (key.getRepresentation() instanceof Key) + { + return ((Key)key.getRepresentation()).getEncoded(); + } + + if (key.getRepresentation() instanceof byte[]) + { + return (byte[])key.getRepresentation(); + } + + throw new IllegalArgumentException("unknown generic key type"); + } +}
\ No newline at end of file diff --git a/pkix/src/main/java/org/spongycastle/operator/bc/SEEDUtil.java b/pkix/src/main/java/org/spongycastle/operator/bc/SEEDUtil.java new file mode 100644 index 00000000..4d977310 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/bc/SEEDUtil.java @@ -0,0 +1,14 @@ +package org.spongycastle.operator.bc; + +import org.spongycastle.asn1.kisa.KISAObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +class SEEDUtil +{ + static AlgorithmIdentifier determineKeyEncAlg() + { + // parameters absent + return new AlgorithmIdentifier( + KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap); + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/jcajce/JcaAlgorithmParametersConverter.java b/pkix/src/main/java/org/spongycastle/operator/jcajce/JcaAlgorithmParametersConverter.java new file mode 100644 index 00000000..98e2b929 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/jcajce/JcaAlgorithmParametersConverter.java @@ -0,0 +1,73 @@ +package org.spongycastle.operator.jcajce; + + +import java.io.IOException; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.MGF1ParameterSpec; + +import javax.crypto.spec.OAEPParameterSpec; +import javax.crypto.spec.PSource; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.RSAESOAEPparams; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.operator.DefaultDigestAlgorithmIdentifierFinder; + +public class JcaAlgorithmParametersConverter +{ + public JcaAlgorithmParametersConverter() + { + } + + public AlgorithmIdentifier getAlgorithmIdentifier(ASN1ObjectIdentifier algId, AlgorithmParameters parameters) + throws InvalidAlgorithmParameterException + { + try + { + ASN1Encodable params = ASN1Primitive.fromByteArray(parameters.getEncoded()); + + return new AlgorithmIdentifier(algId, params); + } + catch (IOException e) + { + throw new InvalidAlgorithmParameterException("unable to encode parameters object: " + e.getMessage()); + } + } + + public AlgorithmIdentifier getAlgorithmIdentifier(ASN1ObjectIdentifier algorithm, AlgorithmParameterSpec algorithmSpec) + throws InvalidAlgorithmParameterException + { + if (algorithmSpec instanceof OAEPParameterSpec) + { + if (algorithmSpec.equals(OAEPParameterSpec.DEFAULT)) + { + return new AlgorithmIdentifier(algorithm, + new RSAESOAEPparams(RSAESOAEPparams.DEFAULT_HASH_ALGORITHM, RSAESOAEPparams.DEFAULT_MASK_GEN_FUNCTION, RSAESOAEPparams.DEFAULT_P_SOURCE_ALGORITHM)); + } + else + { + OAEPParameterSpec oaepSpec = (OAEPParameterSpec)algorithmSpec; + PSource pSource = oaepSpec.getPSource(); + + if (!oaepSpec.getMGFAlgorithm().equals(OAEPParameterSpec.DEFAULT.getMGFAlgorithm())) + { + throw new InvalidAlgorithmParameterException("only " + OAEPParameterSpec.DEFAULT.getMGFAlgorithm() + " mask generator supported."); + } + + AlgorithmIdentifier hashAlgorithm = new DefaultDigestAlgorithmIdentifierFinder().find(oaepSpec.getDigestAlgorithm()); + AlgorithmIdentifier mgf1HashAlgorithm = new DefaultDigestAlgorithmIdentifierFinder().find((((MGF1ParameterSpec)oaepSpec.getMGFParameters()).getDigestAlgorithm())); + return new AlgorithmIdentifier(algorithm, + new RSAESOAEPparams(hashAlgorithm, new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, mgf1HashAlgorithm), + new AlgorithmIdentifier(PKCSObjectIdentifiers.id_pSpecified, new DEROctetString(((PSource.PSpecified)pSource).getValue())))); + } + } + + throw new InvalidAlgorithmParameterException("unknown parameter spec passed."); + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/jcajce/JcaContentSignerBuilder.java b/pkix/src/main/java/org/spongycastle/operator/jcajce/JcaContentSignerBuilder.java new file mode 100644 index 00000000..f3a008dd --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/jcajce/JcaContentSignerBuilder.java @@ -0,0 +1,160 @@ +package org.spongycastle.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.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.jcajce.util.DefaultJcaJceHelper; +import org.spongycastle.jcajce.util.NamedJcaJceHelper; +import org.spongycastle.jcajce.util.ProviderJcaJceHelper; +import org.spongycastle.operator.ContentSigner; +import org.spongycastle.operator.DefaultSignatureAlgorithmIdentifierFinder; +import org.spongycastle.operator.OperatorCreationException; +import org.spongycastle.operator.OperatorStreamException; +import org.spongycastle.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/pkix/src/main/java/org/spongycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java b/pkix/src/main/java/org/spongycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java new file mode 100644 index 00000000..7b5690e4 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java @@ -0,0 +1,312 @@ +package org.spongycastle.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.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.cert.X509CertificateHolder; +import org.spongycastle.cert.jcajce.JcaX509CertificateHolder; +import org.spongycastle.jcajce.util.DefaultJcaJceHelper; +import org.spongycastle.jcajce.util.NamedJcaJceHelper; +import org.spongycastle.jcajce.util.ProviderJcaJceHelper; +import org.spongycastle.operator.ContentVerifier; +import org.spongycastle.operator.ContentVerifierProvider; +import org.spongycastle.operator.OperatorCreationException; +import org.spongycastle.operator.OperatorStreamException; +import org.spongycastle.operator.RawContentVerifier; +import org.spongycastle.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); + } + } + }; + } + + public ContentVerifierProvider build(SubjectPublicKeyInfo publicKey) + throws OperatorCreationException + { + return this.build(helper.convertPublicKey(publicKey)); + } + + 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/pkix/src/main/java/org/spongycastle/operator/jcajce/JcaDigestCalculatorProviderBuilder.java b/pkix/src/main/java/org/spongycastle/operator/jcajce/JcaDigestCalculatorProviderBuilder.java new file mode 100644 index 00000000..c734b866 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/jcajce/JcaDigestCalculatorProviderBuilder.java @@ -0,0 +1,114 @@ +package org.spongycastle.operator.jcajce; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.GeneralSecurityException; +import java.security.MessageDigest; +import java.security.Provider; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.jcajce.util.DefaultJcaJceHelper; +import org.spongycastle.jcajce.util.NamedJcaJceHelper; +import org.spongycastle.jcajce.util.ProviderJcaJceHelper; +import org.spongycastle.operator.DigestCalculator; +import org.spongycastle.operator.DigestCalculatorProvider; +import org.spongycastle.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/pkix/src/main/java/org/spongycastle/operator/jcajce/JceAsymmetricKeyUnwrapper.java b/pkix/src/main/java/org/spongycastle/operator/jcajce/JceAsymmetricKeyUnwrapper.java new file mode 100644 index 00000000..0b5cbf60 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/jcajce/JceAsymmetricKeyUnwrapper.java @@ -0,0 +1,133 @@ +package org.spongycastle.operator.jcajce; + +import java.security.AlgorithmParameters; +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.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.jcajce.util.DefaultJcaJceHelper; +import org.spongycastle.jcajce.util.NamedJcaJceHelper; +import org.spongycastle.jcajce.util.ProviderJcaJceHelper; +import org.spongycastle.operator.AsymmetricKeyUnwrapper; +import org.spongycastle.operator.GenericKey; +import org.spongycastle.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); + AlgorithmParameters algParams = helper.createAlgorithmParameters(this.getAlgorithmIdentifier()); + + try + { + if (algParams != null) + { + keyCipher.init(Cipher.UNWRAP_MODE, privKey, algParams); + } + else + { + 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/pkix/src/main/java/org/spongycastle/operator/jcajce/JceAsymmetricKeyWrapper.java b/pkix/src/main/java/org/spongycastle/operator/jcajce/JceAsymmetricKeyWrapper.java new file mode 100644 index 00000000..95eb3b9a --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/jcajce/JceAsymmetricKeyWrapper.java @@ -0,0 +1,157 @@ +package org.spongycastle.operator.jcajce; + +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +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.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.jcajce.util.DefaultJcaJceHelper; +import org.spongycastle.jcajce.util.NamedJcaJceHelper; +import org.spongycastle.jcajce.util.ProviderJcaJceHelper; +import org.spongycastle.operator.AsymmetricKeyWrapper; +import org.spongycastle.operator.GenericKey; +import org.spongycastle.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()); + } + + /** + * Create a wrapper, overriding the algorithm type that is stored in the public key. + * + * @param algorithmIdentifier identifier for encryption algorithm to be used. + * @param publicKey the public key to be used. + */ + public JceAsymmetricKeyWrapper(AlgorithmIdentifier algorithmIdentifier, PublicKey publicKey) + { + super(algorithmIdentifier); + + this.publicKey = publicKey; + } + + 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); + AlgorithmParameters algParams = helper.createAlgorithmParameters(this.getAlgorithmIdentifier()); + + byte[] encryptedKeyBytes = null; + + try + { + if (algParams != null) + { + keyEncryptionCipher.init(Cipher.WRAP_MODE, publicKey, algParams, random); + } + else + { + keyEncryptionCipher.init(Cipher.WRAP_MODE, publicKey, random); + } + encryptedKeyBytes = keyEncryptionCipher.wrap(OperatorUtils.getJceKey(encryptionKey)); + } + catch (InvalidKeyException e) + { + } + 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 (InvalidKeyException e) + { + throw new OperatorException("unable to encrypt contents key", e); + } + catch (GeneralSecurityException e) + { + throw new OperatorException("unable to encrypt contents key", e); + } + } + + return encryptedKeyBytes; + } +} diff --git a/pkix/src/main/java/org/spongycastle/operator/jcajce/JceGenericKey.java b/pkix/src/main/java/org/spongycastle/operator/jcajce/JceGenericKey.java new file mode 100644 index 00000000..a11a535e --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/jcajce/JceGenericKey.java @@ -0,0 +1,33 @@ +package org.spongycastle.operator.jcajce; + +import java.security.Key; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.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/pkix/src/main/java/org/spongycastle/operator/jcajce/JceSymmetricKeyUnwrapper.java b/pkix/src/main/java/org/spongycastle/operator/jcajce/JceSymmetricKeyUnwrapper.java new file mode 100644 index 00000000..dd9e7e0a --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/jcajce/JceSymmetricKeyUnwrapper.java @@ -0,0 +1,65 @@ +package org.spongycastle.operator.jcajce; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.jcajce.util.DefaultJcaJceHelper; +import org.spongycastle.jcajce.util.NamedJcaJceHelper; +import org.spongycastle.jcajce.util.ProviderJcaJceHelper; +import org.spongycastle.operator.GenericKey; +import org.spongycastle.operator.OperatorException; +import org.spongycastle.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/pkix/src/main/java/org/spongycastle/operator/jcajce/JceSymmetricKeyWrapper.java b/pkix/src/main/java/org/spongycastle/operator/jcajce/JceSymmetricKeyWrapper.java new file mode 100644 index 00000000..3689f6c2 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/jcajce/JceSymmetricKeyWrapper.java @@ -0,0 +1,154 @@ +package org.spongycastle.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.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.kisa.KISAObjectIdentifiers; +import org.spongycastle.asn1.nist.NISTObjectIdentifiers; +import org.spongycastle.asn1.ntt.NTTObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.jcajce.util.DefaultJcaJceHelper; +import org.spongycastle.jcajce.util.NamedJcaJceHelper; +import org.spongycastle.jcajce.util.ProviderJcaJceHelper; +import org.spongycastle.operator.GenericKey; +import org.spongycastle.operator.OperatorException; +import org.spongycastle.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/pkix/src/main/java/org/spongycastle/operator/jcajce/OperatorHelper.java b/pkix/src/main/java/org/spongycastle/operator/jcajce/OperatorHelper.java new file mode 100644 index 00000000..8f2776ae --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/jcajce/OperatorHelper.java @@ -0,0 +1,433 @@ +package org.spongycastle.operator.jcajce; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; +import java.security.Signature; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PSSParameterSpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.HashMap; +import java.util.Map; + +import javax.crypto.Cipher; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.bsi.BSIObjectIdentifiers; +import org.spongycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.spongycastle.asn1.eac.EACObjectIdentifiers; +import org.spongycastle.asn1.kisa.KISAObjectIdentifiers; +import org.spongycastle.asn1.nist.NISTObjectIdentifiers; +import org.spongycastle.asn1.ntt.NTTObjectIdentifiers; +import org.spongycastle.asn1.oiw.OIWObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.RSASSAPSSparams; +import org.spongycastle.asn1.teletrust.TeleTrusTObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.cert.X509CertificateHolder; +import org.spongycastle.jcajce.util.JcaJceHelper; +import org.spongycastle.jcajce.util.JcaJceUtils; +import org.spongycastle.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(BSIObjectIdentifiers.ecdsa_plain_SHA1, "SHA1WITHPLAIN-ECDSA"); + oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA224, "SHA224WITHPLAIN-ECDSA"); + oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA256, "SHA256WITHPLAIN-ECDSA"); + oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA384, "SHA384WITHPLAIN-ECDSA"); + oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA512, "SHA512WITHPLAIN-ECDSA"); + oids.put(BSIObjectIdentifiers.ecdsa_plain_RIPEMD160, "RIPEMD160WITHPLAIN-ECDSA"); + oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, "SHA1WITHCVC-ECDSA"); + oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_224, "SHA224WITHCVC-ECDSA"); + oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_256, "SHA256WITHCVC-ECDSA"); + oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_384, "SHA384WITHCVC-ECDSA"); + oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_512, "SHA512WITHCVC-ECDSA"); + + 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, "RIPEMD128"); + oids.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD160"); + oids.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD256"); + + 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); + } + } + + AlgorithmParameters createAlgorithmParameters(AlgorithmIdentifier cipherAlgId) + throws OperatorCreationException + { + AlgorithmParameters parameters; + + if (cipherAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.rsaEncryption)) + { + return null; + } + + try + { + parameters = helper.createAlgorithmParameters(cipherAlgId.getAlgorithm().getId()); + } + catch (NoSuchAlgorithmException e) + { + return null; // There's a good chance there aren't any! + } + catch (NoSuchProviderException e) + { + throw new OperatorCreationException("cannot create algorithm parameters: " + e.getMessage(), e); + } + + try + { + parameters.init(cipherAlgId.getParameters().toASN1Primitive().getEncoded()); + } + catch (IOException e) + { + throw new OperatorCreationException("cannot initialise algorithm parameters: " + e.getMessage(), e); + } + + return parameters; + } + + MessageDigest createDigest(AlgorithmIdentifier digAlgId) + throws GeneralSecurityException + { + MessageDigest dig; + + try + { + dig = helper.createDigest(JcaJceUtils.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); + + JcaJceUtils.loadParameters(params, algorithm.getParameters()); + + 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 JcaJceUtils.getDigestAlgName(rsaParams.getHashAlgorithm().getAlgorithm()) + "WITHRSAANDMGF1"; + } + } + + if (oids.containsKey(sigAlgId.getAlgorithm())) + { + return (String)oids.get(sigAlgId.getAlgorithm()); + } + + return sigAlgId.getAlgorithm().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); + } + } + + public PublicKey convertPublicKey(SubjectPublicKeyInfo publicKeyInfo) + throws OperatorCreationException + { + try + { + KeyFactory keyFact = helper.createKeyFactory(publicKeyInfo.getAlgorithm().getAlgorithm().getId()); + + return keyFact.generatePublic(new X509EncodedKeySpec(publicKeyInfo.getEncoded())); + } + catch (IOException e) + { + throw new OperatorCreationException("cannot get encoded form of key: " + e.getMessage(), e); + } + catch (NoSuchAlgorithmException e) + { + throw new OperatorCreationException("cannot create key factory: " + e.getMessage(), e); + } + catch (NoSuchProviderException e) + { + throw new OperatorCreationException("cannot find factory provider: " + e.getMessage(), e); + } + catch (InvalidKeySpecException e) + { + throw new OperatorCreationException("cannot create key factory: " + 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/pkix/src/main/java/org/spongycastle/operator/jcajce/OperatorUtils.java b/pkix/src/main/java/org/spongycastle/operator/jcajce/OperatorUtils.java new file mode 100644 index 00000000..81cb4063 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/operator/jcajce/OperatorUtils.java @@ -0,0 +1,25 @@ +package org.spongycastle.operator.jcajce; + +import java.security.Key; + +import javax.crypto.spec.SecretKeySpec; + +import org.spongycastle.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 |