diff options
Diffstat (limited to 'prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric')
93 files changed, 18836 insertions, 0 deletions
diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/DH.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/DH.java new file mode 100644 index 00000000..e14ec801 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/DH.java @@ -0,0 +1,47 @@ +package org.spongycastle.jcajce.provider.asymmetric; + +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.jcajce.provider.asymmetric.dh.KeyFactorySpi; +import org.spongycastle.jcajce.provider.config.ConfigurableProvider; +import org.spongycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; + +public class DH +{ + private static final String PREFIX = "org.spongycastle.jcajce.provider.asymmetric" + ".dh."; + + public static class Mappings + extends AsymmetricAlgorithmProvider + { + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("KeyPairGenerator.DH", PREFIX + "KeyPairGeneratorSpi"); + provider.addAlgorithm("Alg.Alias.KeyPairGenerator.DIFFIEHELLMAN", "DH"); + + provider.addAlgorithm("KeyAgreement.DH", PREFIX + "KeyAgreementSpi"); + provider.addAlgorithm("Alg.Alias.KeyAgreement.DIFFIEHELLMAN", "DH"); + + provider.addAlgorithm("KeyFactory.DH", PREFIX + "KeyFactorySpi"); + provider.addAlgorithm("Alg.Alias.KeyFactory.DIFFIEHELLMAN", "DH"); + + provider.addAlgorithm("AlgorithmParameters.DH", PREFIX + "AlgorithmParametersSpi"); + provider.addAlgorithm("Alg.Alias.AlgorithmParameters.DIFFIEHELLMAN", "DH"); + + provider.addAlgorithm("Alg.Alias.AlgorithmParameterGenerator.DIFFIEHELLMAN", "DH"); + + provider.addAlgorithm("AlgorithmParameterGenerator.DH", PREFIX + "AlgorithmParameterGeneratorSpi"); + + provider.addAlgorithm("Cipher.DHIES", PREFIX + "IESCipher$IES"); + provider.addAlgorithm("Cipher.DHIESwithAES", PREFIX + "IESCipher$IESwithAES"); + provider.addAlgorithm("Cipher.DHIESWITHAES", PREFIX + "IESCipher$IESwithAES"); + provider.addAlgorithm("Cipher.DHIESWITHDESEDE", PREFIX + "IESCipher$IESwithDESede"); + + registerOid(provider, PKCSObjectIdentifiers.dhKeyAgreement, "DH", new KeyFactorySpi()); + registerOid(provider, X9ObjectIdentifiers.dhpublicnumber, "DH", new KeyFactorySpi()); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/DSA.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/DSA.java new file mode 100644 index 00000000..acceacd7 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/DSA.java @@ -0,0 +1,70 @@ +package org.spongycastle.jcajce.provider.asymmetric; + +import org.spongycastle.asn1.nist.NISTObjectIdentifiers; +import org.spongycastle.jcajce.provider.asymmetric.dsa.DSAUtil; +import org.spongycastle.jcajce.provider.asymmetric.dsa.KeyFactorySpi; +import org.spongycastle.jcajce.provider.config.ConfigurableProvider; +import org.spongycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; +import org.spongycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; + +public class DSA +{ + private static final String PREFIX = "org.spongycastle.jcajce.provider.asymmetric" + ".dsa."; + + public static class Mappings + extends AsymmetricAlgorithmProvider + { + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("AlgorithmParameters.DSA", PREFIX + "AlgorithmParametersSpi"); + + provider.addAlgorithm("AlgorithmParameterGenerator.DSA", PREFIX + "AlgorithmParameterGeneratorSpi"); + + provider.addAlgorithm("KeyPairGenerator.DSA", PREFIX + "KeyPairGeneratorSpi"); + provider.addAlgorithm("KeyFactory.DSA", PREFIX + "KeyFactorySpi"); + + provider.addAlgorithm("Signature.DSA", PREFIX + "DSASigner$stdDSA"); + provider.addAlgorithm("Signature.NONEWITHDSA", PREFIX + "DSASigner$noneDSA"); + + provider.addAlgorithm("Alg.Alias.Signature.RAWDSA", "NONEWITHDSA"); + + provider.addAlgorithm("Signature.DETDSA", PREFIX + "DSASigner$detDSA"); + provider.addAlgorithm("Signature.SHA1WITHDETDSA", PREFIX + "DSASigner$detDSA"); + provider.addAlgorithm("Signature.SHA224WITHDETDSA", PREFIX + "DSASigner$detDSA224"); + provider.addAlgorithm("Signature.SHA256WITHDETDSA", PREFIX + "DSASigner$detDSA256"); + provider.addAlgorithm("Signature.SHA384WITHDETDSA", PREFIX + "DSASigner$detDSA384"); + provider.addAlgorithm("Signature.SHA512WITHDETDSA", PREFIX + "DSASigner$detDSA512"); + + addSignatureAlgorithm(provider, "SHA224", "DSA", PREFIX + "DSASigner$dsa224", NISTObjectIdentifiers.dsa_with_sha224); + addSignatureAlgorithm(provider, "SHA256", "DSA", PREFIX + "DSASigner$dsa256", NISTObjectIdentifiers.dsa_with_sha256); + addSignatureAlgorithm(provider, "SHA384", "DSA", PREFIX + "DSASigner$dsa384", NISTObjectIdentifiers.dsa_with_sha384); + addSignatureAlgorithm(provider, "SHA512", "DSA", PREFIX + "DSASigner$dsa512", NISTObjectIdentifiers.dsa_with_sha512); + + provider.addAlgorithm("Alg.Alias.Signature.SHA/DSA", "DSA"); + provider.addAlgorithm("Alg.Alias.Signature.SHA1withDSA", "DSA"); + provider.addAlgorithm("Alg.Alias.Signature.SHA1WITHDSA", "DSA"); + provider.addAlgorithm("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10040.4.1", "DSA"); + provider.addAlgorithm("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10040.4.3", "DSA"); + provider.addAlgorithm("Alg.Alias.Signature.DSAwithSHA1", "DSA"); + provider.addAlgorithm("Alg.Alias.Signature.DSAWITHSHA1", "DSA"); + provider.addAlgorithm("Alg.Alias.Signature.SHA1WithDSA", "DSA"); + provider.addAlgorithm("Alg.Alias.Signature.DSAWithSHA1", "DSA"); + + provider.addAlgorithm("Alg.Alias.Signature.1.2.840.10040.4.3", "DSA"); + + AsymmetricKeyInfoConverter keyFact = new KeyFactorySpi(); + + for (int i = 0; i != DSAUtil.dsaOids.length; i++) + { + provider.addAlgorithm("Alg.Alias.Signature." + DSAUtil.dsaOids[i], "DSA"); + + registerOid(provider, DSAUtil.dsaOids[i], "DSA", keyFact); + registerOidAlgorithmParameters(provider, DSAUtil.dsaOids[i], "DSA"); + } + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/DSTU4145.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/DSTU4145.java new file mode 100644 index 00000000..5e82f2ca --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/DSTU4145.java @@ -0,0 +1,42 @@ +package org.spongycastle.jcajce.provider.asymmetric; + +import org.spongycastle.asn1.ua.UAObjectIdentifiers; +import org.spongycastle.jcajce.provider.asymmetric.dstu.KeyFactorySpi; +import org.spongycastle.jcajce.provider.config.ConfigurableProvider; +import org.spongycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; + +public class DSTU4145 +{ + private static final String PREFIX = "org.spongycastle.jcajce.provider.asymmetric" + ".dstu."; + + public static class Mappings + extends AsymmetricAlgorithmProvider + { + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("KeyFactory.DSTU4145", PREFIX + "KeyFactorySpi"); + provider.addAlgorithm("Alg.Alias.KeyFactory.DSTU-4145-2002", "DSTU4145"); + provider.addAlgorithm("Alg.Alias.KeyFactory.DSTU4145-3410", "DSTU4145"); + + registerOid(provider, UAObjectIdentifiers.dstu4145le, "DSTU4145", new KeyFactorySpi()); + registerOidAlgorithmParameters(provider, UAObjectIdentifiers.dstu4145le, "DSTU4145"); + registerOid(provider, UAObjectIdentifiers.dstu4145be, "DSTU4145", new KeyFactorySpi()); + registerOidAlgorithmParameters(provider, UAObjectIdentifiers.dstu4145be, "DSTU4145"); + + provider.addAlgorithm("KeyPairGenerator.DSTU4145", PREFIX + "KeyPairGeneratorSpi"); + provider.addAlgorithm("Alg.Alias.KeyPairGenerator.DSTU-4145", "DSTU4145"); + provider.addAlgorithm("Alg.Alias.KeyPairGenerator.DSTU-4145-2002", "DSTU4145"); + + provider.addAlgorithm("Signature.DSTU4145", PREFIX + "SignatureSpi"); + provider.addAlgorithm("Alg.Alias.Signature.DSTU-4145", "DSTU4145"); + provider.addAlgorithm("Alg.Alias.Signature.DSTU-4145-2002", "DSTU4145"); + + addSignatureAlgorithm(provider, "GOST3411", "DSTU4145LE", PREFIX + "SignatureSpiLe", UAObjectIdentifiers.dstu4145le); + addSignatureAlgorithm(provider, "GOST3411", "DSTU4145", PREFIX + "SignatureSpi", UAObjectIdentifiers.dstu4145be); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/EC.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/EC.java new file mode 100644 index 00000000..178a301e --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/EC.java @@ -0,0 +1,110 @@ +package org.spongycastle.jcajce.provider.asymmetric; + +import org.spongycastle.asn1.bsi.BSIObjectIdentifiers; +import org.spongycastle.asn1.eac.EACObjectIdentifiers; +import org.spongycastle.asn1.teletrust.TeleTrusTObjectIdentifiers; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.jcajce.provider.asymmetric.ec.KeyFactorySpi; +import org.spongycastle.jcajce.provider.config.ConfigurableProvider; +import org.spongycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; + +public class EC +{ + private static final String PREFIX = "org.spongycastle.jcajce.provider.asymmetric" + ".ec."; + + public static class Mappings + extends AsymmetricAlgorithmProvider + { + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("KeyAgreement.ECDH", PREFIX + "KeyAgreementSpi$DH"); + provider.addAlgorithm("KeyAgreement.ECDHC", PREFIX + "KeyAgreementSpi$DHC"); + provider.addAlgorithm("KeyAgreement.ECMQV", PREFIX + "KeyAgreementSpi$MQV"); + provider.addAlgorithm("KeyAgreement." + X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA1KDF"); + provider.addAlgorithm("KeyAgreement." + X9ObjectIdentifiers.mqvSinglePass_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA1KDF"); + provider.addAlgorithm("KeyAgreement.ECDHWITHSHA1KDF", PREFIX + "KeyAgreementSpi$DHwithSHA1KDF"); + + registerOid(provider, X9ObjectIdentifiers.id_ecPublicKey, "EC", new KeyFactorySpi.EC()); + // TODO Should this be an alias for ECDH? + registerOid(provider, X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme, "EC", new KeyFactorySpi.EC()); + registerOid(provider, X9ObjectIdentifiers.mqvSinglePass_sha1kdf_scheme, "ECMQV", new KeyFactorySpi.ECMQV()); + + registerOidAlgorithmParameters(provider, X9ObjectIdentifiers.id_ecPublicKey, "EC"); + // TODO Should this be an alias for ECDH? + registerOidAlgorithmParameters(provider, X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme, "EC"); + registerOidAlgorithmParameters(provider, X9ObjectIdentifiers.mqvSinglePass_sha1kdf_scheme, "EC"); + + provider.addAlgorithm("KeyFactory.EC", PREFIX + "KeyFactorySpi$EC"); + provider.addAlgorithm("KeyFactory.ECDSA", PREFIX + "KeyFactorySpi$ECDSA"); + provider.addAlgorithm("KeyFactory.ECDH", PREFIX + "KeyFactorySpi$ECDH"); + provider.addAlgorithm("KeyFactory.ECDHC", PREFIX + "KeyFactorySpi$ECDHC"); + provider.addAlgorithm("KeyFactory.ECMQV", PREFIX + "KeyFactorySpi$ECMQV"); + + provider.addAlgorithm("KeyPairGenerator.EC", PREFIX + "KeyPairGeneratorSpi$EC"); + provider.addAlgorithm("KeyPairGenerator.ECDSA", PREFIX + "KeyPairGeneratorSpi$ECDSA"); + provider.addAlgorithm("KeyPairGenerator.ECDH", PREFIX + "KeyPairGeneratorSpi$ECDH"); + provider.addAlgorithm("KeyPairGenerator.ECDHWITHSHA1KDF", PREFIX + "KeyPairGeneratorSpi$ECDH"); + provider.addAlgorithm("KeyPairGenerator.ECDHC", PREFIX + "KeyPairGeneratorSpi$ECDHC"); + provider.addAlgorithm("KeyPairGenerator.ECIES", PREFIX + "KeyPairGeneratorSpi$ECDH"); + provider.addAlgorithm("KeyPairGenerator.ECMQV", PREFIX + "KeyPairGeneratorSpi$ECMQV"); + + provider.addAlgorithm("Cipher.ECIES", PREFIX + "IESCipher$ECIES"); + provider.addAlgorithm("Cipher.ECIESwithAES", PREFIX + "IESCipher$ECIESwithAES"); + provider.addAlgorithm("Cipher.ECIESWITHAES", PREFIX + "IESCipher$ECIESwithAES"); + provider.addAlgorithm("Cipher.ECIESwithDESEDE", PREFIX + "IESCipher$ECIESwithDESede"); + provider.addAlgorithm("Cipher.ECIESWITHDESEDE", PREFIX + "IESCipher$ECIESwithDESede"); + provider.addAlgorithm("Cipher.ECIESwithAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC"); + provider.addAlgorithm("Cipher.ECIESWITHAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC"); + provider.addAlgorithm("Cipher.ECIESwithDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC"); + provider.addAlgorithm("Cipher.ECIESWITHDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC"); + + provider.addAlgorithm("Signature.ECDSA", PREFIX + "SignatureSpi$ecDSA"); + provider.addAlgorithm("Signature.NONEwithECDSA", PREFIX + "SignatureSpi$ecDSAnone"); + + provider.addAlgorithm("Alg.Alias.Signature.SHA1withECDSA", "ECDSA"); + provider.addAlgorithm("Alg.Alias.Signature.ECDSAwithSHA1", "ECDSA"); + provider.addAlgorithm("Alg.Alias.Signature.SHA1WITHECDSA", "ECDSA"); + provider.addAlgorithm("Alg.Alias.Signature.ECDSAWITHSHA1", "ECDSA"); + provider.addAlgorithm("Alg.Alias.Signature.SHA1WithECDSA", "ECDSA"); + provider.addAlgorithm("Alg.Alias.Signature.ECDSAWithSHA1", "ECDSA"); + provider.addAlgorithm("Alg.Alias.Signature.1.2.840.10045.4.1", "ECDSA"); + provider.addAlgorithm("Alg.Alias.Signature." + TeleTrusTObjectIdentifiers.ecSignWithSha1, "ECDSA"); + + provider.addAlgorithm("Signature.DETECDSA", PREFIX + "SignatureSpi$ecDetDSA"); + provider.addAlgorithm("Signature.SHA1WITHDETECDSA", PREFIX + "SignatureSpi$ecDetDSA"); + provider.addAlgorithm("Signature.SHA224WITHDETECDSA", PREFIX + "SignatureSpi$ecDetDSA224"); + provider.addAlgorithm("Signature.SHA256WITHDETECDSA", PREFIX + "SignatureSpi$ecDetDSA256"); + provider.addAlgorithm("Signature.SHA384WITHDETECDSA", PREFIX + "SignatureSpi$ecDetDSA384"); + provider.addAlgorithm("Signature.SHA512WITHDETECDSA", PREFIX + "SignatureSpi$ecDetDSA512"); + + addSignatureAlgorithm(provider, "SHA224", "ECDSA", PREFIX + "SignatureSpi$ecDSA224", X9ObjectIdentifiers.ecdsa_with_SHA224); + addSignatureAlgorithm(provider, "SHA256", "ECDSA", PREFIX + "SignatureSpi$ecDSA256", X9ObjectIdentifiers.ecdsa_with_SHA256); + addSignatureAlgorithm(provider, "SHA384", "ECDSA", PREFIX + "SignatureSpi$ecDSA384", X9ObjectIdentifiers.ecdsa_with_SHA384); + addSignatureAlgorithm(provider, "SHA512", "ECDSA", PREFIX + "SignatureSpi$ecDSA512", X9ObjectIdentifiers.ecdsa_with_SHA512); + addSignatureAlgorithm(provider, "RIPEMD160", "ECDSA", PREFIX + "SignatureSpi$ecDSARipeMD160",TeleTrusTObjectIdentifiers.ecSignWithRipemd160); + + provider.addAlgorithm("Signature.SHA1WITHECNR", PREFIX + "SignatureSpi$ecNR"); + provider.addAlgorithm("Signature.SHA224WITHECNR", PREFIX + "SignatureSpi$ecNR224"); + provider.addAlgorithm("Signature.SHA256WITHECNR", PREFIX + "SignatureSpi$ecNR256"); + provider.addAlgorithm("Signature.SHA384WITHECNR", PREFIX + "SignatureSpi$ecNR384"); + provider.addAlgorithm("Signature.SHA512WITHECNR", PREFIX + "SignatureSpi$ecNR512"); + + addSignatureAlgorithm(provider, "SHA1", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_1); + addSignatureAlgorithm(provider, "SHA224", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA224", EACObjectIdentifiers.id_TA_ECDSA_SHA_224); + addSignatureAlgorithm(provider, "SHA256", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA256", EACObjectIdentifiers.id_TA_ECDSA_SHA_256); + addSignatureAlgorithm(provider, "SHA384", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA384", EACObjectIdentifiers.id_TA_ECDSA_SHA_384); + addSignatureAlgorithm(provider, "SHA512", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA512", EACObjectIdentifiers.id_TA_ECDSA_SHA_512); + + addSignatureAlgorithm(provider, "SHA1", "PLAIN-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA", BSIObjectIdentifiers.ecdsa_plain_SHA1); + addSignatureAlgorithm(provider, "SHA224", "PLAIN-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA224", BSIObjectIdentifiers.ecdsa_plain_SHA224); + addSignatureAlgorithm(provider, "SHA256", "PLAIN-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA256", BSIObjectIdentifiers.ecdsa_plain_SHA256); + addSignatureAlgorithm(provider, "SHA384", "PLAIN-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA384", BSIObjectIdentifiers.ecdsa_plain_SHA384); + addSignatureAlgorithm(provider, "SHA512", "PLAIN-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA512", BSIObjectIdentifiers.ecdsa_plain_SHA512); + addSignatureAlgorithm(provider, "RIPEMD160", "PLAIN-ECDSA", PREFIX + "SignatureSpi$ecPlainDSARP160", BSIObjectIdentifiers.ecdsa_plain_RIPEMD160); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ECGOST.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ECGOST.java new file mode 100644 index 00000000..cefae1a2 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ECGOST.java @@ -0,0 +1,39 @@ +package org.spongycastle.jcajce.provider.asymmetric; + +import org.spongycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.spongycastle.jcajce.provider.asymmetric.ecgost.KeyFactorySpi; +import org.spongycastle.jcajce.provider.config.ConfigurableProvider; +import org.spongycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; + +public class ECGOST +{ + private static final String PREFIX = "org.spongycastle.jcajce.provider.asymmetric" + ".ecgost."; + + public static class Mappings + extends AsymmetricAlgorithmProvider + { + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("KeyFactory.ECGOST3410", PREFIX + "KeyFactorySpi"); + provider.addAlgorithm("Alg.Alias.KeyFactory.GOST-3410-2001", "ECGOST3410"); + provider.addAlgorithm("Alg.Alias.KeyFactory.ECGOST-3410", "ECGOST3410"); + + registerOid(provider, CryptoProObjectIdentifiers.gostR3410_2001, "ECGOST3410", new KeyFactorySpi()); + registerOidAlgorithmParameters(provider, CryptoProObjectIdentifiers.gostR3410_2001, "ECGOST3410"); + + provider.addAlgorithm("KeyPairGenerator.ECGOST3410", PREFIX + "KeyPairGeneratorSpi"); + provider.addAlgorithm("Alg.Alias.KeyPairGenerator.ECGOST-3410", "ECGOST3410"); + provider.addAlgorithm("Alg.Alias.KeyPairGenerator.GOST-3410-2001", "ECGOST3410"); + + provider.addAlgorithm("Signature.ECGOST3410", PREFIX + "SignatureSpi"); + provider.addAlgorithm("Alg.Alias.Signature.ECGOST-3410", "ECGOST3410"); + provider.addAlgorithm("Alg.Alias.Signature.GOST-3410-2001", "ECGOST3410"); + + addSignatureAlgorithm(provider, "GOST3411", "ECGOST3410", PREFIX + "SignatureSpi", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ElGamal.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ElGamal.java new file mode 100644 index 00000000..e2f44880 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ElGamal.java @@ -0,0 +1,46 @@ +package org.spongycastle.jcajce.provider.asymmetric; + +import org.spongycastle.asn1.oiw.OIWObjectIdentifiers; +import org.spongycastle.jcajce.provider.asymmetric.elgamal.KeyFactorySpi; +import org.spongycastle.jcajce.provider.config.ConfigurableProvider; +import org.spongycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; +import org.spongycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; + +public class ElGamal +{ + private static final String PREFIX = "org.spongycastle.jcajce.provider.asymmetric" + ".elgamal."; + + public static class Mappings + extends AsymmetricAlgorithmProvider + { + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("AlgorithmParameterGenerator.ELGAMAL", PREFIX + "AlgorithmParameterGeneratorSpi"); + provider.addAlgorithm("AlgorithmParameterGenerator.ElGamal", PREFIX + "AlgorithmParameterGeneratorSpi"); + provider.addAlgorithm("AlgorithmParameters.ELGAMAL", PREFIX + "AlgorithmParametersSpi"); + provider.addAlgorithm("AlgorithmParameters.ElGamal", PREFIX + "AlgorithmParametersSpi"); + + provider.addAlgorithm("Cipher.ELGAMAL", PREFIX + "CipherSpi$NoPadding"); + provider.addAlgorithm("Cipher.ElGamal", PREFIX + "CipherSpi$NoPadding"); + provider.addAlgorithm("Alg.Alias.Cipher.ELGAMAL/ECB/PKCS1PADDING", "ELGAMAL/PKCS1"); + provider.addAlgorithm("Alg.Alias.Cipher.ELGAMAL/NONE/PKCS1PADDING", "ELGAMAL/PKCS1"); + provider.addAlgorithm("Alg.Alias.Cipher.ELGAMAL/NONE/NOPADDING", "ELGAMAL"); + + provider.addAlgorithm("Cipher.ELGAMAL/PKCS1", PREFIX + "CipherSpi$PKCS1v1_5Padding"); + provider.addAlgorithm("KeyFactory.ELGAMAL", PREFIX + "KeyFactorySpi"); + provider.addAlgorithm("KeyFactory.ElGamal", PREFIX + "KeyFactorySpi"); + + provider.addAlgorithm("KeyPairGenerator.ELGAMAL", PREFIX + "KeyPairGeneratorSpi"); + provider.addAlgorithm("KeyPairGenerator.ElGamal", PREFIX + "KeyPairGeneratorSpi"); + + AsymmetricKeyInfoConverter keyFact = new KeyFactorySpi(); + + registerOid(provider, OIWObjectIdentifiers.elGamalAlgorithm, "ELGAMAL", keyFact); + registerOidAlgorithmParameters(provider, OIWObjectIdentifiers.elGamalAlgorithm, "ELGAMAL"); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/GOST.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/GOST.java new file mode 100644 index 00000000..39e149b7 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/GOST.java @@ -0,0 +1,49 @@ +package org.spongycastle.jcajce.provider.asymmetric; + +import org.spongycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.spongycastle.jcajce.provider.asymmetric.gost.KeyFactorySpi; +import org.spongycastle.jcajce.provider.config.ConfigurableProvider; +import org.spongycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; + +public class GOST +{ + private static final String PREFIX = "org.spongycastle.jcajce.provider.asymmetric" + ".gost."; + + public static class Mappings + extends AsymmetricAlgorithmProvider + { + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("KeyPairGenerator.GOST3410", PREFIX + "KeyPairGeneratorSpi"); + provider.addAlgorithm("Alg.Alias.KeyPairGenerator.GOST-3410", "GOST3410"); + provider.addAlgorithm("Alg.Alias.KeyPairGenerator.GOST-3410-94", "GOST3410"); + + provider.addAlgorithm("KeyFactory.GOST3410", PREFIX + "KeyFactorySpi"); + provider.addAlgorithm("Alg.Alias.KeyFactory.GOST-3410", "GOST3410"); + provider.addAlgorithm("Alg.Alias.KeyFactory.GOST-3410-94", "GOST3410"); + + + provider.addAlgorithm("AlgorithmParameters.GOST3410", PREFIX + "AlgorithmParametersSpi"); + provider.addAlgorithm("AlgorithmParameterGenerator.GOST3410", PREFIX + "AlgorithmParameterGeneratorSpi"); + + registerOid(provider, CryptoProObjectIdentifiers.gostR3410_94, "GOST3410", new KeyFactorySpi()); + registerOidAlgorithmParameters(provider, CryptoProObjectIdentifiers.gostR3410_94, "GOST3410"); + + provider.addAlgorithm("Signature.GOST3410", PREFIX + "SignatureSpi"); + provider.addAlgorithm("Alg.Alias.Signature.GOST-3410", "GOST3410"); + provider.addAlgorithm("Alg.Alias.Signature.GOST-3410-94", "GOST3410"); + provider.addAlgorithm("Alg.Alias.Signature.GOST3411withGOST3410", "GOST3410"); + provider.addAlgorithm("Alg.Alias.Signature.GOST3411WITHGOST3410", "GOST3410"); + provider.addAlgorithm("Alg.Alias.Signature.GOST3411WithGOST3410", "GOST3410"); + provider.addAlgorithm("Alg.Alias.Signature." + CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3410"); + + + provider.addAlgorithm("Alg.Alias.AlgorithmParameterGenerator.GOST-3410", "GOST3410"); + provider.addAlgorithm("Alg.Alias.AlgorithmParameters.GOST-3410", "GOST3410"); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/IES.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/IES.java new file mode 100644 index 00000000..89c43806 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/IES.java @@ -0,0 +1,23 @@ +package org.spongycastle.jcajce.provider.asymmetric; + +import org.spongycastle.jcajce.provider.config.ConfigurableProvider; +import org.spongycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; + +public class IES +{ + private static final String PREFIX = "org.spongycastle.jcajce.provider.asymmetric" + ".ies."; + + public static class Mappings + extends AsymmetricAlgorithmProvider + { + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("AlgorithmParameters.IES", PREFIX + "AlgorithmParametersSpi"); + provider.addAlgorithm("Cipher.IES", PREFIX + "CipherSpi$IES"); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/RSA.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/RSA.java new file mode 100644 index 00000000..5087d1ee --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/RSA.java @@ -0,0 +1,197 @@ +package org.spongycastle.jcajce.provider.asymmetric; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.oiw.OIWObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.teletrust.TeleTrusTObjectIdentifiers; +import org.spongycastle.asn1.x509.X509ObjectIdentifiers; +import org.spongycastle.jcajce.provider.asymmetric.rsa.KeyFactorySpi; +import org.spongycastle.jcajce.provider.config.ConfigurableProvider; +import org.spongycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; +import org.spongycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; + +public class RSA +{ + private static final String PREFIX = "org.spongycastle.jcajce.provider.asymmetric" + ".rsa."; + + public static class Mappings + extends AsymmetricAlgorithmProvider + { + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("AlgorithmParameters.OAEP", PREFIX + "AlgorithmParametersSpi$OAEP"); + provider.addAlgorithm("AlgorithmParameters.PSS", PREFIX + "AlgorithmParametersSpi$PSS"); + + provider.addAlgorithm("Alg.Alias.AlgorithmParameters.RSAPSS", "PSS"); + provider.addAlgorithm("Alg.Alias.AlgorithmParameters.RSASSA-PSS", "PSS"); + + provider.addAlgorithm("Alg.Alias.AlgorithmParameters.SHA224withRSA/PSS", "PSS"); + provider.addAlgorithm("Alg.Alias.AlgorithmParameters.SHA256withRSA/PSS", "PSS"); + provider.addAlgorithm("Alg.Alias.AlgorithmParameters.SHA384withRSA/PSS", "PSS"); + provider.addAlgorithm("Alg.Alias.AlgorithmParameters.SHA512withRSA/PSS", "PSS"); + + provider.addAlgorithm("Alg.Alias.AlgorithmParameters.SHA224WITHRSAANDMGF1", "PSS"); + provider.addAlgorithm("Alg.Alias.AlgorithmParameters.SHA256WITHRSAANDMGF1", "PSS"); + provider.addAlgorithm("Alg.Alias.AlgorithmParameters.SHA384WITHRSAANDMGF1", "PSS"); + provider.addAlgorithm("Alg.Alias.AlgorithmParameters.SHA512WITHRSAANDMGF1", "PSS"); + provider.addAlgorithm("Alg.Alias.AlgorithmParameters.RAWRSAPSS", "PSS"); + provider.addAlgorithm("Alg.Alias.AlgorithmParameters.NONEWITHRSAPSS", "PSS"); + provider.addAlgorithm("Alg.Alias.AlgorithmParameters.NONEWITHRSASSA-PSS", "PSS"); + provider.addAlgorithm("Alg.Alias.AlgorithmParameters.NONEWITHRSAANDMGF1", "PSS"); + + provider.addAlgorithm("Cipher.RSA", PREFIX + "CipherSpi$NoPadding"); + provider.addAlgorithm("Cipher.RSA/RAW", PREFIX + "CipherSpi$NoPadding"); + provider.addAlgorithm("Cipher.RSA/PKCS1", PREFIX + "CipherSpi$PKCS1v1_5Padding"); + provider.addAlgorithm("Cipher.1.2.840.113549.1.1.1", PREFIX + "CipherSpi$PKCS1v1_5Padding"); + provider.addAlgorithm("Cipher.2.5.8.1.1", PREFIX + "CipherSpi$PKCS1v1_5Padding"); + provider.addAlgorithm("Cipher.RSA/1", PREFIX + "CipherSpi$PKCS1v1_5Padding_PrivateOnly"); + provider.addAlgorithm("Cipher.RSA/2", PREFIX + "CipherSpi$PKCS1v1_5Padding_PublicOnly"); + provider.addAlgorithm("Cipher.RSA/OAEP", PREFIX + "CipherSpi$OAEPPadding"); + provider.addAlgorithm("Cipher." + PKCSObjectIdentifiers.id_RSAES_OAEP, PREFIX + "CipherSpi$OAEPPadding"); + provider.addAlgorithm("Cipher.RSA/ISO9796-1", PREFIX + "CipherSpi$ISO9796d1Padding"); + + provider.addAlgorithm("Alg.Alias.Cipher.RSA//RAW", "RSA"); + provider.addAlgorithm("Alg.Alias.Cipher.RSA//NOPADDING", "RSA"); + provider.addAlgorithm("Alg.Alias.Cipher.RSA//PKCS1PADDING", "RSA/PKCS1"); + provider.addAlgorithm("Alg.Alias.Cipher.RSA//OAEPPADDING", "RSA/OAEP"); + provider.addAlgorithm("Alg.Alias.Cipher.RSA//ISO9796-1PADDING", "RSA/ISO9796-1"); + + provider.addAlgorithm("KeyFactory.RSA", PREFIX + "KeyFactorySpi"); + provider.addAlgorithm("KeyPairGenerator.RSA", PREFIX + "KeyPairGeneratorSpi"); + + AsymmetricKeyInfoConverter keyFact = new KeyFactorySpi(); + + registerOid(provider, PKCSObjectIdentifiers.rsaEncryption, "RSA", keyFact); + registerOid(provider, X509ObjectIdentifiers.id_ea_rsa, "RSA", keyFact); + registerOid(provider, PKCSObjectIdentifiers.id_RSAES_OAEP, "RSA", keyFact); + registerOid(provider, PKCSObjectIdentifiers.id_RSASSA_PSS, "RSA", keyFact); + + registerOidAlgorithmParameters(provider, PKCSObjectIdentifiers.rsaEncryption, "RSA"); + registerOidAlgorithmParameters(provider, X509ObjectIdentifiers.id_ea_rsa, "RSA"); + registerOidAlgorithmParameters(provider, PKCSObjectIdentifiers.id_RSAES_OAEP, "OAEP"); + registerOidAlgorithmParameters(provider, PKCSObjectIdentifiers.id_RSASSA_PSS, "PSS"); + + + provider.addAlgorithm("Signature.RSASSA-PSS", PREFIX + "PSSSignatureSpi$PSSwithRSA"); + provider.addAlgorithm("Signature." + PKCSObjectIdentifiers.id_RSASSA_PSS, PREFIX + "PSSSignatureSpi$PSSwithRSA"); + provider.addAlgorithm("Signature.OID." + PKCSObjectIdentifiers.id_RSASSA_PSS, PREFIX + "PSSSignatureSpi$PSSwithRSA"); + + provider.addAlgorithm("Signature.SHA224WITHRSAANDMGF1", PREFIX + "PSSSignatureSpi$SHA224withRSA"); + provider.addAlgorithm("Signature.SHA256WITHRSAANDMGF1", PREFIX + "PSSSignatureSpi$SHA256withRSA"); + provider.addAlgorithm("Signature.SHA384WITHRSAANDMGF1", PREFIX + "PSSSignatureSpi$SHA384withRSA"); + provider.addAlgorithm("Signature.SHA512WITHRSAANDMGF1", PREFIX + "PSSSignatureSpi$SHA512withRSA"); + provider.addAlgorithm("Signature.SHA224withRSA/PSS", PREFIX + "PSSSignatureSpi$SHA224withRSA"); + provider.addAlgorithm("Signature.SHA256withRSA/PSS", PREFIX + "PSSSignatureSpi$SHA256withRSA"); + provider.addAlgorithm("Signature.SHA384withRSA/PSS", PREFIX + "PSSSignatureSpi$SHA384withRSA"); + provider.addAlgorithm("Signature.SHA512withRSA/PSS", PREFIX + "PSSSignatureSpi$SHA512withRSA"); + + provider.addAlgorithm("Signature.RSA", PREFIX + "DigestSignatureSpi$noneRSA"); + provider.addAlgorithm("Signature.RAWRSASSA-PSS", PREFIX + "PSSSignatureSpi$nonePSS"); + + provider.addAlgorithm("Alg.Alias.Signature.RAWRSA", "RSA"); + provider.addAlgorithm("Alg.Alias.Signature.NONEWITHRSA", "RSA"); + provider.addAlgorithm("Alg.Alias.Signature.RAWRSAPSS", "RAWRSASSA-PSS"); + provider.addAlgorithm("Alg.Alias.Signature.NONEWITHRSAPSS", "RAWRSASSA-PSS"); + provider.addAlgorithm("Alg.Alias.Signature.NONEWITHRSASSA-PSS", "RAWRSASSA-PSS"); + provider.addAlgorithm("Alg.Alias.Signature.NONEWITHRSAANDMGF1", "RAWRSASSA-PSS"); + provider.addAlgorithm("Alg.Alias.Signature.RSAPSS", "RSASSA-PSS"); + + + provider.addAlgorithm("Alg.Alias.Signature.SHA224withRSAandMGF1", "SHA224withRSA/PSS"); + provider.addAlgorithm("Alg.Alias.Signature.SHA256withRSAandMGF1", "SHA256withRSA/PSS"); + provider.addAlgorithm("Alg.Alias.Signature.SHA384withRSAandMGF1", "SHA384withRSA/PSS"); + provider.addAlgorithm("Alg.Alias.Signature.SHA512withRSAandMGF1", "SHA512withRSA/PSS"); + + if (provider.hasAlgorithm("MessageDigest", "MD2")) + { + addDigestSignature(provider, "MD2", PREFIX + "DigestSignatureSpi$MD2", PKCSObjectIdentifiers.md2WithRSAEncryption); + } + + if (provider.hasAlgorithm("MessageDigest", "MD4")) + { + addDigestSignature(provider, "MD4", PREFIX + "DigestSignatureSpi$MD4", PKCSObjectIdentifiers.md4WithRSAEncryption); + } + + if (provider.hasAlgorithm("MessageDigest", "MD5")) + { + addDigestSignature(provider, "MD5", PREFIX + "DigestSignatureSpi$MD5", PKCSObjectIdentifiers.md5WithRSAEncryption); + provider.addAlgorithm("Signature.MD5withRSA/ISO9796-2", PREFIX + "ISOSignatureSpi$MD5WithRSAEncryption"); + provider.addAlgorithm("Alg.Alias.Signature.MD5WithRSA/ISO9796-2", "MD5withRSA/ISO9796-2"); + } + + if (provider.hasAlgorithm("MessageDigest", "SHA1")) + { + provider.addAlgorithm("Alg.Alias.AlgorithmParameters.SHA1withRSA/PSS", "PSS"); + provider.addAlgorithm("Alg.Alias.AlgorithmParameters.SHA1WITHRSAANDMGF1", "PSS"); + provider.addAlgorithm("Signature.SHA1withRSA/PSS", PREFIX + "PSSSignatureSpi$SHA1withRSA"); + provider.addAlgorithm("Alg.Alias.Signature.SHA1withRSAandMGF1", "SHA1withRSA/PSS"); + provider.addAlgorithm("Alg.Alias.Signature.SHA1WITHRSAANDMGF1", "SHA1withRSA/PSS"); + + addDigestSignature(provider, "SHA1", PREFIX + "DigestSignatureSpi$SHA1", PKCSObjectIdentifiers.sha1WithRSAEncryption); + + provider.addAlgorithm("Alg.Alias.Signature.SHA1WithRSA/ISO9796-2", "SHA1withRSA/ISO9796-2"); + provider.addAlgorithm("Signature.SHA1withRSA/ISO9796-2", PREFIX + "ISOSignatureSpi$SHA1WithRSAEncryption"); + provider.addAlgorithm("Alg.Alias.Signature." + OIWObjectIdentifiers.sha1WithRSA, "SHA1WITHRSA"); + provider.addAlgorithm("Alg.Alias.Signature.OID." + OIWObjectIdentifiers.sha1WithRSA, "SHA1WITHRSA"); + } + + addDigestSignature(provider, "SHA224", PREFIX + "DigestSignatureSpi$SHA224", PKCSObjectIdentifiers.sha224WithRSAEncryption); + addDigestSignature(provider, "SHA256", PREFIX + "DigestSignatureSpi$SHA256", PKCSObjectIdentifiers.sha256WithRSAEncryption); + addDigestSignature(provider, "SHA384", PREFIX + "DigestSignatureSpi$SHA384", PKCSObjectIdentifiers.sha384WithRSAEncryption); + addDigestSignature(provider, "SHA512", PREFIX + "DigestSignatureSpi$SHA512", PKCSObjectIdentifiers.sha512WithRSAEncryption); + + if (provider.hasAlgorithm("MessageDigest", "RIPEMD128")) + { + addDigestSignature(provider, "RIPEMD128", PREFIX + "DigestSignatureSpi$RIPEMD128", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128); + addDigestSignature(provider, "RMD128", PREFIX + "DigestSignatureSpi$RIPEMD128", null); + } + + if (provider.hasAlgorithm("MessageDigest", "RIPEMD160")) + { + addDigestSignature(provider, "RIPEMD160", PREFIX + "DigestSignatureSpi$RIPEMD160", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160); + addDigestSignature(provider, "RMD160", PREFIX + "DigestSignatureSpi$RIPEMD160", null); + provider.addAlgorithm("Alg.Alias.Signature.RIPEMD160WithRSA/ISO9796-2", "RIPEMD160withRSA/ISO9796-2"); + provider.addAlgorithm("Signature.RIPEMD160withRSA/ISO9796-2", PREFIX + "ISOSignatureSpi$RIPEMD160WithRSAEncryption"); + } + + if (provider.hasAlgorithm("MessageDigest", "RIPEMD256")) + { + addDigestSignature(provider, "RIPEMD256", PREFIX + "DigestSignatureSpi$RIPEMD256", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256); + addDigestSignature(provider, "RMD256", PREFIX + "DigestSignatureSpi$RIPEMD256", null); + } + } + + private void addDigestSignature( + ConfigurableProvider provider, + String digest, + String className, + ASN1ObjectIdentifier oid) + { + String mainName = digest + "WITHRSA"; + String jdk11Variation1 = digest + "withRSA"; + String jdk11Variation2 = digest + "WithRSA"; + String alias = digest + "/" + "RSA"; + String longName = digest + "WITHRSAENCRYPTION"; + String longJdk11Variation1 = digest + "withRSAEncryption"; + String longJdk11Variation2 = digest + "WithRSAEncryption"; + + provider.addAlgorithm("Signature." + mainName, className); + provider.addAlgorithm("Alg.Alias.Signature." + jdk11Variation1, mainName); + provider.addAlgorithm("Alg.Alias.Signature." + jdk11Variation2, mainName); + provider.addAlgorithm("Alg.Alias.Signature." + longName, mainName); + provider.addAlgorithm("Alg.Alias.Signature." + longJdk11Variation1, mainName); + provider.addAlgorithm("Alg.Alias.Signature." + longJdk11Variation2, mainName); + provider.addAlgorithm("Alg.Alias.Signature." + alias, mainName); + + if (oid != null) + { + provider.addAlgorithm("Alg.Alias.Signature." + oid, mainName); + provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, mainName); + } + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/X509.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/X509.java new file mode 100644 index 00000000..6b5a9779 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/X509.java @@ -0,0 +1,31 @@ +package org.spongycastle.jcajce.provider.asymmetric; + +import org.spongycastle.jcajce.provider.config.ConfigurableProvider; +import org.spongycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; + +/** + * For some reason the class path project thinks that such a KeyFactory will exist. + */ +public class X509 +{ + public static class Mappings + extends AsymmetricAlgorithmProvider + { + public Mappings() + { + + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("KeyFactory.X.509", "org.spongycastle.jcajce.provider.asymmetric.x509.KeyFactory"); + provider.addAlgorithm("Alg.Alias.KeyFactory.X509", "X.509"); + + // + // certificate factories. + // + provider.addAlgorithm("CertificateFactory.X.509", "org.spongycastle.jcajce.provider.asymmetric.x509.CertificateFactory"); + provider.addAlgorithm("Alg.Alias.CertificateFactory.X509", "X.509"); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/AlgorithmParameterGeneratorSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/AlgorithmParameterGeneratorSpi.java new file mode 100644 index 00000000..0b394287 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/AlgorithmParameterGeneratorSpi.java @@ -0,0 +1,77 @@ +package org.spongycastle.jcajce.provider.asymmetric.dh; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.spec.DHGenParameterSpec; +import javax.crypto.spec.DHParameterSpec; + +import org.spongycastle.crypto.generators.DHParametersGenerator; +import org.spongycastle.crypto.params.DHParameters; +import org.spongycastle.jce.provider.BouncyCastleProvider; + +public class AlgorithmParameterGeneratorSpi + extends java.security.AlgorithmParameterGeneratorSpi +{ + protected SecureRandom random; + protected int strength = 1024; + + private int l = 0; + + protected void engineInit( + int strength, + SecureRandom random) + { + this.strength = strength; + this.random = random; + } + + protected void engineInit( + AlgorithmParameterSpec genParamSpec, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + if (!(genParamSpec instanceof DHGenParameterSpec)) + { + throw new InvalidAlgorithmParameterException("DH parameter generator requires a DHGenParameterSpec for initialisation"); + } + DHGenParameterSpec spec = (DHGenParameterSpec)genParamSpec; + + this.strength = spec.getPrimeSize(); + this.l = spec.getExponentSize(); + this.random = random; + } + + protected AlgorithmParameters engineGenerateParameters() + { + DHParametersGenerator pGen = new DHParametersGenerator(); + + if (random != null) + { + pGen.init(strength, 20, random); + } + else + { + pGen.init(strength, 20, new SecureRandom()); + } + + DHParameters p = pGen.generateParameters(); + + AlgorithmParameters params; + + try + { + params = AlgorithmParameters.getInstance("DH", BouncyCastleProvider.PROVIDER_NAME); + params.init(new DHParameterSpec(p.getP(), p.getG(), l)); + } + catch (Exception e) + { + throw new RuntimeException(e.getMessage()); + } + + return params; + } + +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/AlgorithmParametersSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/AlgorithmParametersSpi.java new file mode 100644 index 00000000..ae89fba9 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/AlgorithmParametersSpi.java @@ -0,0 +1,142 @@ +package org.spongycastle.jcajce.provider.asymmetric.dh; + +import java.io.IOException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; + +import javax.crypto.spec.DHParameterSpec; + +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.pkcs.DHParameter; + +public class AlgorithmParametersSpi + extends java.security.AlgorithmParametersSpi +{ + DHParameterSpec currentSpec; + + protected boolean isASN1FormatString(String format) + { + return format == null || format.equals("ASN.1"); + } + + protected AlgorithmParameterSpec engineGetParameterSpec( + Class paramSpec) + throws InvalidParameterSpecException + { + if (paramSpec == null) + { + throw new NullPointerException("argument to getParameterSpec must not be null"); + } + + return localEngineGetParameterSpec(paramSpec); + } + + + + + /** + * Return the PKCS#3 ASN.1 structure DHParameter. + * <p> + * <pre> + * DHParameter ::= SEQUENCE { + * prime INTEGER, -- p + * base INTEGER, -- g + * privateValueLength INTEGER OPTIONAL} + * </pre> + */ + protected byte[] engineGetEncoded() + { + DHParameter dhP = new DHParameter(currentSpec.getP(), currentSpec.getG(), currentSpec.getL()); + + try + { + return dhP.getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + throw new RuntimeException("Error encoding DHParameters"); + } + } + + protected byte[] engineGetEncoded( + String format) + { + if (isASN1FormatString(format)) + { + return engineGetEncoded(); + } + + return null; + } + + protected AlgorithmParameterSpec localEngineGetParameterSpec( + Class paramSpec) + throws InvalidParameterSpecException + { + if (paramSpec == DHParameterSpec.class) + { + return currentSpec; + } + + throw new InvalidParameterSpecException("unknown parameter spec passed to DH parameters object."); + } + + protected void engineInit( + AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException + { + if (!(paramSpec instanceof DHParameterSpec)) + { + throw new InvalidParameterSpecException("DHParameterSpec required to initialise a Diffie-Hellman algorithm parameters object"); + } + + this.currentSpec = (DHParameterSpec)paramSpec; + } + + protected void engineInit( + byte[] params) + throws IOException + { + try + { + DHParameter dhP = DHParameter.getInstance(params); + + if (dhP.getL() != null) + { + currentSpec = new DHParameterSpec(dhP.getP(), dhP.getG(), dhP.getL().intValue()); + } + else + { + currentSpec = new DHParameterSpec(dhP.getP(), dhP.getG()); + } + } + catch (ClassCastException e) + { + throw new IOException("Not a valid DH Parameter encoding."); + } + catch (ArrayIndexOutOfBoundsException e) + { + throw new IOException("Not a valid DH Parameter encoding."); + } + } + + protected void engineInit( + byte[] params, + String format) + throws IOException + { + if (isASN1FormatString(format)) + { + engineInit(params); + } + else + { + throw new IOException("Unknown parameter format " + format); + } + } + + protected String engineToString() + { + return "Diffie-Hellman Parameters"; + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/BCDHPrivateKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/BCDHPrivateKey.java new file mode 100644 index 00000000..9949a58d --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/BCDHPrivateKey.java @@ -0,0 +1,213 @@ +package org.spongycastle.jcajce.provider.asymmetric.dh; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; +import java.util.Enumeration; + +import javax.crypto.interfaces.DHPrivateKey; +import javax.crypto.spec.DHParameterSpec; +import javax.crypto.spec.DHPrivateKeySpec; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.pkcs.DHParameter; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x9.DHDomainParameters; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.crypto.params.DHPrivateKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl; +import org.spongycastle.jce.interfaces.PKCS12BagAttributeCarrier; + + +public class BCDHPrivateKey + implements DHPrivateKey, PKCS12BagAttributeCarrier +{ + static final long serialVersionUID = 311058815616901812L; + + private BigInteger x; + + private transient DHParameterSpec dhSpec; + private transient PrivateKeyInfo info; + + private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl(); + + protected BCDHPrivateKey() + { + } + + BCDHPrivateKey( + DHPrivateKey key) + { + this.x = key.getX(); + this.dhSpec = key.getParams(); + } + + BCDHPrivateKey( + DHPrivateKeySpec spec) + { + this.x = spec.getX(); + this.dhSpec = new DHParameterSpec(spec.getP(), spec.getG()); + } + + public BCDHPrivateKey( + PrivateKeyInfo info) + throws IOException + { + ASN1Sequence seq = ASN1Sequence.getInstance(info.getPrivateKeyAlgorithm().getParameters()); + ASN1Integer derX = (ASN1Integer)info.parsePrivateKey(); + ASN1ObjectIdentifier id = info.getPrivateKeyAlgorithm().getAlgorithm(); + + this.info = info; + this.x = derX.getValue(); + + if (id.equals(PKCSObjectIdentifiers.dhKeyAgreement)) + { + DHParameter params = DHParameter.getInstance(seq); + + if (params.getL() != null) + { + this.dhSpec = new DHParameterSpec(params.getP(), params.getG(), params.getL().intValue()); + } + else + { + this.dhSpec = new DHParameterSpec(params.getP(), params.getG()); + } + } + else if (id.equals(X9ObjectIdentifiers.dhpublicnumber)) + { + DHDomainParameters params = DHDomainParameters.getInstance(seq); + + this.dhSpec = new DHParameterSpec(params.getP().getValue(), params.getG().getValue()); + } + else + { + throw new IllegalArgumentException("unknown algorithm type: " + id); + } + } + + BCDHPrivateKey( + DHPrivateKeyParameters params) + { + this.x = params.getX(); + this.dhSpec = new DHParameterSpec(params.getParameters().getP(), params.getParameters().getG(), params.getParameters().getL()); + } + + public String getAlgorithm() + { + return "DH"; + } + + /** + * return the encoding format we produce in getEncoded(). + * + * @return the string "PKCS#8" + */ + public String getFormat() + { + return "PKCS#8"; + } + + /** + * Return a PKCS8 representation of the key. The sequence returned + * represents a full PrivateKeyInfo object. + * + * @return a PKCS8 representation of the key. + */ + public byte[] getEncoded() + { + try + { + if (info != null) + { + return info.getEncoded(ASN1Encoding.DER); + } + + PrivateKeyInfo info = new PrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.dhKeyAgreement, new DHParameter(dhSpec.getP(), dhSpec.getG(), dhSpec.getL()).toASN1Primitive()), new ASN1Integer(getX())); + + return info.getEncoded(ASN1Encoding.DER); + } + catch (Exception e) + { + return null; + } + } + + public DHParameterSpec getParams() + { + return dhSpec; + } + + public BigInteger getX() + { + return x; + } + + public boolean equals( + Object o) + { + if (!(o instanceof DHPrivateKey)) + { + return false; + } + + DHPrivateKey other = (DHPrivateKey)o; + + return this.getX().equals(other.getX()) + && this.getParams().getG().equals(other.getParams().getG()) + && this.getParams().getP().equals(other.getParams().getP()) + && this.getParams().getL() == other.getParams().getL(); + } + + public int hashCode() + { + return this.getX().hashCode() ^ this.getParams().getG().hashCode() + ^ this.getParams().getP().hashCode() ^ this.getParams().getL(); + } + + public void setBagAttribute( + ASN1ObjectIdentifier oid, + ASN1Encodable attribute) + { + attrCarrier.setBagAttribute(oid, attribute); + } + + public ASN1Encodable getBagAttribute( + ASN1ObjectIdentifier oid) + { + return attrCarrier.getBagAttribute(oid); + } + + public Enumeration getBagAttributeKeys() + { + return attrCarrier.getBagAttributeKeys(); + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + this.dhSpec = new DHParameterSpec((BigInteger)in.readObject(), (BigInteger)in.readObject(), in.readInt()); + this.info = null; + this.attrCarrier = new PKCS12BagAttributeCarrierImpl(); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(dhSpec.getP()); + out.writeObject(dhSpec.getG()); + out.writeInt(dhSpec.getL()); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/BCDHPublicKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/BCDHPublicKey.java new file mode 100644 index 00000000..26d261ab --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/BCDHPublicKey.java @@ -0,0 +1,204 @@ +package org.spongycastle.jcajce.provider.asymmetric.dh; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; + +import javax.crypto.interfaces.DHPublicKey; +import javax.crypto.spec.DHParameterSpec; +import javax.crypto.spec.DHPublicKeySpec; + +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.pkcs.DHParameter; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.DHDomainParameters; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.crypto.params.DHPublicKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.KeyUtil; + +public class BCDHPublicKey + implements DHPublicKey +{ + static final long serialVersionUID = -216691575254424324L; + + private BigInteger y; + + private transient DHParameterSpec dhSpec; + private transient SubjectPublicKeyInfo info; + + BCDHPublicKey( + DHPublicKeySpec spec) + { + this.y = spec.getY(); + this.dhSpec = new DHParameterSpec(spec.getP(), spec.getG()); + } + + BCDHPublicKey( + DHPublicKey key) + { + this.y = key.getY(); + this.dhSpec = key.getParams(); + } + + BCDHPublicKey( + DHPublicKeyParameters params) + { + this.y = params.getY(); + this.dhSpec = new DHParameterSpec(params.getParameters().getP(), params.getParameters().getG(), params.getParameters().getL()); + } + + BCDHPublicKey( + BigInteger y, + DHParameterSpec dhSpec) + { + this.y = y; + this.dhSpec = dhSpec; + } + + public BCDHPublicKey( + SubjectPublicKeyInfo info) + { + this.info = info; + + ASN1Integer derY; + try + { + derY = (ASN1Integer)info.parsePublicKey(); + } + catch (IOException e) + { + throw new IllegalArgumentException("invalid info structure in DH public key"); + } + + this.y = derY.getValue(); + + ASN1Sequence seq = ASN1Sequence.getInstance(info.getAlgorithm().getParameters()); + ASN1ObjectIdentifier id = info.getAlgorithm().getAlgorithm(); + + // we need the PKCS check to handle older keys marked with the X9 oid. + if (id.equals(PKCSObjectIdentifiers.dhKeyAgreement) || isPKCSParam(seq)) + { + DHParameter params = DHParameter.getInstance(seq); + + if (params.getL() != null) + { + this.dhSpec = new DHParameterSpec(params.getP(), params.getG(), params.getL().intValue()); + } + else + { + this.dhSpec = new DHParameterSpec(params.getP(), params.getG()); + } + } + else if (id.equals(X9ObjectIdentifiers.dhpublicnumber)) + { + DHDomainParameters params = DHDomainParameters.getInstance(seq); + + this.dhSpec = new DHParameterSpec(params.getP().getValue(), params.getG().getValue()); + } + else + { + throw new IllegalArgumentException("unknown algorithm type: " + id); + } + } + + public String getAlgorithm() + { + return "DH"; + } + + public String getFormat() + { + return "X.509"; + } + + public byte[] getEncoded() + { + if (info != null) + { + return KeyUtil.getEncodedSubjectPublicKeyInfo(info); + } + + return KeyUtil.getEncodedSubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.dhKeyAgreement, new DHParameter(dhSpec.getP(), dhSpec.getG(), dhSpec.getL()).toASN1Primitive()), new ASN1Integer(y)); + } + + public DHParameterSpec getParams() + { + return dhSpec; + } + + public BigInteger getY() + { + return y; + } + + private boolean isPKCSParam(ASN1Sequence seq) + { + if (seq.size() == 2) + { + return true; + } + + if (seq.size() > 3) + { + return false; + } + + ASN1Integer l = ASN1Integer.getInstance(seq.getObjectAt(2)); + ASN1Integer p = ASN1Integer.getInstance(seq.getObjectAt(0)); + + if (l.getValue().compareTo(BigInteger.valueOf(p.getValue().bitLength())) > 0) + { + return false; + } + + return true; + } + + public int hashCode() + { + return this.getY().hashCode() ^ this.getParams().getG().hashCode() + ^ this.getParams().getP().hashCode() ^ this.getParams().getL(); + } + + public boolean equals( + Object o) + { + if (!(o instanceof DHPublicKey)) + { + return false; + } + + DHPublicKey other = (DHPublicKey)o; + + return this.getY().equals(other.getY()) + && this.getParams().getG().equals(other.getParams().getG()) + && this.getParams().getP().equals(other.getParams().getP()) + && this.getParams().getL() == other.getParams().getL(); + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + this.dhSpec = new DHParameterSpec((BigInteger)in.readObject(), (BigInteger)in.readObject(), in.readInt()); + this.info = null; + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(dhSpec.getP()); + out.writeObject(dhSpec.getG()); + out.writeInt(dhSpec.getL()); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/IESCipher.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/IESCipher.java new file mode 100644 index 00000000..6857f24a --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/IESCipher.java @@ -0,0 +1,507 @@ +package org.spongycastle.jcajce.provider.asymmetric.dh; + +import java.io.ByteArrayOutputStream; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.CipherSpi; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; +import javax.crypto.interfaces.DHKey; +import javax.crypto.interfaces.DHPrivateKey; +import javax.crypto.interfaces.DHPublicKey; + +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.KeyEncoder; +import org.spongycastle.crypto.agreement.DHBasicAgreement; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.engines.AESEngine; +import org.spongycastle.crypto.engines.DESedeEngine; +import org.spongycastle.crypto.engines.IESEngine; +import org.spongycastle.crypto.generators.DHKeyPairGenerator; +import org.spongycastle.crypto.generators.EphemeralKeyPairGenerator; +import org.spongycastle.crypto.generators.KDF2BytesGenerator; +import org.spongycastle.crypto.macs.HMac; +import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.DHKeyGenerationParameters; +import org.spongycastle.crypto.params.DHKeyParameters; +import org.spongycastle.crypto.params.DHParameters; +import org.spongycastle.crypto.params.DHPublicKeyParameters; +import org.spongycastle.crypto.params.IESParameters; +import org.spongycastle.crypto.params.IESWithCipherParameters; +import org.spongycastle.crypto.parsers.DHIESPublicKeyParser; +import org.spongycastle.jcajce.provider.asymmetric.util.DHUtil; +import org.spongycastle.jcajce.provider.asymmetric.util.IESUtil; +import org.spongycastle.jce.interfaces.IESKey; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.IESParameterSpec; +import org.spongycastle.util.BigIntegers; +import org.spongycastle.util.Strings; + + +public class IESCipher + extends CipherSpi +{ + private IESEngine engine; + private int state = -1; + private ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + private AlgorithmParameters engineParam = null; + private IESParameterSpec engineSpec = null; + private AsymmetricKeyParameter key; + private SecureRandom random; + private boolean dhaesMode = false; + private AsymmetricKeyParameter otherKeyParameter = null; + + public IESCipher(IESEngine engine) + { + this.engine = engine; + } + + + public int engineGetBlockSize() + { + if (engine.getCipher() != null) + { + return engine.getCipher().getBlockSize(); + } + else + { + return 0; + } + } + + + public int engineGetKeySize(Key key) + { + if (key instanceof DHKey) + { + return ((DHKey)key).getParams().getP().bitLength(); + } + else + { + throw new IllegalArgumentException("not a DH key"); + } + } + + + public byte[] engineGetIV() + { + return null; + } + + public AlgorithmParameters engineGetParameters() + { + if (engineParam == null && engineSpec != null) + { + try + { + engineParam = AlgorithmParameters.getInstance("IES", BouncyCastleProvider.PROVIDER_NAME); + engineParam.init(engineSpec); + } + catch (Exception e) + { + throw new RuntimeException(e.toString()); + } + } + + return engineParam; + } + + + public void engineSetMode(String mode) + throws NoSuchAlgorithmException + { + String modeName = Strings.toUpperCase(mode); + + if (modeName.equals("NONE")) + { + dhaesMode = false; + } + else if (modeName.equals("DHAES")) + { + dhaesMode = true; + } + else + { + throw new IllegalArgumentException("can't support mode " + mode); + } + } + + public int engineGetOutputSize(int inputLen) + { + int len1, len2, len3; + + len1 = engine.getMac().getMacSize(); + + if (key != null) + { + len2 = ((DHKey)key).getParams().getP().bitLength() / 8 + 1; + } + else + { + throw new IllegalStateException("cipher not initialised"); + } + + if (engine.getCipher() == null) + { + len3 = inputLen; + } + else if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE) + { + len3 = engine.getCipher().getOutputSize(inputLen); + } + else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE) + { + len3 = engine.getCipher().getOutputSize(inputLen - len1 - len2); + } + else + { + throw new IllegalStateException("cipher not initialised"); + } + + if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE) + { + return buffer.size() + len1 + len2 + len3; + } + else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE) + { + return buffer.size() - len1 - len2 + len3; + } + else + { + throw new IllegalStateException("IESCipher not initialised"); + } + + } + + public void engineSetPadding(String padding) + throws NoSuchPaddingException + { + String paddingName = Strings.toUpperCase(padding); + + // TDOD: make this meaningful... + if (paddingName.equals("NOPADDING")) + { + + } + else if (paddingName.equals("PKCS5PADDING") || paddingName.equals("PKCS7PADDING")) + { + + } + else + { + throw new NoSuchPaddingException("padding not available with IESCipher"); + } + } + + // Initialisation methods + + public void engineInit( + int opmode, + Key key, + AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + AlgorithmParameterSpec paramSpec = null; + + if (params != null) + { + try + { + paramSpec = params.getParameterSpec(IESParameterSpec.class); + } + catch (Exception e) + { + throw new InvalidAlgorithmParameterException("cannot recognise parameters: " + e.toString()); + } + } + + engineParam = params; + engineInit(opmode, key, paramSpec, random); + } + + + public void engineInit( + int opmode, + Key key, + AlgorithmParameterSpec engineSpec, + SecureRandom random) + throws InvalidAlgorithmParameterException, InvalidKeyException + { + // Use default parameters (including cipher key size) if none are specified + if (engineSpec == null) + { + this.engineSpec = IESUtil.guessParameterSpec(engine); + } + else if (engineSpec instanceof IESParameterSpec) + { + this.engineSpec = (IESParameterSpec)engineSpec; + } + else + { + throw new InvalidAlgorithmParameterException("must be passed IES parameters"); + } + + // Parse the recipient's key + if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) + { + if (key instanceof DHPublicKey) + { + this.key = DHUtil.generatePublicKeyParameter((PublicKey)key); + } + else if (key instanceof IESKey) + { + IESKey ieKey = (IESKey)key; + + this.key = DHUtil.generatePublicKeyParameter(ieKey.getPublic()); + this.otherKeyParameter = DHUtil.generatePrivateKeyParameter(ieKey.getPrivate()); + } + else + { + throw new InvalidKeyException("must be passed recipient's public DH key for encryption"); + } + } + else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) + { + if (key instanceof DHPrivateKey) + { + this.key = DHUtil.generatePrivateKeyParameter((PrivateKey)key); + } + else if (key instanceof IESKey) + { + IESKey ieKey = (IESKey)key; + + this.otherKeyParameter = DHUtil.generatePublicKeyParameter(ieKey.getPublic()); + this.key = DHUtil.generatePrivateKeyParameter(ieKey.getPrivate()); + } + else + { + throw new InvalidKeyException("must be passed recipient's private DH key for decryption"); + } + } + else + { + throw new InvalidKeyException("must be passed EC key"); + } + + this.random = random; + this.state = opmode; + buffer.reset(); + + } + + + public void engineInit( + int opmode, + Key key, + SecureRandom random) + throws InvalidKeyException + { + try + { + engineInit(opmode, key, (AlgorithmParameterSpec)null, random); + } + catch (InvalidAlgorithmParameterException e) + { + throw new IllegalArgumentException("can't handle supplied parameter spec"); + } + + } + + + // Update methods - buffer the input + + public byte[] engineUpdate( + byte[] input, + int inputOffset, + int inputLen) + { + buffer.write(input, inputOffset, inputLen); + return null; + } + + + public int engineUpdate( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + { + buffer.write(input, inputOffset, inputLen); + return 0; + } + + + // Finalisation methods + + public byte[] engineDoFinal( + byte[] input, + int inputOffset, + int inputLen) + throws IllegalBlockSizeException, BadPaddingException + { + if (inputLen != 0) + { + buffer.write(input, inputOffset, inputLen); + } + + byte[] in = buffer.toByteArray(); + buffer.reset(); + + // Convert parameters for use in IESEngine + IESParameters params = new IESWithCipherParameters(engineSpec.getDerivationV(), + engineSpec.getEncodingV(), + engineSpec.getMacKeySize(), + engineSpec.getCipherKeySize()); + + DHParameters dhParams = ((DHKeyParameters)key).getParameters(); + + byte[] V; + if (otherKeyParameter != null) + { + try + { + if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE) + { + engine.init(true, otherKeyParameter, key, params); + } + else + { + engine.init(false, key, otherKeyParameter, params); + } + return engine.processBlock(in, 0, in.length); + } + catch (Exception e) + { + throw new BadPaddingException(e.getMessage()); + } + } + + if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE) + { + // Generate the ephemeral key pair + DHKeyPairGenerator gen = new DHKeyPairGenerator(); + gen.init(new DHKeyGenerationParameters(random, dhParams)); + + EphemeralKeyPairGenerator kGen = new EphemeralKeyPairGenerator(gen, new KeyEncoder() + { + public byte[] getEncoded(AsymmetricKeyParameter keyParameter) + { + byte[] Vloc = new byte[(((DHKeyParameters)keyParameter).getParameters().getP().bitLength() + 7) / 8]; + byte[] Vtmp = BigIntegers.asUnsignedByteArray(((DHPublicKeyParameters)keyParameter).getY()); + + if (Vtmp.length > Vloc.length) + { + throw new IllegalArgumentException("Senders's public key longer than expected."); + } + else + { + System.arraycopy(Vtmp, 0, Vloc, Vloc.length - Vtmp.length, Vtmp.length); + } + + return Vloc; + } + }); + + // Encrypt the buffer + try + { + engine.init(key, params, kGen); + + return engine.processBlock(in, 0, in.length); + } + catch (Exception e) + { + throw new BadPaddingException(e.getMessage()); + } + } + else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE) + { + // Decrypt the buffer + try + { + engine.init(key, params, new DHIESPublicKeyParser(((DHKeyParameters)key).getParameters())); + + return engine.processBlock(in, 0, in.length); + } + catch (InvalidCipherTextException e) + { + throw new BadPaddingException(e.getMessage()); + } + } + else + { + throw new IllegalStateException("IESCipher not initialised"); + } + + } + + + public int engineDoFinal( + byte[] input, + int inputOffset, + int inputLength, + byte[] output, + int outputOffset) + throws ShortBufferException, IllegalBlockSizeException, BadPaddingException + { + + byte[] buf = engineDoFinal(input, inputOffset, inputLength); + System.arraycopy(buf, 0, output, outputOffset, buf.length); + return buf.length; + + } + + + /** + * Classes that inherit from us + */ + + static public class IES + extends IESCipher + { + public IES() + { + super(new IESEngine(new DHBasicAgreement(), + new KDF2BytesGenerator(new SHA1Digest()), + new HMac(new SHA1Digest()))); + } + } + + static public class IESwithDESede + extends IESCipher + { + public IESwithDESede() + { + super(new IESEngine(new DHBasicAgreement(), + new KDF2BytesGenerator(new SHA1Digest()), + new HMac(new SHA1Digest()), + new PaddedBufferedBlockCipher(new DESedeEngine()))); + } + } + + static public class IESwithAES + extends IESCipher + { + public IESwithAES() + { + super(new IESEngine(new DHBasicAgreement(), + new KDF2BytesGenerator(new SHA1Digest()), + new HMac(new SHA1Digest()), + new PaddedBufferedBlockCipher(new AESEngine()))); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java new file mode 100644 index 00000000..63127236 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java @@ -0,0 +1,227 @@ +package org.spongycastle.jcajce.provider.asymmetric.dh; + +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Hashtable; + +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import javax.crypto.interfaces.DHPrivateKey; +import javax.crypto.interfaces.DHPublicKey; +import javax.crypto.spec.DHParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.spongycastle.crypto.params.DESParameters; +import org.spongycastle.util.Integers; +import org.spongycastle.util.Strings; + +/** + * Diffie-Hellman key agreement. There's actually a better way of doing this + * if you are using long term public keys, see the light-weight version for + * details. + */ +public class KeyAgreementSpi + extends javax.crypto.KeyAgreementSpi +{ + private BigInteger x; + private BigInteger p; + private BigInteger g; + private BigInteger result; + + private static final Hashtable algorithms = new Hashtable(); + + static + { + Integer i64 = Integers.valueOf(64); + Integer i192 = Integers.valueOf(192); + Integer i128 = Integers.valueOf(128); + Integer i256 = Integers.valueOf(256); + + algorithms.put("DES", i64); + algorithms.put("DESEDE", i192); + algorithms.put("BLOWFISH", i128); + algorithms.put("AES", i256); + } + + private byte[] bigIntToBytes( + BigInteger r) + { + // + // RFC 2631 (2.1.2) specifies that the secret should be padded with leading zeros if necessary + // must be the same length as p + // + int expectedLength = (p.bitLength() + 7) / 8; + + byte[] tmp = r.toByteArray(); + + if (tmp.length == expectedLength) + { + return tmp; + } + + if (tmp[0] == 0 && tmp.length == expectedLength + 1) + { + byte[] rv = new byte[tmp.length - 1]; + + System.arraycopy(tmp, 1, rv, 0, rv.length); + return rv; + } + + // tmp must be shorter than expectedLength + // pad to the left with zeros. + byte[] rv = new byte[expectedLength]; + + System.arraycopy(tmp, 0, rv, rv.length - tmp.length, tmp.length); + + return rv; + } + + protected Key engineDoPhase( + Key key, + boolean lastPhase) + throws InvalidKeyException, IllegalStateException + { + if (x == null) + { + throw new IllegalStateException("Diffie-Hellman not initialised."); + } + + if (!(key instanceof DHPublicKey)) + { + throw new InvalidKeyException("DHKeyAgreement doPhase requires DHPublicKey"); + } + DHPublicKey pubKey = (DHPublicKey)key; + + if (!pubKey.getParams().getG().equals(g) || !pubKey.getParams().getP().equals(p)) + { + throw new InvalidKeyException("DHPublicKey not for this KeyAgreement!"); + } + + if (lastPhase) + { + result = ((DHPublicKey)key).getY().modPow(x, p); + return null; + } + else + { + result = ((DHPublicKey)key).getY().modPow(x, p); + } + + return new BCDHPublicKey(result, pubKey.getParams()); + } + + protected byte[] engineGenerateSecret() + throws IllegalStateException + { + if (x == null) + { + throw new IllegalStateException("Diffie-Hellman not initialised."); + } + + return bigIntToBytes(result); + } + + protected int engineGenerateSecret( + byte[] sharedSecret, + int offset) + throws IllegalStateException, ShortBufferException + { + if (x == null) + { + throw new IllegalStateException("Diffie-Hellman not initialised."); + } + + byte[] secret = bigIntToBytes(result); + + if (sharedSecret.length - offset < secret.length) + { + throw new ShortBufferException("DHKeyAgreement - buffer too short"); + } + + System.arraycopy(secret, 0, sharedSecret, offset, secret.length); + + return secret.length; + } + + protected SecretKey engineGenerateSecret( + String algorithm) + { + if (x == null) + { + throw new IllegalStateException("Diffie-Hellman not initialised."); + } + + String algKey = Strings.toUpperCase(algorithm); + byte[] res = bigIntToBytes(result); + + if (algorithms.containsKey(algKey)) + { + Integer length = (Integer)algorithms.get(algKey); + + byte[] key = new byte[length.intValue() / 8]; + System.arraycopy(res, 0, key, 0, key.length); + + if (algKey.startsWith("DES")) + { + DESParameters.setOddParity(key); + } + + return new SecretKeySpec(key, algorithm); + } + + return new SecretKeySpec(res, algorithm); + } + + protected void engineInit( + Key key, + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + if (!(key instanceof DHPrivateKey)) + { + throw new InvalidKeyException("DHKeyAgreement requires DHPrivateKey for initialisation"); + } + DHPrivateKey privKey = (DHPrivateKey)key; + + if (params != null) + { + if (!(params instanceof DHParameterSpec)) + { + throw new InvalidAlgorithmParameterException("DHKeyAgreement only accepts DHParameterSpec"); + } + DHParameterSpec p = (DHParameterSpec)params; + + this.p = p.getP(); + this.g = p.getG(); + } + else + { + this.p = privKey.getParams().getP(); + this.g = privKey.getParams().getG(); + } + + this.x = this.result = privKey.getX(); + } + + protected void engineInit( + Key key, + SecureRandom random) + throws InvalidKeyException + { + if (!(key instanceof DHPrivateKey)) + { + throw new InvalidKeyException("DHKeyAgreement requires DHPrivateKey"); + } + + DHPrivateKey privKey = (DHPrivateKey)key; + + this.p = privKey.getParams().getP(); + this.g = privKey.getParams().getG(); + this.x = this.result = privKey.getX(); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyFactorySpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyFactorySpi.java new file mode 100644 index 00000000..92ccdd3e --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyFactorySpi.java @@ -0,0 +1,128 @@ +package org.spongycastle.jcajce.provider.asymmetric.dh; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +import javax.crypto.interfaces.DHPrivateKey; +import javax.crypto.interfaces.DHPublicKey; +import javax.crypto.spec.DHPrivateKeySpec; +import javax.crypto.spec.DHPublicKeySpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi; + +public class KeyFactorySpi + extends BaseKeyFactorySpi +{ + public KeyFactorySpi() + { + } + + protected KeySpec engineGetKeySpec( + Key key, + Class spec) + throws InvalidKeySpecException + { + if (spec.isAssignableFrom(DHPrivateKeySpec.class) && key instanceof DHPrivateKey) + { + DHPrivateKey k = (DHPrivateKey)key; + + return new DHPrivateKeySpec(k.getX(), k.getParams().getP(), k.getParams().getG()); + } + else if (spec.isAssignableFrom(DHPublicKeySpec.class) && key instanceof DHPublicKey) + { + DHPublicKey k = (DHPublicKey)key; + + return new DHPublicKeySpec(k.getY(), k.getParams().getP(), k.getParams().getG()); + } + + return super.engineGetKeySpec(key, spec); + } + + protected Key engineTranslateKey( + Key key) + throws InvalidKeyException + { + if (key instanceof DHPublicKey) + { + return new BCDHPublicKey((DHPublicKey)key); + } + else if (key instanceof DHPrivateKey) + { + return new BCDHPrivateKey((DHPrivateKey)key); + } + + throw new InvalidKeyException("key type unknown"); + } + + protected PrivateKey engineGeneratePrivate( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof DHPrivateKeySpec) + { + return new BCDHPrivateKey((DHPrivateKeySpec)keySpec); + } + + return super.engineGeneratePrivate(keySpec); + } + + protected PublicKey engineGeneratePublic( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof DHPublicKeySpec) + { + return new BCDHPublicKey((DHPublicKeySpec)keySpec); + } + + return super.engineGeneratePublic(keySpec); + } + + public PrivateKey generatePrivate(PrivateKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getPrivateKeyAlgorithm().getAlgorithm(); + + if (algOid.equals(PKCSObjectIdentifiers.dhKeyAgreement)) + { + return new BCDHPrivateKey(keyInfo); + } + else if (algOid.equals(X9ObjectIdentifiers.dhpublicnumber)) + { + return new BCDHPrivateKey(keyInfo); + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } + + public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getAlgorithm().getAlgorithm(); + + if (algOid.equals(PKCSObjectIdentifiers.dhKeyAgreement)) + { + return new BCDHPublicKey(keyInfo); + } + else if (algOid.equals(X9ObjectIdentifiers.dhpublicnumber)) + { + return new BCDHPublicKey(keyInfo); + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyPairGeneratorSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyPairGeneratorSpi.java new file mode 100644 index 00000000..67b0d5dc --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyPairGeneratorSpi.java @@ -0,0 +1,119 @@ +package org.spongycastle.jcajce.provider.asymmetric.dh; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Hashtable; + +import javax.crypto.spec.DHParameterSpec; + +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.generators.DHBasicKeyPairGenerator; +import org.spongycastle.crypto.generators.DHParametersGenerator; +import org.spongycastle.crypto.params.DHKeyGenerationParameters; +import org.spongycastle.crypto.params.DHParameters; +import org.spongycastle.crypto.params.DHPrivateKeyParameters; +import org.spongycastle.crypto.params.DHPublicKeyParameters; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.util.Integers; + +public class KeyPairGeneratorSpi + extends java.security.KeyPairGenerator +{ + private static Hashtable params = new Hashtable(); + private static Object lock = new Object(); + + DHKeyGenerationParameters param; + DHBasicKeyPairGenerator engine = new DHBasicKeyPairGenerator(); + int strength = 1024; + int certainty = 20; + SecureRandom random = new SecureRandom(); + boolean initialised = false; + + public KeyPairGeneratorSpi() + { + super("DH"); + } + + public void initialize( + int strength, + SecureRandom random) + { + this.strength = strength; + this.random = random; + } + + public void initialize( + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + if (!(params instanceof DHParameterSpec)) + { + throw new InvalidAlgorithmParameterException("parameter object not a DHParameterSpec"); + } + DHParameterSpec dhParams = (DHParameterSpec)params; + + param = new DHKeyGenerationParameters(random, new DHParameters(dhParams.getP(), dhParams.getG(), null, dhParams.getL())); + + engine.init(param); + initialised = true; + } + + public KeyPair generateKeyPair() + { + if (!initialised) + { + Integer paramStrength = Integers.valueOf(strength); + + if (params.containsKey(paramStrength)) + { + param = (DHKeyGenerationParameters)params.get(paramStrength); + } + else + { + DHParameterSpec dhParams = BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(strength); + + if (dhParams != null) + { + param = new DHKeyGenerationParameters(random, new DHParameters(dhParams.getP(), dhParams.getG(), null, dhParams.getL())); + } + else + { + synchronized (lock) + { + // we do the check again in case we were blocked by a generator for + // our key size. + if (params.containsKey(paramStrength)) + { + param = (DHKeyGenerationParameters)params.get(paramStrength); + } + else + { + + DHParametersGenerator pGen = new DHParametersGenerator(); + + pGen.init(strength, certainty, random); + + param = new DHKeyGenerationParameters(random, pGen.generateParameters()); + + params.put(paramStrength, param); + } + } + } + } + + engine.init(param); + + initialised = true; + } + + AsymmetricCipherKeyPair pair = engine.generateKeyPair(); + DHPublicKeyParameters pub = (DHPublicKeyParameters)pair.getPublic(); + DHPrivateKeyParameters priv = (DHPrivateKeyParameters)pair.getPrivate(); + + return new KeyPair(new BCDHPublicKey(pub), + new BCDHPrivateKey(priv)); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java new file mode 100644 index 00000000..9bbdca3d --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java @@ -0,0 +1,103 @@ +package org.spongycastle.jcajce.provider.asymmetric.dsa; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidParameterException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.DSAParameterSpec; + +import org.spongycastle.crypto.digests.SHA256Digest; +import org.spongycastle.crypto.generators.DSAParametersGenerator; +import org.spongycastle.crypto.params.DSAParameterGenerationParameters; +import org.spongycastle.crypto.params.DSAParameters; +import org.spongycastle.jce.provider.BouncyCastleProvider; + +public class AlgorithmParameterGeneratorSpi + extends java.security.AlgorithmParameterGeneratorSpi +{ + protected SecureRandom random; + protected int strength = 1024; + protected DSAParameterGenerationParameters params; + + protected void engineInit( + int strength, + SecureRandom random) + { + if (strength < 512 || strength > 3072) + { + throw new InvalidParameterException("strength must be from 512 - 3072"); + } + + if (strength <= 1024 && strength % 64 != 0) + { + throw new InvalidParameterException("strength must be a multiple of 64 below 1024 bits."); + } + + if (strength > 1024 && strength % 1024 != 0) + { + throw new InvalidParameterException("strength must be a multiple of 1024 above 1024 bits."); + } + + this.strength = strength; + this.random = random; + } + + protected void engineInit( + AlgorithmParameterSpec genParamSpec, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for DSA parameter generation."); + } + + protected AlgorithmParameters engineGenerateParameters() + { + DSAParametersGenerator pGen; + + if (strength <= 1024) + { + pGen = new DSAParametersGenerator(); + } + else + { + pGen = new DSAParametersGenerator(new SHA256Digest()); + } + + if (random == null) + { + random = new SecureRandom(); + } + + if (strength == 1024) + { + params = new DSAParameterGenerationParameters(1024, 160, 80, random); + pGen.init(params); + } + else if (strength > 1024) + { + params = new DSAParameterGenerationParameters(strength, 256, 80, random); + pGen.init(params); + } + else + { + pGen.init(strength, 20, random); + } + + DSAParameters p = pGen.generateParameters(); + + AlgorithmParameters params; + + try + { + params = AlgorithmParameters.getInstance("DSA", BouncyCastleProvider.PROVIDER_NAME); + params.init(new DSAParameterSpec(p.getP(), p.getQ(), p.getG())); + } + catch (Exception e) + { + throw new RuntimeException(e.getMessage()); + } + + return params; + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/AlgorithmParametersSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/AlgorithmParametersSpi.java new file mode 100644 index 00000000..2ace504d --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/AlgorithmParametersSpi.java @@ -0,0 +1,132 @@ +package org.spongycastle.jcajce.provider.asymmetric.dsa; + +import java.io.IOException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.DSAParameterSpec; +import java.security.spec.InvalidParameterSpecException; + +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.x509.DSAParameter; + +public class AlgorithmParametersSpi + extends java.security.AlgorithmParametersSpi +{ + DSAParameterSpec currentSpec; + + protected boolean isASN1FormatString(String format) + { + return format == null || format.equals("ASN.1"); + } + + protected AlgorithmParameterSpec engineGetParameterSpec( + Class paramSpec) + throws InvalidParameterSpecException + { + if (paramSpec == null) + { + throw new NullPointerException("argument to getParameterSpec must not be null"); + } + + return localEngineGetParameterSpec(paramSpec); + } + + /** + * Return the X.509 ASN.1 structure DSAParameter. + * <p/> + * <pre> + * DSAParameter ::= SEQUENCE { + * prime INTEGER, -- p + * subprime INTEGER, -- q + * base INTEGER, -- g} + * </pre> + */ + protected byte[] engineGetEncoded() + { + DSAParameter dsaP = new DSAParameter(currentSpec.getP(), currentSpec.getQ(), currentSpec.getG()); + + try + { + return dsaP.getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + throw new RuntimeException("Error encoding DSAParameters"); + } + } + + protected byte[] engineGetEncoded( + String format) + { + if (isASN1FormatString(format)) + { + return engineGetEncoded(); + } + + return null; + } + + protected AlgorithmParameterSpec localEngineGetParameterSpec( + Class paramSpec) + throws InvalidParameterSpecException + { + if (paramSpec == DSAParameterSpec.class) + { + return currentSpec; + } + + throw new InvalidParameterSpecException("unknown parameter spec passed to DSA parameters object."); + } + + protected void engineInit( + AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException + { + if (!(paramSpec instanceof DSAParameterSpec)) + { + throw new InvalidParameterSpecException("DSAParameterSpec required to initialise a DSA algorithm parameters object"); + } + + this.currentSpec = (DSAParameterSpec)paramSpec; + } + + protected void engineInit( + byte[] params) + throws IOException + { + try + { + DSAParameter dsaP = DSAParameter.getInstance(ASN1Primitive.fromByteArray(params)); + + currentSpec = new DSAParameterSpec(dsaP.getP(), dsaP.getQ(), dsaP.getG()); + } + catch (ClassCastException e) + { + throw new IOException("Not a valid DSA Parameter encoding."); + } + catch (ArrayIndexOutOfBoundsException e) + { + throw new IOException("Not a valid DSA Parameter encoding."); + } + } + + protected void engineInit( + byte[] params, + String format) + throws IOException + { + if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509")) + { + engineInit(params); + } + else + { + throw new IOException("Unknown parameter format " + format); + } + } + + protected String engineToString() + { + return "DSA Parameters"; + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/BCDSAPrivateKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/BCDSAPrivateKey.java new file mode 100644 index 00000000..bcd3dfe8 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/BCDSAPrivateKey.java @@ -0,0 +1,167 @@ +package org.spongycastle.jcajce.provider.asymmetric.dsa; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; +import java.security.interfaces.DSAParams; +import java.security.interfaces.DSAPrivateKey; +import java.security.spec.DSAParameterSpec; +import java.security.spec.DSAPrivateKeySpec; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.DSAParameter; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.crypto.params.DSAPrivateKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.KeyUtil; +import org.spongycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl; +import org.spongycastle.jce.interfaces.PKCS12BagAttributeCarrier; + +public class BCDSAPrivateKey + implements DSAPrivateKey, PKCS12BagAttributeCarrier +{ + private static final long serialVersionUID = -4677259546958385734L; + + private BigInteger x; + private transient DSAParams dsaSpec; + + private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl(); + + protected BCDSAPrivateKey() + { + } + + BCDSAPrivateKey( + DSAPrivateKey key) + { + this.x = key.getX(); + this.dsaSpec = key.getParams(); + } + + BCDSAPrivateKey( + DSAPrivateKeySpec spec) + { + this.x = spec.getX(); + this.dsaSpec = new DSAParameterSpec(spec.getP(), spec.getQ(), spec.getG()); + } + + public BCDSAPrivateKey( + PrivateKeyInfo info) + throws IOException + { + DSAParameter params = DSAParameter.getInstance(info.getPrivateKeyAlgorithm().getParameters()); + ASN1Integer derX = (ASN1Integer)info.parsePrivateKey(); + + this.x = derX.getValue(); + this.dsaSpec = new DSAParameterSpec(params.getP(), params.getQ(), params.getG()); + } + + BCDSAPrivateKey( + DSAPrivateKeyParameters params) + { + this.x = params.getX(); + this.dsaSpec = new DSAParameterSpec(params.getParameters().getP(), params.getParameters().getQ(), params.getParameters().getG()); + } + + public String getAlgorithm() + { + return "DSA"; + } + + /** + * return the encoding format we produce in getEncoded(). + * + * @return the string "PKCS#8" + */ + public String getFormat() + { + return "PKCS#8"; + } + + /** + * Return a PKCS8 representation of the key. The sequence returned + * represents a full PrivateKeyInfo object. + * + * @return a PKCS8 representation of the key. + */ + public byte[] getEncoded() + { + return KeyUtil.getEncodedPrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, new DSAParameter(dsaSpec.getP(), dsaSpec.getQ(), dsaSpec.getG()).toASN1Primitive()), new ASN1Integer(getX())); + } + + public DSAParams getParams() + { + return dsaSpec; + } + + public BigInteger getX() + { + return x; + } + + public boolean equals( + Object o) + { + if (!(o instanceof DSAPrivateKey)) + { + return false; + } + + DSAPrivateKey other = (DSAPrivateKey)o; + + return this.getX().equals(other.getX()) + && this.getParams().getG().equals(other.getParams().getG()) + && this.getParams().getP().equals(other.getParams().getP()) + && this.getParams().getQ().equals(other.getParams().getQ()); + } + + public int hashCode() + { + return this.getX().hashCode() ^ this.getParams().getG().hashCode() + ^ this.getParams().getP().hashCode() ^ this.getParams().getQ().hashCode(); + } + + public void setBagAttribute( + ASN1ObjectIdentifier oid, + ASN1Encodable attribute) + { + attrCarrier.setBagAttribute(oid, attribute); + } + + public ASN1Encodable getBagAttribute( + ASN1ObjectIdentifier oid) + { + return attrCarrier.getBagAttribute(oid); + } + + public Enumeration getBagAttributeKeys() + { + return attrCarrier.getBagAttributeKeys(); + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + this.dsaSpec = new DSAParameterSpec((BigInteger)in.readObject(), (BigInteger)in.readObject(), (BigInteger)in.readObject()); + this.attrCarrier = new PKCS12BagAttributeCarrierImpl(); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(dsaSpec.getP()); + out.writeObject(dsaSpec.getQ()); + out.writeObject(dsaSpec.getG()); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/BCDSAPublicKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/BCDSAPublicKey.java new file mode 100644 index 00000000..f4ccaeae --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/BCDSAPublicKey.java @@ -0,0 +1,171 @@ +package org.spongycastle.jcajce.provider.asymmetric.dsa; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; +import java.security.interfaces.DSAParams; +import java.security.interfaces.DSAPublicKey; +import java.security.spec.DSAParameterSpec; +import java.security.spec.DSAPublicKeySpec; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.DSAParameter; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.crypto.params.DSAPublicKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.KeyUtil; + +public class BCDSAPublicKey + implements DSAPublicKey +{ + private static final long serialVersionUID = 1752452449903495175L; + + private BigInteger y; + private transient DSAParams dsaSpec; + + BCDSAPublicKey( + DSAPublicKeySpec spec) + { + this.y = spec.getY(); + this.dsaSpec = new DSAParameterSpec(spec.getP(), spec.getQ(), spec.getG()); + } + + BCDSAPublicKey( + DSAPublicKey key) + { + this.y = key.getY(); + this.dsaSpec = key.getParams(); + } + + BCDSAPublicKey( + DSAPublicKeyParameters params) + { + this.y = params.getY(); + this.dsaSpec = new DSAParameterSpec(params.getParameters().getP(), params.getParameters().getQ(), params.getParameters().getG()); + } + + BCDSAPublicKey( + BigInteger y, + DSAParameterSpec dsaSpec) + { + this.y = y; + this.dsaSpec = dsaSpec; + } + + public BCDSAPublicKey( + SubjectPublicKeyInfo info) + { + + ASN1Integer derY; + + try + { + derY = (ASN1Integer)info.parsePublicKey(); + } + catch (IOException e) + { + throw new IllegalArgumentException("invalid info structure in DSA public key"); + } + + this.y = derY.getValue(); + + if (isNotNull(info.getAlgorithm().getParameters())) + { + DSAParameter params = DSAParameter.getInstance(info.getAlgorithm().getParameters()); + + this.dsaSpec = new DSAParameterSpec(params.getP(), params.getQ(), params.getG()); + } + } + + private boolean isNotNull(ASN1Encodable parameters) + { + return parameters != null && !DERNull.INSTANCE.equals(parameters.toASN1Primitive()); + } + + public String getAlgorithm() + { + return "DSA"; + } + + public String getFormat() + { + return "X.509"; + } + + public byte[] getEncoded() + { + if (dsaSpec == null) + { + return KeyUtil.getEncodedSubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa), new ASN1Integer(y)); + } + + return KeyUtil.getEncodedSubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, new DSAParameter(dsaSpec.getP(), dsaSpec.getQ(), dsaSpec.getG()).toASN1Primitive()), new ASN1Integer(y)); + } + + public DSAParams getParams() + { + return dsaSpec; + } + + public BigInteger getY() + { + return y; + } + + public String toString() + { + StringBuffer buf = new StringBuffer(); + String nl = System.getProperty("line.separator"); + + buf.append("DSA Public Key").append(nl); + buf.append(" y: ").append(this.getY().toString(16)).append(nl); + + return buf.toString(); + } + + public int hashCode() + { + return this.getY().hashCode() ^ this.getParams().getG().hashCode() + ^ this.getParams().getP().hashCode() ^ this.getParams().getQ().hashCode(); + } + + public boolean equals( + Object o) + { + if (!(o instanceof DSAPublicKey)) + { + return false; + } + + DSAPublicKey other = (DSAPublicKey)o; + + return this.getY().equals(other.getY()) + && this.getParams().getG().equals(other.getParams().getG()) + && this.getParams().getP().equals(other.getParams().getP()) + && this.getParams().getQ().equals(other.getParams().getQ()); + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + this.dsaSpec = new DSAParameterSpec((BigInteger)in.readObject(), (BigInteger)in.readObject(), (BigInteger)in.readObject()); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(dsaSpec.getP()); + out.writeObject(dsaSpec.getQ()); + out.writeObject(dsaSpec.getG()); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/DSASigner.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/DSASigner.java new file mode 100644 index 00000000..c2b9e5d3 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/DSASigner.java @@ -0,0 +1,313 @@ +package org.spongycastle.jcajce.provider.asymmetric.dsa; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.SignatureException; +import java.security.SignatureSpi; +import java.security.interfaces.DSAKey; +import java.security.spec.AlgorithmParameterSpec; + +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x509.X509ObjectIdentifiers; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DSA; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.NullDigest; +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.crypto.params.ParametersWithRandom; +import org.spongycastle.crypto.signers.HMacDSAKCalculator; + +public class DSASigner + extends SignatureSpi + implements PKCSObjectIdentifiers, X509ObjectIdentifiers +{ + private Digest digest; + private DSA signer; + private SecureRandom random; + + protected DSASigner( + Digest digest, + DSA signer) + { + this.digest = digest; + this.signer = signer; + } + + protected void engineInitVerify( + PublicKey publicKey) + throws InvalidKeyException + { + CipherParameters param; + + if (publicKey instanceof DSAKey) + { + param = DSAUtil.generatePublicKeyParameter(publicKey); + } + else + { + try + { + byte[] bytes = publicKey.getEncoded(); + + publicKey = new BCDSAPublicKey(SubjectPublicKeyInfo.getInstance(bytes)); + + if (publicKey instanceof DSAKey) + { + param = DSAUtil.generatePublicKeyParameter(publicKey); + } + else + { + throw new InvalidKeyException("can't recognise key type in DSA based signer"); + } + } + catch (Exception e) + { + throw new InvalidKeyException("can't recognise key type in DSA based signer"); + } + } + + digest.reset(); + signer.init(false, param); + } + + protected void engineInitSign( + PrivateKey privateKey, + SecureRandom random) + throws InvalidKeyException + { + this.random = random; + engineInitSign(privateKey); + } + + protected void engineInitSign( + PrivateKey privateKey) + throws InvalidKeyException + { + CipherParameters param; + + param = DSAUtil.generatePrivateKeyParameter(privateKey); + + if (random != null) + { + param = new ParametersWithRandom(param, random); + } + + digest.reset(); + signer.init(true, param); + } + + protected void engineUpdate( + byte b) + throws SignatureException + { + digest.update(b); + } + + protected void engineUpdate( + byte[] b, + int off, + int len) + throws SignatureException + { + digest.update(b, off, len); + } + + protected byte[] engineSign() + throws SignatureException + { + byte[] hash = new byte[digest.getDigestSize()]; + + digest.doFinal(hash, 0); + + try + { + BigInteger[] sig = signer.generateSignature(hash); + + return derEncode(sig[0], sig[1]); + } + catch (Exception e) + { + throw new SignatureException(e.toString()); + } + } + + protected boolean engineVerify( + byte[] sigBytes) + throws SignatureException + { + byte[] hash = new byte[digest.getDigestSize()]; + + digest.doFinal(hash, 0); + + BigInteger[] sig; + + try + { + sig = derDecode(sigBytes); + } + catch (Exception e) + { + throw new SignatureException("error decoding signature bytes."); + } + + return signer.verifySignature(hash, sig[0], sig[1]); + } + + protected void engineSetParameter( + AlgorithmParameterSpec params) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated replaced with <a href = "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)"> + */ + protected void engineSetParameter( + String param, + Object value) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated + */ + protected Object engineGetParameter( + String param) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + private byte[] derEncode( + BigInteger r, + BigInteger s) + throws IOException + { + ASN1Integer[] rs = new ASN1Integer[]{ new ASN1Integer(r), new ASN1Integer(s) }; + return new DERSequence(rs).getEncoded(ASN1Encoding.DER); + } + + private BigInteger[] derDecode( + byte[] encoding) + throws IOException + { + ASN1Sequence s = (ASN1Sequence)ASN1Primitive.fromByteArray(encoding); + return new BigInteger[]{ + ((ASN1Integer)s.getObjectAt(0)).getValue(), + ((ASN1Integer)s.getObjectAt(1)).getValue() + }; + } + + static public class stdDSA + extends DSASigner + { + public stdDSA() + { + super(new SHA1Digest(), new org.spongycastle.crypto.signers.DSASigner()); + } + } + + static public class detDSA + extends DSASigner + { + public detDSA() + { + super(new SHA1Digest(), new org.spongycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA1Digest()))); + } + } + + static public class dsa224 + extends DSASigner + { + public dsa224() + { + super(new SHA224Digest(), new org.spongycastle.crypto.signers.DSASigner()); + } + } + + static public class detDSA224 + extends DSASigner + { + public detDSA224() + { + super(new SHA224Digest(), new org.spongycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA224Digest()))); + } + } + + static public class dsa256 + extends DSASigner + { + public dsa256() + { + super(new SHA256Digest(), new org.spongycastle.crypto.signers.DSASigner()); + } + } + + static public class detDSA256 + extends DSASigner + { + public detDSA256() + { + super(new SHA256Digest(), new org.spongycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA256Digest()))); + } + } + + static public class dsa384 + extends DSASigner + { + public dsa384() + { + super(new SHA384Digest(), new org.spongycastle.crypto.signers.DSASigner()); + } + } + + static public class detDSA384 + extends DSASigner + { + public detDSA384() + { + super(new SHA384Digest(), new org.spongycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA384Digest()))); + } + } + + static public class dsa512 + extends DSASigner + { + public dsa512() + { + super(new SHA512Digest(), new org.spongycastle.crypto.signers.DSASigner()); + } + } + + static public class detDSA512 + extends DSASigner + { + public detDSA512() + { + super(new SHA512Digest(), new org.spongycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA512Digest()))); + } + } + + static public class noneDSA + extends DSASigner + { + public noneDSA() + { + super(new NullDigest(), new org.spongycastle.crypto.signers.DSASigner()); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/DSAUtil.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/DSAUtil.java new file mode 100644 index 00000000..ae627195 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/DSAUtil.java @@ -0,0 +1,72 @@ +package org.spongycastle.jcajce.provider.asymmetric.dsa; + +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.interfaces.DSAPrivateKey; +import java.security.interfaces.DSAPublicKey; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.oiw.OIWObjectIdentifiers; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.DSAParameters; +import org.spongycastle.crypto.params.DSAPrivateKeyParameters; +import org.spongycastle.crypto.params.DSAPublicKeyParameters; + +/** + * utility class for converting jce/jca DSA objects + * objects into their org.spongycastle.crypto counterparts. + */ +public class DSAUtil +{ + public static final ASN1ObjectIdentifier[] dsaOids = + { + X9ObjectIdentifiers.id_dsa, + OIWObjectIdentifiers.dsaWithSHA1 + }; + + public static boolean isDsaOid( + ASN1ObjectIdentifier algOid) + { + for (int i = 0; i != dsaOids.length; i++) + { + if (algOid.equals(dsaOids[i])) + { + return true; + } + } + + return false; + } + + static public AsymmetricKeyParameter generatePublicKeyParameter( + PublicKey key) + throws InvalidKeyException + { + if (key instanceof DSAPublicKey) + { + DSAPublicKey k = (DSAPublicKey)key; + + return new DSAPublicKeyParameters(k.getY(), + new DSAParameters(k.getParams().getP(), k.getParams().getQ(), k.getParams().getG())); + } + + throw new InvalidKeyException("can't identify DSA public key: " + key.getClass().getName()); + } + + static public AsymmetricKeyParameter generatePrivateKeyParameter( + PrivateKey key) + throws InvalidKeyException + { + if (key instanceof DSAPrivateKey) + { + DSAPrivateKey k = (DSAPrivateKey)key; + + return new DSAPrivateKeyParameters(k.getX(), + new DSAParameters(k.getParams().getP(), k.getParams().getQ(), k.getParams().getG())); + } + + throw new InvalidKeyException("can't identify DSA private key."); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/KeyFactorySpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/KeyFactorySpi.java new file mode 100644 index 00000000..2b5c693e --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/KeyFactorySpi.java @@ -0,0 +1,117 @@ +package org.spongycastle.jcajce.provider.asymmetric.dsa; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.interfaces.DSAPrivateKey; +import java.security.interfaces.DSAPublicKey; +import java.security.spec.DSAPrivateKeySpec; +import java.security.spec.DSAPublicKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi; + +public class KeyFactorySpi + extends BaseKeyFactorySpi +{ + public KeyFactorySpi() + { + } + + protected KeySpec engineGetKeySpec( + Key key, + Class spec) + throws InvalidKeySpecException + { + if (spec.isAssignableFrom(DSAPublicKeySpec.class) && key instanceof DSAPublicKey) + { + DSAPublicKey k = (DSAPublicKey)key; + + return new DSAPublicKeySpec(k.getY(), k.getParams().getP(), k.getParams().getQ(), k.getParams().getG()); + } + else if (spec.isAssignableFrom(DSAPrivateKeySpec.class) && key instanceof java.security.interfaces.DSAPrivateKey) + { + java.security.interfaces.DSAPrivateKey k = (java.security.interfaces.DSAPrivateKey)key; + + return new DSAPrivateKeySpec(k.getX(), k.getParams().getP(), k.getParams().getQ(), k.getParams().getG()); + } + + return super.engineGetKeySpec(key, spec); + } + + protected Key engineTranslateKey( + Key key) + throws InvalidKeyException + { + if (key instanceof DSAPublicKey) + { + return new BCDSAPublicKey((DSAPublicKey)key); + } + else if (key instanceof DSAPrivateKey) + { + return new BCDSAPrivateKey((DSAPrivateKey)key); + } + + throw new InvalidKeyException("key type unknown"); + } + + public PrivateKey generatePrivate(PrivateKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getPrivateKeyAlgorithm().getAlgorithm(); + + if (DSAUtil.isDsaOid(algOid)) + { + return new BCDSAPrivateKey(keyInfo); + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } + + public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getAlgorithm().getAlgorithm(); + + if (DSAUtil.isDsaOid(algOid)) + { + return new BCDSAPublicKey(keyInfo); + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } + + protected PrivateKey engineGeneratePrivate( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof DSAPrivateKeySpec) + { + return new BCDSAPrivateKey((DSAPrivateKeySpec)keySpec); + } + + return super.engineGeneratePrivate(keySpec); + } + + protected PublicKey engineGeneratePublic( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof DSAPublicKeySpec) + { + return new BCDSAPublicKey((DSAPublicKeySpec)keySpec); + } + + return super.engineGeneratePublic(keySpec); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java new file mode 100644 index 00000000..c67c417f --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java @@ -0,0 +1,82 @@ +package org.spongycastle.jcajce.provider.asymmetric.dsa; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidParameterException; +import java.security.KeyPair; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.DSAParameterSpec; + +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.generators.DSAKeyPairGenerator; +import org.spongycastle.crypto.generators.DSAParametersGenerator; +import org.spongycastle.crypto.params.DSAKeyGenerationParameters; +import org.spongycastle.crypto.params.DSAParameters; +import org.spongycastle.crypto.params.DSAPrivateKeyParameters; +import org.spongycastle.crypto.params.DSAPublicKeyParameters; + +public class KeyPairGeneratorSpi + extends java.security.KeyPairGenerator +{ + DSAKeyGenerationParameters param; + DSAKeyPairGenerator engine = new DSAKeyPairGenerator(); + int strength = 1024; + int certainty = 20; + SecureRandom random = new SecureRandom(); + boolean initialised = false; + + public KeyPairGeneratorSpi() + { + super("DSA"); + } + + public void initialize( + int strength, + SecureRandom random) + { + if (strength < 512 || strength > 4096 || ((strength < 1024) && strength % 64 != 0) || (strength >= 1024 && strength % 1024 != 0)) + { + throw new InvalidParameterException("strength must be from 512 - 4096 and a multiple of 1024 above 1024"); + } + + this.strength = strength; + this.random = random; + } + + public void initialize( + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + if (!(params instanceof DSAParameterSpec)) + { + throw new InvalidAlgorithmParameterException("parameter object not a DSAParameterSpec"); + } + DSAParameterSpec dsaParams = (DSAParameterSpec)params; + + param = new DSAKeyGenerationParameters(random, new DSAParameters(dsaParams.getP(), dsaParams.getQ(), dsaParams.getG())); + + engine.init(param); + initialised = true; + } + + public KeyPair generateKeyPair() + { + if (!initialised) + { + DSAParametersGenerator pGen = new DSAParametersGenerator(); + + pGen.init(strength, certainty, random); + param = new DSAKeyGenerationParameters(random, pGen.generateParameters()); + engine.init(param); + initialised = true; + } + + AsymmetricCipherKeyPair pair = engine.generateKeyPair(); + DSAPublicKeyParameters pub = (DSAPublicKeyParameters)pair.getPublic(); + DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters)pair.getPrivate(); + + return new KeyPair(new BCDSAPublicKey(pub), + new BCDSAPrivateKey(priv)); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PrivateKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PrivateKey.java new file mode 100644 index 00000000..2a70970d --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PrivateKey.java @@ -0,0 +1,467 @@ +package org.spongycastle.jcajce.provider.asymmetric.dstu; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.EllipticCurve; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.ua.DSTU4145NamedCurves; +import org.spongycastle.asn1.ua.UAObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X962Parameters; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.EC5Util; +import org.spongycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.spongycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl; +import org.spongycastle.jce.interfaces.ECPointEncoder; +import org.spongycastle.jce.interfaces.PKCS12BagAttributeCarrier; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECNamedCurveSpec; +import org.spongycastle.math.ec.ECCurve; + +public class BCDSTU4145PrivateKey + implements ECPrivateKey, org.spongycastle.jce.interfaces.ECPrivateKey, PKCS12BagAttributeCarrier, ECPointEncoder +{ + static final long serialVersionUID = 7245981689601667138L; + + private String algorithm = "DSTU4145"; + private boolean withCompression; + + private transient BigInteger d; + private transient ECParameterSpec ecSpec; + private transient DERBitString publicKey; + private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl(); + + protected BCDSTU4145PrivateKey() + { + } + + public BCDSTU4145PrivateKey( + ECPrivateKey key) + { + this.d = key.getS(); + this.algorithm = key.getAlgorithm(); + this.ecSpec = key.getParams(); + } + + public BCDSTU4145PrivateKey( + org.spongycastle.jce.spec.ECPrivateKeySpec spec) + { + this.d = spec.getD(); + + if (spec.getParams() != null) // can be null if implicitlyCA + { + ECCurve curve = spec.getParams().getCurve(); + EllipticCurve ellipticCurve; + + ellipticCurve = EC5Util.convertCurve(curve, spec.getParams().getSeed()); + + this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec.getParams()); + } + else + { + this.ecSpec = null; + } + } + + + public BCDSTU4145PrivateKey( + ECPrivateKeySpec spec) + { + this.d = spec.getS(); + this.ecSpec = spec.getParams(); + } + + public BCDSTU4145PrivateKey( + BCDSTU4145PrivateKey key) + { + this.d = key.d; + this.ecSpec = key.ecSpec; + this.withCompression = key.withCompression; + this.attrCarrier = key.attrCarrier; + this.publicKey = key.publicKey; + } + + public BCDSTU4145PrivateKey( + String algorithm, + ECPrivateKeyParameters params, + BCDSTU4145PublicKey pubKey, + ECParameterSpec spec) + { + ECDomainParameters dp = params.getParameters(); + + this.algorithm = algorithm; + this.d = params.getD(); + + if (spec == null) + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); + + this.ecSpec = new ECParameterSpec( + ellipticCurve, + new ECPoint( + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), + dp.getN(), + dp.getH().intValue()); + } + else + { + this.ecSpec = spec; + } + + publicKey = getPublicKeyDetails(pubKey); + } + + public BCDSTU4145PrivateKey( + String algorithm, + ECPrivateKeyParameters params, + BCDSTU4145PublicKey pubKey, + org.spongycastle.jce.spec.ECParameterSpec spec) + { + ECDomainParameters dp = params.getParameters(); + + this.algorithm = algorithm; + this.d = params.getD(); + + if (spec == null) + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); + + this.ecSpec = new ECParameterSpec( + ellipticCurve, + new ECPoint( + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), + dp.getN(), + dp.getH().intValue()); + } + else + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed()); + + this.ecSpec = new ECParameterSpec( + ellipticCurve, + new ECPoint( + spec.getG().getAffineXCoord().toBigInteger(), + spec.getG().getAffineYCoord().toBigInteger()), + spec.getN(), + spec.getH().intValue()); + } + + publicKey = getPublicKeyDetails(pubKey); + } + + public BCDSTU4145PrivateKey( + String algorithm, + ECPrivateKeyParameters params) + { + this.algorithm = algorithm; + this.d = params.getD(); + this.ecSpec = null; + } + + BCDSTU4145PrivateKey( + PrivateKeyInfo info) + throws IOException + { + populateFromPrivKeyInfo(info); + } + + private void populateFromPrivKeyInfo(PrivateKeyInfo info) + throws IOException + { + X962Parameters params = new X962Parameters((ASN1Primitive)info.getPrivateKeyAlgorithm().getParameters()); + + if (params.isNamedCurve()) + { + ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters()); + X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid); + + if (ecP == null) // DSTU Curve + { + ECDomainParameters gParam = DSTU4145NamedCurves.getByOID(oid); + EllipticCurve ellipticCurve = EC5Util.convertCurve(gParam.getCurve(), gParam.getSeed()); + + ecSpec = new ECNamedCurveSpec( + oid.getId(), + ellipticCurve, + new ECPoint( + gParam.getG().getAffineXCoord().toBigInteger(), + gParam.getG().getAffineYCoord().toBigInteger()), + gParam.getN(), + gParam.getH()); + } + else + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed()); + + ecSpec = new ECNamedCurveSpec( + ECUtil.getCurveName(oid), + ellipticCurve, + new ECPoint( + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), + ecP.getN(), + ecP.getH()); + } + } + else if (params.isImplicitlyCA()) + { + ecSpec = null; + } + else + { + X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters()); + EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed()); + + this.ecSpec = new ECParameterSpec( + ellipticCurve, + new ECPoint( + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), + ecP.getN(), + ecP.getH().intValue()); + } + + ASN1Encodable privKey = info.parsePrivateKey(); + if (privKey instanceof ASN1Integer) + { + ASN1Integer derD = ASN1Integer.getInstance(privKey); + + this.d = derD.getValue(); + } + else + { + org.spongycastle.asn1.sec.ECPrivateKey ec = org.spongycastle.asn1.sec.ECPrivateKey.getInstance(privKey); + + this.d = ec.getKey(); + this.publicKey = ec.getPublicKey(); + } + } + + public String getAlgorithm() + { + return algorithm; + } + + /** + * return the encoding format we produce in getEncoded(). + * + * @return the string "PKCS#8" + */ + public String getFormat() + { + return "PKCS#8"; + } + + /** + * Return a PKCS8 representation of the key. The sequence returned + * represents a full PrivateKeyInfo object. + * + * @return a PKCS8 representation of the key. + */ + public byte[] getEncoded() + { + X962Parameters params; + + if (ecSpec instanceof ECNamedCurveSpec) + { + ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName()); + if (curveOid == null) // guess it's the OID + { + curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName()); + } + params = new X962Parameters(curveOid); + } + else if (ecSpec == null) + { + params = new X962Parameters(DERNull.INSTANCE); + } + else + { + ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve()); + + X9ECParameters ecP = new X9ECParameters( + curve, + EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression), + ecSpec.getOrder(), + BigInteger.valueOf(ecSpec.getCofactor()), + ecSpec.getCurve().getSeed()); + + params = new X962Parameters(ecP); + } + + PrivateKeyInfo info; + org.spongycastle.asn1.sec.ECPrivateKey keyStructure; + + if (publicKey != null) + { + keyStructure = new org.spongycastle.asn1.sec.ECPrivateKey(this.getS(), publicKey, params); + } + else + { + keyStructure = new org.spongycastle.asn1.sec.ECPrivateKey(this.getS(), params); + } + + try + { + if (algorithm.equals("DSTU4145")) + { + info = new PrivateKeyInfo(new AlgorithmIdentifier(UAObjectIdentifiers.dstu4145be, params.toASN1Primitive()), keyStructure.toASN1Primitive()); + } + else + { + + info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params.toASN1Primitive()), keyStructure.toASN1Primitive()); + } + + return info.getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + return null; + } + } + + public ECParameterSpec getParams() + { + return ecSpec; + } + + public org.spongycastle.jce.spec.ECParameterSpec getParameters() + { + if (ecSpec == null) + { + return null; + } + + return EC5Util.convertSpec(ecSpec, withCompression); + } + + org.spongycastle.jce.spec.ECParameterSpec engineGetSpec() + { + if (ecSpec != null) + { + return EC5Util.convertSpec(ecSpec, withCompression); + } + + return BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + } + + public BigInteger getS() + { + return d; + } + + public BigInteger getD() + { + return d; + } + + public void setBagAttribute( + ASN1ObjectIdentifier oid, + ASN1Encodable attribute) + { + attrCarrier.setBagAttribute(oid, attribute); + } + + public ASN1Encodable getBagAttribute( + ASN1ObjectIdentifier oid) + { + return attrCarrier.getBagAttribute(oid); + } + + public Enumeration getBagAttributeKeys() + { + return attrCarrier.getBagAttributeKeys(); + } + + public void setPointFormat(String style) + { + withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style)); + } + + public boolean equals(Object o) + { + if (!(o instanceof BCDSTU4145PrivateKey)) + { + return false; + } + + BCDSTU4145PrivateKey other = (BCDSTU4145PrivateKey)o; + + return getD().equals(other.getD()) && (engineGetSpec().equals(other.engineGetSpec())); + } + + public int hashCode() + { + return getD().hashCode() ^ engineGetSpec().hashCode(); + } + + public String toString() + { + StringBuffer buf = new StringBuffer(); + String nl = System.getProperty("line.separator"); + + buf.append("EC Private Key").append(nl); + buf.append(" S: ").append(this.d.toString(16)).append(nl); + + return buf.toString(); + + } + + private DERBitString getPublicKeyDetails(BCDSTU4145PublicKey pub) + { + try + { + SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(pub.getEncoded())); + + return info.getPublicKeyData(); + } + catch (IOException e) + { // should never happen + return null; + } + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + byte[] enc = (byte[])in.readObject(); + + populateFromPrivKeyInfo(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc))); + + this.attrCarrier = new PKCS12BagAttributeCarrierImpl(); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(this.getEncoded()); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PublicKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PublicKey.java new file mode 100644 index 00000000..891c39da --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PublicKey.java @@ -0,0 +1,431 @@ +package org.spongycastle.jcajce.provider.asymmetric.dstu; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.EllipticCurve; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.ua.DSTU4145BinaryField; +import org.spongycastle.asn1.ua.DSTU4145ECBinary; +import org.spongycastle.asn1.ua.DSTU4145NamedCurves; +import org.spongycastle.asn1.ua.DSTU4145Params; +import org.spongycastle.asn1.ua.DSTU4145PointEncoder; +import org.spongycastle.asn1.ua.UAObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X962Parameters; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.EC5Util; +import org.spongycastle.jcajce.provider.asymmetric.util.KeyUtil; +import org.spongycastle.jce.interfaces.ECPointEncoder; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; +import org.spongycastle.jce.spec.ECNamedCurveSpec; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.custom.sec.SecP256K1Point; +import org.spongycastle.math.ec.custom.sec.SecP256R1Point; + +public class BCDSTU4145PublicKey + implements ECPublicKey, org.spongycastle.jce.interfaces.ECPublicKey, ECPointEncoder +{ + static final long serialVersionUID = 7026240464295649314L; + + private String algorithm = "DSTU4145"; + private boolean withCompression; + + private transient org.spongycastle.math.ec.ECPoint q; + private transient ECParameterSpec ecSpec; + private transient DSTU4145Params dstuParams; + + public BCDSTU4145PublicKey( + BCDSTU4145PublicKey key) + { + this.q = key.q; + this.ecSpec = key.ecSpec; + this.withCompression = key.withCompression; + this.dstuParams = key.dstuParams; + } + + public BCDSTU4145PublicKey( + ECPublicKeySpec spec) + { + this.ecSpec = spec.getParams(); + this.q = EC5Util.convertPoint(ecSpec, spec.getW(), false); + } + + public BCDSTU4145PublicKey( + org.spongycastle.jce.spec.ECPublicKeySpec spec) + { + this.q = spec.getQ(); + + if (spec.getParams() != null) // can be null if implictlyCa + { + ECCurve curve = spec.getParams().getCurve(); + EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getParams().getSeed()); + + this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec.getParams()); + } + else + { + if (q.getCurve() == null) + { + org.spongycastle.jce.spec.ECParameterSpec s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + q = s.getCurve().createPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger()); + } + this.ecSpec = null; + } + } + + public BCDSTU4145PublicKey( + String algorithm, + ECPublicKeyParameters params, + ECParameterSpec spec) + { + ECDomainParameters dp = params.getParameters(); + + this.algorithm = algorithm; + this.q = params.getQ(); + + if (spec == null) + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); + + this.ecSpec = createSpec(ellipticCurve, dp); + } + else + { + this.ecSpec = spec; + } + } + + public BCDSTU4145PublicKey( + String algorithm, + ECPublicKeyParameters params, + org.spongycastle.jce.spec.ECParameterSpec spec) + { + ECDomainParameters dp = params.getParameters(); + + this.algorithm = algorithm; + this.q = params.getQ(); + + if (spec == null) + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); + + this.ecSpec = createSpec(ellipticCurve, dp); + } + else + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed()); + + this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec); + } + } + + /* + * called for implicitCA + */ + public BCDSTU4145PublicKey( + String algorithm, + ECPublicKeyParameters params) + { + this.algorithm = algorithm; + this.q = params.getQ(); + this.ecSpec = null; + } + + private ECParameterSpec createSpec(EllipticCurve ellipticCurve, ECDomainParameters dp) + { + return new ECParameterSpec( + ellipticCurve, + new ECPoint( + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), + dp.getN(), + dp.getH().intValue()); + } + + public BCDSTU4145PublicKey( + ECPublicKey key) + { + this.algorithm = key.getAlgorithm(); + this.ecSpec = key.getParams(); + this.q = EC5Util.convertPoint(this.ecSpec, key.getW(), false); + } + + BCDSTU4145PublicKey( + SubjectPublicKeyInfo info) + { + populateFromPubKeyInfo(info); + } + + private void reverseBytes(byte[] bytes) + { + byte tmp; + + for (int i = 0; i < bytes.length / 2; i++) + { + tmp = bytes[i]; + bytes[i] = bytes[bytes.length - 1 - i]; + bytes[bytes.length - 1 - i] = tmp; + } + } + + private void populateFromPubKeyInfo(SubjectPublicKeyInfo info) + { + DERBitString bits = info.getPublicKeyData(); + ASN1OctetString key; + this.algorithm = "DSTU4145"; + + try + { + key = (ASN1OctetString)ASN1Primitive.fromByteArray(bits.getBytes()); + } + catch (IOException ex) + { + throw new IllegalArgumentException("error recovering public key"); + } + + byte[] keyEnc = key.getOctets(); + + if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le)) + { + reverseBytes(keyEnc); + } + + dstuParams = DSTU4145Params.getInstance((ASN1Sequence)info.getAlgorithm().getParameters()); + + //ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet())); + org.spongycastle.jce.spec.ECParameterSpec spec = null; + if (dstuParams.isNamedCurve()) + { + ASN1ObjectIdentifier curveOid = dstuParams.getNamedCurve(); + ECDomainParameters ecP = DSTU4145NamedCurves.getByOID(curveOid); + + spec = new ECNamedCurveParameterSpec(curveOid.getId(), ecP.getCurve(), ecP.getG(), ecP.getN(), ecP.getH(), ecP.getSeed()); + } + else + { + DSTU4145ECBinary binary = dstuParams.getECBinary(); + byte[] b_bytes = binary.getB(); + if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le)) + { + reverseBytes(b_bytes); + } + DSTU4145BinaryField field = binary.getField(); + ECCurve curve = new ECCurve.F2m(field.getM(), field.getK1(), field.getK2(), field.getK3(), binary.getA(), new BigInteger(1, b_bytes)); + byte[] g_bytes = binary.getG(); + if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le)) + { + reverseBytes(g_bytes); + } + spec = new org.spongycastle.jce.spec.ECParameterSpec(curve, DSTU4145PointEncoder.decodePoint(curve, g_bytes), binary.getN()); + } + + ECCurve curve = spec.getCurve(); + EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed()); + + //this.q = curve.createPoint(new BigInteger(1, x), new BigInteger(1, y), false); + this.q = DSTU4145PointEncoder.decodePoint(curve, keyEnc); + + if (dstuParams.isNamedCurve()) + { + ecSpec = new ECNamedCurveSpec( + dstuParams.getNamedCurve().getId(), + ellipticCurve, + new ECPoint( + spec.getG().getAffineXCoord().toBigInteger(), + spec.getG().getAffineYCoord().toBigInteger()), + spec.getN(), spec.getH()); + } + else + { + ecSpec = new ECParameterSpec( + ellipticCurve, + new ECPoint( + spec.getG().getAffineXCoord().toBigInteger(), + spec.getG().getAffineYCoord().toBigInteger()), + spec.getN(), spec.getH().intValue()); + } + } + + public byte[] getSbox() + { + if (null != dstuParams) + { + return dstuParams.getDKE(); + } + else + { + return DSTU4145Params.getDefaultDKE(); + } + } + + public String getAlgorithm() + { + return algorithm; + } + + public String getFormat() + { + return "X.509"; + } + + public byte[] getEncoded() + { + ASN1Encodable params; + SubjectPublicKeyInfo info; + + if (dstuParams != null) + { + params = dstuParams; + } + else + { + if (ecSpec instanceof ECNamedCurveSpec) + { + params = new DSTU4145Params(new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName())); + } + else + { // strictly speaking this may not be applicable... + ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve()); + + X9ECParameters ecP = new X9ECParameters( + curve, + EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression), + ecSpec.getOrder(), + BigInteger.valueOf(ecSpec.getCofactor()), + ecSpec.getCurve().getSeed()); + + params = new X962Parameters(ecP); + } + } + + byte[] encKey = DSTU4145PointEncoder.encodePoint(this.q); + + try + { + info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(UAObjectIdentifiers.dstu4145be, params), new DEROctetString(encKey)); + } + catch (IOException e) + { + return null; + } + + return KeyUtil.getEncodedSubjectPublicKeyInfo(info); + } + + public ECParameterSpec getParams() + { + return ecSpec; + } + + public org.spongycastle.jce.spec.ECParameterSpec getParameters() + { + if (ecSpec == null) // implictlyCA + { + return null; + } + + return EC5Util.convertSpec(ecSpec, withCompression); + } + + public ECPoint getW() + { + return new ECPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger()); + } + + public org.spongycastle.math.ec.ECPoint getQ() + { + if (ecSpec == null) + { + return q.getDetachedPoint(); + } + + return q; + } + + public org.spongycastle.math.ec.ECPoint engineGetQ() + { + return q; + } + + org.spongycastle.jce.spec.ECParameterSpec engineGetSpec() + { + if (ecSpec != null) + { + return EC5Util.convertSpec(ecSpec, withCompression); + } + + return BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + } + + public String toString() + { + StringBuffer buf = new StringBuffer(); + String nl = System.getProperty("line.separator"); + + buf.append("EC Public Key").append(nl); + buf.append(" X: ").append(this.q.getAffineXCoord().toBigInteger().toString(16)).append(nl); + buf.append(" Y: ").append(this.q.getAffineYCoord().toBigInteger().toString(16)).append(nl); + + return buf.toString(); + } + + public void setPointFormat(String style) + { + withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style)); + } + + public boolean equals(Object o) + { + if (!(o instanceof BCDSTU4145PublicKey)) + { + return false; + } + + BCDSTU4145PublicKey other = (BCDSTU4145PublicKey)o; + + return engineGetQ().equals(other.engineGetQ()) && (engineGetSpec().equals(other.engineGetSpec())); + } + + public int hashCode() + { + return engineGetQ().hashCode() ^ engineGetSpec().hashCode(); + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + byte[] enc = (byte[])in.readObject(); + + populateFromPubKeyInfo(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc))); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(this.getEncoded()); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/KeyFactorySpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/KeyFactorySpi.java new file mode 100644 index 00000000..995db98c --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/KeyFactorySpi.java @@ -0,0 +1,166 @@ +package org.spongycastle.jcajce.provider.asymmetric.dstu; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.ua.UAObjectIdentifiers; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.jcajce.provider.asymmetric.util.EC5Util; +import org.spongycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECParameterSpec; +import org.spongycastle.jce.spec.ECPrivateKeySpec; +import org.spongycastle.jce.spec.ECPublicKeySpec; + +public class KeyFactorySpi + extends BaseKeyFactorySpi +{ + public KeyFactorySpi() + { + } + + protected KeySpec engineGetKeySpec( + Key key, + Class spec) + throws InvalidKeySpecException + { + if (spec.isAssignableFrom(java.security.spec.ECPublicKeySpec.class) && key instanceof ECPublicKey) + { + ECPublicKey k = (ECPublicKey)key; + if (k.getParams() != null) + { + return new java.security.spec.ECPublicKeySpec(k.getW(), k.getParams()); + } + else + { + ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + return new java.security.spec.ECPublicKeySpec(k.getW(), EC5Util.convertSpec(EC5Util.convertCurve(implicitSpec.getCurve(), implicitSpec.getSeed()), implicitSpec)); + } + } + else if (spec.isAssignableFrom(java.security.spec.ECPrivateKeySpec.class) && key instanceof ECPrivateKey) + { + ECPrivateKey k = (ECPrivateKey)key; + + if (k.getParams() != null) + { + return new java.security.spec.ECPrivateKeySpec(k.getS(), k.getParams()); + } + else + { + ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + return new java.security.spec.ECPrivateKeySpec(k.getS(), EC5Util.convertSpec(EC5Util.convertCurve(implicitSpec.getCurve(), implicitSpec.getSeed()), implicitSpec)); + } + } + else if (spec.isAssignableFrom(org.spongycastle.jce.spec.ECPublicKeySpec.class) && key instanceof ECPublicKey) + { + ECPublicKey k = (ECPublicKey)key; + if (k.getParams() != null) + { + return new org.spongycastle.jce.spec.ECPublicKeySpec(EC5Util.convertPoint(k.getParams(), k.getW(), false), EC5Util.convertSpec(k.getParams(), false)); + } + else + { + ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + return new org.spongycastle.jce.spec.ECPublicKeySpec(EC5Util.convertPoint(k.getParams(), k.getW(), false), implicitSpec); + } + } + else if (spec.isAssignableFrom(org.spongycastle.jce.spec.ECPrivateKeySpec.class) && key instanceof ECPrivateKey) + { + ECPrivateKey k = (ECPrivateKey)key; + + if (k.getParams() != null) + { + return new org.spongycastle.jce.spec.ECPrivateKeySpec(k.getS(), EC5Util.convertSpec(k.getParams(), false)); + } + else + { + ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + return new org.spongycastle.jce.spec.ECPrivateKeySpec(k.getS(), implicitSpec); + } + } + + return super.engineGetKeySpec(key, spec); + } + + protected Key engineTranslateKey( + Key key) + throws InvalidKeyException + { + throw new InvalidKeyException("key type unknown"); + } + + protected PrivateKey engineGeneratePrivate( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof ECPrivateKeySpec) + { + return new BCDSTU4145PrivateKey((ECPrivateKeySpec)keySpec); + } + else if (keySpec instanceof java.security.spec.ECPrivateKeySpec) + { + return new BCDSTU4145PrivateKey((java.security.spec.ECPrivateKeySpec)keySpec); + } + + return super.engineGeneratePrivate(keySpec); + } + + protected PublicKey engineGeneratePublic( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof ECPublicKeySpec) + { + return new BCDSTU4145PublicKey((ECPublicKeySpec)keySpec); + } + else if (keySpec instanceof java.security.spec.ECPublicKeySpec) + { + return new BCDSTU4145PublicKey((java.security.spec.ECPublicKeySpec)keySpec); + } + + return super.engineGeneratePublic(keySpec); + } + + public PrivateKey generatePrivate(PrivateKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getPrivateKeyAlgorithm().getAlgorithm(); + + if (algOid.equals(UAObjectIdentifiers.dstu4145le) || algOid.equals(UAObjectIdentifiers.dstu4145be)) + { + return new BCDSTU4145PrivateKey(keyInfo); + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } + + public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getAlgorithm().getAlgorithm(); + + if (algOid.equals(UAObjectIdentifiers.dstu4145le) || algOid.equals(UAObjectIdentifiers.dstu4145be)) + { + return new BCDSTU4145PublicKey(keyInfo); + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/KeyPairGeneratorSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/KeyPairGeneratorSpi.java new file mode 100644 index 00000000..4c9cbaa6 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/KeyPairGeneratorSpi.java @@ -0,0 +1,188 @@ +package org.spongycastle.jcajce.provider.asymmetric.dstu; + +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidParameterException; +import java.security.KeyPair; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECGenParameterSpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ua.DSTU4145NamedCurves; +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.generators.DSTU4145KeyPairGenerator; +import org.spongycastle.crypto.generators.ECKeyPairGenerator; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECKeyGenerationParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.EC5Util; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECNamedCurveGenParameterSpec; +import org.spongycastle.jce.spec.ECNamedCurveSpec; +import org.spongycastle.jce.spec.ECParameterSpec; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECPoint; + +public class KeyPairGeneratorSpi + extends java.security.KeyPairGenerator +{ + Object ecParams = null; + ECKeyPairGenerator engine = new DSTU4145KeyPairGenerator(); + + String algorithm = "DSTU4145"; + ECKeyGenerationParameters param; + //int strength = 239; + SecureRandom random = null; + boolean initialised = false; + + public KeyPairGeneratorSpi() + { + super("DSTU4145"); + } + + public void initialize( + int strength, + SecureRandom random) + { + this.random = random; + + if (ecParams != null) + { + try + { + initialize((ECGenParameterSpec)ecParams, random); + } + catch (InvalidAlgorithmParameterException e) + { + throw new InvalidParameterException("key size not configurable."); + } + } + else + { + throw new InvalidParameterException("unknown key size."); + } + } + + public void initialize( + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + if (params instanceof ECParameterSpec) + { + ECParameterSpec p = (ECParameterSpec)params; + this.ecParams = params; + + param = new ECKeyGenerationParameters(new ECDomainParameters(p.getCurve(), p.getG(), p.getN()), random); + + engine.init(param); + initialised = true; + } + else if (params instanceof java.security.spec.ECParameterSpec) + { + java.security.spec.ECParameterSpec p = (java.security.spec.ECParameterSpec)params; + this.ecParams = params; + + ECCurve curve = EC5Util.convertCurve(p.getCurve()); + ECPoint g = EC5Util.convertPoint(curve, p.getGenerator(), false); + + param = new ECKeyGenerationParameters(new ECDomainParameters(curve, g, p.getOrder(), BigInteger.valueOf(p.getCofactor())), random); + + engine.init(param); + initialised = true; + } + else if (params instanceof ECGenParameterSpec || params instanceof ECNamedCurveGenParameterSpec) + { + String curveName; + + if (params instanceof ECGenParameterSpec) + { + curveName = ((ECGenParameterSpec)params).getName(); + } + else + { + curveName = ((ECNamedCurveGenParameterSpec)params).getName(); + } + + //ECDomainParameters ecP = ECGOST3410NamedCurves.getByName(curveName); + ECDomainParameters ecP = DSTU4145NamedCurves.getByOID(new ASN1ObjectIdentifier(curveName)); + if (ecP == null) + { + throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName); + } + + this.ecParams = new ECNamedCurveSpec( + curveName, + ecP.getCurve(), + ecP.getG(), + ecP.getN(), + ecP.getH(), + ecP.getSeed()); + + java.security.spec.ECParameterSpec p = (java.security.spec.ECParameterSpec)ecParams; + + ECCurve curve = EC5Util.convertCurve(p.getCurve()); + ECPoint g = EC5Util.convertPoint(curve, p.getGenerator(), false); + + param = new ECKeyGenerationParameters(new ECDomainParameters(curve, g, p.getOrder(), BigInteger.valueOf(p.getCofactor())), random); + + engine.init(param); + initialised = true; + } + else if (params == null && BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa() != null) + { + ECParameterSpec p = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + this.ecParams = params; + + param = new ECKeyGenerationParameters(new ECDomainParameters(p.getCurve(), p.getG(), p.getN()), random); + + engine.init(param); + initialised = true; + } + else if (params == null && BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa() == null) + { + throw new InvalidAlgorithmParameterException("null parameter passed but no implicitCA set"); + } + else + { + throw new InvalidAlgorithmParameterException("parameter object not a ECParameterSpec: " + params.getClass().getName()); + } + } + + public KeyPair generateKeyPair() + { + if (!initialised) + { + throw new IllegalStateException("DSTU Key Pair Generator not initialised"); + } + + AsymmetricCipherKeyPair pair = engine.generateKeyPair(); + ECPublicKeyParameters pub = (ECPublicKeyParameters)pair.getPublic(); + ECPrivateKeyParameters priv = (ECPrivateKeyParameters)pair.getPrivate(); + + if (ecParams instanceof ECParameterSpec) + { + ECParameterSpec p = (ECParameterSpec)ecParams; + + BCDSTU4145PublicKey pubKey = new BCDSTU4145PublicKey(algorithm, pub, p); + return new KeyPair(pubKey, + new BCDSTU4145PrivateKey(algorithm, priv, pubKey, p)); + } + else if (ecParams == null) + { + return new KeyPair(new BCDSTU4145PublicKey(algorithm, pub), + new BCDSTU4145PrivateKey(algorithm, priv)); + } + else + { + java.security.spec.ECParameterSpec p = (java.security.spec.ECParameterSpec)ecParams; + + BCDSTU4145PublicKey pubKey = new BCDSTU4145PublicKey(algorithm, pub, p); + + return new KeyPair(pubKey, new BCDSTU4145PrivateKey(algorithm, priv, pubKey, p)); + } + } +} + diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/SignatureSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/SignatureSpi.java new file mode 100644 index 00000000..a6256281 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/SignatureSpi.java @@ -0,0 +1,221 @@ +package org.spongycastle.jcajce.provider.asymmetric.dstu; + +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.spec.AlgorithmParameterSpec; + +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x509.X509ObjectIdentifiers; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DSA; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.GOST3411Digest; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.crypto.signers.DSTU4145Signer; +import org.spongycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.spongycastle.jce.interfaces.ECKey; +import org.spongycastle.jce.interfaces.ECPublicKey; +import org.spongycastle.jce.provider.BouncyCastleProvider; + +public class SignatureSpi + extends java.security.SignatureSpi + implements PKCSObjectIdentifiers, X509ObjectIdentifiers +{ + private Digest digest; + private DSA signer; + + private static byte[] DEFAULT_SBOX = { + 0xa, 0x9, 0xd, 0x6, 0xe, 0xb, 0x4, 0x5, 0xf, 0x1, 0x3, 0xc, 0x7, 0x0, 0x8, 0x2, + 0x8, 0x0, 0xc, 0x4, 0x9, 0x6, 0x7, 0xb, 0x2, 0x3, 0x1, 0xf, 0x5, 0xe, 0xa, 0xd, + 0xf, 0x6, 0x5, 0x8, 0xe, 0xb, 0xa, 0x4, 0xc, 0x0, 0x3, 0x7, 0x2, 0x9, 0x1, 0xd, + 0x3, 0x8, 0xd, 0x9, 0x6, 0xb, 0xf, 0x0, 0x2, 0x5, 0xc, 0xa, 0x4, 0xe, 0x1, 0x7, + 0xf, 0x8, 0xe, 0x9, 0x7, 0x2, 0x0, 0xd, 0xc, 0x6, 0x1, 0x5, 0xb, 0x4, 0x3, 0xa, + 0x2, 0x8, 0x9, 0x7, 0x5, 0xf, 0x0, 0xb, 0xc, 0x1, 0xd, 0xe, 0xa, 0x3, 0x6, 0x4, + 0x3, 0x8, 0xb, 0x5, 0x6, 0x4, 0xe, 0xa, 0x2, 0xc, 0x1, 0x7, 0x9, 0xf, 0xd, 0x0, + 0x1, 0x2, 0x3, 0xe, 0x6, 0xd, 0xb, 0x8, 0xf, 0xa, 0xc, 0x5, 0x7, 0x9, 0x0, 0x4 + }; + + public SignatureSpi() + { + //TODO: Add default ua s-box + //this.digest = new GOST3411Digest(DEFAULT_SBOX); + this.signer = new DSTU4145Signer(); + } + + protected void engineInitVerify( + PublicKey publicKey) + throws InvalidKeyException + { + CipherParameters param; + + if (publicKey instanceof ECPublicKey) + { + param = ECUtil.generatePublicKeyParameter(publicKey); + } + else + { + try + { + byte[] bytes = publicKey.getEncoded(); + + publicKey = BouncyCastleProvider.getPublicKey(SubjectPublicKeyInfo.getInstance(bytes)); + + if (publicKey instanceof ECPublicKey) + { + param = ECUtil.generatePublicKeyParameter(publicKey); + } + else + { + throw new InvalidKeyException("can't recognise key type in DSA based signer"); + } + } + catch (Exception e) + { + throw new InvalidKeyException("can't recognise key type in DSA based signer"); + } + } + + digest = new GOST3411Digest(expandSbox(((BCDSTU4145PublicKey)publicKey).getSbox())); + signer.init(false, param); + } + + byte[] expandSbox(byte[] compressed) + { + byte[] expanded = new byte[128]; + + for (int i = 0; i < compressed.length; i++) + { + expanded[i * 2] = (byte)((compressed[i] >> 4) & 0xf); + expanded[i * 2 + 1] = (byte)(compressed[i] & 0xf); + } + return expanded; + } + + protected void engineInitSign( + PrivateKey privateKey) + throws InvalidKeyException + { + CipherParameters param = null; + + if (privateKey instanceof ECKey) + { + param = ECUtil.generatePrivateKeyParameter(privateKey); + } + + digest = new GOST3411Digest(DEFAULT_SBOX); + + if (appRandom != null) + { + signer.init(true, new ParametersWithRandom(param, appRandom)); + } + else + { + signer.init(true, param); + } + } + + protected void engineUpdate( + byte b) + throws SignatureException + { + digest.update(b); + } + + protected void engineUpdate( + byte[] b, + int off, + int len) + throws SignatureException + { + digest.update(b, off, len); + } + + protected byte[] engineSign() + throws SignatureException + { + byte[] hash = new byte[digest.getDigestSize()]; + + digest.doFinal(hash, 0); + + try + { + BigInteger[] sig = signer.generateSignature(hash); + byte[] r = sig[0].toByteArray(); + byte[] s = sig[1].toByteArray(); + + byte[] sigBytes = new byte[(r.length > s.length ? r.length * 2 : s.length * 2)]; + System.arraycopy(s, 0, sigBytes, (sigBytes.length / 2) - s.length, s.length); + System.arraycopy(r, 0, sigBytes, sigBytes.length - r.length, r.length); + + return new DEROctetString(sigBytes).getEncoded(); + } + catch (Exception e) + { + throw new SignatureException(e.toString()); + } + } + + protected boolean engineVerify( + byte[] sigBytes) + throws SignatureException + { + byte[] hash = new byte[digest.getDigestSize()]; + + digest.doFinal(hash, 0); + + BigInteger[] sig; + + try + { + byte[] bytes = ((ASN1OctetString)ASN1OctetString.fromByteArray(sigBytes)).getOctets(); + + byte[] r = new byte[bytes.length / 2]; + byte[] s = new byte[bytes.length / 2]; + + System.arraycopy(bytes, 0, s, 0, bytes.length / 2); + + System.arraycopy(bytes, bytes.length / 2, r, 0, bytes.length / 2); + + sig = new BigInteger[2]; + sig[0] = new BigInteger(1, r); + sig[1] = new BigInteger(1, s); + } + catch (Exception e) + { + throw new SignatureException("error decoding signature bytes."); + } + + return signer.verifySignature(hash, sig[0], sig[1]); + } + + protected void engineSetParameter( + AlgorithmParameterSpec params) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated replaced with <a href = "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)"> + */ + protected void engineSetParameter( + String param, + Object value) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated + */ + protected Object engineGetParameter( + String param) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/SignatureSpiLe.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/SignatureSpiLe.java new file mode 100644 index 00000000..1b83d55e --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/SignatureSpiLe.java @@ -0,0 +1,69 @@ +package org.spongycastle.jcajce.provider.asymmetric.dstu; + +import java.io.IOException; +import java.security.SignatureException; + +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.DEROctetString; + +public class SignatureSpiLe + extends SignatureSpi +{ + void reverseBytes(byte[] bytes) + { + byte tmp; + + for (int i = 0; i < bytes.length / 2; i++) + { + tmp = bytes[i]; + bytes[i] = bytes[bytes.length - 1 - i]; + bytes[bytes.length - 1 - i] = tmp; + } + } + + protected byte[] engineSign() + throws SignatureException + { + byte[] signature = ASN1OctetString.getInstance(super.engineSign()).getOctets(); + reverseBytes(signature); + try + { + return (new DEROctetString(signature)).getEncoded(); + } + catch (Exception e) + { + throw new SignatureException(e.toString()); + } + } + + protected boolean engineVerify( + byte[] sigBytes) + throws SignatureException + { + byte[] bytes = null; + + try + { + bytes = ((ASN1OctetString)ASN1OctetString.fromByteArray(sigBytes)).getOctets(); + } + catch (IOException e) + { + throw new SignatureException("error decoding signature bytes."); + } + + reverseBytes(bytes); + + try + { + return super.engineVerify((new DEROctetString(bytes)).getEncoded()); + } + catch (SignatureException e) + { + throw e; + } + catch (Exception e) + { + throw new SignatureException(e.toString()); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java new file mode 100644 index 00000000..451b4b68 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java @@ -0,0 +1,462 @@ +package org.spongycastle.jcajce.provider.asymmetric.ec; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.EllipticCurve; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X962Parameters; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.EC5Util; +import org.spongycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.spongycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl; +import org.spongycastle.jcajce.provider.config.ProviderConfiguration; +import org.spongycastle.jce.interfaces.ECPointEncoder; +import org.spongycastle.jce.interfaces.PKCS12BagAttributeCarrier; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECNamedCurveSpec; +import org.spongycastle.math.ec.ECCurve; + +public class BCECPrivateKey + implements ECPrivateKey, org.spongycastle.jce.interfaces.ECPrivateKey, PKCS12BagAttributeCarrier, ECPointEncoder +{ + static final long serialVersionUID = 994553197664784084L; + + private String algorithm = "EC"; + private boolean withCompression; + + private transient BigInteger d; + private transient ECParameterSpec ecSpec; + private transient ProviderConfiguration configuration; + private transient DERBitString publicKey; + + private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl(); + + protected BCECPrivateKey() + { + } + + public BCECPrivateKey( + ECPrivateKey key, + ProviderConfiguration configuration) + { + this.d = key.getS(); + this.algorithm = key.getAlgorithm(); + this.ecSpec = key.getParams(); + this.configuration = configuration; + } + + public BCECPrivateKey( + String algorithm, + org.spongycastle.jce.spec.ECPrivateKeySpec spec, + ProviderConfiguration configuration) + { + this.algorithm = algorithm; + this.d = spec.getD(); + + if (spec.getParams() != null) // can be null if implicitlyCA + { + ECCurve curve = spec.getParams().getCurve(); + EllipticCurve ellipticCurve; + + ellipticCurve = EC5Util.convertCurve(curve, spec.getParams().getSeed()); + + this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec.getParams()); + } + else + { + this.ecSpec = null; + } + + this.configuration = configuration; + } + + + public BCECPrivateKey( + String algorithm, + ECPrivateKeySpec spec, + ProviderConfiguration configuration) + { + this.algorithm = algorithm; + this.d = spec.getS(); + this.ecSpec = spec.getParams(); + this.configuration = configuration; + } + + public BCECPrivateKey( + String algorithm, + BCECPrivateKey key) + { + this.algorithm = algorithm; + this.d = key.d; + this.ecSpec = key.ecSpec; + this.withCompression = key.withCompression; + this.attrCarrier = key.attrCarrier; + this.publicKey = key.publicKey; + this.configuration = key.configuration; + } + + public BCECPrivateKey( + String algorithm, + ECPrivateKeyParameters params, + BCECPublicKey pubKey, + ECParameterSpec spec, + ProviderConfiguration configuration) + { + ECDomainParameters dp = params.getParameters(); + + this.algorithm = algorithm; + this.d = params.getD(); + this.configuration = configuration; + + if (spec == null) + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); + + this.ecSpec = new ECParameterSpec( + ellipticCurve, + new ECPoint( + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), + dp.getN(), + dp.getH().intValue()); + } + else + { + this.ecSpec = spec; + } + + publicKey = getPublicKeyDetails(pubKey); + } + + public BCECPrivateKey( + String algorithm, + ECPrivateKeyParameters params, + BCECPublicKey pubKey, + org.spongycastle.jce.spec.ECParameterSpec spec, + ProviderConfiguration configuration) + { + ECDomainParameters dp = params.getParameters(); + + this.algorithm = algorithm; + this.d = params.getD(); + this.configuration = configuration; + + if (spec == null) + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); + + this.ecSpec = new ECParameterSpec( + ellipticCurve, + new ECPoint( + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), + dp.getN(), + dp.getH().intValue()); + } + else + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed()); + + this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec); + } + + publicKey = getPublicKeyDetails(pubKey); + } + + public BCECPrivateKey( + String algorithm, + ECPrivateKeyParameters params, + ProviderConfiguration configuration) + { + this.algorithm = algorithm; + this.d = params.getD(); + this.ecSpec = null; + this.configuration = configuration; + } + + BCECPrivateKey( + String algorithm, + PrivateKeyInfo info, + ProviderConfiguration configuration) + throws IOException + { + this.algorithm = algorithm; + this.configuration = configuration; + populateFromPrivKeyInfo(info); + } + + private void populateFromPrivKeyInfo(PrivateKeyInfo info) + throws IOException + { + X962Parameters params = X962Parameters.getInstance(info.getPrivateKeyAlgorithm().getParameters()); + + if (params.isNamedCurve()) + { + ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters()); + X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid); + EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed()); + + ecSpec = new ECNamedCurveSpec( + ECUtil.getCurveName(oid), + ellipticCurve, + new ECPoint( + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), + ecP.getN(), + ecP.getH()); + } + else if (params.isImplicitlyCA()) + { + ecSpec = null; + } + else + { + X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters()); + EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed()); + + this.ecSpec = new ECParameterSpec( + ellipticCurve, + new ECPoint( + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), + ecP.getN(), + ecP.getH().intValue()); + } + + ASN1Encodable privKey = info.parsePrivateKey(); + if (privKey instanceof ASN1Integer) + { + ASN1Integer derD = ASN1Integer.getInstance(privKey); + + this.d = derD.getValue(); + } + else + { + org.spongycastle.asn1.sec.ECPrivateKey ec = org.spongycastle.asn1.sec.ECPrivateKey.getInstance(privKey); + + this.d = ec.getKey(); + this.publicKey = ec.getPublicKey(); + } + } + + public String getAlgorithm() + { + return algorithm; + } + + /** + * return the encoding format we produce in getEncoded(). + * + * @return the string "PKCS#8" + */ + public String getFormat() + { + return "PKCS#8"; + } + + /** + * Return a PKCS8 representation of the key. The sequence returned + * represents a full PrivateKeyInfo object. + * + * @return a PKCS8 representation of the key. + */ + public byte[] getEncoded() + { + X962Parameters params; + + if (ecSpec instanceof ECNamedCurveSpec) + { + ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName()); + if (curveOid == null) // guess it's the OID + { + curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName()); + } + + params = new X962Parameters(curveOid); + } + else if (ecSpec == null) + { + params = new X962Parameters(DERNull.INSTANCE); + } + else + { + ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve()); + + X9ECParameters ecP = new X9ECParameters( + curve, + EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression), + ecSpec.getOrder(), + BigInteger.valueOf(ecSpec.getCofactor()), + ecSpec.getCurve().getSeed()); + + params = new X962Parameters(ecP); + } + + PrivateKeyInfo info; + org.spongycastle.asn1.sec.ECPrivateKey keyStructure; + + if (publicKey != null) + { + keyStructure = new org.spongycastle.asn1.sec.ECPrivateKey(this.getS(), publicKey, params); + } + else + { + keyStructure = new org.spongycastle.asn1.sec.ECPrivateKey(this.getS(), params); + } + + try + { + info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), keyStructure); + + return info.getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + return null; + } + } + + public ECParameterSpec getParams() + { + return ecSpec; + } + + public org.spongycastle.jce.spec.ECParameterSpec getParameters() + { + if (ecSpec == null) + { + return null; + } + + return EC5Util.convertSpec(ecSpec, withCompression); + } + + org.spongycastle.jce.spec.ECParameterSpec engineGetSpec() + { + if (ecSpec != null) + { + return EC5Util.convertSpec(ecSpec, withCompression); + } + + return configuration.getEcImplicitlyCa(); + } + + public BigInteger getS() + { + return d; + } + + public BigInteger getD() + { + return d; + } + + public void setBagAttribute( + ASN1ObjectIdentifier oid, + ASN1Encodable attribute) + { + attrCarrier.setBagAttribute(oid, attribute); + } + + public ASN1Encodable getBagAttribute( + ASN1ObjectIdentifier oid) + { + return attrCarrier.getBagAttribute(oid); + } + + public Enumeration getBagAttributeKeys() + { + return attrCarrier.getBagAttributeKeys(); + } + + public void setPointFormat(String style) + { + withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style)); + } + + public boolean equals(Object o) + { + if (!(o instanceof BCECPrivateKey)) + { + return false; + } + + BCECPrivateKey other = (BCECPrivateKey)o; + + return getD().equals(other.getD()) && (engineGetSpec().equals(other.engineGetSpec())); + } + + public int hashCode() + { + return getD().hashCode() ^ engineGetSpec().hashCode(); + } + + public String toString() + { + StringBuffer buf = new StringBuffer(); + String nl = System.getProperty("line.separator"); + + buf.append("EC Private Key").append(nl); + buf.append(" S: ").append(this.d.toString(16)).append(nl); + + return buf.toString(); + + } + + private DERBitString getPublicKeyDetails(BCECPublicKey pub) + { + try + { + SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(pub.getEncoded())); + + return info.getPublicKeyData(); + } + catch (IOException e) + { // should never happen + return null; + } + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + byte[] enc = (byte[])in.readObject(); + + populateFromPrivKeyInfo(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc))); + + this.configuration = BouncyCastleProvider.CONFIGURATION; + this.attrCarrier = new PKCS12BagAttributeCarrierImpl(); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(this.getEncoded()); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java new file mode 100644 index 00000000..482776df --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java @@ -0,0 +1,454 @@ +package org.spongycastle.jcajce.provider.asymmetric.ec; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.EllipticCurve; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X962Parameters; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.asn1.x9.X9ECPoint; +import org.spongycastle.asn1.x9.X9IntegerConverter; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.EC5Util; +import org.spongycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.spongycastle.jcajce.provider.asymmetric.util.KeyUtil; +import org.spongycastle.jcajce.provider.config.ProviderConfiguration; +import org.spongycastle.jce.interfaces.ECPointEncoder; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECNamedCurveSpec; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.custom.sec.SecP256K1Point; +import org.spongycastle.math.ec.custom.sec.SecP256R1Point; + +public class BCECPublicKey + implements ECPublicKey, org.spongycastle.jce.interfaces.ECPublicKey, ECPointEncoder +{ + static final long serialVersionUID = 2422789860422731812L; + + private String algorithm = "EC"; + private boolean withCompression; + + private transient org.spongycastle.math.ec.ECPoint q; + private transient ECParameterSpec ecSpec; + private transient ProviderConfiguration configuration; + + public BCECPublicKey( + String algorithm, + BCECPublicKey key) + { + this.algorithm = algorithm; + this.q = key.q; + this.ecSpec = key.ecSpec; + this.withCompression = key.withCompression; + this.configuration = key.configuration; + } + + public BCECPublicKey( + String algorithm, + ECPublicKeySpec spec, + ProviderConfiguration configuration) + { + this.algorithm = algorithm; + this.ecSpec = spec.getParams(); + this.q = EC5Util.convertPoint(ecSpec, spec.getW(), false); + this.configuration = configuration; + } + + public BCECPublicKey( + String algorithm, + org.spongycastle.jce.spec.ECPublicKeySpec spec, + ProviderConfiguration configuration) + { + this.algorithm = algorithm; + this.q = spec.getQ(); + + if (spec.getParams() != null) // can be null if implictlyCa + { + ECCurve curve = spec.getParams().getCurve(); + EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getParams().getSeed()); + + // this may seem a little long-winded but it's how we pick up the custom curve. + this.q = EC5Util.convertCurve(ellipticCurve).createPoint(spec.getQ().getAffineXCoord().toBigInteger(), spec.getQ().getAffineYCoord().toBigInteger()); + this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec.getParams()); + } + else + { + if (q.getCurve() == null) + { + org.spongycastle.jce.spec.ECParameterSpec s = configuration.getEcImplicitlyCa(); + + q = s.getCurve().createPoint(q.getXCoord().toBigInteger(), q.getYCoord().toBigInteger(), false); + } + this.ecSpec = null; + } + + this.configuration = configuration; + } + + public BCECPublicKey( + String algorithm, + ECPublicKeyParameters params, + ECParameterSpec spec, + ProviderConfiguration configuration) + { + ECDomainParameters dp = params.getParameters(); + + this.algorithm = algorithm; + this.q = params.getQ(); + + if (spec == null) + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); + + this.ecSpec = createSpec(ellipticCurve, dp); + } + else + { + this.ecSpec = spec; + } + + this.configuration = configuration; + } + + public BCECPublicKey( + String algorithm, + ECPublicKeyParameters params, + org.spongycastle.jce.spec.ECParameterSpec spec, + ProviderConfiguration configuration) + { + ECDomainParameters dp = params.getParameters(); + + this.algorithm = algorithm; + + if (spec == null) + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); + + this.ecSpec = createSpec(ellipticCurve, dp); + } + else + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed()); + + this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec); + } + + this.q = EC5Util.convertCurve(ecSpec.getCurve()).createPoint(params.getQ().getAffineXCoord().toBigInteger(), params.getQ().getAffineYCoord().toBigInteger()); + + this.configuration = configuration; + } + + /* + * called for implicitCA + */ + public BCECPublicKey( + String algorithm, + ECPublicKeyParameters params, + ProviderConfiguration configuration) + { + this.algorithm = algorithm; + this.q = params.getQ(); + this.ecSpec = null; + this.configuration = configuration; + } + + public BCECPublicKey( + ECPublicKey key, + ProviderConfiguration configuration) + { + this.algorithm = key.getAlgorithm(); + this.ecSpec = key.getParams(); + this.q = EC5Util.convertPoint(this.ecSpec, key.getW(), false); + } + + BCECPublicKey( + String algorithm, + SubjectPublicKeyInfo info, + ProviderConfiguration configuration) + { + this.algorithm = algorithm; + this.configuration = configuration; + populateFromPubKeyInfo(info); + } + + private ECParameterSpec createSpec(EllipticCurve ellipticCurve, ECDomainParameters dp) + { + return new ECParameterSpec( + ellipticCurve, + new ECPoint( + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), + dp.getN(), + dp.getH().intValue()); + } + + private void populateFromPubKeyInfo(SubjectPublicKeyInfo info) + { + X962Parameters params = new X962Parameters((ASN1Primitive)info.getAlgorithm().getParameters()); + ECCurve curve; + EllipticCurve ellipticCurve; + + if (params.isNamedCurve()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters(); + X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid); + + curve = ecP.getCurve(); + ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed()); + + ecSpec = new ECNamedCurveSpec( + ECUtil.getCurveName(oid), + ellipticCurve, + new ECPoint( + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), + ecP.getN(), + ecP.getH()); + } + else if (params.isImplicitlyCA()) + { + ecSpec = null; + curve = configuration.getEcImplicitlyCa().getCurve(); + } + else + { + X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters()); + + curve = ecP.getCurve(); + ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed()); + + this.ecSpec = new ECParameterSpec( + ellipticCurve, + new ECPoint( + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), + ecP.getN(), + ecP.getH().intValue()); + } + + DERBitString bits = info.getPublicKeyData(); + byte[] data = bits.getBytes(); + ASN1OctetString key = new DEROctetString(data); + + // + // extra octet string - one of our old certs... + // + if (data[0] == 0x04 && data[1] == data.length - 2 + && (data[2] == 0x02 || data[2] == 0x03)) + { + int qLength = new X9IntegerConverter().getByteLength(curve); + + if (qLength >= data.length - 3) + { + try + { + key = (ASN1OctetString) ASN1Primitive.fromByteArray(data); + } + catch (IOException ex) + { + throw new IllegalArgumentException("error recovering public key"); + } + } + } + X9ECPoint derQ = new X9ECPoint(curve, key); + + this.q = derQ.getPoint(); + } + + public String getAlgorithm() + { + return algorithm; + } + + public String getFormat() + { + return "X.509"; + } + + public byte[] getEncoded() + { + ASN1Encodable params; + SubjectPublicKeyInfo info; + + if (ecSpec instanceof ECNamedCurveSpec) + { + ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName()); + if (curveOid == null) + { + curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName()); + } + params = new X962Parameters(curveOid); + } + else if (ecSpec == null) + { + params = new X962Parameters(DERNull.INSTANCE); + } + else + { + ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve()); + + X9ECParameters ecP = new X9ECParameters( + curve, + EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression), + ecSpec.getOrder(), + BigInteger.valueOf(ecSpec.getCofactor()), + ecSpec.getCurve().getSeed()); + + params = new X962Parameters(ecP); + } + + ECCurve curve = this.engineGetQ().getCurve(); + ASN1OctetString p; + + // stored curve is null if ImplicitlyCa + if (ecSpec == null) + { + p = (ASN1OctetString) + new X9ECPoint(curve.createPoint(this.getQ().getXCoord().toBigInteger(), this.getQ().getYCoord().toBigInteger(), withCompression)).toASN1Primitive(); + } + else + { + p = (ASN1OctetString) + new X9ECPoint(curve.createPoint(this.getQ().getAffineXCoord().toBigInteger(), this.getQ().getAffineYCoord().toBigInteger(), withCompression)).toASN1Primitive(); + } + + info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets()); + + return KeyUtil.getEncodedSubjectPublicKeyInfo(info); + } + + private void extractBytes(byte[] encKey, int offSet, BigInteger bI) + { + byte[] val = bI.toByteArray(); + if (val.length < 32) + { + byte[] tmp = new byte[32]; + System.arraycopy(val, 0, tmp, tmp.length - val.length, val.length); + val = tmp; + } + + for (int i = 0; i != 32; i++) + { + encKey[offSet + i] = val[val.length - 1 - i]; + } + } + + public ECParameterSpec getParams() + { + return ecSpec; + } + + public org.spongycastle.jce.spec.ECParameterSpec getParameters() + { + if (ecSpec == null) // implictlyCA + { + return null; + } + + return EC5Util.convertSpec(ecSpec, withCompression); + } + + public ECPoint getW() + { + return new ECPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger()); + } + + public org.spongycastle.math.ec.ECPoint getQ() + { + if (ecSpec == null) + { + return q.getDetachedPoint(); + } + + return q; + } + + public org.spongycastle.math.ec.ECPoint engineGetQ() + { + return q; + } + + org.spongycastle.jce.spec.ECParameterSpec engineGetSpec() + { + if (ecSpec != null) + { + return EC5Util.convertSpec(ecSpec, withCompression); + } + + return configuration.getEcImplicitlyCa(); + } + + public String toString() + { + StringBuffer buf = new StringBuffer(); + String nl = System.getProperty("line.separator"); + + buf.append("EC Public Key").append(nl); + buf.append(" X: ").append(this.q.getAffineXCoord().toBigInteger().toString(16)).append(nl); + buf.append(" Y: ").append(this.q.getAffineYCoord().toBigInteger().toString(16)).append(nl); + + return buf.toString(); + + } + + public void setPointFormat(String style) + { + withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style)); + } + + public boolean equals(Object o) + { + if (!(o instanceof BCECPublicKey)) + { + return false; + } + + BCECPublicKey other = (BCECPublicKey)o; + + return engineGetQ().equals(other.engineGetQ()) && (engineGetSpec().equals(other.engineGetSpec())); + } + + public int hashCode() + { + return engineGetQ().hashCode() ^ engineGetSpec().hashCode(); + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + byte[] enc = (byte[])in.readObject(); + + populateFromPubKeyInfo(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc))); + + this.configuration = BouncyCastleProvider.CONFIGURATION; + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(this.getEncoded()); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/IESCipher.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/IESCipher.java new file mode 100644 index 00000000..69b61495 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/IESCipher.java @@ -0,0 +1,552 @@ +package org.spongycastle.jcajce.provider.asymmetric.ec; + +import java.io.ByteArrayOutputStream; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.CipherSpi; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.KeyEncoder; +import org.spongycastle.crypto.agreement.ECDHBasicAgreement; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.engines.AESEngine; +import org.spongycastle.crypto.engines.DESedeEngine; +import org.spongycastle.crypto.engines.IESEngine; +import org.spongycastle.crypto.generators.ECKeyPairGenerator; +import org.spongycastle.crypto.generators.EphemeralKeyPairGenerator; +import org.spongycastle.crypto.generators.KDF2BytesGenerator; +import org.spongycastle.crypto.macs.HMac; +import org.spongycastle.crypto.modes.CBCBlockCipher; +import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECKeyGenerationParameters; +import org.spongycastle.crypto.params.ECKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.crypto.params.IESWithCipherParameters; +import org.spongycastle.crypto.params.ParametersWithIV; +import org.spongycastle.crypto.parsers.ECIESPublicKeyParser; +import org.spongycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.spongycastle.jcajce.provider.asymmetric.util.IESUtil; +import org.spongycastle.jce.interfaces.ECKey; +import org.spongycastle.jce.interfaces.ECPrivateKey; +import org.spongycastle.jce.interfaces.ECPublicKey; +import org.spongycastle.jce.interfaces.IESKey; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.IESParameterSpec; +import org.spongycastle.util.Strings; + + +public class IESCipher + extends CipherSpi +{ + private int ivLength; + private IESEngine engine; + private int state = -1; + private ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + private AlgorithmParameters engineParam = null; + private IESParameterSpec engineSpec = null; + private AsymmetricKeyParameter key; + private SecureRandom random; + private boolean dhaesMode = false; + private AsymmetricKeyParameter otherKeyParameter = null; + + public IESCipher(IESEngine engine) + { + this.engine = engine; + this.ivLength = 0; + } + + public IESCipher(IESEngine engine, int ivLength) + { + this.engine = engine; + this.ivLength = ivLength; + } + + public int engineGetBlockSize() + { + if (engine.getCipher() != null) + { + return engine.getCipher().getBlockSize(); + } + else + { + return 0; + } + } + + + public int engineGetKeySize(Key key) + { + if (key instanceof ECKey) + { + return ((ECKey)key).getParameters().getCurve().getFieldSize(); + } + else + { + throw new IllegalArgumentException("not an EC key"); + } + } + + + public byte[] engineGetIV() + { + return null; + } + + public AlgorithmParameters engineGetParameters() + { + if (engineParam == null && engineSpec != null) + { + try + { + engineParam = AlgorithmParameters.getInstance("IES", BouncyCastleProvider.PROVIDER_NAME); + engineParam.init(engineSpec); + } + catch (Exception e) + { + throw new RuntimeException(e.toString()); + } + } + + return engineParam; + } + + + public void engineSetMode(String mode) + throws NoSuchAlgorithmException + { + String modeName = Strings.toUpperCase(mode); + + if (modeName.equals("NONE")) + { + dhaesMode = false; + } + else if (modeName.equals("DHAES")) + { + dhaesMode = true; + } + else + { + throw new IllegalArgumentException("can't support mode " + mode); + } + } + + + public int engineGetOutputSize(int inputLen) + { + int len1, len2, len3; + + len1 = engine.getMac().getMacSize(); + + if (key != null) + { + len2 = 1 + 2 * (((ECKey)key).getParameters().getCurve().getFieldSize() + 7) / 8; + } + else + { + throw new IllegalStateException("cipher not initialised"); + } + + if (engine.getCipher() == null) + { + len3 = inputLen; + } + else if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE) + { + len3 = engine.getCipher().getOutputSize(inputLen); + } + else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE) + { + len3 = engine.getCipher().getOutputSize(inputLen - len1 - len2); + } + else + { + throw new IllegalStateException("cipher not initialised"); + } + + if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE) + { + return buffer.size() + len1 + len2 + len3; + } + else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE) + { + return buffer.size() - len1 - len2 + len3; + } + else + { + throw new IllegalStateException("cipher not initialised"); + } + + } + + public void engineSetPadding(String padding) + throws NoSuchPaddingException + { + String paddingName = Strings.toUpperCase(padding); + + // TDOD: make this meaningful... + if (paddingName.equals("NOPADDING")) + { + + } + else if (paddingName.equals("PKCS5PADDING") || paddingName.equals("PKCS7PADDING")) + { + + } + else + { + throw new NoSuchPaddingException("padding not available with IESCipher"); + } + } + + + // Initialisation methods + + public void engineInit( + int opmode, + Key key, + AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + AlgorithmParameterSpec paramSpec = null; + + if (params != null) + { + try + { + paramSpec = params.getParameterSpec(IESParameterSpec.class); + } + catch (Exception e) + { + throw new InvalidAlgorithmParameterException("cannot recognise parameters: " + e.toString()); + } + } + + engineParam = params; + engineInit(opmode, key, paramSpec, random); + + } + + + public void engineInit( + int opmode, + Key key, + AlgorithmParameterSpec engineSpec, + SecureRandom random) + throws InvalidAlgorithmParameterException, InvalidKeyException + { + otherKeyParameter = null; + + // Use default parameters (including cipher key size) if none are specified + if (engineSpec == null) + { + this.engineSpec = IESUtil.guessParameterSpec(engine); + } + else if (engineSpec instanceof IESParameterSpec) + { + this.engineSpec = (IESParameterSpec)engineSpec; + } + else + { + throw new InvalidAlgorithmParameterException("must be passed IES parameters"); + } + + byte[] nonce = this.engineSpec.getNonce(); + + if (nonce != null) + { + if (ivLength == 0) + { + throw new InvalidAlgorithmParameterException("NONCE present in IES Parameters when none required"); + } + else if (nonce.length != ivLength) + { + throw new InvalidAlgorithmParameterException("NONCE in IES Parameters needs to be " + ivLength + " bytes long"); + } + } + + // Parse the recipient's key + if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) + { + if (key instanceof ECPublicKey) + { + this.key = ECUtil.generatePublicKeyParameter((PublicKey)key); + } + else if (key instanceof IESKey) + { + IESKey ieKey = (IESKey)key; + + this.key = ECUtil.generatePublicKeyParameter(ieKey.getPublic()); + this.otherKeyParameter = ECUtil.generatePrivateKeyParameter(ieKey.getPrivate()); + } + else + { + throw new InvalidKeyException("must be passed recipient's public EC key for encryption"); + } + } + else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) + { + if (key instanceof ECPrivateKey) + { + this.key = ECUtil.generatePrivateKeyParameter((PrivateKey)key); + } + else if (key instanceof IESKey) + { + IESKey ieKey = (IESKey)key; + + this.otherKeyParameter = ECUtil.generatePublicKeyParameter(ieKey.getPublic()); + this.key = ECUtil.generatePrivateKeyParameter(ieKey.getPrivate()); + } + else + { + throw new InvalidKeyException("must be passed recipient's private EC key for decryption"); + } + } + else + { + throw new InvalidKeyException("must be passed EC key"); + } + + + this.random = random; + this.state = opmode; + buffer.reset(); + + } + + + public void engineInit( + int opmode, + Key key, + SecureRandom random) + throws InvalidKeyException + { + try + { + engineInit(opmode, key, (AlgorithmParameterSpec)null, random); + } + catch (InvalidAlgorithmParameterException e) + { + throw new IllegalArgumentException("can't handle supplied parameter spec"); + } + + } + + + // Update methods - buffer the input + + public byte[] engineUpdate( + byte[] input, + int inputOffset, + int inputLen) + { + buffer.write(input, inputOffset, inputLen); + return null; + } + + + public int engineUpdate( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + { + buffer.write(input, inputOffset, inputLen); + return 0; + } + + + // Finalisation methods + + public byte[] engineDoFinal( + byte[] input, + int inputOffset, + int inputLen) + throws IllegalBlockSizeException, BadPaddingException + { + if (inputLen != 0) + { + buffer.write(input, inputOffset, inputLen); + } + + final byte[] in = buffer.toByteArray(); + buffer.reset(); + + // Convert parameters for use in IESEngine + CipherParameters params = new IESWithCipherParameters(engineSpec.getDerivationV(), + engineSpec.getEncodingV(), + engineSpec.getMacKeySize(), + engineSpec.getCipherKeySize()); + + if (engineSpec.getNonce() != null) + { + params = new ParametersWithIV(params, engineSpec.getNonce()); + } + + final ECDomainParameters ecParams = ((ECKeyParameters)key).getParameters(); + + final byte[] V; + + if (otherKeyParameter != null) + { + try + { + if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE) + { + engine.init(true, otherKeyParameter, key, params); + } + else + { + engine.init(false, key, otherKeyParameter, params); + } + return engine.processBlock(in, 0, in.length); + } + catch (Exception e) + { + throw new BadPaddingException(e.getMessage()); + } + } + + if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE) + { + // Generate the ephemeral key pair + ECKeyPairGenerator gen = new ECKeyPairGenerator(); + gen.init(new ECKeyGenerationParameters(ecParams, random)); + + EphemeralKeyPairGenerator kGen = new EphemeralKeyPairGenerator(gen, new KeyEncoder() + { + public byte[] getEncoded(AsymmetricKeyParameter keyParameter) + { + return ((ECPublicKeyParameters)keyParameter).getQ().getEncoded(); + } + }); + + // Encrypt the buffer + try + { + engine.init(key, params, kGen); + + return engine.processBlock(in, 0, in.length); + } + catch (Exception e) + { + throw new BadPaddingException(e.getMessage()); + } + + } + else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE) + { + // Decrypt the buffer + try + { + engine.init(key, params, new ECIESPublicKeyParser(ecParams)); + + return engine.processBlock(in, 0, in.length); + } + catch (InvalidCipherTextException e) + { + throw new BadPaddingException(e.getMessage()); + } + } + else + { + throw new IllegalStateException("cipher not initialised"); + } + + } + + public int engineDoFinal( + byte[] input, + int inputOffset, + int inputLength, + byte[] output, + int outputOffset) + throws ShortBufferException, IllegalBlockSizeException, BadPaddingException + { + + byte[] buf = engineDoFinal(input, inputOffset, inputLength); + System.arraycopy(buf, 0, output, outputOffset, buf.length); + return buf.length; + } + + + /** + * Classes that inherit from us + */ + + static public class ECIES + extends IESCipher + { + public ECIES() + { + super(new IESEngine(new ECDHBasicAgreement(), + new KDF2BytesGenerator(new SHA1Digest()), + new HMac(new SHA1Digest()))); + } + } + + static public class ECIESwithDESede + extends IESCipher + { + public ECIESwithDESede() + { + super(new IESEngine(new ECDHBasicAgreement(), + new KDF2BytesGenerator(new SHA1Digest()), + new HMac(new SHA1Digest()), + new PaddedBufferedBlockCipher(new DESedeEngine()))); + } + } + + static public class ECIESwithAES + extends IESCipher + { + public ECIESwithAES() + { + super(new IESEngine(new ECDHBasicAgreement(), + new KDF2BytesGenerator(new SHA1Digest()), + new HMac(new SHA1Digest()), + new PaddedBufferedBlockCipher(new AESEngine()))); + } + } + + static public class ECIESwithDESedeCBC + extends IESCipher + { + public ECIESwithDESedeCBC() + { + super(new IESEngine(new ECDHBasicAgreement(), + new KDF2BytesGenerator(new SHA1Digest()), + new HMac(new SHA1Digest()), + new PaddedBufferedBlockCipher(new CBCBlockCipher(new DESedeEngine()))), 8); + } + } + + static public class ECIESwithAESCBC + extends IESCipher + { + public ECIESwithAESCBC() + { + super(new IESEngine(new ECDHBasicAgreement(), + new KDF2BytesGenerator(new SHA1Digest()), + new HMac(new SHA1Digest()), + new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()))), 16); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java new file mode 100644 index 00000000..590cd155 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java @@ -0,0 +1,361 @@ +package org.spongycastle.jcajce.provider.asymmetric.ec; + +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Hashtable; + +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.SecretKeySpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.nist.NISTObjectIdentifiers; +import org.spongycastle.asn1.oiw.OIWObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x9.X9IntegerConverter; +import org.spongycastle.crypto.BasicAgreement; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DerivationFunction; +import org.spongycastle.crypto.agreement.ECDHBasicAgreement; +import org.spongycastle.crypto.agreement.ECDHCBasicAgreement; +import org.spongycastle.crypto.agreement.ECMQVBasicAgreement; +import org.spongycastle.crypto.agreement.kdf.DHKDFParameters; +import org.spongycastle.crypto.agreement.kdf.ECDHKEKGenerator; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.params.DESParameters; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.crypto.params.MQVPrivateParameters; +import org.spongycastle.crypto.params.MQVPublicParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.spongycastle.jce.interfaces.ECPrivateKey; +import org.spongycastle.jce.interfaces.ECPublicKey; +import org.spongycastle.jce.interfaces.MQVPrivateKey; +import org.spongycastle.jce.interfaces.MQVPublicKey; +import org.spongycastle.util.Integers; +import org.spongycastle.util.Strings; + +/** + * Diffie-Hellman key agreement using elliptic curve keys, ala IEEE P1363 + * both the simple one, and the simple one with cofactors are supported. + * + * Also, MQV key agreement per SEC-1 + */ +public class KeyAgreementSpi + extends javax.crypto.KeyAgreementSpi +{ + private static final X9IntegerConverter converter = new X9IntegerConverter(); + private static final Hashtable algorithms = new Hashtable(); + private static final Hashtable oids = new Hashtable(); + private static final Hashtable des = new Hashtable(); + + static + { + Integer i64 = Integers.valueOf(64); + Integer i128 = Integers.valueOf(128); + Integer i192 = Integers.valueOf(192); + Integer i256 = Integers.valueOf(256); + + algorithms.put(NISTObjectIdentifiers.id_aes128_CBC.getId(), i128); + algorithms.put(NISTObjectIdentifiers.id_aes192_CBC.getId(), i192); + algorithms.put(NISTObjectIdentifiers.id_aes256_CBC.getId(), i256); + algorithms.put(NISTObjectIdentifiers.id_aes128_wrap.getId(), i128); + algorithms.put(NISTObjectIdentifiers.id_aes192_wrap.getId(), i192); + algorithms.put(NISTObjectIdentifiers.id_aes256_wrap.getId(), i256); + algorithms.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId(), i192); + algorithms.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), i192); + algorithms.put(OIWObjectIdentifiers.desCBC.getId(), i64); + + oids.put("DESEDE", PKCSObjectIdentifiers.des_EDE3_CBC); + oids.put("AES", NISTObjectIdentifiers.id_aes256_CBC); + oids.put("DES", OIWObjectIdentifiers.desCBC); + + des.put("DES", "DES"); + des.put("DESEDE", "DES"); + des.put(OIWObjectIdentifiers.desCBC.getId(), "DES"); + des.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), "DES"); + des.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId(), "DES"); + } + + private String kaAlgorithm; + private BigInteger result; + private ECDomainParameters parameters; + private BasicAgreement agreement; + private DerivationFunction kdf; + + private byte[] bigIntToBytes( + BigInteger r) + { + return converter.integerToBytes(r, converter.getByteLength(parameters.getCurve())); + } + + protected KeyAgreementSpi( + String kaAlgorithm, + BasicAgreement agreement, + DerivationFunction kdf) + { + this.kaAlgorithm = kaAlgorithm; + this.agreement = agreement; + this.kdf = kdf; + } + + protected Key engineDoPhase( + Key key, + boolean lastPhase) + throws InvalidKeyException, IllegalStateException + { + if (parameters == null) + { + throw new IllegalStateException(kaAlgorithm + " not initialised."); + } + + if (!lastPhase) + { + throw new IllegalStateException(kaAlgorithm + " can only be between two parties."); + } + + CipherParameters pubKey; + if (agreement instanceof ECMQVBasicAgreement) + { + if (!(key instanceof MQVPublicKey)) + { + throw new InvalidKeyException(kaAlgorithm + " key agreement requires " + + getSimpleName(MQVPublicKey.class) + " for doPhase"); + } + + MQVPublicKey mqvPubKey = (MQVPublicKey)key; + ECPublicKeyParameters staticKey = (ECPublicKeyParameters) + ECUtil.generatePublicKeyParameter(mqvPubKey.getStaticKey()); + ECPublicKeyParameters ephemKey = (ECPublicKeyParameters) + ECUtil.generatePublicKeyParameter(mqvPubKey.getEphemeralKey()); + + pubKey = new MQVPublicParameters(staticKey, ephemKey); + + // TODO Validate that all the keys are using the same parameters? + } + else + { + if (!(key instanceof PublicKey)) + { + throw new InvalidKeyException(kaAlgorithm + " key agreement requires " + + getSimpleName(ECPublicKey.class) + " for doPhase"); + } + + pubKey = ECUtil.generatePublicKeyParameter((PublicKey)key); + + // TODO Validate that all the keys are using the same parameters? + } + + result = agreement.calculateAgreement(pubKey); + + return null; + } + + protected byte[] engineGenerateSecret() + throws IllegalStateException + { + if (kdf != null) + { + throw new UnsupportedOperationException( + "KDF can only be used when algorithm is known"); + } + + return bigIntToBytes(result); + } + + protected int engineGenerateSecret( + byte[] sharedSecret, + int offset) + throws IllegalStateException, ShortBufferException + { + byte[] secret = engineGenerateSecret(); + + if (sharedSecret.length - offset < secret.length) + { + throw new ShortBufferException(kaAlgorithm + " key agreement: need " + secret.length + " bytes"); + } + + System.arraycopy(secret, 0, sharedSecret, offset, secret.length); + + return secret.length; + } + + protected SecretKey engineGenerateSecret( + String algorithm) + throws NoSuchAlgorithmException + { + byte[] secret = bigIntToBytes(result); + String algKey = Strings.toUpperCase(algorithm); + String oidAlgorithm = algorithm; + + if (oids.containsKey(algKey)) + { + oidAlgorithm = ((ASN1ObjectIdentifier)oids.get(algKey)).getId(); + } + + if (kdf != null) + { + if (!algorithms.containsKey(oidAlgorithm)) + { + throw new NoSuchAlgorithmException("unknown algorithm encountered: " + algorithm); + } + + int keySize = ((Integer)algorithms.get(oidAlgorithm)).intValue(); + + DHKDFParameters params = new DHKDFParameters(new ASN1ObjectIdentifier(oidAlgorithm), keySize, secret); + + byte[] keyBytes = new byte[keySize / 8]; + kdf.init(params); + kdf.generateBytes(keyBytes, 0, keyBytes.length); + secret = keyBytes; + } + else + { + if (algorithms.containsKey(oidAlgorithm)) + { + Integer length = (Integer)algorithms.get(oidAlgorithm); + + byte[] key = new byte[length.intValue() / 8]; + + System.arraycopy(secret, 0, key, 0, key.length); + + secret = key; + } + } + + if (des.containsKey(oidAlgorithm)) + { + DESParameters.setOddParity(secret); + } + + return new SecretKeySpec(secret, algorithm); + } + + protected void engineInit( + Key key, + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + if (params != null) + { + throw new InvalidAlgorithmParameterException("No algorithm parameters supported"); + } + + initFromKey(key); + } + + protected void engineInit( + Key key, + SecureRandom random) + throws InvalidKeyException + { + initFromKey(key); + } + + private void initFromKey(Key key) + throws InvalidKeyException + { + if (agreement instanceof ECMQVBasicAgreement) + { + if (!(key instanceof MQVPrivateKey)) + { + throw new InvalidKeyException(kaAlgorithm + " key agreement requires " + + getSimpleName(MQVPrivateKey.class) + " for initialisation"); + } + + MQVPrivateKey mqvPrivKey = (MQVPrivateKey)key; + ECPrivateKeyParameters staticPrivKey = (ECPrivateKeyParameters) + ECUtil.generatePrivateKeyParameter(mqvPrivKey.getStaticPrivateKey()); + ECPrivateKeyParameters ephemPrivKey = (ECPrivateKeyParameters) + ECUtil.generatePrivateKeyParameter(mqvPrivKey.getEphemeralPrivateKey()); + + ECPublicKeyParameters ephemPubKey = null; + if (mqvPrivKey.getEphemeralPublicKey() != null) + { + ephemPubKey = (ECPublicKeyParameters) + ECUtil.generatePublicKeyParameter(mqvPrivKey.getEphemeralPublicKey()); + } + + MQVPrivateParameters localParams = new MQVPrivateParameters(staticPrivKey, ephemPrivKey, ephemPubKey); + this.parameters = staticPrivKey.getParameters(); + + // TODO Validate that all the keys are using the same parameters? + + agreement.init(localParams); + } + else + { + if (!(key instanceof PrivateKey)) + { + throw new InvalidKeyException(kaAlgorithm + " key agreement requires " + + getSimpleName(ECPrivateKey.class) + " for initialisation"); + } + + ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)ECUtil.generatePrivateKeyParameter((PrivateKey)key); + this.parameters = privKey.getParameters(); + + agreement.init(privKey); + } + } + + private static String getSimpleName(Class clazz) + { + String fullName = clazz.getName(); + + return fullName.substring(fullName.lastIndexOf('.') + 1); + } + + public static class DH + extends KeyAgreementSpi + { + public DH() + { + super("ECDH", new ECDHBasicAgreement(), null); + } + } + + public static class DHC + extends KeyAgreementSpi + { + public DHC() + { + super("ECDHC", new ECDHCBasicAgreement(), null); + } + } + + public static class MQV + extends KeyAgreementSpi + { + public MQV() + { + super("ECMQV", new ECMQVBasicAgreement(), null); + } + } + + public static class DHwithSHA1KDF + extends KeyAgreementSpi + { + public DHwithSHA1KDF() + { + super("ECDHwithSHA1KDF", new ECDHBasicAgreement(), new ECDHKEKGenerator(new SHA1Digest())); + } + } + + public static class MQVwithSHA1KDF + extends KeyAgreementSpi + { + public MQVwithSHA1KDF() + { + super("ECMQVwithSHA1KDF", new ECMQVBasicAgreement(), new ECDHKEKGenerator(new SHA1Digest())); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java new file mode 100644 index 00000000..5e77a74c --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java @@ -0,0 +1,239 @@ +package org.spongycastle.jcajce.provider.asymmetric.ec; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi; +import org.spongycastle.jcajce.provider.asymmetric.util.EC5Util; +import org.spongycastle.jcajce.provider.config.ProviderConfiguration; +import org.spongycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECParameterSpec; +import org.spongycastle.jce.spec.ECPrivateKeySpec; +import org.spongycastle.jce.spec.ECPublicKeySpec; + +public class KeyFactorySpi + extends BaseKeyFactorySpi + implements AsymmetricKeyInfoConverter +{ + String algorithm; + ProviderConfiguration configuration; + + KeyFactorySpi( + String algorithm, + ProviderConfiguration configuration) + { + this.algorithm = algorithm; + this.configuration = configuration; + } + + protected Key engineTranslateKey( + Key key) + throws InvalidKeyException + { + if (key instanceof ECPublicKey) + { + return new BCECPublicKey((ECPublicKey)key, configuration); + } + else if (key instanceof ECPrivateKey) + { + return new BCECPrivateKey((ECPrivateKey)key, configuration); + } + + throw new InvalidKeyException("key type unknown"); + } + + protected KeySpec engineGetKeySpec( + Key key, + Class spec) + throws InvalidKeySpecException + { + if (spec.isAssignableFrom(java.security.spec.ECPublicKeySpec.class) && key instanceof ECPublicKey) + { + ECPublicKey k = (ECPublicKey)key; + if (k.getParams() != null) + { + return new java.security.spec.ECPublicKeySpec(k.getW(), k.getParams()); + } + else + { + ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + return new java.security.spec.ECPublicKeySpec(k.getW(), EC5Util.convertSpec(EC5Util.convertCurve(implicitSpec.getCurve(), implicitSpec.getSeed()), implicitSpec)); + } + } + else if (spec.isAssignableFrom(java.security.spec.ECPrivateKeySpec.class) && key instanceof ECPrivateKey) + { + ECPrivateKey k = (ECPrivateKey)key; + + if (k.getParams() != null) + { + return new java.security.spec.ECPrivateKeySpec(k.getS(), k.getParams()); + } + else + { + ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + return new java.security.spec.ECPrivateKeySpec(k.getS(), EC5Util.convertSpec(EC5Util.convertCurve(implicitSpec.getCurve(), implicitSpec.getSeed()), implicitSpec)); + } + } + else if (spec.isAssignableFrom(org.spongycastle.jce.spec.ECPublicKeySpec.class) && key instanceof ECPublicKey) + { + ECPublicKey k = (ECPublicKey)key; + if (k.getParams() != null) + { + return new org.spongycastle.jce.spec.ECPublicKeySpec(EC5Util.convertPoint(k.getParams(), k.getW(), false), EC5Util.convertSpec(k.getParams(), false)); + } + else + { + ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + return new org.spongycastle.jce.spec.ECPublicKeySpec(EC5Util.convertPoint(k.getParams(), k.getW(), false), implicitSpec); + } + } + else if (spec.isAssignableFrom(org.spongycastle.jce.spec.ECPrivateKeySpec.class) && key instanceof ECPrivateKey) + { + ECPrivateKey k = (ECPrivateKey)key; + + if (k.getParams() != null) + { + return new org.spongycastle.jce.spec.ECPrivateKeySpec(k.getS(), EC5Util.convertSpec(k.getParams(), false)); + } + else + { + ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + return new org.spongycastle.jce.spec.ECPrivateKeySpec(k.getS(), implicitSpec); + } + } + + return super.engineGetKeySpec(key, spec); + } + + protected PrivateKey engineGeneratePrivate( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof ECPrivateKeySpec) + { + return new BCECPrivateKey(algorithm, (ECPrivateKeySpec)keySpec, configuration); + } + else if (keySpec instanceof java.security.spec.ECPrivateKeySpec) + { + return new BCECPrivateKey(algorithm, (java.security.spec.ECPrivateKeySpec)keySpec, configuration); + } + + return super.engineGeneratePrivate(keySpec); + } + + protected PublicKey engineGeneratePublic( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof ECPublicKeySpec) + { + return new BCECPublicKey(algorithm, (ECPublicKeySpec)keySpec, configuration); + } + else if (keySpec instanceof java.security.spec.ECPublicKeySpec) + { + return new BCECPublicKey(algorithm, (java.security.spec.ECPublicKeySpec)keySpec, configuration); + } + + return super.engineGeneratePublic(keySpec); + } + + public PrivateKey generatePrivate(PrivateKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getPrivateKeyAlgorithm().getAlgorithm(); + + if (algOid.equals(X9ObjectIdentifiers.id_ecPublicKey)) + { + return new BCECPrivateKey(algorithm, keyInfo, configuration); + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } + + public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getAlgorithm().getAlgorithm(); + + if (algOid.equals(X9ObjectIdentifiers.id_ecPublicKey)) + { + return new BCECPublicKey(algorithm, keyInfo, configuration); + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } + + public static class EC + extends KeyFactorySpi + { + public EC() + { + super("EC", BouncyCastleProvider.CONFIGURATION); + } + } + + public static class ECDSA + extends KeyFactorySpi + { + public ECDSA() + { + super("ECDSA", BouncyCastleProvider.CONFIGURATION); + } + } + + public static class ECGOST3410 + extends KeyFactorySpi + { + public ECGOST3410() + { + super("ECGOST3410", BouncyCastleProvider.CONFIGURATION); + } + } + + public static class ECDH + extends KeyFactorySpi + { + public ECDH() + { + super("ECDH", BouncyCastleProvider.CONFIGURATION); + } + } + + public static class ECDHC + extends KeyFactorySpi + { + public ECDHC() + { + super("ECDHC", BouncyCastleProvider.CONFIGURATION); + } + } + + public static class ECMQV + extends KeyFactorySpi + { + public ECMQV() + { + super("ECMQV", BouncyCastleProvider.CONFIGURATION); + } + } +}
\ No newline at end of file diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java new file mode 100644 index 00000000..bd24d1aa --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java @@ -0,0 +1,270 @@ +package org.spongycastle.jcajce.provider.asymmetric.ec; + +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidParameterException; +import java.security.KeyPair; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECGenParameterSpec; +import java.util.Hashtable; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.x9.ECNamedCurveTable; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.generators.ECKeyPairGenerator; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECKeyGenerationParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.EC5Util; +import org.spongycastle.jcajce.provider.config.ProviderConfiguration; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECNamedCurveGenParameterSpec; +import org.spongycastle.jce.spec.ECNamedCurveSpec; +import org.spongycastle.jce.spec.ECParameterSpec; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECPoint; +import org.spongycastle.util.Integers; + +public abstract class KeyPairGeneratorSpi + extends java.security.KeyPairGenerator +{ + public KeyPairGeneratorSpi(String algorithmName) + { + super(algorithmName); + } + + public static class EC + extends KeyPairGeneratorSpi + { + ECKeyGenerationParameters param; + ECKeyPairGenerator engine = new ECKeyPairGenerator(); + Object ecParams = null; + int strength = 239; + int certainty = 50; + SecureRandom random = new SecureRandom(); + boolean initialised = false; + String algorithm; + ProviderConfiguration configuration; + + static private Hashtable ecParameters; + + static { + ecParameters = new Hashtable(); + + ecParameters.put(Integers.valueOf(192), new ECGenParameterSpec("prime192v1")); // a.k.a P-192 + ecParameters.put(Integers.valueOf(239), new ECGenParameterSpec("prime239v1")); + ecParameters.put(Integers.valueOf(256), new ECGenParameterSpec("prime256v1")); // a.k.a P-256 + + ecParameters.put(Integers.valueOf(224), new ECGenParameterSpec("P-224")); + ecParameters.put(Integers.valueOf(384), new ECGenParameterSpec("P-384")); + ecParameters.put(Integers.valueOf(521), new ECGenParameterSpec("P-521")); + } + + public EC() + { + super("EC"); + this.algorithm = "EC"; + this.configuration = BouncyCastleProvider.CONFIGURATION; + } + + public EC( + String algorithm, + ProviderConfiguration configuration) + { + super(algorithm); + this.algorithm = algorithm; + this.configuration = configuration; + } + + public void initialize( + int strength, + SecureRandom random) + { + this.strength = strength; + this.random = random; + + ECGenParameterSpec ecParams = (ECGenParameterSpec)ecParameters.get(Integers.valueOf(strength)); + if (ecParams == null) + { + throw new InvalidParameterException("unknown key size."); + } + + try + { + initialize(ecParams, random); + } + catch (InvalidAlgorithmParameterException e) + { + throw new InvalidParameterException("key size not configurable."); + } + } + + public void initialize( + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + if (params == null) + { + ECParameterSpec implicitCA = configuration.getEcImplicitlyCa(); + if (implicitCA == null) + { + throw new InvalidAlgorithmParameterException("null parameter passed but no implicitCA set"); + } + + this.ecParams = null; + this.param = createKeyGenParamsBC(implicitCA, random); + } + else if (params instanceof ECParameterSpec) + { + this.ecParams = params; + this.param = createKeyGenParamsBC((ECParameterSpec)params, random); + } + else if (params instanceof java.security.spec.ECParameterSpec) + { + this.ecParams = params; + this.param = createKeyGenParamsJCE((java.security.spec.ECParameterSpec)params, random); + } + else if (params instanceof ECGenParameterSpec) + { + initializeNamedCurve(((ECGenParameterSpec)params).getName(), random); + } + else if (params instanceof ECNamedCurveGenParameterSpec) + { + initializeNamedCurve(((ECNamedCurveGenParameterSpec)params).getName(), random); + } + else + { + throw new InvalidAlgorithmParameterException("parameter object not a ECParameterSpec"); + } + + engine.init(param); + initialised = true; + } + + public KeyPair generateKeyPair() + { + if (!initialised) + { + initialize(strength, new SecureRandom()); + } + + AsymmetricCipherKeyPair pair = engine.generateKeyPair(); + ECPublicKeyParameters pub = (ECPublicKeyParameters)pair.getPublic(); + ECPrivateKeyParameters priv = (ECPrivateKeyParameters)pair.getPrivate(); + + if (ecParams instanceof ECParameterSpec) + { + ECParameterSpec p = (ECParameterSpec)ecParams; + + BCECPublicKey pubKey = new BCECPublicKey(algorithm, pub, p, configuration); + return new KeyPair(pubKey, + new BCECPrivateKey(algorithm, priv, pubKey, p, configuration)); + } + else if (ecParams == null) + { + return new KeyPair(new BCECPublicKey(algorithm, pub, configuration), + new BCECPrivateKey(algorithm, priv, configuration)); + } + else + { + java.security.spec.ECParameterSpec p = (java.security.spec.ECParameterSpec)ecParams; + + BCECPublicKey pubKey = new BCECPublicKey(algorithm, pub, p, configuration); + + return new KeyPair(pubKey, new BCECPrivateKey(algorithm, priv, pubKey, p, configuration)); + } + } + + protected ECKeyGenerationParameters createKeyGenParamsBC(ECParameterSpec p, SecureRandom r) + { + return new ECKeyGenerationParameters(new ECDomainParameters(p.getCurve(), p.getG(), p.getN()), r); + } + + protected ECKeyGenerationParameters createKeyGenParamsJCE(java.security.spec.ECParameterSpec p, SecureRandom r) + { + ECCurve curve = EC5Util.convertCurve(p.getCurve()); + ECPoint g = EC5Util.convertPoint(curve, p.getGenerator(), false); + BigInteger n = p.getOrder(); + BigInteger h = BigInteger.valueOf(p.getCofactor()); + ECDomainParameters dp = new ECDomainParameters(curve, g, n, h); + return new ECKeyGenerationParameters(dp, r); + } + + protected ECNamedCurveSpec createNamedCurveSpec(String curveName) + throws InvalidAlgorithmParameterException + { + // NOTE: Don't bother with custom curves here as the curve will be converted to JCE type shortly + + X9ECParameters p = ECNamedCurveTable.getByName(curveName); + if (p == null) + { + try + { + // Check whether it's actually an OID string (SunJSSE ServerHandshaker setupEphemeralECDHKeys bug) + p = ECNamedCurveTable.getByOID(new ASN1ObjectIdentifier(curveName)); + if (p == null) + { + throw new InvalidAlgorithmParameterException("unknown curve OID: " + curveName); + } + } + catch (IllegalArgumentException ex) + { + throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName); + } + } + + // Work-around for JDK bug -- it won't look up named curves properly if seed is present + byte[] seed = null; //p.getSeed(); + + return new ECNamedCurveSpec(curveName, p.getCurve(), p.getG(), p.getN(), p.getH(), seed); + } + + protected void initializeNamedCurve(String curveName, SecureRandom random) + throws InvalidAlgorithmParameterException + { + ECNamedCurveSpec namedCurve = createNamedCurveSpec(curveName); + this.ecParams = namedCurve; + this.param = createKeyGenParamsJCE(namedCurve, random); + } + } + + public static class ECDSA + extends EC + { + public ECDSA() + { + super("ECDSA", BouncyCastleProvider.CONFIGURATION); + } + } + + public static class ECDH + extends EC + { + public ECDH() + { + super("ECDH", BouncyCastleProvider.CONFIGURATION); + } + } + + public static class ECDHC + extends EC + { + public ECDHC() + { + super("ECDHC", BouncyCastleProvider.CONFIGURATION); + } + } + + public static class ECMQV + extends EC + { + public ECMQV() + { + super("ECMQV", BouncyCastleProvider.CONFIGURATION); + } + } +}
\ No newline at end of file diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java new file mode 100644 index 00000000..5846e393 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java @@ -0,0 +1,367 @@ +package org.spongycastle.jcajce.provider.asymmetric.ec; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DSA; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.NullDigest; +import org.spongycastle.crypto.digests.RIPEMD160Digest; +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.crypto.params.ParametersWithRandom; +import org.spongycastle.crypto.signers.ECDSASigner; +import org.spongycastle.crypto.signers.ECNRSigner; +import org.spongycastle.crypto.signers.HMacDSAKCalculator; +import org.spongycastle.jcajce.provider.asymmetric.util.DSABase; +import org.spongycastle.jcajce.provider.asymmetric.util.DSAEncoder; +import org.spongycastle.jcajce.provider.asymmetric.util.ECUtil; + +public class SignatureSpi + extends DSABase +{ + SignatureSpi(Digest digest, DSA signer, DSAEncoder encoder) + { + super(digest, signer, encoder); + } + + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException + { + CipherParameters param = ECUtil.generatePublicKeyParameter(publicKey); + + digest.reset(); + signer.init(false, param); + } + + protected void engineInitSign( + PrivateKey privateKey) + throws InvalidKeyException + { + CipherParameters param = ECUtil.generatePrivateKeyParameter(privateKey); + + digest.reset(); + + if (appRandom != null) + { + signer.init(true, new ParametersWithRandom(param, appRandom)); + } + else + { + signer.init(true, param); + } + } + + static public class ecDSA + extends SignatureSpi + { + public ecDSA() + { + super(new SHA1Digest(), new ECDSASigner(), new StdDSAEncoder()); + } + } + + static public class ecDetDSA + extends SignatureSpi + { + public ecDetDSA() + { + super(new SHA1Digest(), new ECDSASigner(new HMacDSAKCalculator(new SHA1Digest())), new StdDSAEncoder()); + } + } + + static public class ecDSAnone + extends SignatureSpi + { + public ecDSAnone() + { + super(new NullDigest(), new ECDSASigner(), new StdDSAEncoder()); + } + } + + static public class ecDSA224 + extends SignatureSpi + { + public ecDSA224() + { + super(new SHA224Digest(), new ECDSASigner(), new StdDSAEncoder()); + } + } + + static public class ecDetDSA224 + extends SignatureSpi + { + public ecDetDSA224() + { + super(new SHA224Digest(), new ECDSASigner(new HMacDSAKCalculator(new SHA224Digest())), new StdDSAEncoder()); + } + } + + static public class ecDSA256 + extends SignatureSpi + { + public ecDSA256() + { + super(new SHA256Digest(), new ECDSASigner(), new StdDSAEncoder()); + } + } + + static public class ecDetDSA256 + extends SignatureSpi + { + public ecDetDSA256() + { + super(new SHA256Digest(), new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest())), new StdDSAEncoder()); + } + } + + static public class ecDSA384 + extends SignatureSpi + { + public ecDSA384() + { + super(new SHA384Digest(), new ECDSASigner(), new StdDSAEncoder()); + } + } + + static public class ecDetDSA384 + extends SignatureSpi + { + public ecDetDSA384() + { + super(new SHA384Digest(), new ECDSASigner(new HMacDSAKCalculator(new SHA384Digest())), new StdDSAEncoder()); + } + } + + static public class ecDSA512 + extends SignatureSpi + { + public ecDSA512() + { + super(new SHA512Digest(), new ECDSASigner(), new StdDSAEncoder()); + } + } + + static public class ecDetDSA512 + extends SignatureSpi + { + public ecDetDSA512() + { + super(new SHA512Digest(), new ECDSASigner(new HMacDSAKCalculator(new SHA512Digest())), new StdDSAEncoder()); + } + } + + static public class ecDSARipeMD160 + extends SignatureSpi + { + public ecDSARipeMD160() + { + super(new RIPEMD160Digest(), new ECDSASigner(), new StdDSAEncoder()); + } + } + + static public class ecNR + extends SignatureSpi + { + public ecNR() + { + super(new SHA1Digest(), new ECNRSigner(), new StdDSAEncoder()); + } + } + + static public class ecNR224 + extends SignatureSpi + { + public ecNR224() + { + super(new SHA224Digest(), new ECNRSigner(), new StdDSAEncoder()); + } + } + + static public class ecNR256 + extends SignatureSpi + { + public ecNR256() + { + super(new SHA256Digest(), new ECNRSigner(), new StdDSAEncoder()); + } + } + + static public class ecNR384 + extends SignatureSpi + { + public ecNR384() + { + super(new SHA384Digest(), new ECNRSigner(), new StdDSAEncoder()); + } + } + + static public class ecNR512 + extends SignatureSpi + { + public ecNR512() + { + super(new SHA512Digest(), new ECNRSigner(), new StdDSAEncoder()); + } + } + + static public class ecCVCDSA + extends SignatureSpi + { + public ecCVCDSA() + { + super(new SHA1Digest(), new ECDSASigner(), new PlainDSAEncoder()); + } + } + + static public class ecCVCDSA224 + extends SignatureSpi + { + public ecCVCDSA224() + { + super(new SHA224Digest(), new ECDSASigner(), new PlainDSAEncoder()); + } + } + + static public class ecCVCDSA256 + extends SignatureSpi + { + public ecCVCDSA256() + { + super(new SHA256Digest(), new ECDSASigner(), new PlainDSAEncoder()); + } + } + + static public class ecCVCDSA384 + extends SignatureSpi + { + public ecCVCDSA384() + { + super(new SHA384Digest(), new ECDSASigner(), new PlainDSAEncoder()); + } + } + + static public class ecCVCDSA512 + extends SignatureSpi + { + public ecCVCDSA512() + { + super(new SHA512Digest(), new ECDSASigner(), new PlainDSAEncoder()); + } + } + + static public class ecPlainDSARP160 + extends SignatureSpi + { + public ecPlainDSARP160() + { + super(new RIPEMD160Digest(), new ECDSASigner(), new PlainDSAEncoder()); + } + } + + private static class StdDSAEncoder + implements DSAEncoder + { + public byte[] encode( + BigInteger r, + BigInteger s) + throws IOException + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new ASN1Integer(r)); + v.add(new ASN1Integer(s)); + + return new DERSequence(v).getEncoded(ASN1Encoding.DER); + } + + public BigInteger[] decode( + byte[] encoding) + throws IOException + { + ASN1Sequence s = (ASN1Sequence)ASN1Primitive.fromByteArray(encoding); + BigInteger[] sig = new BigInteger[2]; + + sig[0] = ASN1Integer.getInstance(s.getObjectAt(0)).getValue(); + sig[1] = ASN1Integer.getInstance(s.getObjectAt(1)).getValue(); + + return sig; + } + } + + private static class PlainDSAEncoder + implements DSAEncoder + { + public byte[] encode( + BigInteger r, + BigInteger s) + throws IOException + { + byte[] first = makeUnsigned(r); + byte[] second = makeUnsigned(s); + byte[] res; + + if (first.length > second.length) + { + res = new byte[first.length * 2]; + } + else + { + res = new byte[second.length * 2]; + } + + System.arraycopy(first, 0, res, res.length / 2 - first.length, first.length); + System.arraycopy(second, 0, res, res.length - second.length, second.length); + + return res; + } + + + private byte[] makeUnsigned(BigInteger val) + { + byte[] res = val.toByteArray(); + + if (res[0] == 0) + { + byte[] tmp = new byte[res.length - 1]; + + System.arraycopy(res, 1, tmp, 0, tmp.length); + + return tmp; + } + + return res; + } + + public BigInteger[] decode( + byte[] encoding) + throws IOException + { + BigInteger[] sig = new BigInteger[2]; + + byte[] first = new byte[encoding.length / 2]; + byte[] second = new byte[encoding.length / 2]; + + System.arraycopy(encoding, 0, first, 0, first.length); + System.arraycopy(encoding, first.length, second, 0, second.length); + + sig[0] = new BigInteger(1, first); + sig[1] = new BigInteger(1, second); + + return sig; + } + } +}
\ No newline at end of file diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java new file mode 100644 index 00000000..ca441966 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java @@ -0,0 +1,541 @@ +package org.spongycastle.jcajce.provider.asymmetric.ecgost; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.EllipticCurve; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.spongycastle.asn1.cryptopro.ECGOST3410NamedCurves; +import org.spongycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X962Parameters; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.EC5Util; +import org.spongycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.spongycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl; +import org.spongycastle.jce.ECGOST3410NamedCurveTable; +import org.spongycastle.jce.interfaces.ECPointEncoder; +import org.spongycastle.jce.interfaces.PKCS12BagAttributeCarrier; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; +import org.spongycastle.jce.spec.ECNamedCurveSpec; +import org.spongycastle.math.ec.ECCurve; + +public class BCECGOST3410PrivateKey + implements ECPrivateKey, org.spongycastle.jce.interfaces.ECPrivateKey, PKCS12BagAttributeCarrier, ECPointEncoder +{ + static final long serialVersionUID = 7245981689601667138L; + + private String algorithm = "ECGOST3410"; + private boolean withCompression; + + private transient GOST3410PublicKeyAlgParameters gostParams; + private transient BigInteger d; + private transient ECParameterSpec ecSpec; + private transient DERBitString publicKey; + private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl(); + + protected BCECGOST3410PrivateKey() + { + } + + public BCECGOST3410PrivateKey( + ECPrivateKey key) + { + this.d = key.getS(); + this.algorithm = key.getAlgorithm(); + this.ecSpec = key.getParams(); + } + + public BCECGOST3410PrivateKey( + org.spongycastle.jce.spec.ECPrivateKeySpec spec) + { + this.d = spec.getD(); + + if (spec.getParams() != null) // can be null if implicitlyCA + { + ECCurve curve = spec.getParams().getCurve(); + EllipticCurve ellipticCurve; + + ellipticCurve = EC5Util.convertCurve(curve, spec.getParams().getSeed()); + + this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec.getParams()); + } + else + { + this.ecSpec = null; + } + } + + + public BCECGOST3410PrivateKey( + ECPrivateKeySpec spec) + { + this.d = spec.getS(); + this.ecSpec = spec.getParams(); + } + + public BCECGOST3410PrivateKey( + BCECGOST3410PrivateKey key) + { + this.d = key.d; + this.ecSpec = key.ecSpec; + this.withCompression = key.withCompression; + this.attrCarrier = key.attrCarrier; + this.publicKey = key.publicKey; + this.gostParams = key.gostParams; + } + + public BCECGOST3410PrivateKey( + String algorithm, + ECPrivateKeyParameters params, + BCECGOST3410PublicKey pubKey, + ECParameterSpec spec) + { + ECDomainParameters dp = params.getParameters(); + + this.algorithm = algorithm; + this.d = params.getD(); + + if (spec == null) + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); + + this.ecSpec = new ECParameterSpec( + ellipticCurve, + new ECPoint( + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), + dp.getN(), + dp.getH().intValue()); + } + else + { + this.ecSpec = spec; + } + + this.gostParams = pubKey.getGostParams(); + + publicKey = getPublicKeyDetails(pubKey); + } + + public BCECGOST3410PrivateKey( + String algorithm, + ECPrivateKeyParameters params, + BCECGOST3410PublicKey pubKey, + org.spongycastle.jce.spec.ECParameterSpec spec) + { + ECDomainParameters dp = params.getParameters(); + + this.algorithm = algorithm; + this.d = params.getD(); + + if (spec == null) + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); + + this.ecSpec = new ECParameterSpec( + ellipticCurve, + new ECPoint( + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), + dp.getN(), + dp.getH().intValue()); + } + else + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed()); + + this.ecSpec = new ECParameterSpec( + ellipticCurve, + new ECPoint( + spec.getG().getAffineXCoord().toBigInteger(), + spec.getG().getAffineYCoord().toBigInteger()), + spec.getN(), + spec.getH().intValue()); + } + + this.gostParams = pubKey.getGostParams(); + + publicKey = getPublicKeyDetails(pubKey); + } + + public BCECGOST3410PrivateKey( + String algorithm, + ECPrivateKeyParameters params) + { + this.algorithm = algorithm; + this.d = params.getD(); + this.ecSpec = null; + } + + BCECGOST3410PrivateKey( + PrivateKeyInfo info) + throws IOException + { + populateFromPrivKeyInfo(info); + } + + private void populateFromPrivKeyInfo(PrivateKeyInfo info) + throws IOException + { + ASN1Primitive p = info.getPrivateKeyAlgorithm().getParameters().toASN1Primitive(); + + if (p instanceof ASN1Sequence && (ASN1Sequence.getInstance(p).size() == 2 || ASN1Sequence.getInstance(p).size() == 3)) + { + gostParams = GOST3410PublicKeyAlgParameters.getInstance(info.getPrivateKeyAlgorithm().getParameters()); + + ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet())); + + ECCurve curve = spec.getCurve(); + EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed()); + + ecSpec = new ECNamedCurveSpec( + ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()), + ellipticCurve, + new ECPoint( + spec.getG().getAffineXCoord().toBigInteger(), + spec.getG().getAffineYCoord().toBigInteger()), + spec.getN(), spec.getH()); + + ASN1Encodable privKey = info.parsePrivateKey(); + + byte[] encVal = ASN1OctetString.getInstance(privKey).getOctets(); + byte[] dVal = new byte[encVal.length]; + + for (int i = 0; i != encVal.length; i++) + { + dVal[i] = encVal[encVal.length - 1 - i]; + } + + this.d = new BigInteger(1, dVal); + } + else + { + // for backwards compatibility + X962Parameters params = X962Parameters.getInstance(info.getPrivateKeyAlgorithm().getParameters()); + + if (params.isNamedCurve()) + { + ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters()); + X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid); + + if (ecP == null) // GOST Curve + { + ECDomainParameters gParam = ECGOST3410NamedCurves.getByOID(oid); + EllipticCurve ellipticCurve = EC5Util.convertCurve(gParam.getCurve(), gParam.getSeed()); + + ecSpec = new ECNamedCurveSpec( + ECGOST3410NamedCurves.getName(oid), + ellipticCurve, + new ECPoint( + gParam.getG().getAffineXCoord().toBigInteger(), + gParam.getG().getAffineYCoord().toBigInteger()), + gParam.getN(), + gParam.getH()); + } + else + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed()); + + ecSpec = new ECNamedCurveSpec( + ECUtil.getCurveName(oid), + ellipticCurve, + new ECPoint( + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), + ecP.getN(), + ecP.getH()); + } + } + else if (params.isImplicitlyCA()) + { + ecSpec = null; + } + else + { + X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters()); + EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed()); + + this.ecSpec = new ECParameterSpec( + ellipticCurve, + new ECPoint( + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), + ecP.getN(), + ecP.getH().intValue()); + } + + ASN1Encodable privKey = info.parsePrivateKey(); + if (privKey instanceof ASN1Integer) + { + ASN1Integer derD = ASN1Integer.getInstance(privKey); + + this.d = derD.getValue(); + } + else + { + org.spongycastle.asn1.sec.ECPrivateKey ec = org.spongycastle.asn1.sec.ECPrivateKey.getInstance(privKey); + + this.d = ec.getKey(); + this.publicKey = ec.getPublicKey(); + } + } + } + + public String getAlgorithm() + { + return algorithm; + } + + /** + * return the encoding format we produce in getEncoded(). + * + * @return the string "PKCS#8" + */ + public String getFormat() + { + return "PKCS#8"; + } + + /** + * Return a PKCS8 representation of the key. The sequence returned + * represents a full PrivateKeyInfo object. + * + * @return a PKCS8 representation of the key. + */ + public byte[] getEncoded() + { + if (gostParams != null) + { + byte[] encKey = new byte[32]; + + extractBytes(encKey, 0, this.getS()); + + try + { + PrivateKeyInfo info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, gostParams), new DEROctetString(encKey)); + + return info.getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + return null; + } + } + else + { + X962Parameters params; + + if (ecSpec instanceof ECNamedCurveSpec) + { + ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName()); + if (curveOid == null) // guess it's the OID + { + curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName()); + } + params = new X962Parameters(curveOid); + } + else if (ecSpec == null) + { + params = new X962Parameters(DERNull.INSTANCE); + } + else + { + ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve()); + + X9ECParameters ecP = new X9ECParameters( + curve, + EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression), + ecSpec.getOrder(), + BigInteger.valueOf(ecSpec.getCofactor()), + ecSpec.getCurve().getSeed()); + + params = new X962Parameters(ecP); + } + + PrivateKeyInfo info; + org.spongycastle.asn1.sec.ECPrivateKey keyStructure; + + if (publicKey != null) + { + keyStructure = new org.spongycastle.asn1.sec.ECPrivateKey(this.getS(), publicKey, params); + } + else + { + keyStructure = new org.spongycastle.asn1.sec.ECPrivateKey(this.getS(), params); + } + + try + { + info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params.toASN1Primitive()), keyStructure.toASN1Primitive()); + + return info.getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + return null; + } + } + } + + private void extractBytes(byte[] encKey, int offSet, BigInteger bI) + { + byte[] val = bI.toByteArray(); + if (val.length < 32) + { + byte[] tmp = new byte[32]; + System.arraycopy(val, 0, tmp, tmp.length - val.length, val.length); + val = tmp; + } + + for (int i = 0; i != 32; i++) + { + encKey[offSet + i] = val[val.length - 1 - i]; + } + } + + public ECParameterSpec getParams() + { + return ecSpec; + } + + public org.spongycastle.jce.spec.ECParameterSpec getParameters() + { + if (ecSpec == null) + { + return null; + } + + return EC5Util.convertSpec(ecSpec, withCompression); + } + + org.spongycastle.jce.spec.ECParameterSpec engineGetSpec() + { + if (ecSpec != null) + { + return EC5Util.convertSpec(ecSpec, withCompression); + } + + return BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + } + + public BigInteger getS() + { + return d; + } + + public BigInteger getD() + { + return d; + } + + public void setBagAttribute( + ASN1ObjectIdentifier oid, + ASN1Encodable attribute) + { + attrCarrier.setBagAttribute(oid, attribute); + } + + public ASN1Encodable getBagAttribute( + ASN1ObjectIdentifier oid) + { + return attrCarrier.getBagAttribute(oid); + } + + public Enumeration getBagAttributeKeys() + { + return attrCarrier.getBagAttributeKeys(); + } + + public void setPointFormat(String style) + { + withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style)); + } + + public boolean equals(Object o) + { + if (!(o instanceof BCECGOST3410PrivateKey)) + { + return false; + } + + BCECGOST3410PrivateKey other = (BCECGOST3410PrivateKey)o; + + return getD().equals(other.getD()) && (engineGetSpec().equals(other.engineGetSpec())); + } + + public int hashCode() + { + return getD().hashCode() ^ engineGetSpec().hashCode(); + } + + public String toString() + { + StringBuffer buf = new StringBuffer(); + String nl = System.getProperty("line.separator"); + + buf.append("EC Private Key").append(nl); + buf.append(" S: ").append(this.d.toString(16)).append(nl); + + return buf.toString(); + + } + + private DERBitString getPublicKeyDetails(BCECGOST3410PublicKey pub) + { + try + { + SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(pub.getEncoded())); + + return info.getPublicKeyData(); + } + catch (IOException e) + { // should never happen + return null; + } + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + byte[] enc = (byte[])in.readObject(); + + populateFromPrivKeyInfo(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc))); + + this.attrCarrier = new PKCS12BagAttributeCarrierImpl(); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(this.getEncoded()); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java new file mode 100644 index 00000000..9aa744bf --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java @@ -0,0 +1,400 @@ +package org.spongycastle.jcajce.provider.asymmetric.ecgost; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.EllipticCurve; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.spongycastle.asn1.cryptopro.ECGOST3410NamedCurves; +import org.spongycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X962Parameters; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.EC5Util; +import org.spongycastle.jcajce.provider.asymmetric.util.KeyUtil; +import org.spongycastle.jce.ECGOST3410NamedCurveTable; +import org.spongycastle.jce.interfaces.ECPointEncoder; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; +import org.spongycastle.jce.spec.ECNamedCurveSpec; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.custom.sec.SecP256K1Point; +import org.spongycastle.math.ec.custom.sec.SecP256R1Point; + +public class BCECGOST3410PublicKey + implements ECPublicKey, org.spongycastle.jce.interfaces.ECPublicKey, ECPointEncoder +{ + static final long serialVersionUID = 7026240464295649314L; + + private String algorithm = "ECGOST3410"; + private boolean withCompression; + + private transient org.spongycastle.math.ec.ECPoint q; + private transient ECParameterSpec ecSpec; + private transient GOST3410PublicKeyAlgParameters gostParams; + + public BCECGOST3410PublicKey( + BCECGOST3410PublicKey key) + { + this.q = key.q; + this.ecSpec = key.ecSpec; + this.withCompression = key.withCompression; + this.gostParams = key.gostParams; + } + + public BCECGOST3410PublicKey( + ECPublicKeySpec spec) + { + this.ecSpec = spec.getParams(); + this.q = EC5Util.convertPoint(ecSpec, spec.getW(), false); + } + + public BCECGOST3410PublicKey( + org.spongycastle.jce.spec.ECPublicKeySpec spec) + { + this.q = spec.getQ(); + + if (spec.getParams() != null) // can be null if implictlyCa + { + ECCurve curve = spec.getParams().getCurve(); + EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getParams().getSeed()); + + this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec.getParams()); + } + else + { + if (q.getCurve() == null) + { + org.spongycastle.jce.spec.ECParameterSpec s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + q = s.getCurve().createPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger()); + } + this.ecSpec = null; + } + } + + public BCECGOST3410PublicKey( + String algorithm, + ECPublicKeyParameters params, + ECParameterSpec spec) + { + ECDomainParameters dp = params.getParameters(); + + this.algorithm = algorithm; + this.q = params.getQ(); + + if (spec == null) + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); + + this.ecSpec = createSpec(ellipticCurve, dp); + } + else + { + this.ecSpec = spec; + } + } + + public BCECGOST3410PublicKey( + String algorithm, + ECPublicKeyParameters params, + org.spongycastle.jce.spec.ECParameterSpec spec) + { + ECDomainParameters dp = params.getParameters(); + + this.algorithm = algorithm; + this.q = params.getQ(); + + if (spec == null) + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); + + this.ecSpec = createSpec(ellipticCurve, dp); + } + else + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed()); + + this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec); + } + } + + /* + * called for implicitCA + */ + public BCECGOST3410PublicKey( + String algorithm, + ECPublicKeyParameters params) + { + this.algorithm = algorithm; + this.q = params.getQ(); + this.ecSpec = null; + } + + private ECParameterSpec createSpec(EllipticCurve ellipticCurve, ECDomainParameters dp) + { + return new ECParameterSpec( + ellipticCurve, + new ECPoint( + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), + dp.getN(), + dp.getH().intValue()); + } + + public BCECGOST3410PublicKey( + ECPublicKey key) + { + this.algorithm = key.getAlgorithm(); + this.ecSpec = key.getParams(); + this.q = EC5Util.convertPoint(this.ecSpec, key.getW(), false); + } + + BCECGOST3410PublicKey( + SubjectPublicKeyInfo info) + { + populateFromPubKeyInfo(info); + } + + private void populateFromPubKeyInfo(SubjectPublicKeyInfo info) + { + DERBitString bits = info.getPublicKeyData(); + ASN1OctetString key; + this.algorithm = "ECGOST3410"; + + try + { + key = (ASN1OctetString)ASN1Primitive.fromByteArray(bits.getBytes()); + } + catch (IOException ex) + { + throw new IllegalArgumentException("error recovering public key"); + } + + byte[] keyEnc = key.getOctets(); + byte[] x = new byte[32]; + byte[] y = new byte[32]; + + for (int i = 0; i != x.length; i++) + { + x[i] = keyEnc[32 - 1 - i]; + } + + for (int i = 0; i != y.length; i++) + { + y[i] = keyEnc[64 - 1 - i]; + } + + gostParams = GOST3410PublicKeyAlgParameters.getInstance(info.getAlgorithm().getParameters()); + + ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet())); + + ECCurve curve = spec.getCurve(); + EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed()); + + this.q = curve.createPoint(new BigInteger(1, x), new BigInteger(1, y)); + + ecSpec = new ECNamedCurveSpec( + ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()), + ellipticCurve, + new ECPoint( + spec.getG().getAffineXCoord().toBigInteger(), + spec.getG().getAffineYCoord().toBigInteger()), + spec.getN(), spec.getH()); + } + + public String getAlgorithm() + { + return algorithm; + } + + public String getFormat() + { + return "X.509"; + } + + public byte[] getEncoded() + { + ASN1Encodable params; + SubjectPublicKeyInfo info; + + if (gostParams != null) + { + params = gostParams; + } + else + { + if (ecSpec instanceof ECNamedCurveSpec) + { + params = new GOST3410PublicKeyAlgParameters( + ECGOST3410NamedCurves.getOID(((ECNamedCurveSpec)ecSpec).getName()), + CryptoProObjectIdentifiers.gostR3411_94_CryptoProParamSet); + } + else + { // strictly speaking this may not be applicable... + ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve()); + + X9ECParameters ecP = new X9ECParameters( + curve, + EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression), + ecSpec.getOrder(), + BigInteger.valueOf(ecSpec.getCofactor()), + ecSpec.getCurve().getSeed()); + + params = new X962Parameters(ecP); + } + } + + BigInteger bX = this.q.getAffineXCoord().toBigInteger(); + BigInteger bY = this.q.getAffineYCoord().toBigInteger(); + byte[] encKey = new byte[64]; + + extractBytes(encKey, 0, bX); + extractBytes(encKey, 32, bY); + + try + { + info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params), new DEROctetString(encKey)); + } + catch (IOException e) + { + return null; + } + + return KeyUtil.getEncodedSubjectPublicKeyInfo(info); + } + + private void extractBytes(byte[] encKey, int offSet, BigInteger bI) + { + byte[] val = bI.toByteArray(); + if (val.length < 32) + { + byte[] tmp = new byte[32]; + System.arraycopy(val, 0, tmp, tmp.length - val.length, val.length); + val = tmp; + } + + for (int i = 0; i != 32; i++) + { + encKey[offSet + i] = val[val.length - 1 - i]; + } + } + + public ECParameterSpec getParams() + { + return ecSpec; + } + + public org.spongycastle.jce.spec.ECParameterSpec getParameters() + { + if (ecSpec == null) // implictlyCA + { + return null; + } + + return EC5Util.convertSpec(ecSpec, withCompression); + } + + public ECPoint getW() + { + return new ECPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger()); + } + + public org.spongycastle.math.ec.ECPoint getQ() + { + if (ecSpec == null) + { + return q.getDetachedPoint(); + } + + return q; + } + + public org.spongycastle.math.ec.ECPoint engineGetQ() + { + return q; + } + + org.spongycastle.jce.spec.ECParameterSpec engineGetSpec() + { + if (ecSpec != null) + { + return EC5Util.convertSpec(ecSpec, withCompression); + } + + return BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + } + + public String toString() + { + StringBuffer buf = new StringBuffer(); + String nl = System.getProperty("line.separator"); + + buf.append("EC Public Key").append(nl); + buf.append(" X: ").append(this.q.getAffineXCoord().toBigInteger().toString(16)).append(nl); + buf.append(" Y: ").append(this.q.getAffineYCoord().toBigInteger().toString(16)).append(nl); + + return buf.toString(); + } + + public void setPointFormat(String style) + { + withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style)); + } + + public boolean equals(Object o) + { + if (!(o instanceof BCECGOST3410PublicKey)) + { + return false; + } + + BCECGOST3410PublicKey other = (BCECGOST3410PublicKey)o; + + return engineGetQ().equals(other.engineGetQ()) && (engineGetSpec().equals(other.engineGetSpec())); + } + + public int hashCode() + { + return engineGetQ().hashCode() ^ engineGetSpec().hashCode(); + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + byte[] enc = (byte[])in.readObject(); + + populateFromPubKeyInfo(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc))); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(this.getEncoded()); + } + + public GOST3410PublicKeyAlgParameters getGostParams() + { + return gostParams; + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/KeyFactorySpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/KeyFactorySpi.java new file mode 100644 index 00000000..1552ff0a --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/KeyFactorySpi.java @@ -0,0 +1,166 @@ +package org.spongycastle.jcajce.provider.asymmetric.ecgost; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.jcajce.provider.asymmetric.util.EC5Util; +import org.spongycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECParameterSpec; +import org.spongycastle.jce.spec.ECPrivateKeySpec; +import org.spongycastle.jce.spec.ECPublicKeySpec; + +public class KeyFactorySpi + extends BaseKeyFactorySpi +{ + public KeyFactorySpi() + { + } + + protected KeySpec engineGetKeySpec( + Key key, + Class spec) + throws InvalidKeySpecException + { + if (spec.isAssignableFrom(java.security.spec.ECPublicKeySpec.class) && key instanceof ECPublicKey) + { + ECPublicKey k = (ECPublicKey)key; + if (k.getParams() != null) + { + return new java.security.spec.ECPublicKeySpec(k.getW(), k.getParams()); + } + else + { + ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + return new java.security.spec.ECPublicKeySpec(k.getW(), EC5Util.convertSpec(EC5Util.convertCurve(implicitSpec.getCurve(), implicitSpec.getSeed()), implicitSpec)); + } + } + else if (spec.isAssignableFrom(java.security.spec.ECPrivateKeySpec.class) && key instanceof ECPrivateKey) + { + ECPrivateKey k = (ECPrivateKey)key; + + if (k.getParams() != null) + { + return new java.security.spec.ECPrivateKeySpec(k.getS(), k.getParams()); + } + else + { + ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + return new java.security.spec.ECPrivateKeySpec(k.getS(), EC5Util.convertSpec(EC5Util.convertCurve(implicitSpec.getCurve(), implicitSpec.getSeed()), implicitSpec)); + } + } + else if (spec.isAssignableFrom(org.spongycastle.jce.spec.ECPublicKeySpec.class) && key instanceof ECPublicKey) + { + ECPublicKey k = (ECPublicKey)key; + if (k.getParams() != null) + { + return new org.spongycastle.jce.spec.ECPublicKeySpec(EC5Util.convertPoint(k.getParams(), k.getW(), false), EC5Util.convertSpec(k.getParams(), false)); + } + else + { + ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + return new org.spongycastle.jce.spec.ECPublicKeySpec(EC5Util.convertPoint(k.getParams(), k.getW(), false), implicitSpec); + } + } + else if (spec.isAssignableFrom(org.spongycastle.jce.spec.ECPrivateKeySpec.class) && key instanceof ECPrivateKey) + { + ECPrivateKey k = (ECPrivateKey)key; + + if (k.getParams() != null) + { + return new org.spongycastle.jce.spec.ECPrivateKeySpec(k.getS(), EC5Util.convertSpec(k.getParams(), false)); + } + else + { + ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + return new org.spongycastle.jce.spec.ECPrivateKeySpec(k.getS(), implicitSpec); + } + } + + return super.engineGetKeySpec(key, spec); + } + + protected Key engineTranslateKey( + Key key) + throws InvalidKeyException + { + throw new InvalidKeyException("key type unknown"); + } + + protected PrivateKey engineGeneratePrivate( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof ECPrivateKeySpec) + { + return new BCECGOST3410PrivateKey((ECPrivateKeySpec)keySpec); + } + else if (keySpec instanceof java.security.spec.ECPrivateKeySpec) + { + return new BCECGOST3410PrivateKey((java.security.spec.ECPrivateKeySpec)keySpec); + } + + return super.engineGeneratePrivate(keySpec); + } + + protected PublicKey engineGeneratePublic( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof ECPublicKeySpec) + { + return new BCECGOST3410PublicKey((ECPublicKeySpec)keySpec); + } + else if (keySpec instanceof java.security.spec.ECPublicKeySpec) + { + return new BCECGOST3410PublicKey((java.security.spec.ECPublicKeySpec)keySpec); + } + + return super.engineGeneratePublic(keySpec); + } + + public PrivateKey generatePrivate(PrivateKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getPrivateKeyAlgorithm().getAlgorithm(); + + if (algOid.equals(CryptoProObjectIdentifiers.gostR3410_2001)) + { + return new BCECGOST3410PrivateKey(keyInfo); + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } + + public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getAlgorithm().getAlgorithm(); + + if (algOid.equals(CryptoProObjectIdentifiers.gostR3410_2001)) + { + return new BCECGOST3410PublicKey(keyInfo); + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/KeyPairGeneratorSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/KeyPairGeneratorSpi.java new file mode 100644 index 00000000..5ceefa3d --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/KeyPairGeneratorSpi.java @@ -0,0 +1,186 @@ +package org.spongycastle.jcajce.provider.asymmetric.ecgost; + +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidParameterException; +import java.security.KeyPair; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECGenParameterSpec; + +import org.spongycastle.asn1.cryptopro.ECGOST3410NamedCurves; +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.generators.ECKeyPairGenerator; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECKeyGenerationParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.EC5Util; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECNamedCurveGenParameterSpec; +import org.spongycastle.jce.spec.ECNamedCurveSpec; +import org.spongycastle.jce.spec.ECParameterSpec; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECPoint; + +public class KeyPairGeneratorSpi + extends java.security.KeyPairGenerator +{ + Object ecParams = null; + ECKeyPairGenerator engine = new ECKeyPairGenerator(); + + String algorithm = "ECGOST3410"; + ECKeyGenerationParameters param; + int strength = 239; + SecureRandom random = null; + boolean initialised = false; + + public KeyPairGeneratorSpi() + { + super("ECGOST3410"); + } + + public void initialize( + int strength, + SecureRandom random) + { + this.strength = strength; + this.random = random; + + if (ecParams != null) + { + try + { + initialize((ECGenParameterSpec)ecParams, random); + } + catch (InvalidAlgorithmParameterException e) + { + throw new InvalidParameterException("key size not configurable."); + } + } + else + { + throw new InvalidParameterException("unknown key size."); + } + } + + public void initialize( + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + if (params instanceof ECParameterSpec) + { + ECParameterSpec p = (ECParameterSpec)params; + this.ecParams = params; + + param = new ECKeyGenerationParameters(new ECDomainParameters(p.getCurve(), p.getG(), p.getN()), random); + + engine.init(param); + initialised = true; + } + else if (params instanceof java.security.spec.ECParameterSpec) + { + java.security.spec.ECParameterSpec p = (java.security.spec.ECParameterSpec)params; + this.ecParams = params; + + ECCurve curve = EC5Util.convertCurve(p.getCurve()); + ECPoint g = EC5Util.convertPoint(curve, p.getGenerator(), false); + + param = new ECKeyGenerationParameters(new ECDomainParameters(curve, g, p.getOrder(), BigInteger.valueOf(p.getCofactor())), random); + + engine.init(param); + initialised = true; + } + else if (params instanceof ECGenParameterSpec || params instanceof ECNamedCurveGenParameterSpec) + { + String curveName; + + if (params instanceof ECGenParameterSpec) + { + curveName = ((ECGenParameterSpec)params).getName(); + } + else + { + curveName = ((ECNamedCurveGenParameterSpec)params).getName(); + } + + ECDomainParameters ecP = ECGOST3410NamedCurves.getByName(curveName); + if (ecP == null) + { + throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName); + } + + this.ecParams = new ECNamedCurveSpec( + curveName, + ecP.getCurve(), + ecP.getG(), + ecP.getN(), + ecP.getH(), + ecP.getSeed()); + + java.security.spec.ECParameterSpec p = (java.security.spec.ECParameterSpec)ecParams; + + ECCurve curve = EC5Util.convertCurve(p.getCurve()); + ECPoint g = EC5Util.convertPoint(curve, p.getGenerator(), false); + + param = new ECKeyGenerationParameters(new ECDomainParameters(curve, g, p.getOrder(), BigInteger.valueOf(p.getCofactor())), random); + + engine.init(param); + initialised = true; + } + else if (params == null && BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa() != null) + { + ECParameterSpec p = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + this.ecParams = params; + + param = new ECKeyGenerationParameters(new ECDomainParameters(p.getCurve(), p.getG(), p.getN()), random); + + engine.init(param); + initialised = true; + } + else if (params == null && BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa() == null) + { + throw new InvalidAlgorithmParameterException("null parameter passed but no implicitCA set"); + } + else + { + throw new InvalidAlgorithmParameterException("parameter object not a ECParameterSpec: " + params.getClass().getName()); + } + } + + public KeyPair generateKeyPair() + { + if (!initialised) + { + throw new IllegalStateException("EC Key Pair Generator not initialised"); + } + + AsymmetricCipherKeyPair pair = engine.generateKeyPair(); + ECPublicKeyParameters pub = (ECPublicKeyParameters)pair.getPublic(); + ECPrivateKeyParameters priv = (ECPrivateKeyParameters)pair.getPrivate(); + + if (ecParams instanceof ECParameterSpec) + { + ECParameterSpec p = (ECParameterSpec)ecParams; + + BCECGOST3410PublicKey pubKey = new BCECGOST3410PublicKey(algorithm, pub, p); + return new KeyPair(pubKey, + new BCECGOST3410PrivateKey(algorithm, priv, pubKey, p)); + } + else if (ecParams == null) + { + return new KeyPair(new BCECGOST3410PublicKey(algorithm, pub), + new BCECGOST3410PrivateKey(algorithm, priv)); + } + else + { + java.security.spec.ECParameterSpec p = (java.security.spec.ECParameterSpec)ecParams; + + BCECGOST3410PublicKey pubKey = new BCECGOST3410PublicKey(algorithm, pub, p); + + return new KeyPair(pubKey, new BCECGOST3410PrivateKey(algorithm, priv, pubKey, p)); + } + } +} + diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/SignatureSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/SignatureSpi.java new file mode 100644 index 00000000..726dd9fc --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/SignatureSpi.java @@ -0,0 +1,218 @@ +package org.spongycastle.jcajce.provider.asymmetric.ecgost; + +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.spec.AlgorithmParameterSpec; + +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x509.X509ObjectIdentifiers; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DSA; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.GOST3411Digest; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.crypto.signers.ECGOST3410Signer; +import org.spongycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.spongycastle.jce.interfaces.ECKey; +import org.spongycastle.jce.interfaces.ECPublicKey; +import org.spongycastle.jce.interfaces.GOST3410Key; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jcajce.provider.asymmetric.util.GOST3410Util; + +public class SignatureSpi + extends java.security.SignatureSpi + implements PKCSObjectIdentifiers, X509ObjectIdentifiers +{ + private Digest digest; + private DSA signer; + + public SignatureSpi() + { + this.digest = new GOST3411Digest(); + this.signer = new ECGOST3410Signer(); + } + + protected void engineInitVerify( + PublicKey publicKey) + throws InvalidKeyException + { + CipherParameters param; + + if (publicKey instanceof ECPublicKey) + { + param = ECUtil.generatePublicKeyParameter(publicKey); + } + else if (publicKey instanceof GOST3410Key) + { + param = GOST3410Util.generatePublicKeyParameter(publicKey); + } + else + { + try + { + byte[] bytes = publicKey.getEncoded(); + + publicKey = BouncyCastleProvider.getPublicKey(SubjectPublicKeyInfo.getInstance(bytes)); + + if (publicKey instanceof ECPublicKey) + { + param = ECUtil.generatePublicKeyParameter(publicKey); + } + else + { + throw new InvalidKeyException("can't recognise key type in DSA based signer"); + } + } + catch (Exception e) + { + throw new InvalidKeyException("can't recognise key type in DSA based signer"); + } + } + + digest.reset(); + signer.init(false, param); + } + + protected void engineInitSign( + PrivateKey privateKey) + throws InvalidKeyException + { + CipherParameters param; + + if (privateKey instanceof ECKey) + { + param = ECUtil.generatePrivateKeyParameter(privateKey); + } + else + { + param = GOST3410Util.generatePrivateKeyParameter(privateKey); + } + + digest.reset(); + + if (appRandom != null) + { + signer.init(true, new ParametersWithRandom(param, appRandom)); + } + else + { + signer.init(true, param); + } + } + + protected void engineUpdate( + byte b) + throws SignatureException + { + digest.update(b); + } + + protected void engineUpdate( + byte[] b, + int off, + int len) + throws SignatureException + { + digest.update(b, off, len); + } + + protected byte[] engineSign() + throws SignatureException + { + byte[] hash = new byte[digest.getDigestSize()]; + + digest.doFinal(hash, 0); + + try + { + byte[] sigBytes = new byte[64]; + BigInteger[] sig = signer.generateSignature(hash); + byte[] r = sig[0].toByteArray(); + byte[] s = sig[1].toByteArray(); + + if (s[0] != 0) + { + System.arraycopy(s, 0, sigBytes, 32 - s.length, s.length); + } + else + { + System.arraycopy(s, 1, sigBytes, 32 - (s.length - 1), s.length - 1); + } + + if (r[0] != 0) + { + System.arraycopy(r, 0, sigBytes, 64 - r.length, r.length); + } + else + { + System.arraycopy(r, 1, sigBytes, 64 - (r.length - 1), r.length - 1); + } + + return sigBytes; + } + catch (Exception e) + { + throw new SignatureException(e.toString()); + } + } + + protected boolean engineVerify( + byte[] sigBytes) + throws SignatureException + { + byte[] hash = new byte[digest.getDigestSize()]; + + digest.doFinal(hash, 0); + + BigInteger[] sig; + + try + { + byte[] r = new byte[32]; + byte[] s = new byte[32]; + + System.arraycopy(sigBytes, 0, s, 0, 32); + + System.arraycopy(sigBytes, 32, r, 0, 32); + + sig = new BigInteger[2]; + sig[0] = new BigInteger(1, r); + sig[1] = new BigInteger(1, s); + } + catch (Exception e) + { + throw new SignatureException("error decoding signature bytes."); + } + + return signer.verifySignature(hash, sig[0], sig[1]); + } + + protected void engineSetParameter( + AlgorithmParameterSpec params) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated replaced with <a href = "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)"> + */ + protected void engineSetParameter( + String param, + Object value) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated + */ + protected Object engineGetParameter( + String param) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/AlgorithmParameterGeneratorSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/AlgorithmParameterGeneratorSpi.java new file mode 100644 index 00000000..332d6db1 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/AlgorithmParameterGeneratorSpi.java @@ -0,0 +1,76 @@ +package org.spongycastle.jcajce.provider.asymmetric.elgamal; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.spec.DHGenParameterSpec; +import javax.crypto.spec.DHParameterSpec; + +import org.spongycastle.crypto.generators.ElGamalParametersGenerator; +import org.spongycastle.crypto.params.ElGamalParameters; +import org.spongycastle.jce.provider.BouncyCastleProvider; + +public class AlgorithmParameterGeneratorSpi + extends java.security.AlgorithmParameterGeneratorSpi +{ + protected SecureRandom random; + protected int strength = 1024; + + private int l = 0; + + protected void engineInit( + int strength, + SecureRandom random) + { + this.strength = strength; + this.random = random; + } + + protected void engineInit( + AlgorithmParameterSpec genParamSpec, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + if (!(genParamSpec instanceof DHGenParameterSpec)) + { + throw new InvalidAlgorithmParameterException("DH parameter generator requires a DHGenParameterSpec for initialisation"); + } + DHGenParameterSpec spec = (DHGenParameterSpec)genParamSpec; + + this.strength = spec.getPrimeSize(); + this.l = spec.getExponentSize(); + this.random = random; + } + + protected AlgorithmParameters engineGenerateParameters() + { + ElGamalParametersGenerator pGen = new ElGamalParametersGenerator(); + + if (random != null) + { + pGen.init(strength, 20, random); + } + else + { + pGen.init(strength, 20, new SecureRandom()); + } + + ElGamalParameters p = pGen.generateParameters(); + + AlgorithmParameters params; + + try + { + params = AlgorithmParameters.getInstance("ElGamal", BouncyCastleProvider.PROVIDER_NAME); + params.init(new DHParameterSpec(p.getP(), p.getG(), l)); + } + catch (Exception e) + { + throw new RuntimeException(e.getMessage()); + } + + return params; + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/AlgorithmParametersSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/AlgorithmParametersSpi.java new file mode 100644 index 00000000..5a65c0b2 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/AlgorithmParametersSpi.java @@ -0,0 +1,130 @@ +package org.spongycastle.jcajce.provider.asymmetric.elgamal; + +import java.io.IOException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; + +import javax.crypto.spec.DHParameterSpec; + +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.oiw.ElGamalParameter; +import org.spongycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameters; +import org.spongycastle.jce.spec.ElGamalParameterSpec; + +public class AlgorithmParametersSpi + extends BaseAlgorithmParameters +{ + ElGamalParameterSpec currentSpec; + + /** + * Return the X.509 ASN.1 structure ElGamalParameter. + * <p/> + * <pre> + * ElGamalParameter ::= SEQUENCE { + * prime INTEGER, -- p + * base INTEGER, -- g} + * </pre> + */ + protected byte[] engineGetEncoded() + { + ElGamalParameter elP = new ElGamalParameter(currentSpec.getP(), currentSpec.getG()); + + try + { + return elP.getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + throw new RuntimeException("Error encoding ElGamalParameters"); + } + } + + protected byte[] engineGetEncoded( + String format) + { + if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509")) + { + return engineGetEncoded(); + } + + return null; + } + + protected AlgorithmParameterSpec localEngineGetParameterSpec( + Class paramSpec) + throws InvalidParameterSpecException + { + if (paramSpec == ElGamalParameterSpec.class) + { + return currentSpec; + } + else if (paramSpec == DHParameterSpec.class) + { + return new DHParameterSpec(currentSpec.getP(), currentSpec.getG()); + } + + throw new InvalidParameterSpecException("unknown parameter spec passed to ElGamal parameters object."); + } + + protected void engineInit( + AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException + { + if (!(paramSpec instanceof ElGamalParameterSpec) && !(paramSpec instanceof DHParameterSpec)) + { + throw new InvalidParameterSpecException("DHParameterSpec required to initialise a ElGamal algorithm parameters object"); + } + + if (paramSpec instanceof ElGamalParameterSpec) + { + this.currentSpec = (ElGamalParameterSpec)paramSpec; + } + else + { + DHParameterSpec s = (DHParameterSpec)paramSpec; + + this.currentSpec = new ElGamalParameterSpec(s.getP(), s.getG()); + } + } + + protected void engineInit( + byte[] params) + throws IOException + { + try + { + ElGamalParameter elP = ElGamalParameter.getInstance(ASN1Primitive.fromByteArray(params)); + + currentSpec = new ElGamalParameterSpec(elP.getP(), elP.getG()); + } + catch (ClassCastException e) + { + throw new IOException("Not a valid ElGamal Parameter encoding."); + } + catch (ArrayIndexOutOfBoundsException e) + { + throw new IOException("Not a valid ElGamal Parameter encoding."); + } + } + + protected void engineInit( + byte[] params, + String format) + throws IOException + { + if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509")) + { + engineInit(params); + } + else + { + throw new IOException("Unknown parameter format " + format); + } + } + + protected String engineToString() + { + return "ElGamal Parameters"; + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/BCElGamalPrivateKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/BCElGamalPrivateKey.java new file mode 100644 index 00000000..73a965a7 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/BCElGamalPrivateKey.java @@ -0,0 +1,197 @@ +package org.spongycastle.jcajce.provider.asymmetric.elgamal; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; +import java.util.Enumeration; + +import javax.crypto.interfaces.DHPrivateKey; +import javax.crypto.spec.DHParameterSpec; +import javax.crypto.spec.DHPrivateKeySpec; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.oiw.ElGamalParameter; +import org.spongycastle.asn1.oiw.OIWObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.params.ElGamalPrivateKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl; +import org.spongycastle.jce.interfaces.ElGamalPrivateKey; +import org.spongycastle.jce.interfaces.PKCS12BagAttributeCarrier; +import org.spongycastle.jce.spec.ElGamalParameterSpec; +import org.spongycastle.jce.spec.ElGamalPrivateKeySpec; + +public class BCElGamalPrivateKey + implements ElGamalPrivateKey, DHPrivateKey, PKCS12BagAttributeCarrier +{ + static final long serialVersionUID = 4819350091141529678L; + + private BigInteger x; + + private transient ElGamalParameterSpec elSpec; + private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl(); + + protected BCElGamalPrivateKey() + { + } + + BCElGamalPrivateKey( + ElGamalPrivateKey key) + { + this.x = key.getX(); + this.elSpec = key.getParameters(); + } + + BCElGamalPrivateKey( + DHPrivateKey key) + { + this.x = key.getX(); + this.elSpec = new ElGamalParameterSpec(key.getParams().getP(), key.getParams().getG()); + } + + BCElGamalPrivateKey( + ElGamalPrivateKeySpec spec) + { + this.x = spec.getX(); + this.elSpec = new ElGamalParameterSpec(spec.getParams().getP(), spec.getParams().getG()); + } + + BCElGamalPrivateKey( + DHPrivateKeySpec spec) + { + this.x = spec.getX(); + this.elSpec = new ElGamalParameterSpec(spec.getP(), spec.getG()); + } + + BCElGamalPrivateKey( + PrivateKeyInfo info) + throws IOException + { + ElGamalParameter params = ElGamalParameter.getInstance(info.getPrivateKeyAlgorithm().getParameters()); + ASN1Integer derX = ASN1Integer.getInstance(info.parsePrivateKey()); + + this.x = derX.getValue(); + this.elSpec = new ElGamalParameterSpec(params.getP(), params.getG()); + } + + BCElGamalPrivateKey( + ElGamalPrivateKeyParameters params) + { + this.x = params.getX(); + this.elSpec = new ElGamalParameterSpec(params.getParameters().getP(), params.getParameters().getG()); + } + + public String getAlgorithm() + { + return "ElGamal"; + } + + /** + * return the encoding format we produce in getEncoded(). + * + * @return the string "PKCS#8" + */ + public String getFormat() + { + return "PKCS#8"; + } + + /** + * Return a PKCS8 representation of the key. The sequence returned + * represents a full PrivateKeyInfo object. + * + * @return a PKCS8 representation of the key. + */ + public byte[] getEncoded() + { + try + { + PrivateKeyInfo info = new PrivateKeyInfo(new AlgorithmIdentifier(OIWObjectIdentifiers.elGamalAlgorithm, new ElGamalParameter(elSpec.getP(), elSpec.getG())), new ASN1Integer(getX())); + + return info.getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + return null; + } + } + + public ElGamalParameterSpec getParameters() + { + return elSpec; + } + + public DHParameterSpec getParams() + { + return new DHParameterSpec(elSpec.getP(), elSpec.getG()); + } + + public BigInteger getX() + { + return x; + } + + public boolean equals( + Object o) + { + if (!(o instanceof DHPrivateKey)) + { + return false; + } + + DHPrivateKey other = (DHPrivateKey)o; + + return this.getX().equals(other.getX()) + && this.getParams().getG().equals(other.getParams().getG()) + && this.getParams().getP().equals(other.getParams().getP()) + && this.getParams().getL() == other.getParams().getL(); + } + + public int hashCode() + { + return this.getX().hashCode() ^ this.getParams().getG().hashCode() + ^ this.getParams().getP().hashCode() ^ this.getParams().getL(); + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + this.elSpec = new ElGamalParameterSpec((BigInteger)in.readObject(), (BigInteger)in.readObject()); + this.attrCarrier = new PKCS12BagAttributeCarrierImpl(); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(elSpec.getP()); + out.writeObject(elSpec.getG()); + } + + public void setBagAttribute( + ASN1ObjectIdentifier oid, + ASN1Encodable attribute) + { + attrCarrier.setBagAttribute(oid, attribute); + } + + public ASN1Encodable getBagAttribute( + ASN1ObjectIdentifier oid) + { + return attrCarrier.getBagAttribute(oid); + } + + public Enumeration getBagAttributeKeys() + { + return attrCarrier.getBagAttributeKeys(); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/BCElGamalPublicKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/BCElGamalPublicKey.java new file mode 100644 index 00000000..86ee5226 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/BCElGamalPublicKey.java @@ -0,0 +1,172 @@ +package org.spongycastle.jcajce.provider.asymmetric.elgamal; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; + +import javax.crypto.interfaces.DHPublicKey; +import javax.crypto.spec.DHParameterSpec; +import javax.crypto.spec.DHPublicKeySpec; + +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.oiw.ElGamalParameter; +import org.spongycastle.asn1.oiw.OIWObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.crypto.params.ElGamalPublicKeyParameters; +import org.spongycastle.jce.interfaces.ElGamalPublicKey; +import org.spongycastle.jce.spec.ElGamalParameterSpec; +import org.spongycastle.jce.spec.ElGamalPublicKeySpec; + +public class BCElGamalPublicKey + implements ElGamalPublicKey, DHPublicKey +{ + static final long serialVersionUID = 8712728417091216948L; + + private BigInteger y; + private transient ElGamalParameterSpec elSpec; + + BCElGamalPublicKey( + ElGamalPublicKeySpec spec) + { + this.y = spec.getY(); + this.elSpec = new ElGamalParameterSpec(spec.getParams().getP(), spec.getParams().getG()); + } + + BCElGamalPublicKey( + DHPublicKeySpec spec) + { + this.y = spec.getY(); + this.elSpec = new ElGamalParameterSpec(spec.getP(), spec.getG()); + } + + BCElGamalPublicKey( + ElGamalPublicKey key) + { + this.y = key.getY(); + this.elSpec = key.getParameters(); + } + + BCElGamalPublicKey( + DHPublicKey key) + { + this.y = key.getY(); + this.elSpec = new ElGamalParameterSpec(key.getParams().getP(), key.getParams().getG()); + } + + BCElGamalPublicKey( + ElGamalPublicKeyParameters params) + { + this.y = params.getY(); + this.elSpec = new ElGamalParameterSpec(params.getParameters().getP(), params.getParameters().getG()); + } + + BCElGamalPublicKey( + BigInteger y, + ElGamalParameterSpec elSpec) + { + this.y = y; + this.elSpec = elSpec; + } + + BCElGamalPublicKey( + SubjectPublicKeyInfo info) + { + ElGamalParameter params = ElGamalParameter.getInstance(info.getAlgorithm().getParameters()); + ASN1Integer derY = null; + + try + { + derY = (ASN1Integer)info.parsePublicKey(); + } + catch (IOException e) + { + throw new IllegalArgumentException("invalid info structure in DSA public key"); + } + + this.y = derY.getValue(); + this.elSpec = new ElGamalParameterSpec(params.getP(), params.getG()); + } + + public String getAlgorithm() + { + return "ElGamal"; + } + + public String getFormat() + { + return "X.509"; + } + + public byte[] getEncoded() + { + try + { + SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(OIWObjectIdentifiers.elGamalAlgorithm, new ElGamalParameter(elSpec.getP(), elSpec.getG())), new ASN1Integer(y)); + + return info.getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + return null; + } + } + + public ElGamalParameterSpec getParameters() + { + return elSpec; + } + + public DHParameterSpec getParams() + { + return new DHParameterSpec(elSpec.getP(), elSpec.getG()); + } + + public BigInteger getY() + { + return y; + } + + public int hashCode() + { + return this.getY().hashCode() ^ this.getParams().getG().hashCode() + ^ this.getParams().getP().hashCode() ^ this.getParams().getL(); + } + + public boolean equals( + Object o) + { + if (!(o instanceof DHPublicKey)) + { + return false; + } + + DHPublicKey other = (DHPublicKey)o; + + return this.getY().equals(other.getY()) + && this.getParams().getG().equals(other.getParams().getG()) + && this.getParams().getP().equals(other.getParams().getP()) + && this.getParams().getL() == other.getParams().getL(); + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + this.elSpec = new ElGamalParameterSpec((BigInteger)in.readObject(), (BigInteger)in.readObject()); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(elSpec.getP()); + out.writeObject(elSpec.getG()); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/CipherSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/CipherSpi.java new file mode 100644 index 00000000..123b88b5 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/CipherSpi.java @@ -0,0 +1,340 @@ +package org.spongycastle.jcajce.provider.asymmetric.elgamal; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.MGF1ParameterSpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.interfaces.DHKey; +import javax.crypto.spec.OAEPParameterSpec; +import javax.crypto.spec.PSource; + +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.BufferedAsymmetricBlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.encodings.ISO9796d1Encoding; +import org.spongycastle.crypto.encodings.OAEPEncoding; +import org.spongycastle.crypto.encodings.PKCS1Encoding; +import org.spongycastle.crypto.engines.ElGamalEngine; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.jcajce.provider.asymmetric.util.BaseCipherSpi; +import org.spongycastle.jcajce.provider.util.DigestFactory; +import org.spongycastle.jce.interfaces.ElGamalKey; +import org.spongycastle.jce.interfaces.ElGamalPrivateKey; +import org.spongycastle.jce.interfaces.ElGamalPublicKey; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.util.Strings; + +public class CipherSpi + extends BaseCipherSpi +{ + private BufferedAsymmetricBlockCipher cipher; + private AlgorithmParameterSpec paramSpec; + private AlgorithmParameters engineParams; + + public CipherSpi( + AsymmetricBlockCipher engine) + { + cipher = new BufferedAsymmetricBlockCipher(engine); + } + + private void initFromSpec( + OAEPParameterSpec pSpec) + throws NoSuchPaddingException + { + MGF1ParameterSpec mgfParams = (MGF1ParameterSpec)pSpec.getMGFParameters(); + Digest digest = DigestFactory.getDigest(mgfParams.getDigestAlgorithm()); + + if (digest == null) + { + throw new NoSuchPaddingException("no match on OAEP constructor for digest algorithm: "+ mgfParams.getDigestAlgorithm()); + } + + cipher = new BufferedAsymmetricBlockCipher(new OAEPEncoding(new ElGamalEngine(), digest, ((PSource.PSpecified)pSpec.getPSource()).getValue())); + paramSpec = pSpec; + } + + protected int engineGetBlockSize() + { + return cipher.getInputBlockSize(); + } + + protected int engineGetKeySize( + Key key) + { + if (key instanceof ElGamalKey) + { + ElGamalKey k = (ElGamalKey)key; + + return k.getParameters().getP().bitLength(); + } + else if (key instanceof DHKey) + { + DHKey k = (DHKey)key; + + return k.getParams().getP().bitLength(); + } + + throw new IllegalArgumentException("not an ElGamal key!"); + } + + protected int engineGetOutputSize( + int inputLen) + { + return cipher.getOutputBlockSize(); + } + + protected AlgorithmParameters engineGetParameters() + { + if (engineParams == null) + { + if (paramSpec != null) + { + try + { + engineParams = AlgorithmParameters.getInstance("OAEP", BouncyCastleProvider.PROVIDER_NAME); + engineParams.init(paramSpec); + } + catch (Exception e) + { + throw new RuntimeException(e.toString()); + } + } + } + + return engineParams; + } + + protected void engineSetMode( + String mode) + throws NoSuchAlgorithmException + { + String md = Strings.toUpperCase(mode); + + if (md.equals("NONE") || md.equals("ECB")) + { + return; + } + + throw new NoSuchAlgorithmException("can't support mode " + mode); + } + + protected void engineSetPadding( + String padding) + throws NoSuchPaddingException + { + String pad = Strings.toUpperCase(padding); + + if (pad.equals("NOPADDING")) + { + cipher = new BufferedAsymmetricBlockCipher(new ElGamalEngine()); + } + else if (pad.equals("PKCS1PADDING")) + { + cipher = new BufferedAsymmetricBlockCipher(new PKCS1Encoding(new ElGamalEngine())); + } + else if (pad.equals("ISO9796-1PADDING")) + { + cipher = new BufferedAsymmetricBlockCipher(new ISO9796d1Encoding(new ElGamalEngine())); + } + else if (pad.equals("OAEPPADDING")) + { + initFromSpec(OAEPParameterSpec.DEFAULT); + } + else if (pad.equals("OAEPWITHMD5ANDMGF1PADDING")) + { + initFromSpec(new OAEPParameterSpec("MD5", "MGF1", new MGF1ParameterSpec("MD5"), PSource.PSpecified.DEFAULT)); + } + else if (pad.equals("OAEPWITHSHA1ANDMGF1PADDING")) + { + initFromSpec(OAEPParameterSpec.DEFAULT); + } + else if (pad.equals("OAEPWITHSHA224ANDMGF1PADDING")) + { + initFromSpec(new OAEPParameterSpec("SHA-224", "MGF1", new MGF1ParameterSpec("SHA-224"), PSource.PSpecified.DEFAULT)); + } + else if (pad.equals("OAEPWITHSHA256ANDMGF1PADDING")) + { + initFromSpec(new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT)); + } + else if (pad.equals("OAEPWITHSHA384ANDMGF1PADDING")) + { + initFromSpec(new OAEPParameterSpec("SHA-384", "MGF1", MGF1ParameterSpec.SHA384, PSource.PSpecified.DEFAULT)); + } + else if (pad.equals("OAEPWITHSHA512ANDMGF1PADDING")) + { + initFromSpec(new OAEPParameterSpec("SHA-512", "MGF1", MGF1ParameterSpec.SHA512, PSource.PSpecified.DEFAULT)); + } + else + { + throw new NoSuchPaddingException(padding + " unavailable with ElGamal."); + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException + { + CipherParameters param; + + if (params == null) + { + if (key instanceof ElGamalPublicKey) + { + param = ElGamalUtil.generatePublicKeyParameter((PublicKey)key); + } + else if (key instanceof ElGamalPrivateKey) + { + param = ElGamalUtil.generatePrivateKeyParameter((PrivateKey)key); + } + else + { + throw new InvalidKeyException("unknown key type passed to ElGamal"); + } + } + else + { + throw new IllegalArgumentException("unknown parameter type."); + } + + if (random != null) + { + param = new ParametersWithRandom(param, random); + } + + switch (opmode) + { + case javax.crypto.Cipher.ENCRYPT_MODE: + case javax.crypto.Cipher.WRAP_MODE: + cipher.init(true, param); + break; + case javax.crypto.Cipher.DECRYPT_MODE: + case javax.crypto.Cipher.UNWRAP_MODE: + cipher.init(false, param); + break; + default: + throw new InvalidParameterException("unknown opmode " + opmode + " passed to ElGamal"); + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + throw new InvalidAlgorithmParameterException("can't handle parameters in ElGamal"); + } + + protected void engineInit( + int opmode, + Key key, + SecureRandom random) + throws InvalidKeyException + { + engineInit(opmode, key, (AlgorithmParameterSpec)null, random); + } + + protected byte[] engineUpdate( + byte[] input, + int inputOffset, + int inputLen) + { + cipher.processBytes(input, inputOffset, inputLen); + return null; + } + + protected int engineUpdate( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + { + cipher.processBytes(input, inputOffset, inputLen); + return 0; + } + + protected byte[] engineDoFinal( + byte[] input, + int inputOffset, + int inputLen) + throws IllegalBlockSizeException, BadPaddingException + { + cipher.processBytes(input, inputOffset, inputLen); + try + { + return cipher.doFinal(); + } + catch (InvalidCipherTextException e) + { + throw new BadPaddingException(e.getMessage()); + } + } + + protected int engineDoFinal( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + throws IllegalBlockSizeException, BadPaddingException + { + byte[] out; + + cipher.processBytes(input, inputOffset, inputLen); + + try + { + out = cipher.doFinal(); + } + catch (InvalidCipherTextException e) + { + throw new BadPaddingException(e.getMessage()); + } + + for (int i = 0; i != out.length; i++) + { + output[outputOffset + i] = out[i]; + } + + return out.length; + } + + /** + * classes that inherit from us. + */ + static public class NoPadding + extends CipherSpi + { + public NoPadding() + { + super(new ElGamalEngine()); + } + } + + static public class PKCS1v1_5Padding + extends CipherSpi + { + public PKCS1v1_5Padding() + { + super(new PKCS1Encoding(new ElGamalEngine())); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/ElGamalUtil.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/ElGamalUtil.java new file mode 100644 index 00000000..65c7c412 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/ElGamalUtil.java @@ -0,0 +1,66 @@ +package org.spongycastle.jcajce.provider.asymmetric.elgamal; + +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; + +import javax.crypto.interfaces.DHPrivateKey; +import javax.crypto.interfaces.DHPublicKey; + +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ElGamalParameters; +import org.spongycastle.crypto.params.ElGamalPrivateKeyParameters; +import org.spongycastle.crypto.params.ElGamalPublicKeyParameters; +import org.spongycastle.jce.interfaces.ElGamalPrivateKey; +import org.spongycastle.jce.interfaces.ElGamalPublicKey; + +/** + * utility class for converting jce/jca ElGamal objects + * objects into their org.spongycastle.crypto counterparts. + */ +public class ElGamalUtil +{ + static public AsymmetricKeyParameter generatePublicKeyParameter( + PublicKey key) + throws InvalidKeyException + { + if (key instanceof ElGamalPublicKey) + { + ElGamalPublicKey k = (ElGamalPublicKey)key; + + return new ElGamalPublicKeyParameters(k.getY(), + new ElGamalParameters(k.getParameters().getP(), k.getParameters().getG())); + } + else if (key instanceof DHPublicKey) + { + DHPublicKey k = (DHPublicKey)key; + + return new ElGamalPublicKeyParameters(k.getY(), + new ElGamalParameters(k.getParams().getP(), k.getParams().getG())); + } + + throw new InvalidKeyException("can't identify public key for El Gamal."); + } + + static public AsymmetricKeyParameter generatePrivateKeyParameter( + PrivateKey key) + throws InvalidKeyException + { + if (key instanceof ElGamalPrivateKey) + { + ElGamalPrivateKey k = (ElGamalPrivateKey)key; + + return new ElGamalPrivateKeyParameters(k.getX(), + new ElGamalParameters(k.getParameters().getP(), k.getParameters().getG())); + } + else if (key instanceof DHPrivateKey) + { + DHPrivateKey k = (DHPrivateKey)key; + + return new ElGamalPrivateKeyParameters(k.getX(), + new ElGamalParameters(k.getParams().getP(), k.getParams().getG())); + } + + throw new InvalidKeyException("can't identify private key for El Gamal."); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/KeyFactorySpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/KeyFactorySpi.java new file mode 100644 index 00000000..03c685e3 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/KeyFactorySpi.java @@ -0,0 +1,156 @@ +package org.spongycastle.jcajce.provider.asymmetric.elgamal; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +import javax.crypto.interfaces.DHPrivateKey; +import javax.crypto.interfaces.DHPublicKey; +import javax.crypto.spec.DHPrivateKeySpec; +import javax.crypto.spec.DHPublicKeySpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.oiw.OIWObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi; +import org.spongycastle.jce.interfaces.ElGamalPrivateKey; +import org.spongycastle.jce.interfaces.ElGamalPublicKey; +import org.spongycastle.jce.spec.ElGamalPrivateKeySpec; +import org.spongycastle.jce.spec.ElGamalPublicKeySpec; + +public class KeyFactorySpi + extends BaseKeyFactorySpi +{ + public KeyFactorySpi() + { + } + + protected PrivateKey engineGeneratePrivate( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof ElGamalPrivateKeySpec) + { + return new BCElGamalPrivateKey((ElGamalPrivateKeySpec)keySpec); + } + else if (keySpec instanceof DHPrivateKeySpec) + { + return new BCElGamalPrivateKey((DHPrivateKeySpec)keySpec); + } + + return super.engineGeneratePrivate(keySpec); + } + + protected PublicKey engineGeneratePublic( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof ElGamalPublicKeySpec) + { + return new BCElGamalPublicKey((ElGamalPublicKeySpec)keySpec); + } + else if (keySpec instanceof DHPublicKeySpec) + { + return new BCElGamalPublicKey((DHPublicKeySpec)keySpec); + } + return super.engineGeneratePublic(keySpec); + } + + protected KeySpec engineGetKeySpec( + Key key, + Class spec) + throws InvalidKeySpecException + { + if (spec.isAssignableFrom(DHPrivateKeySpec.class) && key instanceof DHPrivateKey) + { + DHPrivateKey k = (DHPrivateKey)key; + + return new DHPrivateKeySpec(k.getX(), k.getParams().getP(), k.getParams().getG()); + } + else if (spec.isAssignableFrom(DHPublicKeySpec.class) && key instanceof DHPublicKey) + { + DHPublicKey k = (DHPublicKey)key; + + return new DHPublicKeySpec(k.getY(), k.getParams().getP(), k.getParams().getG()); + } + + return super.engineGetKeySpec(key, spec); + } + + protected Key engineTranslateKey( + Key key) + throws InvalidKeyException + { + if (key instanceof DHPublicKey) + { + return new BCElGamalPublicKey((DHPublicKey)key); + } + else if (key instanceof DHPrivateKey) + { + return new BCElGamalPrivateKey((DHPrivateKey)key); + } + else if (key instanceof ElGamalPublicKey) + { + return new BCElGamalPublicKey((ElGamalPublicKey)key); + } + else if (key instanceof ElGamalPrivateKey) + { + return new BCElGamalPrivateKey((ElGamalPrivateKey)key); + } + + throw new InvalidKeyException("key type unknown"); + } + + public PrivateKey generatePrivate(PrivateKeyInfo info) + throws IOException + { + ASN1ObjectIdentifier algOid = info.getPrivateKeyAlgorithm().getAlgorithm(); + + if (algOid.equals(PKCSObjectIdentifiers.dhKeyAgreement)) + { + return new BCElGamalPrivateKey(info); + } + else if (algOid.equals(X9ObjectIdentifiers.dhpublicnumber)) + { + return new BCElGamalPrivateKey(info); + } + else if (algOid.equals(OIWObjectIdentifiers.elGamalAlgorithm)) + { + return new BCElGamalPrivateKey(info); + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } + + public PublicKey generatePublic(SubjectPublicKeyInfo info) + throws IOException + { + ASN1ObjectIdentifier algOid = info.getAlgorithm().getAlgorithm(); + + if (algOid.equals(PKCSObjectIdentifiers.dhKeyAgreement)) + { + return new BCElGamalPublicKey(info); + } + else if (algOid.equals(X9ObjectIdentifiers.dhpublicnumber)) + { + return new BCElGamalPublicKey(info); + } + else if (algOid.equals(OIWObjectIdentifiers.elGamalAlgorithm)) + { + return new BCElGamalPublicKey(info); + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/KeyPairGeneratorSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/KeyPairGeneratorSpi.java new file mode 100644 index 00000000..9c92b575 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/KeyPairGeneratorSpi.java @@ -0,0 +1,100 @@ +package org.spongycastle.jcajce.provider.asymmetric.elgamal; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.spec.DHParameterSpec; + +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.generators.ElGamalKeyPairGenerator; +import org.spongycastle.crypto.generators.ElGamalParametersGenerator; +import org.spongycastle.crypto.params.ElGamalKeyGenerationParameters; +import org.spongycastle.crypto.params.ElGamalParameters; +import org.spongycastle.crypto.params.ElGamalPrivateKeyParameters; +import org.spongycastle.crypto.params.ElGamalPublicKeyParameters; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ElGamalParameterSpec; + +public class KeyPairGeneratorSpi + extends java.security.KeyPairGenerator +{ + ElGamalKeyGenerationParameters param; + ElGamalKeyPairGenerator engine = new ElGamalKeyPairGenerator(); + int strength = 1024; + int certainty = 20; + SecureRandom random = new SecureRandom(); + boolean initialised = false; + + public KeyPairGeneratorSpi() + { + super("ElGamal"); + } + + public void initialize( + int strength, + SecureRandom random) + { + this.strength = strength; + this.random = random; + } + + public void initialize( + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + if (!(params instanceof ElGamalParameterSpec) && !(params instanceof DHParameterSpec)) + { + throw new InvalidAlgorithmParameterException("parameter object not a DHParameterSpec or an ElGamalParameterSpec"); + } + + if (params instanceof ElGamalParameterSpec) + { + ElGamalParameterSpec elParams = (ElGamalParameterSpec)params; + + param = new ElGamalKeyGenerationParameters(random, new ElGamalParameters(elParams.getP(), elParams.getG())); + } + else + { + DHParameterSpec dhParams = (DHParameterSpec)params; + + param = new ElGamalKeyGenerationParameters(random, new ElGamalParameters(dhParams.getP(), dhParams.getG(), dhParams.getL())); + } + + engine.init(param); + initialised = true; + } + + public KeyPair generateKeyPair() + { + if (!initialised) + { + DHParameterSpec dhParams = BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(strength); + + if (dhParams != null) + { + param = new ElGamalKeyGenerationParameters(random, new ElGamalParameters(dhParams.getP(), dhParams.getG(), dhParams.getL())); + } + else + { + ElGamalParametersGenerator pGen = new ElGamalParametersGenerator(); + + pGen.init(strength, certainty, random); + param = new ElGamalKeyGenerationParameters(random, pGen.generateParameters()); + } + + engine.init(param); + initialised = true; + } + + AsymmetricCipherKeyPair pair = engine.generateKeyPair(); + ElGamalPublicKeyParameters pub = (ElGamalPublicKeyParameters)pair.getPublic(); + ElGamalPrivateKeyParameters priv = (ElGamalPrivateKeyParameters)pair.getPrivate(); + + return new KeyPair(new BCElGamalPublicKey(pub), + new BCElGamalPrivateKey(priv)); + } +} + diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/AlgorithmParameterGeneratorSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/AlgorithmParameterGeneratorSpi.java new file mode 100644 index 00000000..0cf5908a --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/AlgorithmParameterGeneratorSpi.java @@ -0,0 +1,65 @@ +package org.spongycastle.jcajce.provider.asymmetric.gost; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import org.spongycastle.crypto.generators.GOST3410ParametersGenerator; +import org.spongycastle.crypto.params.GOST3410Parameters; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.GOST3410ParameterSpec; +import org.spongycastle.jce.spec.GOST3410PublicKeyParameterSetSpec; + +public abstract class AlgorithmParameterGeneratorSpi + extends java.security.AlgorithmParameterGeneratorSpi +{ + protected SecureRandom random; + protected int strength = 1024; + + protected void engineInit( + int strength, + SecureRandom random) + { + this.strength = strength; + this.random = random; + } + + protected void engineInit( + AlgorithmParameterSpec genParamSpec, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for GOST3410 parameter generation."); + } + + protected AlgorithmParameters engineGenerateParameters() + { + GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator(); + + if (random != null) + { + pGen.init(strength, 2, random); + } + else + { + pGen.init(strength, 2, new SecureRandom()); + } + + GOST3410Parameters p = pGen.generateParameters(); + + AlgorithmParameters params; + + try + { + params = AlgorithmParameters.getInstance("GOST3410", BouncyCastleProvider.PROVIDER_NAME); + params.init(new GOST3410ParameterSpec(new GOST3410PublicKeyParameterSetSpec(p.getP(), p.getQ(), p.getA()))); + } + catch (Exception e) + { + throw new RuntimeException(e.getMessage()); + } + + return params; + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/AlgorithmParametersSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/AlgorithmParametersSpi.java new file mode 100644 index 00000000..c046d008 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/AlgorithmParametersSpi.java @@ -0,0 +1,138 @@ +package org.spongycastle.jcajce.provider.asymmetric.gost; + +import java.io.IOException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; + +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters; +import org.spongycastle.jce.spec.GOST3410ParameterSpec; +import org.spongycastle.jce.spec.GOST3410PublicKeyParameterSetSpec; + +public class AlgorithmParametersSpi + extends java.security.AlgorithmParametersSpi +{ + GOST3410ParameterSpec currentSpec; + + protected boolean isASN1FormatString(String format) + { + return format == null || format.equals("ASN.1"); + } + + protected AlgorithmParameterSpec engineGetParameterSpec( + Class paramSpec) + throws InvalidParameterSpecException + { + if (paramSpec == null) + { + throw new NullPointerException("argument to getParameterSpec must not be null"); + } + + return localEngineGetParameterSpec(paramSpec); + } + + + /** + * Return the X.509 ASN.1 structure GOST3410Parameter. + * <p/> + * <pre> + * GOST3410Parameter ::= SEQUENCE { + * prime INTEGER, -- p + * subprime INTEGER, -- q + * base INTEGER, -- a} + * </pre> + */ + protected byte[] engineGetEncoded() + { + GOST3410PublicKeyAlgParameters gost3410P = new GOST3410PublicKeyAlgParameters(new ASN1ObjectIdentifier(currentSpec.getPublicKeyParamSetOID()), new ASN1ObjectIdentifier(currentSpec.getDigestParamSetOID()), new ASN1ObjectIdentifier(currentSpec.getEncryptionParamSetOID())); + + try + { + return gost3410P.getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + throw new RuntimeException("Error encoding GOST3410Parameters"); + } + } + + protected byte[] engineGetEncoded( + String format) + { + if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509")) + { + return engineGetEncoded(); + } + + return null; + } + + protected AlgorithmParameterSpec localEngineGetParameterSpec( + Class paramSpec) + throws InvalidParameterSpecException + { + if (paramSpec == GOST3410PublicKeyParameterSetSpec.class) + { + return currentSpec; + } + + throw new InvalidParameterSpecException("unknown parameter spec passed to GOST3410 parameters object."); + } + + protected void engineInit( + AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException + { + if (!(paramSpec instanceof GOST3410ParameterSpec)) + { + throw new InvalidParameterSpecException("GOST3410ParameterSpec required to initialise a GOST3410 algorithm parameters object"); + } + + this.currentSpec = (GOST3410ParameterSpec)paramSpec; + } + + protected void engineInit( + byte[] params) + throws IOException + { + try + { + ASN1Sequence seq = (ASN1Sequence)ASN1Primitive.fromByteArray(params); + + this.currentSpec = GOST3410ParameterSpec.fromPublicKeyAlg( + new GOST3410PublicKeyAlgParameters(seq)); + } + catch (ClassCastException e) + { + throw new IOException("Not a valid GOST3410 Parameter encoding."); + } + catch (ArrayIndexOutOfBoundsException e) + { + throw new IOException("Not a valid GOST3410 Parameter encoding."); + } + } + + protected void engineInit( + byte[] params, + String format) + throws IOException + { + if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509")) + { + engineInit(params); + } + else + { + throw new IOException("Unknown parameter format " + format); + } + } + + protected String engineToString() + { + return "GOST3410 Parameters"; + } + +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/BCGOST3410PrivateKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/BCGOST3410PrivateKey.java new file mode 100644 index 00000000..13340f1e --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/BCGOST3410PrivateKey.java @@ -0,0 +1,253 @@ +package org.spongycastle.jcajce.provider.asymmetric.gost; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.spongycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.params.GOST3410PrivateKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl; +import org.spongycastle.jce.interfaces.GOST3410Params; +import org.spongycastle.jce.interfaces.GOST3410PrivateKey; +import org.spongycastle.jce.interfaces.PKCS12BagAttributeCarrier; +import org.spongycastle.jce.spec.GOST3410ParameterSpec; +import org.spongycastle.jce.spec.GOST3410PrivateKeySpec; +import org.spongycastle.jce.spec.GOST3410PublicKeyParameterSetSpec; + +public class BCGOST3410PrivateKey + implements GOST3410PrivateKey, PKCS12BagAttributeCarrier +{ + static final long serialVersionUID = 8581661527592305464L; + + private BigInteger x; + + private transient GOST3410Params gost3410Spec; + private transient PKCS12BagAttributeCarrier attrCarrier = new PKCS12BagAttributeCarrierImpl(); + + protected BCGOST3410PrivateKey() + { + } + + BCGOST3410PrivateKey( + GOST3410PrivateKey key) + { + this.x = key.getX(); + this.gost3410Spec = key.getParameters(); + } + + BCGOST3410PrivateKey( + GOST3410PrivateKeySpec spec) + { + this.x = spec.getX(); + this.gost3410Spec = new GOST3410ParameterSpec(new GOST3410PublicKeyParameterSetSpec(spec.getP(), spec.getQ(), spec.getA())); + } + + BCGOST3410PrivateKey( + PrivateKeyInfo info) + throws IOException + { + GOST3410PublicKeyAlgParameters params = new GOST3410PublicKeyAlgParameters((ASN1Sequence)info.getAlgorithmId().getParameters()); + ASN1OctetString derX = ASN1OctetString.getInstance(info.parsePrivateKey()); + byte[] keyEnc = derX.getOctets(); + byte[] keyBytes = new byte[keyEnc.length]; + + for (int i = 0; i != keyEnc.length; i++) + { + keyBytes[i] = keyEnc[keyEnc.length - 1 - i]; // was little endian + } + + this.x = new BigInteger(1, keyBytes); + this.gost3410Spec = GOST3410ParameterSpec.fromPublicKeyAlg(params); + } + + BCGOST3410PrivateKey( + GOST3410PrivateKeyParameters params, + GOST3410ParameterSpec spec) + { + this.x = params.getX(); + this.gost3410Spec = spec; + + if (spec == null) + { + throw new IllegalArgumentException("spec is null"); + } + } + + public String getAlgorithm() + { + return "GOST3410"; + } + + /** + * return the encoding format we produce in getEncoded(). + * + * @return the string "PKCS#8" + */ + public String getFormat() + { + return "PKCS#8"; + } + + /** + * Return a PKCS8 representation of the key. The sequence returned + * represents a full PrivateKeyInfo object. + * + * @return a PKCS8 representation of the key. + */ + public byte[] getEncoded() + { + PrivateKeyInfo info; + byte[] keyEnc = this.getX().toByteArray(); + byte[] keyBytes; + + if (keyEnc[0] == 0) + { + keyBytes = new byte[keyEnc.length - 1]; + } + else + { + keyBytes = new byte[keyEnc.length]; + } + + for (int i = 0; i != keyBytes.length; i++) + { + keyBytes[i] = keyEnc[keyEnc.length - 1 - i]; // must be little endian + } + + try + { + if (gost3410Spec instanceof GOST3410ParameterSpec) + { + info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_94, new GOST3410PublicKeyAlgParameters(new ASN1ObjectIdentifier(gost3410Spec.getPublicKeyParamSetOID()), new ASN1ObjectIdentifier(gost3410Spec.getDigestParamSetOID()))), new DEROctetString(keyBytes)); + } + else + { + info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_94), new DEROctetString(keyBytes)); + } + + return info.getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + return null; + } + } + + public GOST3410Params getParameters() + { + return gost3410Spec; + } + + public BigInteger getX() + { + return x; + } + + public boolean equals( + Object o) + { + if (!(o instanceof GOST3410PrivateKey)) + { + return false; + } + + GOST3410PrivateKey other = (GOST3410PrivateKey)o; + + return this.getX().equals(other.getX()) + && this.getParameters().getPublicKeyParameters().equals(other.getParameters().getPublicKeyParameters()) + && this.getParameters().getDigestParamSetOID().equals(other.getParameters().getDigestParamSetOID()) + && compareObj(this.getParameters().getEncryptionParamSetOID(), other.getParameters().getEncryptionParamSetOID()); + } + + private boolean compareObj(Object o1, Object o2) + { + if (o1 == o2) + { + return true; + } + + if (o1 == null) + { + return false; + } + + return o1.equals(o2); + } + + public int hashCode() + { + return this.getX().hashCode() ^ gost3410Spec.hashCode(); + } + + public void setBagAttribute( + ASN1ObjectIdentifier oid, + ASN1Encodable attribute) + { + attrCarrier.setBagAttribute(oid, attribute); + } + + public ASN1Encodable getBagAttribute( + ASN1ObjectIdentifier oid) + { + return attrCarrier.getBagAttribute(oid); + } + + public Enumeration getBagAttributeKeys() + { + return attrCarrier.getBagAttributeKeys(); + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + String publicKeyParamSetOID = (String)in.readObject(); + if (publicKeyParamSetOID != null) + { + this.gost3410Spec = new GOST3410ParameterSpec(publicKeyParamSetOID, (String)in.readObject(), (String)in.readObject()); + } + else + { + this.gost3410Spec = new GOST3410ParameterSpec(new GOST3410PublicKeyParameterSetSpec((BigInteger)in.readObject(), (BigInteger)in.readObject(), (BigInteger)in.readObject())); + in.readObject(); + in.readObject(); + } + this.attrCarrier = new PKCS12BagAttributeCarrierImpl(); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + if (gost3410Spec.getPublicKeyParamSetOID() != null) + { + out.writeObject(gost3410Spec.getPublicKeyParamSetOID()); + out.writeObject(gost3410Spec.getDigestParamSetOID()); + out.writeObject(gost3410Spec.getEncryptionParamSetOID()); + } + else + { + out.writeObject(null); + out.writeObject(gost3410Spec.getPublicKeyParameters().getP()); + out.writeObject(gost3410Spec.getPublicKeyParameters().getQ()); + out.writeObject(gost3410Spec.getPublicKeyParameters().getA()); + out.writeObject(gost3410Spec.getDigestParamSetOID()); + out.writeObject(gost3410Spec.getEncryptionParamSetOID()); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/BCGOST3410PublicKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/BCGOST3410PublicKey.java new file mode 100644 index 00000000..658715c9 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/BCGOST3410PublicKey.java @@ -0,0 +1,224 @@ +package org.spongycastle.jcajce.provider.asymmetric.gost; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.spongycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.crypto.params.GOST3410PublicKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.KeyUtil; +import org.spongycastle.jce.interfaces.GOST3410Params; +import org.spongycastle.jce.interfaces.GOST3410PublicKey; +import org.spongycastle.jce.spec.GOST3410ParameterSpec; +import org.spongycastle.jce.spec.GOST3410PublicKeyParameterSetSpec; +import org.spongycastle.jce.spec.GOST3410PublicKeySpec; + +public class BCGOST3410PublicKey + implements GOST3410PublicKey +{ + static final long serialVersionUID = -6251023343619275990L; + + private BigInteger y; + private transient GOST3410Params gost3410Spec; + + BCGOST3410PublicKey( + GOST3410PublicKeySpec spec) + { + this.y = spec.getY(); + this.gost3410Spec = new GOST3410ParameterSpec(new GOST3410PublicKeyParameterSetSpec(spec.getP(), spec.getQ(), spec.getA())); + } + + BCGOST3410PublicKey( + GOST3410PublicKey key) + { + this.y = key.getY(); + this.gost3410Spec = key.getParameters(); + } + + BCGOST3410PublicKey( + GOST3410PublicKeyParameters params, + GOST3410ParameterSpec spec) + { + this.y = params.getY(); + this.gost3410Spec = spec; + } + + BCGOST3410PublicKey( + BigInteger y, + GOST3410ParameterSpec gost3410Spec) + { + this.y = y; + this.gost3410Spec = gost3410Spec; + } + + BCGOST3410PublicKey( + SubjectPublicKeyInfo info) + { + GOST3410PublicKeyAlgParameters params = new GOST3410PublicKeyAlgParameters((ASN1Sequence)info.getAlgorithmId().getParameters()); + DEROctetString derY; + + try + { + derY = (DEROctetString)info.parsePublicKey(); + + byte[] keyEnc = derY.getOctets(); + byte[] keyBytes = new byte[keyEnc.length]; + + for (int i = 0; i != keyEnc.length; i++) + { + keyBytes[i] = keyEnc[keyEnc.length - 1 - i]; // was little endian + } + + this.y = new BigInteger(1, keyBytes); + } + catch (IOException e) + { + throw new IllegalArgumentException("invalid info structure in GOST3410 public key"); + } + + this.gost3410Spec = GOST3410ParameterSpec.fromPublicKeyAlg(params); + } + + public String getAlgorithm() + { + return "GOST3410"; + } + + public String getFormat() + { + return "X.509"; + } + + public byte[] getEncoded() + { + SubjectPublicKeyInfo info; + byte[] keyEnc = this.getY().toByteArray(); + byte[] keyBytes; + + if (keyEnc[0] == 0) + { + keyBytes = new byte[keyEnc.length - 1]; + } + else + { + keyBytes = new byte[keyEnc.length]; + } + + for (int i = 0; i != keyBytes.length; i++) + { + keyBytes[i] = keyEnc[keyEnc.length - 1 - i]; // must be little endian + } + + try + { + if (gost3410Spec instanceof GOST3410ParameterSpec) + { + if (gost3410Spec.getEncryptionParamSetOID() != null) + { + info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_94, new GOST3410PublicKeyAlgParameters(new ASN1ObjectIdentifier(gost3410Spec.getPublicKeyParamSetOID()), new ASN1ObjectIdentifier(gost3410Spec.getDigestParamSetOID()), new ASN1ObjectIdentifier(gost3410Spec.getEncryptionParamSetOID()))), new DEROctetString(keyBytes)); + } + else + { + info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_94, new GOST3410PublicKeyAlgParameters(new ASN1ObjectIdentifier(gost3410Spec.getPublicKeyParamSetOID()), new ASN1ObjectIdentifier(gost3410Spec.getDigestParamSetOID()))), new DEROctetString(keyBytes)); + } + } + else + { + info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_94), new DEROctetString(keyBytes)); + } + + return KeyUtil.getEncodedSubjectPublicKeyInfo(info); + } + catch (IOException e) + { + return null; + } + } + + public GOST3410Params getParameters() + { + return gost3410Spec; + } + + public BigInteger getY() + { + return y; + } + + public String toString() + { + StringBuffer buf = new StringBuffer(); + String nl = System.getProperty("line.separator"); + + buf.append("GOST3410 Public Key").append(nl); + buf.append(" y: ").append(this.getY().toString(16)).append(nl); + + return buf.toString(); + } + + public boolean equals(Object o) + { + if (o instanceof BCGOST3410PublicKey) + { + BCGOST3410PublicKey other = (BCGOST3410PublicKey)o; + + return this.y.equals(other.y) && this.gost3410Spec.equals(other.gost3410Spec); + } + + return false; + } + + public int hashCode() + { + return y.hashCode() ^ gost3410Spec.hashCode(); + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + String publicKeyParamSetOID = (String)in.readObject(); + if (publicKeyParamSetOID != null) + { + this.gost3410Spec = new GOST3410ParameterSpec(publicKeyParamSetOID, (String)in.readObject(), (String)in.readObject()); + } + else + { + this.gost3410Spec = new GOST3410ParameterSpec(new GOST3410PublicKeyParameterSetSpec((BigInteger)in.readObject(), (BigInteger)in.readObject(), (BigInteger)in.readObject())); + in.readObject(); + in.readObject(); + } + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + if (gost3410Spec.getPublicKeyParamSetOID() != null) + { + out.writeObject(gost3410Spec.getPublicKeyParamSetOID()); + out.writeObject(gost3410Spec.getDigestParamSetOID()); + out.writeObject(gost3410Spec.getEncryptionParamSetOID()); + } + else + { + out.writeObject(null); + out.writeObject(gost3410Spec.getPublicKeyParameters().getP()); + out.writeObject(gost3410Spec.getPublicKeyParameters().getQ()); + out.writeObject(gost3410Spec.getPublicKeyParameters().getA()); + out.writeObject(gost3410Spec.getDigestParamSetOID()); + out.writeObject(gost3410Spec.getEncryptionParamSetOID()); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/KeyFactorySpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/KeyFactorySpi.java new file mode 100644 index 00000000..acf1f480 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/KeyFactorySpi.java @@ -0,0 +1,121 @@ +package org.spongycastle.jcajce.provider.asymmetric.gost; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi; +import org.spongycastle.jce.interfaces.GOST3410PrivateKey; +import org.spongycastle.jce.interfaces.GOST3410PublicKey; +import org.spongycastle.jce.spec.GOST3410PrivateKeySpec; +import org.spongycastle.jce.spec.GOST3410PublicKeyParameterSetSpec; +import org.spongycastle.jce.spec.GOST3410PublicKeySpec; + +public class KeyFactorySpi + extends BaseKeyFactorySpi +{ + public KeyFactorySpi() + { + } + + protected KeySpec engineGetKeySpec( + Key key, + Class spec) + throws InvalidKeySpecException + { + if (spec.isAssignableFrom(GOST3410PublicKeySpec.class) && key instanceof GOST3410PublicKey) + { + GOST3410PublicKey k = (GOST3410PublicKey)key; + GOST3410PublicKeyParameterSetSpec parameters = k.getParameters().getPublicKeyParameters(); + + return new GOST3410PublicKeySpec(k.getY(), parameters.getP(), parameters.getQ(), parameters.getA()); + } + else if (spec.isAssignableFrom(GOST3410PrivateKeySpec.class) && key instanceof GOST3410PrivateKey) + { + GOST3410PrivateKey k = (GOST3410PrivateKey)key; + GOST3410PublicKeyParameterSetSpec parameters = k.getParameters().getPublicKeyParameters(); + + return new GOST3410PrivateKeySpec(k.getX(), parameters.getP(), parameters.getQ(), parameters.getA()); + } + + return super.engineGetKeySpec(key, spec); + } + + protected Key engineTranslateKey( + Key key) + throws InvalidKeyException + { + if (key instanceof GOST3410PublicKey) + { + return new BCGOST3410PublicKey((GOST3410PublicKey)key); + } + else if (key instanceof GOST3410PrivateKey) + { + return new BCGOST3410PrivateKey((GOST3410PrivateKey)key); + } + + throw new InvalidKeyException("key type unknown"); + } + + protected PrivateKey engineGeneratePrivate( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof GOST3410PrivateKeySpec) + { + return new BCGOST3410PrivateKey((GOST3410PrivateKeySpec)keySpec); + } + + return super.engineGeneratePrivate(keySpec); + } + + protected PublicKey engineGeneratePublic( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof GOST3410PublicKeySpec) + { + return new BCGOST3410PublicKey((GOST3410PublicKeySpec)keySpec); + } + + return super.engineGeneratePublic(keySpec); + } + + public PrivateKey generatePrivate(PrivateKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getPrivateKeyAlgorithm().getAlgorithm(); + + if (algOid.equals(CryptoProObjectIdentifiers.gostR3410_94)) + { + return new BCGOST3410PrivateKey(keyInfo); + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } + + public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getAlgorithm().getAlgorithm(); + + if (algOid.equals(CryptoProObjectIdentifiers.gostR3410_94)) + { + return new BCGOST3410PublicKey(keyInfo); + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/KeyPairGeneratorSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/KeyPairGeneratorSpi.java new file mode 100644 index 00000000..67ac1b91 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/KeyPairGeneratorSpi.java @@ -0,0 +1,81 @@ +package org.spongycastle.jcajce.provider.asymmetric.gost; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import org.spongycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.generators.GOST3410KeyPairGenerator; +import org.spongycastle.crypto.params.GOST3410KeyGenerationParameters; +import org.spongycastle.crypto.params.GOST3410Parameters; +import org.spongycastle.crypto.params.GOST3410PrivateKeyParameters; +import org.spongycastle.crypto.params.GOST3410PublicKeyParameters; +import org.spongycastle.jce.spec.GOST3410ParameterSpec; +import org.spongycastle.jce.spec.GOST3410PublicKeyParameterSetSpec; + +public class KeyPairGeneratorSpi + extends java.security.KeyPairGenerator +{ + GOST3410KeyGenerationParameters param; + GOST3410KeyPairGenerator engine = new GOST3410KeyPairGenerator(); + GOST3410ParameterSpec gost3410Params; + int strength = 1024; + SecureRandom random = null; + boolean initialised = false; + + public KeyPairGeneratorSpi() + { + super("GOST3410"); + } + + public void initialize( + int strength, + SecureRandom random) + { + this.strength = strength; + this.random = random; + } + + private void init( + GOST3410ParameterSpec gParams, + SecureRandom random) + { + GOST3410PublicKeyParameterSetSpec spec = gParams.getPublicKeyParameters(); + + param = new GOST3410KeyGenerationParameters(random, new GOST3410Parameters(spec.getP(), spec.getQ(), spec.getA())); + + engine.init(param); + + initialised = true; + gost3410Params = gParams; + } + + public void initialize( + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + if (!(params instanceof GOST3410ParameterSpec)) + { + throw new InvalidAlgorithmParameterException("parameter object not a GOST3410ParameterSpec"); + } + + init((GOST3410ParameterSpec)params, random); + } + + public KeyPair generateKeyPair() + { + if (!initialised) + { + init(new GOST3410ParameterSpec(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_A.getId()), new SecureRandom()); + } + + AsymmetricCipherKeyPair pair = engine.generateKeyPair(); + GOST3410PublicKeyParameters pub = (GOST3410PublicKeyParameters)pair.getPublic(); + GOST3410PrivateKeyParameters priv = (GOST3410PrivateKeyParameters)pair.getPrivate(); + + return new KeyPair(new BCGOST3410PublicKey(pub, gost3410Params), new BCGOST3410PrivateKey(priv, gost3410Params)); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/SignatureSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/SignatureSpi.java new file mode 100644 index 00000000..199af6dc --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/SignatureSpi.java @@ -0,0 +1,229 @@ +package org.spongycastle.jcajce.provider.asymmetric.gost; + +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.SignatureException; +import java.security.spec.AlgorithmParameterSpec; + +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x509.X509ObjectIdentifiers; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DSA; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.GOST3411Digest; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.crypto.signers.GOST3410Signer; +import org.spongycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.spongycastle.jce.interfaces.ECKey; +import org.spongycastle.jce.interfaces.ECPublicKey; +import org.spongycastle.jce.interfaces.GOST3410Key; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jcajce.provider.asymmetric.util.GOST3410Util; + +public class SignatureSpi + extends java.security.SignatureSpi + implements PKCSObjectIdentifiers, X509ObjectIdentifiers +{ + private Digest digest; + private DSA signer; + private SecureRandom random; + + public SignatureSpi() + { + this.digest = new GOST3411Digest(); + this.signer = new GOST3410Signer(); + } + + protected void engineInitVerify( + PublicKey publicKey) + throws InvalidKeyException + { + CipherParameters param; + + if (publicKey instanceof ECPublicKey) + { + param = ECUtil.generatePublicKeyParameter(publicKey); + } + else if (publicKey instanceof GOST3410Key) + { + param = GOST3410Util.generatePublicKeyParameter(publicKey); + } + else + { + try + { + byte[] bytes = publicKey.getEncoded(); + + publicKey = BouncyCastleProvider.getPublicKey(SubjectPublicKeyInfo.getInstance(bytes)); + + if (publicKey instanceof ECPublicKey) + { + param = ECUtil.generatePublicKeyParameter(publicKey); + } + else + { + throw new InvalidKeyException("can't recognise key type in DSA based signer"); + } + } + catch (Exception e) + { + throw new InvalidKeyException("can't recognise key type in DSA based signer"); + } + } + + digest.reset(); + signer.init(false, param); + } + + protected void engineInitSign( + PrivateKey privateKey, + SecureRandom random) + throws InvalidKeyException + { + this.random = random; + engineInitSign(privateKey); + } + + protected void engineInitSign( + PrivateKey privateKey) + throws InvalidKeyException + { + CipherParameters param; + + if (privateKey instanceof ECKey) + { + param = ECUtil.generatePrivateKeyParameter(privateKey); + } + else + { + param = GOST3410Util.generatePrivateKeyParameter(privateKey); + } + + digest.reset(); + + if (random != null) + { + signer.init(true, new ParametersWithRandom(param, random)); + } + else + { + signer.init(true, param); + } + } + + protected void engineUpdate( + byte b) + throws SignatureException + { + digest.update(b); + } + + protected void engineUpdate( + byte[] b, + int off, + int len) + throws SignatureException + { + digest.update(b, off, len); + } + + protected byte[] engineSign() + throws SignatureException + { + byte[] hash = new byte[digest.getDigestSize()]; + + digest.doFinal(hash, 0); + + try + { + byte[] sigBytes = new byte[64]; + BigInteger[] sig = signer.generateSignature(hash); + byte[] r = sig[0].toByteArray(); + byte[] s = sig[1].toByteArray(); + + if (s[0] != 0) + { + System.arraycopy(s, 0, sigBytes, 32 - s.length, s.length); + } + else + { + System.arraycopy(s, 1, sigBytes, 32 - (s.length - 1), s.length - 1); + } + + if (r[0] != 0) + { + System.arraycopy(r, 0, sigBytes, 64 - r.length, r.length); + } + else + { + System.arraycopy(r, 1, sigBytes, 64 - (r.length - 1), r.length - 1); + } + + return sigBytes; + } + catch (Exception e) + { + throw new SignatureException(e.toString()); + } + } + + protected boolean engineVerify( + byte[] sigBytes) + throws SignatureException + { + byte[] hash = new byte[digest.getDigestSize()]; + + digest.doFinal(hash, 0); + + BigInteger[] sig; + + try + { + byte[] r = new byte[32]; + byte[] s = new byte[32]; + + System.arraycopy(sigBytes, 0, s, 0, 32); + + System.arraycopy(sigBytes, 32, r, 0, 32); + + sig = new BigInteger[2]; + sig[0] = new BigInteger(1, r); + sig[1] = new BigInteger(1, s); + } + catch (Exception e) + { + throw new SignatureException("error decoding signature bytes."); + } + + return signer.verifySignature(hash, sig[0], sig[1]); + } + + protected void engineSetParameter( + AlgorithmParameterSpec params) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated replaced with <a href = "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)"> + */ + protected void engineSetParameter( + String param, + Object value) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated + */ + protected Object engineGetParameter( + String param) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ies/AlgorithmParametersSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ies/AlgorithmParametersSpi.java new file mode 100644 index 00000000..f5de87c8 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ies/AlgorithmParametersSpi.java @@ -0,0 +1,138 @@ +package org.spongycastle.jcajce.provider.asymmetric.ies; + +import java.io.IOException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.jce.spec.IESParameterSpec; + +public class AlgorithmParametersSpi + extends java.security.AlgorithmParametersSpi +{ + protected boolean isASN1FormatString(String format) + { + return format == null || format.equals("ASN.1"); + } + + protected AlgorithmParameterSpec engineGetParameterSpec( + Class paramSpec) + throws InvalidParameterSpecException + { + if (paramSpec == null) + { + throw new NullPointerException("argument to getParameterSpec must not be null"); + } + + return localEngineGetParameterSpec(paramSpec); + } + + IESParameterSpec currentSpec; + + /** + * in the absence of a standard way of doing it this will do for + * now... + */ + protected byte[] engineGetEncoded() + { + try + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new DEROctetString(currentSpec.getDerivationV())); + v.add(new DEROctetString(currentSpec.getEncodingV())); + v.add(new ASN1Integer(currentSpec.getMacKeySize())); + + return new DERSequence(v).getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + throw new RuntimeException("Error encoding IESParameters"); + } + } + + protected byte[] engineGetEncoded( + String format) + { + if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509")) + { + return engineGetEncoded(); + } + + return null; + } + + protected AlgorithmParameterSpec localEngineGetParameterSpec( + Class paramSpec) + throws InvalidParameterSpecException + { + if (paramSpec == IESParameterSpec.class) + { + return currentSpec; + } + + throw new InvalidParameterSpecException("unknown parameter spec passed to ElGamal parameters object."); + } + + protected void engineInit( + AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException + { + if (!(paramSpec instanceof IESParameterSpec)) + { + throw new InvalidParameterSpecException("IESParameterSpec required to initialise a IES algorithm parameters object"); + } + + this.currentSpec = (IESParameterSpec)paramSpec; + } + + protected void engineInit( + byte[] params) + throws IOException + { + try + { + ASN1Sequence s = (ASN1Sequence)ASN1Primitive.fromByteArray(params); + + this.currentSpec = new IESParameterSpec( + ((ASN1OctetString)s.getObjectAt(0)).getOctets(), + ((ASN1OctetString)s.getObjectAt(0)).getOctets(), + ((ASN1Integer)s.getObjectAt(0)).getValue().intValue()); + } + catch (ClassCastException e) + { + throw new IOException("Not a valid IES Parameter encoding."); + } + catch (ArrayIndexOutOfBoundsException e) + { + throw new IOException("Not a valid IES Parameter encoding."); + } + } + + protected void engineInit( + byte[] params, + String format) + throws IOException + { + if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509")) + { + engineInit(params); + } + else + { + throw new IOException("Unknown parameter format " + format); + } + } + + protected String engineToString() + { + return "IES Parameters"; + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ies/CipherSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ies/CipherSpi.java new file mode 100644 index 00000000..9337df04 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ies/CipherSpi.java @@ -0,0 +1,363 @@ +package org.spongycastle.jcajce.provider.asymmetric.ies; + +import java.io.ByteArrayOutputStream; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.interfaces.DHPrivateKey; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.agreement.DHBasicAgreement; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.engines.IESEngine; +import org.spongycastle.crypto.generators.KDF2BytesGenerator; +import org.spongycastle.crypto.macs.HMac; +import org.spongycastle.crypto.params.IESParameters; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jcajce.provider.asymmetric.util.DHUtil; +import org.spongycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.spongycastle.jce.interfaces.ECPrivateKey; +import org.spongycastle.jce.interfaces.ECPublicKey; +import org.spongycastle.jce.interfaces.IESKey; +import org.spongycastle.jce.spec.IESParameterSpec; + +public class CipherSpi + extends javax.crypto.CipherSpi +{ + private IESEngine cipher; + private int state = -1; + private ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + private AlgorithmParameters engineParam = null; + private IESParameterSpec engineParams = null; + + // + // specs we can handle. + // + private Class[] availableSpecs = + { + IESParameterSpec.class + }; + + public CipherSpi( + IESEngine engine) + { + cipher = engine; + } + + protected int engineGetBlockSize() + { + return 0; + } + + protected byte[] engineGetIV() + { + return null; + } + + protected int engineGetKeySize( + Key key) + { + if (!(key instanceof IESKey)) + { + throw new IllegalArgumentException("must be passed IE key"); + } + + IESKey ieKey = (IESKey)key; + + if (ieKey.getPrivate() instanceof DHPrivateKey) + { + DHPrivateKey k = (DHPrivateKey)ieKey.getPrivate(); + + return k.getX().bitLength(); + } + else if (ieKey.getPrivate() instanceof ECPrivateKey) + { + ECPrivateKey k = (ECPrivateKey)ieKey.getPrivate(); + + return k.getD().bitLength(); + } + + throw new IllegalArgumentException("not an IE key!"); + } + + protected int engineGetOutputSize( + int inputLen) + { + if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE) + { + return buffer.size() + inputLen + 20; /* SHA1 MAC size */ + } + else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE) + { + return buffer.size() + inputLen - 20; + } + else + { + throw new IllegalStateException("cipher not initialised"); + } + } + + protected AlgorithmParameters engineGetParameters() + { + if (engineParam == null) + { + if (engineParams != null) + { + String name = "IES"; + + try + { + engineParam = AlgorithmParameters.getInstance(name, BouncyCastleProvider.PROVIDER_NAME); + engineParam.init(engineParams); + } + catch (Exception e) + { + throw new RuntimeException(e.toString()); + } + } + } + + return engineParam; + } + + protected void engineSetMode( + String mode) + { + throw new IllegalArgumentException("can't support mode " + mode); + } + + protected void engineSetPadding( + String padding) + throws NoSuchPaddingException + { + throw new NoSuchPaddingException(padding + " unavailable with RSA."); + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + if (!(key instanceof IESKey)) + { + throw new InvalidKeyException("must be passed IES key"); + } + + if (params == null && (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE)) + { + // + // if nothing is specified we set up for a 128 bit mac, with + // 128 bit derivation vectors. + // + byte[] d = new byte[16]; + byte[] e = new byte[16]; + + if (random == null) + { + random = new SecureRandom(); + } + + random.nextBytes(d); + random.nextBytes(e); + + params = new IESParameterSpec(d, e, 128); + } + else if (!(params instanceof IESParameterSpec)) + { + throw new InvalidAlgorithmParameterException("must be passed IES parameters"); + } + + IESKey ieKey = (IESKey)key; + + CipherParameters pubKey; + CipherParameters privKey; + + if (ieKey.getPublic() instanceof ECPublicKey) + { + pubKey = ECUtil.generatePublicKeyParameter(ieKey.getPublic()); + privKey = ECUtil.generatePrivateKeyParameter(ieKey.getPrivate()); + } + else + { + pubKey = DHUtil.generatePublicKeyParameter(ieKey.getPublic()); + privKey = DHUtil.generatePrivateKeyParameter(ieKey.getPrivate()); + } + + this.engineParams = (IESParameterSpec)params; + + IESParameters p = new IESParameters(engineParams.getDerivationV(), engineParams.getEncodingV(), engineParams.getMacKeySize()); + + this.state = opmode; + + buffer.reset(); + + switch (opmode) + { + case Cipher.ENCRYPT_MODE: + case Cipher.WRAP_MODE: + cipher.init(true, privKey, pubKey, p); + break; + case Cipher.DECRYPT_MODE: + case Cipher.UNWRAP_MODE: + cipher.init(false, privKey, pubKey, p); + break; + default: + System.out.println("eeek!"); + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + AlgorithmParameterSpec paramSpec = null; + + if (params != null) + { + for (int i = 0; i != availableSpecs.length; i++) + { + try + { + paramSpec = params.getParameterSpec(availableSpecs[i]); + break; + } + catch (Exception e) + { + continue; + } + } + + if (paramSpec == null) + { + throw new InvalidAlgorithmParameterException("can't handle parameter " + params.toString()); + } + } + + engineParam = params; + engineInit(opmode, key, paramSpec, random); + } + + protected void engineInit( + int opmode, + Key key, + SecureRandom random) + throws InvalidKeyException + { + if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) + { + try + { + engineInit(opmode, key, (AlgorithmParameterSpec)null, random); + return; + } + catch (InvalidAlgorithmParameterException e) + { + // fall through... + } + } + + throw new IllegalArgumentException("can't handle null parameter spec in IES"); + } + + protected byte[] engineUpdate( + byte[] input, + int inputOffset, + int inputLen) + { + buffer.write(input, inputOffset, inputLen); + return null; + } + + protected int engineUpdate( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + { + buffer.write(input, inputOffset, inputLen); + return 0; + } + + protected byte[] engineDoFinal( + byte[] input, + int inputOffset, + int inputLen) + throws IllegalBlockSizeException, BadPaddingException + { + if (inputLen != 0) + { + buffer.write(input, inputOffset, inputLen); + } + + try + { + byte[] buf = buffer.toByteArray(); + + buffer.reset(); + + return cipher.processBlock(buf, 0, buf.length); + } + catch (InvalidCipherTextException e) + { + throw new BadPaddingException(e.getMessage()); + } + } + + protected int engineDoFinal( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + throws IllegalBlockSizeException, BadPaddingException + { + if (inputLen != 0) + { + buffer.write(input, inputOffset, inputLen); + } + + try + { + byte[] buf = buffer.toByteArray(); + + buffer.reset(); + + buf = cipher.processBlock(buf, 0, buf.length); + + System.arraycopy(buf, 0, output, outputOffset, buf.length); + + return buf.length; + } + catch (InvalidCipherTextException e) + { + throw new BadPaddingException(e.getMessage()); + } + } + + static public class IES + extends CipherSpi + { + public IES() + { + super(new IESEngine( + new DHBasicAgreement(), + new KDF2BytesGenerator(new SHA1Digest()), + new HMac(new SHA1Digest()))); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java new file mode 100644 index 00000000..6402bd28 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java @@ -0,0 +1,265 @@ +package org.spongycastle.jcajce.provider.asymmetric.rsa; + +import java.io.IOException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.security.spec.MGF1ParameterSpec; +import java.security.spec.PSSParameterSpec; + +import javax.crypto.spec.OAEPParameterSpec; +import javax.crypto.spec.PSource; + +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.RSAESOAEPparams; +import org.spongycastle.asn1.pkcs.RSASSAPSSparams; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.jcajce.provider.util.DigestFactory; + +public abstract class AlgorithmParametersSpi + extends java.security.AlgorithmParametersSpi +{ + protected boolean isASN1FormatString(String format) + { + return format == null || format.equals("ASN.1"); + } + + protected AlgorithmParameterSpec engineGetParameterSpec( + Class paramSpec) + throws InvalidParameterSpecException + { + if (paramSpec == null) + { + throw new NullPointerException("argument to getParameterSpec must not be null"); + } + + return localEngineGetParameterSpec(paramSpec); + } + + protected abstract AlgorithmParameterSpec localEngineGetParameterSpec(Class paramSpec) + throws InvalidParameterSpecException; + + public static class OAEP + extends AlgorithmParametersSpi + { + OAEPParameterSpec currentSpec; + + /** + * Return the PKCS#1 ASN.1 structure RSAES-OAEP-params. + */ + protected byte[] engineGetEncoded() + { + AlgorithmIdentifier hashAlgorithm = new AlgorithmIdentifier( + DigestFactory.getOID(currentSpec.getDigestAlgorithm()), + DERNull.INSTANCE); + MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec)currentSpec.getMGFParameters(); + AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier( + PKCSObjectIdentifiers.id_mgf1, + new AlgorithmIdentifier(DigestFactory.getOID(mgfSpec.getDigestAlgorithm()), DERNull.INSTANCE)); + PSource.PSpecified pSource = (PSource.PSpecified)currentSpec.getPSource(); + AlgorithmIdentifier pSourceAlgorithm = new AlgorithmIdentifier( + PKCSObjectIdentifiers.id_pSpecified, new DEROctetString(pSource.getValue())); + RSAESOAEPparams oaepP = new RSAESOAEPparams(hashAlgorithm, maskGenAlgorithm, pSourceAlgorithm); + + try + { + return oaepP.getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + throw new RuntimeException("Error encoding OAEPParameters"); + } + } + + protected byte[] engineGetEncoded( + String format) + { + if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509")) + { + return engineGetEncoded(); + } + + return null; + } + + protected AlgorithmParameterSpec localEngineGetParameterSpec( + Class paramSpec) + throws InvalidParameterSpecException + { + if (paramSpec == OAEPParameterSpec.class && currentSpec != null) + { + return currentSpec; + } + + throw new InvalidParameterSpecException("unknown parameter spec passed to OAEP parameters object."); + } + + protected void engineInit( + AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException + { + if (!(paramSpec instanceof OAEPParameterSpec)) + { + throw new InvalidParameterSpecException("OAEPParameterSpec required to initialise an OAEP algorithm parameters object"); + } + + this.currentSpec = (OAEPParameterSpec)paramSpec; + } + + protected void engineInit( + byte[] params) + throws IOException + { + try + { + RSAESOAEPparams oaepP = RSAESOAEPparams.getInstance(params); + + currentSpec = new OAEPParameterSpec( + oaepP.getHashAlgorithm().getAlgorithm().getId(), + oaepP.getMaskGenAlgorithm().getAlgorithm().getId(), + new MGF1ParameterSpec(AlgorithmIdentifier.getInstance(oaepP.getMaskGenAlgorithm().getParameters()).getAlgorithm().getId()), + new PSource.PSpecified(ASN1OctetString.getInstance(oaepP.getPSourceAlgorithm().getParameters()).getOctets())); + } + catch (ClassCastException e) + { + throw new IOException("Not a valid OAEP Parameter encoding."); + } + catch (ArrayIndexOutOfBoundsException e) + { + throw new IOException("Not a valid OAEP Parameter encoding."); + } + } + + protected void engineInit( + byte[] params, + String format) + throws IOException + { + if (format.equalsIgnoreCase("X.509") + || format.equalsIgnoreCase("ASN.1")) + { + engineInit(params); + } + else + { + throw new IOException("Unknown parameter format " + format); + } + } + + protected String engineToString() + { + return "OAEP Parameters"; + } + } + + public static class PSS + extends AlgorithmParametersSpi + { + PSSParameterSpec currentSpec; + + /** + * Return the PKCS#1 ASN.1 structure RSASSA-PSS-params. + */ + protected byte[] engineGetEncoded() + throws IOException + { + PSSParameterSpec pssSpec = currentSpec; + AlgorithmIdentifier hashAlgorithm = new AlgorithmIdentifier( + DigestFactory.getOID(pssSpec.getDigestAlgorithm()), + DERNull.INSTANCE); + MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec)pssSpec.getMGFParameters(); + AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier( + PKCSObjectIdentifiers.id_mgf1, + new AlgorithmIdentifier(DigestFactory.getOID(mgfSpec.getDigestAlgorithm()), DERNull.INSTANCE)); + RSASSAPSSparams pssP = new RSASSAPSSparams(hashAlgorithm, maskGenAlgorithm, new ASN1Integer(pssSpec.getSaltLength()), new ASN1Integer(pssSpec.getTrailerField())); + + return pssP.getEncoded("DER"); + } + + protected byte[] engineGetEncoded( + String format) + throws IOException + { + if (format.equalsIgnoreCase("X.509") + || format.equalsIgnoreCase("ASN.1")) + { + return engineGetEncoded(); + } + + return null; + } + + protected AlgorithmParameterSpec localEngineGetParameterSpec( + Class paramSpec) + throws InvalidParameterSpecException + { + if (paramSpec == PSSParameterSpec.class && currentSpec != null) + { + return currentSpec; + } + + throw new InvalidParameterSpecException("unknown parameter spec passed to PSS parameters object."); + } + + protected void engineInit( + AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException + { + if (!(paramSpec instanceof PSSParameterSpec)) + { + throw new InvalidParameterSpecException("PSSParameterSpec required to initialise an PSS algorithm parameters object"); + } + + this.currentSpec = (PSSParameterSpec)paramSpec; + } + + protected void engineInit( + byte[] params) + throws IOException + { + try + { + RSASSAPSSparams pssP = RSASSAPSSparams.getInstance(params); + + currentSpec = new PSSParameterSpec( + pssP.getHashAlgorithm().getAlgorithm().getId(), + pssP.getMaskGenAlgorithm().getAlgorithm().getId(), + new MGF1ParameterSpec(AlgorithmIdentifier.getInstance(pssP.getMaskGenAlgorithm().getParameters()).getAlgorithm().getId()), + pssP.getSaltLength().intValue(), + pssP.getTrailerField().intValue()); + } + catch (ClassCastException e) + { + throw new IOException("Not a valid PSS Parameter encoding."); + } + catch (ArrayIndexOutOfBoundsException e) + { + throw new IOException("Not a valid PSS Parameter encoding."); + } + } + + protected void engineInit( + byte[] params, + String format) + throws IOException + { + if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509")) + { + engineInit(params); + } + else + { + throw new IOException("Unknown parameter format " + format); + } + } + + protected String engineToString() + { + return "PSS Parameters"; + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/BCRSAPrivateCrtKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/BCRSAPrivateCrtKey.java new file mode 100644 index 00000000..31772a14 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/BCRSAPrivateCrtKey.java @@ -0,0 +1,241 @@ +package org.spongycastle.jcajce.provider.asymmetric.rsa; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.spec.RSAPrivateCrtKeySpec; + +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.pkcs.RSAPrivateKey; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.KeyUtil; + +/** + * A provider representation for a RSA private key, with CRT factors included. + */ +public class BCRSAPrivateCrtKey + extends BCRSAPrivateKey + implements RSAPrivateCrtKey +{ + static final long serialVersionUID = 7834723820638524718L; + + private BigInteger publicExponent; + private BigInteger primeP; + private BigInteger primeQ; + private BigInteger primeExponentP; + private BigInteger primeExponentQ; + private BigInteger crtCoefficient; + + /** + * construct a private key from it's org.spongycastle.crypto equivalent. + * + * @param key the parameters object representing the private key. + */ + BCRSAPrivateCrtKey( + RSAPrivateCrtKeyParameters key) + { + super(key); + + this.publicExponent = key.getPublicExponent(); + this.primeP = key.getP(); + this.primeQ = key.getQ(); + this.primeExponentP = key.getDP(); + this.primeExponentQ = key.getDQ(); + this.crtCoefficient = key.getQInv(); + } + + /** + * construct a private key from an RSAPrivateCrtKeySpec + * + * @param spec the spec to be used in construction. + */ + BCRSAPrivateCrtKey( + RSAPrivateCrtKeySpec spec) + { + this.modulus = spec.getModulus(); + this.publicExponent = spec.getPublicExponent(); + this.privateExponent = spec.getPrivateExponent(); + this.primeP = spec.getPrimeP(); + this.primeQ = spec.getPrimeQ(); + this.primeExponentP = spec.getPrimeExponentP(); + this.primeExponentQ = spec.getPrimeExponentQ(); + this.crtCoefficient = spec.getCrtCoefficient(); + } + + /** + * construct a private key from another RSAPrivateCrtKey. + * + * @param key the object implementing the RSAPrivateCrtKey interface. + */ + BCRSAPrivateCrtKey( + RSAPrivateCrtKey key) + { + this.modulus = key.getModulus(); + this.publicExponent = key.getPublicExponent(); + this.privateExponent = key.getPrivateExponent(); + this.primeP = key.getPrimeP(); + this.primeQ = key.getPrimeQ(); + this.primeExponentP = key.getPrimeExponentP(); + this.primeExponentQ = key.getPrimeExponentQ(); + this.crtCoefficient = key.getCrtCoefficient(); + } + + /** + * construct an RSA key from a private key info object. + */ + BCRSAPrivateCrtKey( + PrivateKeyInfo info) + throws IOException + { + this(RSAPrivateKey.getInstance(info.parsePrivateKey())); + } + + /** + * construct an RSA key from a ASN.1 RSA private key object. + */ + BCRSAPrivateCrtKey( + RSAPrivateKey key) + { + this.modulus = key.getModulus(); + this.publicExponent = key.getPublicExponent(); + this.privateExponent = key.getPrivateExponent(); + this.primeP = key.getPrime1(); + this.primeQ = key.getPrime2(); + this.primeExponentP = key.getExponent1(); + this.primeExponentQ = key.getExponent2(); + this.crtCoefficient = key.getCoefficient(); + } + + /** + * return the encoding format we produce in getEncoded(). + * + * @return the encoding format we produce in getEncoded(). + */ + public String getFormat() + { + return "PKCS#8"; + } + + /** + * Return a PKCS8 representation of the key. The sequence returned + * represents a full PrivateKeyInfo object. + * + * @return a PKCS8 representation of the key. + */ + public byte[] getEncoded() + { + return KeyUtil.getEncodedPrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new RSAPrivateKey(getModulus(), getPublicExponent(), getPrivateExponent(), getPrimeP(), getPrimeQ(), getPrimeExponentP(), getPrimeExponentQ(), getCrtCoefficient())); + } + + /** + * return the public exponent. + * + * @return the public exponent. + */ + public BigInteger getPublicExponent() + { + return publicExponent; + } + + /** + * return the prime P. + * + * @return the prime P. + */ + public BigInteger getPrimeP() + { + return primeP; + } + + /** + * return the prime Q. + * + * @return the prime Q. + */ + public BigInteger getPrimeQ() + { + return primeQ; + } + + /** + * return the prime exponent for P. + * + * @return the prime exponent for P. + */ + public BigInteger getPrimeExponentP() + { + return primeExponentP; + } + + /** + * return the prime exponent for Q. + * + * @return the prime exponent for Q. + */ + public BigInteger getPrimeExponentQ() + { + return primeExponentQ; + } + + /** + * return the CRT coefficient. + * + * @return the CRT coefficient. + */ + public BigInteger getCrtCoefficient() + { + return crtCoefficient; + } + + public int hashCode() + { + return this.getModulus().hashCode() + ^ this.getPublicExponent().hashCode() + ^ this.getPrivateExponent().hashCode(); + } + + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (!(o instanceof RSAPrivateCrtKey)) + { + return false; + } + + RSAPrivateCrtKey key = (RSAPrivateCrtKey)o; + + return this.getModulus().equals(key.getModulus()) + && this.getPublicExponent().equals(key.getPublicExponent()) + && this.getPrivateExponent().equals(key.getPrivateExponent()) + && this.getPrimeP().equals(key.getPrimeP()) + && this.getPrimeQ().equals(key.getPrimeQ()) + && this.getPrimeExponentP().equals(key.getPrimeExponentP()) + && this.getPrimeExponentQ().equals(key.getPrimeExponentQ()) + && this.getCrtCoefficient().equals(key.getCrtCoefficient()); + } + + public String toString() + { + StringBuffer buf = new StringBuffer(); + String nl = System.getProperty("line.separator"); + + buf.append("RSA Private CRT Key").append(nl); + buf.append(" modulus: ").append(this.getModulus().toString(16)).append(nl); + buf.append(" public exponent: ").append(this.getPublicExponent().toString(16)).append(nl); + buf.append(" private exponent: ").append(this.getPrivateExponent().toString(16)).append(nl); + buf.append(" primeP: ").append(this.getPrimeP().toString(16)).append(nl); + buf.append(" primeQ: ").append(this.getPrimeQ().toString(16)).append(nl); + buf.append(" primeExponentP: ").append(this.getPrimeExponentP().toString(16)).append(nl); + buf.append(" primeExponentQ: ").append(this.getPrimeExponentQ().toString(16)).append(nl); + buf.append(" crtCoefficient: ").append(this.getCrtCoefficient().toString(16)).append(nl); + + return buf.toString(); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/BCRSAPrivateKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/BCRSAPrivateKey.java new file mode 100644 index 00000000..1e63f3b5 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/BCRSAPrivateKey.java @@ -0,0 +1,145 @@ +package org.spongycastle.jcajce.provider.asymmetric.rsa; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; +import java.security.interfaces.RSAPrivateKey; +import java.security.spec.RSAPrivateKeySpec; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.params.RSAKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.KeyUtil; +import org.spongycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl; +import org.spongycastle.jce.interfaces.PKCS12BagAttributeCarrier; + +public class BCRSAPrivateKey + implements RSAPrivateKey, PKCS12BagAttributeCarrier +{ + static final long serialVersionUID = 5110188922551353628L; + + private static BigInteger ZERO = BigInteger.valueOf(0); + + protected BigInteger modulus; + protected BigInteger privateExponent; + + private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl(); + + protected BCRSAPrivateKey() + { + } + + BCRSAPrivateKey( + RSAKeyParameters key) + { + this.modulus = key.getModulus(); + this.privateExponent = key.getExponent(); + } + + BCRSAPrivateKey( + RSAPrivateKeySpec spec) + { + this.modulus = spec.getModulus(); + this.privateExponent = spec.getPrivateExponent(); + } + + BCRSAPrivateKey( + RSAPrivateKey key) + { + this.modulus = key.getModulus(); + this.privateExponent = key.getPrivateExponent(); + } + + BCRSAPrivateKey(org.spongycastle.asn1.pkcs.RSAPrivateKey key) + { + this.modulus = key.getModulus(); + this.privateExponent = key.getPrivateExponent(); + } + + public BigInteger getModulus() + { + return modulus; + } + + public BigInteger getPrivateExponent() + { + return privateExponent; + } + + public String getAlgorithm() + { + return "RSA"; + } + + public String getFormat() + { + return "PKCS#8"; + } + + public byte[] getEncoded() + { + return KeyUtil.getEncodedPrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new org.spongycastle.asn1.pkcs.RSAPrivateKey(getModulus(), ZERO, getPrivateExponent(), ZERO, ZERO, ZERO, ZERO, ZERO)); + } + + public boolean equals(Object o) + { + if (!(o instanceof RSAPrivateKey)) + { + return false; + } + + if (o == this) + { + return true; + } + + RSAPrivateKey key = (RSAPrivateKey)o; + + return getModulus().equals(key.getModulus()) + && getPrivateExponent().equals(key.getPrivateExponent()); + } + + public int hashCode() + { + return getModulus().hashCode() ^ getPrivateExponent().hashCode(); + } + + public void setBagAttribute( + ASN1ObjectIdentifier oid, + ASN1Encodable attribute) + { + attrCarrier.setBagAttribute(oid, attribute); + } + + public ASN1Encodable getBagAttribute( + ASN1ObjectIdentifier oid) + { + return attrCarrier.getBagAttribute(oid); + } + + public Enumeration getBagAttributeKeys() + { + return attrCarrier.getBagAttributeKeys(); + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + this.attrCarrier = new PKCS12BagAttributeCarrierImpl(); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/BCRSAPublicKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/BCRSAPublicKey.java new file mode 100644 index 00000000..7a4454c5 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/BCRSAPublicKey.java @@ -0,0 +1,177 @@ +package org.spongycastle.jcajce.provider.asymmetric.rsa; + +import java.io.EOFException; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OptionalDataException; +import java.math.BigInteger; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.RSAPublicKeySpec; + +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.crypto.params.RSAKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.KeyUtil; + +public class BCRSAPublicKey + implements RSAPublicKey +{ + private static final AlgorithmIdentifier DEFAULT_ALGORITHM_IDENTIFIER = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE); + + static final long serialVersionUID = 2675817738516720772L; + + private BigInteger modulus; + private BigInteger publicExponent; + private transient AlgorithmIdentifier algorithmIdentifier; + + BCRSAPublicKey( + RSAKeyParameters key) + { + this.algorithmIdentifier = DEFAULT_ALGORITHM_IDENTIFIER; + this.modulus = key.getModulus(); + this.publicExponent = key.getExponent(); + } + + BCRSAPublicKey( + RSAPublicKeySpec spec) + { + this.algorithmIdentifier = DEFAULT_ALGORITHM_IDENTIFIER; + this.modulus = spec.getModulus(); + this.publicExponent = spec.getPublicExponent(); + } + + BCRSAPublicKey( + RSAPublicKey key) + { + this.algorithmIdentifier = DEFAULT_ALGORITHM_IDENTIFIER; + this.modulus = key.getModulus(); + this.publicExponent = key.getPublicExponent(); + } + + BCRSAPublicKey( + SubjectPublicKeyInfo info) + { + populateFromPublicKeyInfo(info); + } + + private void populateFromPublicKeyInfo(SubjectPublicKeyInfo info) + { + try + { + org.spongycastle.asn1.pkcs.RSAPublicKey pubKey = org.spongycastle.asn1.pkcs.RSAPublicKey.getInstance(info.parsePublicKey()); + + this.algorithmIdentifier = info.getAlgorithm(); + this.modulus = pubKey.getModulus(); + this.publicExponent = pubKey.getPublicExponent(); + } + catch (IOException e) + { + throw new IllegalArgumentException("invalid info structure in RSA public key"); + } + } + + /** + * return the modulus. + * + * @return the modulus. + */ + public BigInteger getModulus() + { + return modulus; + } + + /** + * return the public exponent. + * + * @return the public exponent. + */ + public BigInteger getPublicExponent() + { + return publicExponent; + } + + public String getAlgorithm() + { + return "RSA"; + } + + public String getFormat() + { + return "X.509"; + } + + public byte[] getEncoded() + { + return KeyUtil.getEncodedSubjectPublicKeyInfo(algorithmIdentifier, new org.spongycastle.asn1.pkcs.RSAPublicKey(getModulus(), getPublicExponent())); + } + + public int hashCode() + { + return this.getModulus().hashCode() ^ this.getPublicExponent().hashCode(); + } + + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (!(o instanceof RSAPublicKey)) + { + return false; + } + + RSAPublicKey key = (RSAPublicKey)o; + + return getModulus().equals(key.getModulus()) + && getPublicExponent().equals(key.getPublicExponent()); + } + + public String toString() + { + StringBuffer buf = new StringBuffer(); + String nl = System.getProperty("line.separator"); + + buf.append("RSA Public Key").append(nl); + buf.append(" modulus: ").append(this.getModulus().toString(16)).append(nl); + buf.append(" public exponent: ").append(this.getPublicExponent().toString(16)).append(nl); + + return buf.toString(); + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + try + { + algorithmIdentifier = AlgorithmIdentifier.getInstance(in.readObject()); + } + catch (OptionalDataException e) + { + algorithmIdentifier = DEFAULT_ALGORITHM_IDENTIFIER; + } + catch (EOFException e) + { + algorithmIdentifier = DEFAULT_ALGORITHM_IDENTIFIER; + } + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + if (!algorithmIdentifier.equals(DEFAULT_ALGORITHM_IDENTIFIER)) + { + out.writeObject(algorithmIdentifier.getEncoded()); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java new file mode 100644 index 00000000..6edd4aef --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java @@ -0,0 +1,586 @@ +package org.spongycastle.jcajce.provider.asymmetric.rsa; + +import java.io.ByteArrayOutputStream; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.security.spec.MGF1ParameterSpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.OAEPParameterSpec; +import javax.crypto.spec.PSource; + +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.encodings.ISO9796d1Encoding; +import org.spongycastle.crypto.encodings.OAEPEncoding; +import org.spongycastle.crypto.encodings.PKCS1Encoding; +import org.spongycastle.crypto.engines.RSABlindedEngine; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.jcajce.provider.asymmetric.util.BaseCipherSpi; +import org.spongycastle.jcajce.provider.util.DigestFactory; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.util.Strings; + +public class CipherSpi + extends BaseCipherSpi +{ + private AsymmetricBlockCipher cipher; + private AlgorithmParameterSpec paramSpec; + private AlgorithmParameters engineParams; + private boolean publicKeyOnly = false; + private boolean privateKeyOnly = false; + private ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + public CipherSpi( + AsymmetricBlockCipher engine) + { + cipher = engine; + } + + public CipherSpi( + OAEPParameterSpec pSpec) + { + try + { + initFromSpec(pSpec); + } + catch (NoSuchPaddingException e) + { + throw new IllegalArgumentException(e.getMessage()); + } + } + + public CipherSpi( + boolean publicKeyOnly, + boolean privateKeyOnly, + AsymmetricBlockCipher engine) + { + this.publicKeyOnly = publicKeyOnly; + this.privateKeyOnly = privateKeyOnly; + cipher = engine; + } + + private void initFromSpec( + OAEPParameterSpec pSpec) + throws NoSuchPaddingException + { + MGF1ParameterSpec mgfParams = (MGF1ParameterSpec)pSpec.getMGFParameters(); + Digest digest = DigestFactory.getDigest(mgfParams.getDigestAlgorithm()); + + if (digest == null) + { + throw new NoSuchPaddingException("no match on OAEP constructor for digest algorithm: "+ mgfParams.getDigestAlgorithm()); + } + + cipher = new OAEPEncoding(new RSABlindedEngine(), digest, ((PSource.PSpecified)pSpec.getPSource()).getValue()); + paramSpec = pSpec; + } + + protected int engineGetBlockSize() + { + try + { + return cipher.getInputBlockSize(); + } + catch (NullPointerException e) + { + throw new IllegalStateException("RSA Cipher not initialised"); + } + } + + protected int engineGetKeySize( + Key key) + { + if (key instanceof RSAPrivateKey) + { + RSAPrivateKey k = (RSAPrivateKey)key; + + return k.getModulus().bitLength(); + } + else if (key instanceof RSAPublicKey) + { + RSAPublicKey k = (RSAPublicKey)key; + + return k.getModulus().bitLength(); + } + + throw new IllegalArgumentException("not an RSA key!"); + } + + protected int engineGetOutputSize( + int inputLen) + { + try + { + return cipher.getOutputBlockSize(); + } + catch (NullPointerException e) + { + throw new IllegalStateException("RSA Cipher not initialised"); + } + } + + protected AlgorithmParameters engineGetParameters() + { + if (engineParams == null) + { + if (paramSpec != null) + { + try + { + engineParams = AlgorithmParameters.getInstance("OAEP", BouncyCastleProvider.PROVIDER_NAME); + engineParams.init(paramSpec); + } + catch (Exception e) + { + throw new RuntimeException(e.toString()); + } + } + } + + return engineParams; + } + + protected void engineSetMode( + String mode) + throws NoSuchAlgorithmException + { + String md = Strings.toUpperCase(mode); + + if (md.equals("NONE") || md.equals("ECB")) + { + return; + } + + if (md.equals("1")) + { + privateKeyOnly = true; + publicKeyOnly = false; + return; + } + else if (md.equals("2")) + { + privateKeyOnly = false; + publicKeyOnly = true; + return; + } + + throw new NoSuchAlgorithmException("can't support mode " + mode); + } + + protected void engineSetPadding( + String padding) + throws NoSuchPaddingException + { + String pad = Strings.toUpperCase(padding); + + if (pad.equals("NOPADDING")) + { + cipher = new RSABlindedEngine(); + } + else if (pad.equals("PKCS1PADDING")) + { + cipher = new PKCS1Encoding(new RSABlindedEngine()); + } + else if (pad.equals("ISO9796-1PADDING")) + { + cipher = new ISO9796d1Encoding(new RSABlindedEngine()); + } + else if (pad.equals("OAEPWITHMD5ANDMGF1PADDING")) + { + initFromSpec(new OAEPParameterSpec("MD5", "MGF1", new MGF1ParameterSpec("MD5"), PSource.PSpecified.DEFAULT)); + } + else if (pad.equals("OAEPPADDING")) + { + initFromSpec(OAEPParameterSpec.DEFAULT); + } + else if (pad.equals("OAEPWITHSHA1ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-1ANDMGF1PADDING")) + { + initFromSpec(OAEPParameterSpec.DEFAULT); + } + else if (pad.equals("OAEPWITHSHA224ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-224ANDMGF1PADDING")) + { + initFromSpec(new OAEPParameterSpec("SHA-224", "MGF1", new MGF1ParameterSpec("SHA-224"), PSource.PSpecified.DEFAULT)); + } + else if (pad.equals("OAEPWITHSHA256ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-256ANDMGF1PADDING")) + { + initFromSpec(new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT)); + } + else if (pad.equals("OAEPWITHSHA384ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-384ANDMGF1PADDING")) + { + initFromSpec(new OAEPParameterSpec("SHA-384", "MGF1", MGF1ParameterSpec.SHA384, PSource.PSpecified.DEFAULT)); + } + else if (pad.equals("OAEPWITHSHA512ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-512ANDMGF1PADDING")) + { + initFromSpec(new OAEPParameterSpec("SHA-512", "MGF1", MGF1ParameterSpec.SHA512, PSource.PSpecified.DEFAULT)); + } + else + { + throw new NoSuchPaddingException(padding + " unavailable with RSA."); + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + CipherParameters param; + + if (params == null || params instanceof OAEPParameterSpec) + { + if (key instanceof RSAPublicKey) + { + if (privateKeyOnly && opmode == Cipher.ENCRYPT_MODE) + { + throw new InvalidKeyException( + "mode 1 requires RSAPrivateKey"); + } + + param = RSAUtil.generatePublicKeyParameter((RSAPublicKey)key); + } + else if (key instanceof RSAPrivateKey) + { + if (publicKeyOnly && opmode == Cipher.ENCRYPT_MODE) + { + throw new InvalidKeyException( + "mode 2 requires RSAPublicKey"); + } + + param = RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)key); + } + else + { + throw new InvalidKeyException("unknown key type passed to RSA"); + } + + if (params != null) + { + OAEPParameterSpec spec = (OAEPParameterSpec)params; + + paramSpec = params; + + if (!spec.getMGFAlgorithm().equalsIgnoreCase("MGF1") && !spec.getMGFAlgorithm().equals(PKCSObjectIdentifiers.id_mgf1.getId())) + { + throw new InvalidAlgorithmParameterException("unknown mask generation function specified"); + } + + if (!(spec.getMGFParameters() instanceof MGF1ParameterSpec)) + { + throw new InvalidAlgorithmParameterException("unkown MGF parameters"); + } + + Digest digest = DigestFactory.getDigest(spec.getDigestAlgorithm()); + + if (digest == null) + { + throw new InvalidAlgorithmParameterException("no match on digest algorithm: "+ spec.getDigestAlgorithm()); + } + + MGF1ParameterSpec mgfParams = (MGF1ParameterSpec)spec.getMGFParameters(); + Digest mgfDigest = DigestFactory.getDigest(mgfParams.getDigestAlgorithm()); + + if (mgfDigest == null) + { + throw new InvalidAlgorithmParameterException("no match on MGF digest algorithm: "+ mgfParams.getDigestAlgorithm()); + } + + cipher = new OAEPEncoding(new RSABlindedEngine(), digest, mgfDigest, ((PSource.PSpecified)spec.getPSource()).getValue()); + } + } + else + { + throw new InvalidAlgorithmParameterException("unknown parameter type: " + params.getClass().getName()); + } + + if (!(cipher instanceof RSABlindedEngine)) + { + if (random != null) + { + param = new ParametersWithRandom(param, random); + } + else + { + param = new ParametersWithRandom(param, new SecureRandom()); + } + } + + bOut.reset(); + + switch (opmode) + { + case Cipher.ENCRYPT_MODE: + case Cipher.WRAP_MODE: + cipher.init(true, param); + break; + case Cipher.DECRYPT_MODE: + case Cipher.UNWRAP_MODE: + cipher.init(false, param); + break; + default: + throw new InvalidParameterException("unknown opmode " + opmode + " passed to RSA"); + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + AlgorithmParameterSpec paramSpec = null; + + if (params != null) + { + try + { + paramSpec = params.getParameterSpec(OAEPParameterSpec.class); + } + catch (InvalidParameterSpecException e) + { + throw new InvalidAlgorithmParameterException("cannot recognise parameters: " + e.toString(), e); + } + } + + engineParams = params; + engineInit(opmode, key, paramSpec, random); + } + + protected void engineInit( + int opmode, + Key key, + SecureRandom random) + throws InvalidKeyException + { + try + { + engineInit(opmode, key, (AlgorithmParameterSpec)null, random); + } + catch (InvalidAlgorithmParameterException e) + { + // this shouldn't happen + throw new InvalidKeyException("Eeeek! " + e.toString(), e); + } + } + + protected byte[] engineUpdate( + byte[] input, + int inputOffset, + int inputLen) + { + bOut.write(input, inputOffset, inputLen); + + if (cipher instanceof RSABlindedEngine) + { + if (bOut.size() > cipher.getInputBlockSize() + 1) + { + throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); + } + } + else + { + if (bOut.size() > cipher.getInputBlockSize()) + { + throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); + } + } + + return null; + } + + protected int engineUpdate( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + { + bOut.write(input, inputOffset, inputLen); + + if (cipher instanceof RSABlindedEngine) + { + if (bOut.size() > cipher.getInputBlockSize() + 1) + { + throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); + } + } + else + { + if (bOut.size() > cipher.getInputBlockSize()) + { + throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); + } + } + + return 0; + } + + protected byte[] engineDoFinal( + byte[] input, + int inputOffset, + int inputLen) + throws IllegalBlockSizeException, BadPaddingException + { + if (input != null) + { + bOut.write(input, inputOffset, inputLen); + } + + if (cipher instanceof RSABlindedEngine) + { + if (bOut.size() > cipher.getInputBlockSize() + 1) + { + throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); + } + } + else + { + if (bOut.size() > cipher.getInputBlockSize()) + { + throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); + } + } + + try + { + byte[] bytes = bOut.toByteArray(); + + bOut.reset(); + + return cipher.processBlock(bytes, 0, bytes.length); + } + catch (InvalidCipherTextException e) + { + throw new BadPaddingException(e.getMessage()); + } + } + + protected int engineDoFinal( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + throws IllegalBlockSizeException, BadPaddingException + { + if (input != null) + { + bOut.write(input, inputOffset, inputLen); + } + + if (cipher instanceof RSABlindedEngine) + { + if (bOut.size() > cipher.getInputBlockSize() + 1) + { + throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); + } + } + else + { + if (bOut.size() > cipher.getInputBlockSize()) + { + throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); + } + } + + byte[] out; + + try + { + byte[] bytes = bOut.toByteArray(); + + out = cipher.processBlock(bytes, 0, bytes.length); + } + catch (InvalidCipherTextException e) + { + throw new BadPaddingException(e.getMessage()); + } + finally + { + bOut.reset(); + } + + for (int i = 0; i != out.length; i++) + { + output[outputOffset + i] = out[i]; + } + + return out.length; + } + + /** + * classes that inherit from us. + */ + + static public class NoPadding + extends CipherSpi + { + public NoPadding() + { + super(new RSABlindedEngine()); + } + } + + static public class PKCS1v1_5Padding + extends CipherSpi + { + public PKCS1v1_5Padding() + { + super(new PKCS1Encoding(new RSABlindedEngine())); + } + } + + static public class PKCS1v1_5Padding_PrivateOnly + extends CipherSpi + { + public PKCS1v1_5Padding_PrivateOnly() + { + super(false, true, new PKCS1Encoding(new RSABlindedEngine())); + } + } + + static public class PKCS1v1_5Padding_PublicOnly + extends CipherSpi + { + public PKCS1v1_5Padding_PublicOnly() + { + super(true, false, new PKCS1Encoding(new RSABlindedEngine())); + } + } + + static public class OAEPPadding + extends CipherSpi + { + public OAEPPadding() + { + super(OAEPParameterSpec.DEFAULT); + } + } + + static public class ISO9796d1Padding + extends CipherSpi + { + public ISO9796d1Padding() + { + super(new ISO9796d1Encoding(new RSABlindedEngine())); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/DigestSignatureSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/DigestSignatureSpi.java new file mode 100644 index 00000000..90e3667c --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/DigestSignatureSpi.java @@ -0,0 +1,366 @@ +package org.spongycastle.jcajce.provider.asymmetric.rsa; + +import java.io.IOException; +import java.security.AlgorithmParameters; +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.SignatureSpi; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.AlgorithmParameterSpec; + +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DERNull; +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.asn1.x509.DigestInfo; +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.MD2Digest; +import org.spongycastle.crypto.digests.MD4Digest; +import org.spongycastle.crypto.digests.MD5Digest; +import org.spongycastle.crypto.digests.NullDigest; +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.crypto.encodings.PKCS1Encoding; +import org.spongycastle.crypto.engines.RSABlindedEngine; + +public class DigestSignatureSpi + extends SignatureSpi +{ + private Digest digest; + private AsymmetricBlockCipher cipher; + private AlgorithmIdentifier algId; + + // care - this constructor is actually used by outside organisations + protected DigestSignatureSpi( + Digest digest, + AsymmetricBlockCipher cipher) + { + this.digest = digest; + this.cipher = cipher; + this.algId = null; + } + + // care - this constructor is actually used by outside organisations + protected DigestSignatureSpi( + ASN1ObjectIdentifier objId, + Digest digest, + AsymmetricBlockCipher cipher) + { + this.digest = digest; + this.cipher = cipher; + this.algId = new AlgorithmIdentifier(objId, DERNull.INSTANCE); + } + + protected void engineInitVerify( + PublicKey publicKey) + throws InvalidKeyException + { + if (!(publicKey instanceof RSAPublicKey)) + { + throw new InvalidKeyException("Supplied key (" + getType(publicKey) + ") is not a RSAPublicKey instance"); + } + + CipherParameters param = RSAUtil.generatePublicKeyParameter((RSAPublicKey)publicKey); + + digest.reset(); + cipher.init(false, param); + } + + protected void engineInitSign( + PrivateKey privateKey) + throws InvalidKeyException + { + if (!(privateKey instanceof RSAPrivateKey)) + { + throw new InvalidKeyException("Supplied key (" + getType(privateKey) + ") is not a RSAPrivateKey instance"); + } + + CipherParameters param = RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)privateKey); + + digest.reset(); + + cipher.init(true, param); + } + + private String getType( + Object o) + { + if (o == null) + { + return null; + } + + return o.getClass().getName(); + } + + protected void engineUpdate( + byte b) + throws SignatureException + { + digest.update(b); + } + + protected void engineUpdate( + byte[] b, + int off, + int len) + throws SignatureException + { + digest.update(b, off, len); + } + + protected byte[] engineSign() + throws SignatureException + { + byte[] hash = new byte[digest.getDigestSize()]; + + digest.doFinal(hash, 0); + + try + { + byte[] bytes = derEncode(hash); + + return cipher.processBlock(bytes, 0, bytes.length); + } + catch (ArrayIndexOutOfBoundsException e) + { + throw new SignatureException("key too small for signature type"); + } + catch (Exception e) + { + throw new SignatureException(e.toString()); + } + } + + protected boolean engineVerify( + byte[] sigBytes) + throws SignatureException + { + byte[] hash = new byte[digest.getDigestSize()]; + + digest.doFinal(hash, 0); + + byte[] sig; + byte[] expected; + + try + { + sig = cipher.processBlock(sigBytes, 0, sigBytes.length); + + expected = derEncode(hash); + } + catch (Exception e) + { + return false; + } + + if (sig.length == expected.length) + { + for (int i = 0; i < sig.length; i++) + { + if (sig[i] != expected[i]) + { + return false; + } + } + } + else if (sig.length == expected.length - 2) // NULL left out + { + int sigOffset = sig.length - hash.length - 2; + int expectedOffset = expected.length - hash.length - 2; + + expected[1] -= 2; // adjust lengths + expected[3] -= 2; + + for (int i = 0; i < hash.length; i++) + { + if (sig[sigOffset + i] != expected[expectedOffset + i]) // check hash + { + return false; + } + } + + for (int i = 0; i < sigOffset; i++) + { + if (sig[i] != expected[i]) // check header less NULL + { + return false; + } + } + } + else + { + return false; + } + + return true; + } + + protected void engineSetParameter( + AlgorithmParameterSpec params) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated replaced with <a href = "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)"> + */ + protected void engineSetParameter( + String param, + Object value) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated + */ + protected Object engineGetParameter( + String param) + { + return null; + } + + protected AlgorithmParameters engineGetParameters() + { + return null; + } + + private byte[] derEncode( + byte[] hash) + throws IOException + { + if (algId == null) + { + // For raw RSA, the DigestInfo must be prepared externally + return hash; + } + + DigestInfo dInfo = new DigestInfo(algId, hash); + + return dInfo.getEncoded(ASN1Encoding.DER); + } + + static public class SHA1 + extends DigestSignatureSpi + { + public SHA1() + { + super(OIWObjectIdentifiers.idSHA1, new SHA1Digest(), new PKCS1Encoding(new RSABlindedEngine())); + } + } + + static public class SHA224 + extends DigestSignatureSpi + { + public SHA224() + { + super(NISTObjectIdentifiers.id_sha224, new SHA224Digest(), new PKCS1Encoding(new RSABlindedEngine())); + } + } + + static public class SHA256 + extends DigestSignatureSpi + { + public SHA256() + { + super(NISTObjectIdentifiers.id_sha256, new SHA256Digest(), new PKCS1Encoding(new RSABlindedEngine())); + } + } + + static public class SHA384 + extends DigestSignatureSpi + { + public SHA384() + { + super(NISTObjectIdentifiers.id_sha384, new SHA384Digest(), new PKCS1Encoding(new RSABlindedEngine())); + } + } + + static public class SHA512 + extends DigestSignatureSpi + { + public SHA512() + { + super(NISTObjectIdentifiers.id_sha512, new SHA512Digest(), new PKCS1Encoding(new RSABlindedEngine())); + } + } + + static public class MD2 + extends DigestSignatureSpi + { + public MD2() + { + super(PKCSObjectIdentifiers.md2, new MD2Digest(), new PKCS1Encoding(new RSABlindedEngine())); + } + } + + static public class MD4 + extends DigestSignatureSpi + { + public MD4() + { + super(PKCSObjectIdentifiers.md4, new MD4Digest(), new PKCS1Encoding(new RSABlindedEngine())); + } + } + + static public class MD5 + extends DigestSignatureSpi + { + public MD5() + { + super(PKCSObjectIdentifiers.md5, new MD5Digest(), new PKCS1Encoding(new RSABlindedEngine())); + } + } + + static public class RIPEMD160 + extends DigestSignatureSpi + { + public RIPEMD160() + { + super(TeleTrusTObjectIdentifiers.ripemd160, new RIPEMD160Digest(), new PKCS1Encoding(new RSABlindedEngine())); + } + } + + static public class RIPEMD128 + extends DigestSignatureSpi + { + public RIPEMD128() + { + super(TeleTrusTObjectIdentifiers.ripemd128, new RIPEMD128Digest(), new PKCS1Encoding(new RSABlindedEngine())); + } + } + + static public class RIPEMD256 + extends DigestSignatureSpi + { + public RIPEMD256() + { + super(TeleTrusTObjectIdentifiers.ripemd256, new RIPEMD256Digest(), new PKCS1Encoding(new RSABlindedEngine())); + } + } + + static public class noneRSA + extends DigestSignatureSpi + { + public noneRSA() + { + super(new NullDigest(), new PKCS1Encoding(new RSABlindedEngine())); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/ISOSignatureSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/ISOSignatureSpi.java new file mode 100644 index 00000000..9337e6b1 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/ISOSignatureSpi.java @@ -0,0 +1,142 @@ +package org.spongycastle.jcajce.provider.asymmetric.rsa; + +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.SignatureSpi; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.AlgorithmParameterSpec; + +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.MD5Digest; +import org.spongycastle.crypto.digests.RIPEMD160Digest; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.engines.RSABlindedEngine; +import org.spongycastle.crypto.signers.ISO9796d2Signer; + +public class ISOSignatureSpi + extends SignatureSpi +{ + private ISO9796d2Signer signer; + + protected ISOSignatureSpi( + Digest digest, + AsymmetricBlockCipher cipher) + { + signer = new ISO9796d2Signer(cipher, digest, true); + } + + protected void engineInitVerify( + PublicKey publicKey) + throws InvalidKeyException + { + CipherParameters param = RSAUtil.generatePublicKeyParameter((RSAPublicKey)publicKey); + + signer.init(false, param); + } + + protected void engineInitSign( + PrivateKey privateKey) + throws InvalidKeyException + { + CipherParameters param = RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)privateKey); + + signer.init(true, param); + } + + protected void engineUpdate( + byte b) + throws SignatureException + { + signer.update(b); + } + + protected void engineUpdate( + byte[] b, + int off, + int len) + throws SignatureException + { + signer.update(b, off, len); + } + + protected byte[] engineSign() + throws SignatureException + { + try + { + byte[] sig = signer.generateSignature(); + + return sig; + } + catch (Exception e) + { + throw new SignatureException(e.toString()); + } + } + + protected boolean engineVerify( + byte[] sigBytes) + throws SignatureException + { + boolean yes = signer.verifySignature(sigBytes); + + return yes; + } + + protected void engineSetParameter( + AlgorithmParameterSpec params) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated replaced with <a href = "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)"> + */ + protected void engineSetParameter( + String param, + Object value) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated + */ + protected Object engineGetParameter( + String param) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + static public class SHA1WithRSAEncryption + extends ISOSignatureSpi + { + public SHA1WithRSAEncryption() + { + super(new SHA1Digest(), new RSABlindedEngine()); + } + } + + static public class MD5WithRSAEncryption + extends ISOSignatureSpi + { + public MD5WithRSAEncryption() + { + super(new MD5Digest(), new RSABlindedEngine()); + } + } + + static public class RIPEMD160WithRSAEncryption + extends ISOSignatureSpi + { + public RIPEMD160WithRSAEncryption() + { + super(new RIPEMD160Digest(), new RSABlindedEngine()); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/KeyFactorySpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/KeyFactorySpi.java new file mode 100644 index 00000000..fcc973af --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/KeyFactorySpi.java @@ -0,0 +1,171 @@ +package org.spongycastle.jcajce.provider.asymmetric.rsa; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.RSAPrivateCrtKeySpec; +import java.security.spec.RSAPrivateKeySpec; +import java.security.spec.RSAPublicKeySpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.pkcs.RSAPrivateKey; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi; +import org.spongycastle.jcajce.provider.asymmetric.util.ExtendedInvalidKeySpecException; + +public class KeyFactorySpi + extends BaseKeyFactorySpi +{ + public KeyFactorySpi() + { + } + + protected KeySpec engineGetKeySpec( + Key key, + Class spec) + throws InvalidKeySpecException + { + if (spec.isAssignableFrom(RSAPublicKeySpec.class) && key instanceof RSAPublicKey) + { + RSAPublicKey k = (RSAPublicKey)key; + + return new RSAPublicKeySpec(k.getModulus(), k.getPublicExponent()); + } + else if (spec.isAssignableFrom(RSAPrivateKeySpec.class) && key instanceof java.security.interfaces.RSAPrivateKey) + { + java.security.interfaces.RSAPrivateKey k = (java.security.interfaces.RSAPrivateKey)key; + + return new RSAPrivateKeySpec(k.getModulus(), k.getPrivateExponent()); + } + else if (spec.isAssignableFrom(RSAPrivateCrtKeySpec.class) && key instanceof RSAPrivateCrtKey) + { + RSAPrivateCrtKey k = (RSAPrivateCrtKey)key; + + return new RSAPrivateCrtKeySpec( + k.getModulus(), k.getPublicExponent(), + k.getPrivateExponent(), + k.getPrimeP(), k.getPrimeQ(), + k.getPrimeExponentP(), k.getPrimeExponentQ(), + k.getCrtCoefficient()); + } + + return super.engineGetKeySpec(key, spec); + } + + protected Key engineTranslateKey( + Key key) + throws InvalidKeyException + { + if (key instanceof RSAPublicKey) + { + return new BCRSAPublicKey((RSAPublicKey)key); + } + else if (key instanceof RSAPrivateCrtKey) + { + return new BCRSAPrivateCrtKey((RSAPrivateCrtKey)key); + } + else if (key instanceof java.security.interfaces.RSAPrivateKey) + { + return new BCRSAPrivateKey((java.security.interfaces.RSAPrivateKey)key); + } + + throw new InvalidKeyException("key type unknown"); + } + + protected PrivateKey engineGeneratePrivate( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof PKCS8EncodedKeySpec) + { + try + { + return generatePrivate(PrivateKeyInfo.getInstance(((PKCS8EncodedKeySpec)keySpec).getEncoded())); + } + catch (Exception e) + { + // + // in case it's just a RSAPrivateKey object... -- openSSL produces these + // + try + { + return new BCRSAPrivateCrtKey( + RSAPrivateKey.getInstance(((PKCS8EncodedKeySpec)keySpec).getEncoded())); + } + catch (Exception ex) + { + throw new ExtendedInvalidKeySpecException("unable to process key spec: " + e.toString(), e); + } + } + } + else if (keySpec instanceof RSAPrivateCrtKeySpec) + { + return new BCRSAPrivateCrtKey((RSAPrivateCrtKeySpec)keySpec); + } + else if (keySpec instanceof RSAPrivateKeySpec) + { + return new BCRSAPrivateKey((RSAPrivateKeySpec)keySpec); + } + + throw new InvalidKeySpecException("Unknown KeySpec type: " + keySpec.getClass().getName()); + } + + protected PublicKey engineGeneratePublic( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof RSAPublicKeySpec) + { + return new BCRSAPublicKey((RSAPublicKeySpec)keySpec); + } + + return super.engineGeneratePublic(keySpec); + } + + public PrivateKey generatePrivate(PrivateKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getPrivateKeyAlgorithm().getAlgorithm(); + + if (RSAUtil.isRsaOid(algOid)) + { + RSAPrivateKey rsaPrivKey = RSAPrivateKey.getInstance(keyInfo.parsePrivateKey()); + + if (rsaPrivKey.getCoefficient().intValue() == 0) + { + return new BCRSAPrivateKey(rsaPrivKey); + } + else + { + return new BCRSAPrivateCrtKey(keyInfo); + } + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } + + public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getAlgorithm().getAlgorithm(); + + if (RSAUtil.isRsaOid(algOid)) + { + return new BCRSAPublicKey(keyInfo); + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/KeyPairGeneratorSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/KeyPairGeneratorSpi.java new file mode 100644 index 00000000..1239628e --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/KeyPairGeneratorSpi.java @@ -0,0 +1,78 @@ +package org.spongycastle.jcajce.provider.asymmetric.rsa; + +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.RSAKeyGenParameterSpec; + +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.generators.RSAKeyPairGenerator; +import org.spongycastle.crypto.params.RSAKeyGenerationParameters; +import org.spongycastle.crypto.params.RSAKeyParameters; +import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters; + +public class KeyPairGeneratorSpi + extends java.security.KeyPairGenerator +{ + public KeyPairGeneratorSpi( + String algorithmName) + { + super(algorithmName); + } + + final static BigInteger defaultPublicExponent = BigInteger.valueOf(0x10001); + final static int defaultTests = 12; + + RSAKeyGenerationParameters param; + RSAKeyPairGenerator engine; + + public KeyPairGeneratorSpi() + { + super("RSA"); + + engine = new RSAKeyPairGenerator(); + param = new RSAKeyGenerationParameters(defaultPublicExponent, + new SecureRandom(), 2048, defaultTests); + engine.init(param); + } + + public void initialize( + int strength, + SecureRandom random) + { + param = new RSAKeyGenerationParameters(defaultPublicExponent, + random, strength, defaultTests); + + engine.init(param); + } + + public void initialize( + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + if (!(params instanceof RSAKeyGenParameterSpec)) + { + throw new InvalidAlgorithmParameterException("parameter object not a RSAKeyGenParameterSpec"); + } + RSAKeyGenParameterSpec rsaParams = (RSAKeyGenParameterSpec)params; + + param = new RSAKeyGenerationParameters( + rsaParams.getPublicExponent(), + random, rsaParams.getKeysize(), defaultTests); + + engine.init(param); + } + + public KeyPair generateKeyPair() + { + AsymmetricCipherKeyPair pair = engine.generateKeyPair(); + RSAKeyParameters pub = (RSAKeyParameters)pair.getPublic(); + RSAPrivateCrtKeyParameters priv = (RSAPrivateCrtKeyParameters)pair.getPrivate(); + + return new KeyPair(new BCRSAPublicKey(pub), + new BCRSAPrivateCrtKey(priv)); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/PSSSignatureSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/PSSSignatureSpi.java new file mode 100644 index 00000000..adce8b59 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/PSSSignatureSpi.java @@ -0,0 +1,394 @@ +package org.spongycastle.jcajce.provider.asymmetric.rsa; + +import java.io.ByteArrayOutputStream; +import java.security.AlgorithmParameters; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.SignatureException; +import java.security.SignatureSpi; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.MGF1ParameterSpec; +import java.security.spec.PSSParameterSpec; + +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.engines.RSABlindedEngine; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.jcajce.provider.util.DigestFactory; +import org.spongycastle.jce.provider.BouncyCastleProvider; + +public class PSSSignatureSpi + extends SignatureSpi +{ + private AlgorithmParameters engineParams; + private PSSParameterSpec paramSpec; + private PSSParameterSpec originalSpec; + private AsymmetricBlockCipher signer; + private Digest contentDigest; + private Digest mgfDigest; + private int saltLength; + private byte trailer; + private boolean isRaw; + + private org.spongycastle.crypto.signers.PSSSigner pss; + + private byte getTrailer( + int trailerField) + { + if (trailerField == 1) + { + return org.spongycastle.crypto.signers.PSSSigner.TRAILER_IMPLICIT; + } + + throw new IllegalArgumentException("unknown trailer field"); + } + + private void setupContentDigest() + { + if (isRaw) + { + this.contentDigest = new NullPssDigest(mgfDigest); + } + else + { + this.contentDigest = mgfDigest; + } + } + + // care - this constructor is actually used by outside organisations + protected PSSSignatureSpi( + AsymmetricBlockCipher signer, + PSSParameterSpec paramSpecArg) + { + this(signer, paramSpecArg, false); + } + + // care - this constructor is actually used by outside organisations + protected PSSSignatureSpi( + AsymmetricBlockCipher signer, + PSSParameterSpec baseParamSpec, + boolean isRaw) + { + this.signer = signer; + this.originalSpec = baseParamSpec; + + if (baseParamSpec == null) + { + this.paramSpec = PSSParameterSpec.DEFAULT; + } + else + { + this.paramSpec = baseParamSpec; + } + + this.mgfDigest = DigestFactory.getDigest(paramSpec.getDigestAlgorithm()); + this.saltLength = paramSpec.getSaltLength(); + this.trailer = getTrailer(paramSpec.getTrailerField()); + this.isRaw = isRaw; + + setupContentDigest(); + } + + protected void engineInitVerify( + PublicKey publicKey) + throws InvalidKeyException + { + if (!(publicKey instanceof RSAPublicKey)) + { + throw new InvalidKeyException("Supplied key is not a RSAPublicKey instance"); + } + + pss = new org.spongycastle.crypto.signers.PSSSigner(signer, contentDigest, mgfDigest, saltLength, trailer); + pss.init(false, + RSAUtil.generatePublicKeyParameter((RSAPublicKey)publicKey)); + } + + protected void engineInitSign( + PrivateKey privateKey, + SecureRandom random) + throws InvalidKeyException + { + if (!(privateKey instanceof RSAPrivateKey)) + { + throw new InvalidKeyException("Supplied key is not a RSAPrivateKey instance"); + } + + pss = new org.spongycastle.crypto.signers.PSSSigner(signer, contentDigest, mgfDigest, saltLength, trailer); + pss.init(true, new ParametersWithRandom(RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)privateKey), random)); + } + + protected void engineInitSign( + PrivateKey privateKey) + throws InvalidKeyException + { + if (!(privateKey instanceof RSAPrivateKey)) + { + throw new InvalidKeyException("Supplied key is not a RSAPrivateKey instance"); + } + + pss = new org.spongycastle.crypto.signers.PSSSigner(signer, contentDigest, mgfDigest, saltLength, trailer); + pss.init(true, RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)privateKey)); + } + + protected void engineUpdate( + byte b) + throws SignatureException + { + pss.update(b); + } + + protected void engineUpdate( + byte[] b, + int off, + int len) + throws SignatureException + { + pss.update(b, off, len); + } + + protected byte[] engineSign() + throws SignatureException + { + try + { + return pss.generateSignature(); + } + catch (CryptoException e) + { + throw new SignatureException(e.getMessage()); + } + } + + protected boolean engineVerify( + byte[] sigBytes) + throws SignatureException + { + return pss.verifySignature(sigBytes); + } + + protected void engineSetParameter( + AlgorithmParameterSpec params) + throws InvalidParameterException + { + if (params instanceof PSSParameterSpec) + { + PSSParameterSpec newParamSpec = (PSSParameterSpec)params; + + if (originalSpec != null) + { + if (!DigestFactory.isSameDigest(originalSpec.getDigestAlgorithm(), newParamSpec.getDigestAlgorithm())) + { + throw new InvalidParameterException("parameter must be using " + originalSpec.getDigestAlgorithm()); + } + } + if (!newParamSpec.getMGFAlgorithm().equalsIgnoreCase("MGF1") && !newParamSpec.getMGFAlgorithm().equals(PKCSObjectIdentifiers.id_mgf1.getId())) + { + throw new InvalidParameterException("unknown mask generation function specified"); + } + + if (!(newParamSpec.getMGFParameters() instanceof MGF1ParameterSpec)) + { + throw new InvalidParameterException("unkown MGF parameters"); + } + + MGF1ParameterSpec mgfParams = (MGF1ParameterSpec)newParamSpec.getMGFParameters(); + + if (!DigestFactory.isSameDigest(mgfParams.getDigestAlgorithm(), newParamSpec.getDigestAlgorithm())) + { + throw new InvalidParameterException("digest algorithm for MGF should be the same as for PSS parameters."); + } + + Digest newDigest = DigestFactory.getDigest(mgfParams.getDigestAlgorithm()); + + if (newDigest == null) + { + throw new InvalidParameterException("no match on MGF digest algorithm: "+ mgfParams.getDigestAlgorithm()); + } + + this.engineParams = null; + this.paramSpec = newParamSpec; + this.mgfDigest = newDigest; + this.saltLength = paramSpec.getSaltLength(); + this.trailer = getTrailer(paramSpec.getTrailerField()); + + setupContentDigest(); + } + else + { + throw new InvalidParameterException("Only PSSParameterSpec supported"); + } + } + + protected AlgorithmParameters engineGetParameters() + { + if (engineParams == null) + { + if (paramSpec != null) + { + try + { + engineParams = AlgorithmParameters.getInstance("PSS", BouncyCastleProvider.PROVIDER_NAME); + engineParams.init(paramSpec); + } + catch (Exception e) + { + throw new RuntimeException(e.toString()); + } + } + } + + return engineParams; + } + + /** + * @deprecated replaced with <a href = "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)"> + */ + protected void engineSetParameter( + String param, + Object value) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + protected Object engineGetParameter( + String param) + { + throw new UnsupportedOperationException("engineGetParameter unsupported"); + } + + static public class nonePSS + extends PSSSignatureSpi + { + public nonePSS() + { + super(new RSABlindedEngine(), null, true); + } + } + + static public class PSSwithRSA + extends PSSSignatureSpi + { + public PSSwithRSA() + { + super(new RSABlindedEngine(), null); + } + } + + static public class SHA1withRSA + extends PSSSignatureSpi + { + public SHA1withRSA() + { + super(new RSABlindedEngine(), PSSParameterSpec.DEFAULT); + } + } + + static public class SHA224withRSA + extends PSSSignatureSpi + { + public SHA224withRSA() + { + super(new RSABlindedEngine(), new PSSParameterSpec("SHA-224", "MGF1", new MGF1ParameterSpec("SHA-224"), 28, 1)); + } + } + + static public class SHA256withRSA + extends PSSSignatureSpi + { + public SHA256withRSA() + { + super(new RSABlindedEngine(), new PSSParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), 32, 1)); + } + } + + static public class SHA384withRSA + extends PSSSignatureSpi + { + public SHA384withRSA() + { + super(new RSABlindedEngine(), new PSSParameterSpec("SHA-384", "MGF1", new MGF1ParameterSpec("SHA-384"), 48, 1)); + } + } + + static public class SHA512withRSA + extends PSSSignatureSpi + { + public SHA512withRSA() + { + super(new RSABlindedEngine(), new PSSParameterSpec("SHA-512", "MGF1", new MGF1ParameterSpec("SHA-512"), 64, 1)); + } + } + + private class NullPssDigest + implements Digest + { + private ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + private Digest baseDigest; + private boolean oddTime = true; + + public NullPssDigest(Digest mgfDigest) + { + this.baseDigest = mgfDigest; + } + + public String getAlgorithmName() + { + return "NULL"; + } + + public int getDigestSize() + { + return baseDigest.getDigestSize(); + } + + public void update(byte in) + { + bOut.write(in); + } + + public void update(byte[] in, int inOff, int len) + { + bOut.write(in, inOff, len); + } + + public int doFinal(byte[] out, int outOff) + { + byte[] res = bOut.toByteArray(); + + if (oddTime) + { + System.arraycopy(res, 0, out, outOff, res.length); + } + else + { + baseDigest.update(res, 0, res.length); + + baseDigest.doFinal(out, outOff); + } + + reset(); + + oddTime = !oddTime; + + return res.length; + } + + public void reset() + { + bOut.reset(); + baseDigest.reset(); + } + + public int getByteLength() + { + return 0; + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/RSAUtil.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/RSAUtil.java new file mode 100644 index 00000000..da57ec3b --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/RSAUtil.java @@ -0,0 +1,66 @@ +package org.spongycastle.jcajce.provider.asymmetric.rsa; + +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.X509ObjectIdentifiers; +import org.spongycastle.crypto.params.RSAKeyParameters; +import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters; + +/** + * utility class for converting java.security RSA objects into their + * org.spongycastle.crypto counterparts. + */ +public class RSAUtil +{ + public static final ASN1ObjectIdentifier[] rsaOids = + { + PKCSObjectIdentifiers.rsaEncryption, + X509ObjectIdentifiers.id_ea_rsa, + PKCSObjectIdentifiers.id_RSAES_OAEP, + PKCSObjectIdentifiers.id_RSASSA_PSS + }; + + public static boolean isRsaOid( + ASN1ObjectIdentifier algOid) + { + for (int i = 0; i != rsaOids.length; i++) + { + if (algOid.equals(rsaOids[i])) + { + return true; + } + } + + return false; + } + + static RSAKeyParameters generatePublicKeyParameter( + RSAPublicKey key) + { + return new RSAKeyParameters(false, key.getModulus(), key.getPublicExponent()); + + } + + static RSAKeyParameters generatePrivateKeyParameter( + RSAPrivateKey key) + { + if (key instanceof RSAPrivateCrtKey) + { + RSAPrivateCrtKey k = (RSAPrivateCrtKey)key; + + return new RSAPrivateCrtKeyParameters(k.getModulus(), + k.getPublicExponent(), k.getPrivateExponent(), + k.getPrimeP(), k.getPrimeQ(), k.getPrimeExponentP(), k.getPrimeExponentQ(), k.getCrtCoefficient()); + } + else + { + RSAPrivateKey k = key; + + return new RSAKeyParameters(true, k.getModulus(), k.getPrivateExponent()); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/BaseCipherSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/BaseCipherSpi.java new file mode 100644 index 00000000..4c1ed0ee --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/BaseCipherSpi.java @@ -0,0 +1,216 @@ +package org.spongycastle.jcajce.provider.asymmetric.util; + +import java.security.AlgorithmParameters; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.CipherSpi; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEParameterSpec; +import javax.crypto.spec.RC2ParameterSpec; +import javax.crypto.spec.RC5ParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.Wrapper; +import org.spongycastle.jce.provider.BouncyCastleProvider; + +public abstract class BaseCipherSpi + extends CipherSpi +{ + // + // specs we can handle. + // + private Class[] availableSpecs = + { + IvParameterSpec.class, + PBEParameterSpec.class, + RC2ParameterSpec.class, + RC5ParameterSpec.class + }; + + + protected AlgorithmParameters engineParams = null; + + protected Wrapper wrapEngine = null; + + private int ivSize; + private byte[] iv; + + protected BaseCipherSpi() + { + } + + protected int engineGetBlockSize() + { + return 0; + } + + protected byte[] engineGetIV() + { + return null; + } + + protected int engineGetKeySize( + Key key) + { + return key.getEncoded().length; + } + + protected int engineGetOutputSize( + int inputLen) + { + return -1; + } + + protected AlgorithmParameters engineGetParameters() + { + return null; + } + + protected void engineSetMode( + String mode) + throws NoSuchAlgorithmException + { + throw new NoSuchAlgorithmException("can't support mode " + mode); + } + + protected void engineSetPadding( + String padding) + throws NoSuchPaddingException + { + throw new NoSuchPaddingException("Padding " + padding + " unknown."); + } + + protected byte[] engineWrap( + Key key) + throws IllegalBlockSizeException, InvalidKeyException + { + byte[] encoded = key.getEncoded(); + if (encoded == null) + { + throw new InvalidKeyException("Cannot wrap key, null encoding."); + } + + try + { + if (wrapEngine == null) + { + return engineDoFinal(encoded, 0, encoded.length); + } + else + { + return wrapEngine.wrap(encoded, 0, encoded.length); + } + } + catch (BadPaddingException e) + { + throw new IllegalBlockSizeException(e.getMessage()); + } + } + + protected Key engineUnwrap( + byte[] wrappedKey, + String wrappedKeyAlgorithm, + int wrappedKeyType) + throws InvalidKeyException + { + byte[] encoded; + try + { + if (wrapEngine == null) + { + encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length); + } + else + { + encoded = wrapEngine.unwrap(wrappedKey, 0, wrappedKey.length); + } + } + catch (InvalidCipherTextException e) + { + throw new InvalidKeyException(e.getMessage()); + } + catch (BadPaddingException e) + { + throw new InvalidKeyException(e.getMessage()); + } + catch (IllegalBlockSizeException e2) + { + throw new InvalidKeyException(e2.getMessage()); + } + + if (wrappedKeyType == Cipher.SECRET_KEY) + { + return new SecretKeySpec(encoded, wrappedKeyAlgorithm); + } + else if (wrappedKeyAlgorithm.equals("") && wrappedKeyType == Cipher.PRIVATE_KEY) + { + /* + * The caller doesn't know the algorithm as it is part of + * the encrypted data. + */ + try + { + PrivateKeyInfo in = PrivateKeyInfo.getInstance(encoded); + + PrivateKey privKey = BouncyCastleProvider.getPrivateKey(in); + + if (privKey != null) + { + return privKey; + } + else + { + throw new InvalidKeyException("algorithm " + in.getPrivateKeyAlgorithm().getAlgorithm() + " not supported"); + } + } + catch (Exception e) + { + throw new InvalidKeyException("Invalid key encoding."); + } + } + else + { + try + { + KeyFactory kf = KeyFactory.getInstance(wrappedKeyAlgorithm, BouncyCastleProvider.PROVIDER_NAME); + + if (wrappedKeyType == Cipher.PUBLIC_KEY) + { + return kf.generatePublic(new X509EncodedKeySpec(encoded)); + } + else if (wrappedKeyType == Cipher.PRIVATE_KEY) + { + return kf.generatePrivate(new PKCS8EncodedKeySpec(encoded)); + } + } + catch (NoSuchProviderException e) + { + throw new InvalidKeyException("Unknown key type " + e.getMessage()); + } + catch (NoSuchAlgorithmException e) + { + throw new InvalidKeyException("Unknown key type " + e.getMessage()); + } + catch (InvalidKeySpecException e2) + { + throw new InvalidKeyException("Unknown key type " + e2.getMessage()); + } + + throw new InvalidKeyException("Unknown key type " + wrappedKeyType); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/BaseKeyFactorySpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/BaseKeyFactorySpi.java new file mode 100644 index 00000000..5ced82ec --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/BaseKeyFactorySpi.java @@ -0,0 +1,77 @@ +package org.spongycastle.jcajce.provider.asymmetric.util; + +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; + +public abstract class BaseKeyFactorySpi + extends java.security.KeyFactorySpi + implements AsymmetricKeyInfoConverter +{ + protected PrivateKey engineGeneratePrivate( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof PKCS8EncodedKeySpec) + { + try + { + return generatePrivate(PrivateKeyInfo.getInstance(((PKCS8EncodedKeySpec)keySpec).getEncoded())); + } + catch (Exception e) + { + throw new InvalidKeySpecException("encoded key spec not recognised"); + } + } + else + { + throw new InvalidKeySpecException("key spec not recognised"); + } + } + + protected PublicKey engineGeneratePublic( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof X509EncodedKeySpec) + { + try + { + return generatePublic(SubjectPublicKeyInfo.getInstance(((X509EncodedKeySpec)keySpec).getEncoded())); + } + catch (Exception e) + { + throw new InvalidKeySpecException("encoded key spec not recognised"); + } + } + else + { + throw new InvalidKeySpecException("key spec not recognised"); + } + } + + protected KeySpec engineGetKeySpec( + Key key, + Class spec) + throws InvalidKeySpecException + { + if (spec.isAssignableFrom(PKCS8EncodedKeySpec.class) && key.getFormat().equals("PKCS#8")) + { + return new PKCS8EncodedKeySpec(key.getEncoded()); + } + else if (spec.isAssignableFrom(X509EncodedKeySpec.class) && key.getFormat().equals("X.509")) + { + return new X509EncodedKeySpec(key.getEncoded()); + } + + throw new InvalidKeySpecException("not implemented yet " + key + " " + spec); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/DHUtil.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/DHUtil.java new file mode 100644 index 00000000..10b633da --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/DHUtil.java @@ -0,0 +1,50 @@ +package org.spongycastle.jcajce.provider.asymmetric.util; + +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; + +import javax.crypto.interfaces.DHPrivateKey; +import javax.crypto.interfaces.DHPublicKey; + +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.DHParameters; +import org.spongycastle.crypto.params.DHPrivateKeyParameters; +import org.spongycastle.crypto.params.DHPublicKeyParameters; + +/** + * utility class for converting jce/jca DH objects + * objects into their org.spongycastle.crypto counterparts. + */ +public class DHUtil +{ + static public AsymmetricKeyParameter generatePublicKeyParameter( + PublicKey key) + throws InvalidKeyException + { + if (key instanceof DHPublicKey) + { + DHPublicKey k = (DHPublicKey)key; + + return new DHPublicKeyParameters(k.getY(), + new DHParameters(k.getParams().getP(), k.getParams().getG(), null, k.getParams().getL())); + } + + throw new InvalidKeyException("can't identify DH public key."); + } + + static public AsymmetricKeyParameter generatePrivateKeyParameter( + PrivateKey key) + throws InvalidKeyException + { + if (key instanceof DHPrivateKey) + { + DHPrivateKey k = (DHPrivateKey)key; + + return new DHPrivateKeyParameters(k.getX(), + new DHParameters(k.getParams().getP(), k.getParams().getG(), null, k.getParams().getL())); + } + + throw new InvalidKeyException("can't identify DH private key."); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/DSABase.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/DSABase.java new file mode 100644 index 00000000..0f659b2c --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/DSABase.java @@ -0,0 +1,112 @@ +package org.spongycastle.jcajce.provider.asymmetric.util; + +import java.math.BigInteger; +import java.security.SignatureException; +import java.security.SignatureSpi; +import java.security.spec.AlgorithmParameterSpec; + +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.X509ObjectIdentifiers; +import org.spongycastle.crypto.DSA; +import org.spongycastle.crypto.Digest; + +public abstract class DSABase + extends SignatureSpi + implements PKCSObjectIdentifiers, X509ObjectIdentifiers +{ + protected Digest digest; + protected DSA signer; + protected DSAEncoder encoder; + + protected DSABase( + Digest digest, + DSA signer, + DSAEncoder encoder) + { + this.digest = digest; + this.signer = signer; + this.encoder = encoder; + } + + protected void engineUpdate( + byte b) + throws SignatureException + { + digest.update(b); + } + + protected void engineUpdate( + byte[] b, + int off, + int len) + throws SignatureException + { + digest.update(b, off, len); + } + + protected byte[] engineSign() + throws SignatureException + { + byte[] hash = new byte[digest.getDigestSize()]; + + digest.doFinal(hash, 0); + + try + { + BigInteger[] sig = signer.generateSignature(hash); + + return encoder.encode(sig[0], sig[1]); + } + catch (Exception e) + { + throw new SignatureException(e.toString()); + } + } + + protected boolean engineVerify( + byte[] sigBytes) + throws SignatureException + { + byte[] hash = new byte[digest.getDigestSize()]; + + digest.doFinal(hash, 0); + + BigInteger[] sig; + + try + { + sig = encoder.decode(sigBytes); + } + catch (Exception e) + { + throw new SignatureException("error decoding signature bytes."); + } + + return signer.verifySignature(hash, sig[0], sig[1]); + } + + protected void engineSetParameter( + AlgorithmParameterSpec params) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated replaced with <a href = "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)"> + */ + protected void engineSetParameter( + String param, + Object value) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated + */ + protected Object engineGetParameter( + String param) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/DSAEncoder.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/DSAEncoder.java new file mode 100644 index 00000000..349641dd --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/DSAEncoder.java @@ -0,0 +1,13 @@ +package org.spongycastle.jcajce.provider.asymmetric.util; + +import java.io.IOException; +import java.math.BigInteger; + +public interface DSAEncoder +{ + byte[] encode(BigInteger r, BigInteger s) + throws IOException; + + BigInteger[] decode(byte[] sig) + throws IOException; +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/EC5Util.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/EC5Util.java new file mode 100644 index 00000000..d1f49a80 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/EC5Util.java @@ -0,0 +1,154 @@ +package org.spongycastle.jcajce.provider.asymmetric.util; + +import java.math.BigInteger; +import java.security.spec.ECField; +import java.security.spec.ECFieldF2m; +import java.security.spec.ECFieldFp; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.EllipticCurve; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +import org.spongycastle.asn1.x9.ECNamedCurveTable; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.crypto.ec.CustomNamedCurves; +import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; +import org.spongycastle.jce.spec.ECNamedCurveSpec; +import org.spongycastle.math.ec.ECAlgorithms; +import org.spongycastle.math.ec.ECCurve; + +public class EC5Util +{ + private static Map customCurves = new HashMap(); + + static + { + Enumeration e = CustomNamedCurves.getNames(); + while (e.hasMoreElements()) + { + String name = (String)e.nextElement(); + + X9ECParameters curveParams = ECNamedCurveTable.getByName(name); + if (curveParams != null) // there may not be a regular curve, may just be a custom curve. + { + customCurves.put(curveParams.getCurve(), CustomNamedCurves.getByName(name).getCurve()); + } + } + } + + public static EllipticCurve convertCurve( + ECCurve curve, + byte[] seed) + { + // TODO: the Sun EC implementation doesn't currently handle the seed properly + // so at the moment it's set to null. Should probably look at making this configurable + if (ECAlgorithms.isFpCurve(curve)) + { + return new EllipticCurve(new ECFieldFp(curve.getField().getCharacteristic()), curve.getA().toBigInteger(), curve.getB().toBigInteger(), null); + } + else + { + ECCurve.F2m curveF2m = (ECCurve.F2m)curve; + int ks[]; + + if (curveF2m.isTrinomial()) + { + ks = new int[] { curveF2m.getK1() }; + + return new EllipticCurve(new ECFieldF2m(curveF2m.getM(), ks), curve.getA().toBigInteger(), curve.getB().toBigInteger(), null); + } + else + { + ks = new int[] { curveF2m.getK3(), curveF2m.getK2(), curveF2m.getK1() }; + + return new EllipticCurve(new ECFieldF2m(curveF2m.getM(), ks), curve.getA().toBigInteger(), curve.getB().toBigInteger(), null); + } + } + } + + public static ECCurve convertCurve( + EllipticCurve ec) + { + ECField field = ec.getField(); + BigInteger a = ec.getA(); + BigInteger b = ec.getB(); + + if (field instanceof ECFieldFp) + { + ECCurve.Fp curve = new ECCurve.Fp(((ECFieldFp)field).getP(), a, b); + + if (customCurves.containsKey(curve)) + { + return (ECCurve)customCurves.get(curve); + } + + return curve; + } + else + { + ECFieldF2m fieldF2m = (ECFieldF2m)field; + int m = fieldF2m.getM(); + int ks[] = ECUtil.convertMidTerms(fieldF2m.getMidTermsOfReductionPolynomial()); + return new ECCurve.F2m(m, ks[0], ks[1], ks[2], a, b); + } + } + + public static ECParameterSpec convertSpec( + EllipticCurve ellipticCurve, + org.spongycastle.jce.spec.ECParameterSpec spec) + { + if (spec instanceof ECNamedCurveParameterSpec) + { + return new ECNamedCurveSpec( + ((ECNamedCurveParameterSpec)spec).getName(), + ellipticCurve, + new ECPoint( + spec.getG().getAffineXCoord().toBigInteger(), + spec.getG().getAffineYCoord().toBigInteger()), + spec.getN(), + spec.getH()); + } + else + { + return new ECParameterSpec( + ellipticCurve, + new ECPoint( + spec.getG().getAffineXCoord().toBigInteger(), + spec.getG().getAffineYCoord().toBigInteger()), + spec.getN(), + spec.getH().intValue()); + } + } + + public static org.spongycastle.jce.spec.ECParameterSpec convertSpec( + ECParameterSpec ecSpec, + boolean withCompression) + { + ECCurve curve = convertCurve(ecSpec.getCurve()); + + return new org.spongycastle.jce.spec.ECParameterSpec( + curve, + convertPoint(curve, ecSpec.getGenerator(), withCompression), + ecSpec.getOrder(), + BigInteger.valueOf(ecSpec.getCofactor()), + ecSpec.getCurve().getSeed()); + } + + public static org.spongycastle.math.ec.ECPoint convertPoint( + ECParameterSpec ecSpec, + ECPoint point, + boolean withCompression) + { + return convertPoint(convertCurve(ecSpec.getCurve()), point, withCompression); + } + + public static org.spongycastle.math.ec.ECPoint convertPoint( + ECCurve curve, + ECPoint point, + boolean withCompression) + { + return curve.createPoint(point.getAffineX(), point.getAffineY(), withCompression); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/ECUtil.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/ECUtil.java new file mode 100644 index 00000000..c3251a8d --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/ECUtil.java @@ -0,0 +1,291 @@ +package org.spongycastle.jcajce.provider.asymmetric.util; + +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.cryptopro.ECGOST3410NamedCurves; +import org.spongycastle.asn1.nist.NISTNamedCurves; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.sec.SECNamedCurves; +import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X962NamedCurves; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.crypto.ec.CustomNamedCurves; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; +import org.spongycastle.jce.interfaces.ECPrivateKey; +import org.spongycastle.jce.interfaces.ECPublicKey; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECParameterSpec; + +/** + * utility class for converting jce/jca ECDSA, ECDH, and ECDHC + * objects into their org.spongycastle.crypto counterparts. + */ +public class ECUtil +{ + /** + * Returns a sorted array of middle terms of the reduction polynomial. + * @param k The unsorted array of middle terms of the reduction polynomial + * of length 1 or 3. + * @return the sorted array of middle terms of the reduction polynomial. + * This array always has length 3. + */ + static int[] convertMidTerms( + int[] k) + { + int[] res = new int[3]; + + if (k.length == 1) + { + res[0] = k[0]; + } + else + { + if (k.length != 3) + { + throw new IllegalArgumentException("Only Trinomials and pentanomials supported"); + } + + if (k[0] < k[1] && k[0] < k[2]) + { + res[0] = k[0]; + if (k[1] < k[2]) + { + res[1] = k[1]; + res[2] = k[2]; + } + else + { + res[1] = k[2]; + res[2] = k[1]; + } + } + else if (k[1] < k[2]) + { + res[0] = k[1]; + if (k[0] < k[2]) + { + res[1] = k[0]; + res[2] = k[2]; + } + else + { + res[1] = k[2]; + res[2] = k[0]; + } + } + else + { + res[0] = k[2]; + if (k[0] < k[1]) + { + res[1] = k[0]; + res[2] = k[1]; + } + else + { + res[1] = k[1]; + res[2] = k[0]; + } + } + } + + return res; + } + + public static AsymmetricKeyParameter generatePublicKeyParameter( + PublicKey key) + throws InvalidKeyException + { + if (key instanceof ECPublicKey) + { + ECPublicKey k = (ECPublicKey)key; + ECParameterSpec s = k.getParameters(); + + if (s == null) + { + s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + return new ECPublicKeyParameters( + ((BCECPublicKey)k).engineGetQ(), + new ECDomainParameters(s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed())); + } + else + { + return new ECPublicKeyParameters( + k.getQ(), + new ECDomainParameters(s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed())); + } + } + else if (key instanceof java.security.interfaces.ECPublicKey) + { + java.security.interfaces.ECPublicKey pubKey = (java.security.interfaces.ECPublicKey)key; + ECParameterSpec s = EC5Util.convertSpec(pubKey.getParams(), false); + return new ECPublicKeyParameters( + EC5Util.convertPoint(pubKey.getParams(), pubKey.getW(), false), + new ECDomainParameters(s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed())); + } + else + { + // see if we can build a key from key.getEncoded() + try + { + byte[] bytes = key.getEncoded(); + + if (bytes == null) + { + throw new InvalidKeyException("no encoding for EC public key"); + } + + PublicKey publicKey = BouncyCastleProvider.getPublicKey(SubjectPublicKeyInfo.getInstance(bytes)); + + if (publicKey instanceof java.security.interfaces.ECPublicKey) + { + return ECUtil.generatePublicKeyParameter(publicKey); + } + } + catch (Exception e) + { + throw new InvalidKeyException("cannot identify EC public key: " + e.toString()); + } + } + + throw new InvalidKeyException("cannot identify EC public key."); + } + + public static AsymmetricKeyParameter generatePrivateKeyParameter( + PrivateKey key) + throws InvalidKeyException + { + if (key instanceof ECPrivateKey) + { + ECPrivateKey k = (ECPrivateKey)key; + ECParameterSpec s = k.getParameters(); + + if (s == null) + { + s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + } + + return new ECPrivateKeyParameters( + k.getD(), + new ECDomainParameters(s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed())); + } + else if (key instanceof java.security.interfaces.ECPrivateKey) + { + java.security.interfaces.ECPrivateKey privKey = (java.security.interfaces.ECPrivateKey)key; + ECParameterSpec s = EC5Util.convertSpec(privKey.getParams(), false); + return new ECPrivateKeyParameters( + privKey.getS(), + new ECDomainParameters(s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed())); + } + else + { + // see if we can build a key from key.getEncoded() + try + { + byte[] bytes = key.getEncoded(); + + if (bytes == null) + { + throw new InvalidKeyException("no encoding for EC private key"); + } + + PrivateKey privateKey = BouncyCastleProvider.getPrivateKey(PrivateKeyInfo.getInstance(bytes)); + + if (privateKey instanceof java.security.interfaces.ECPrivateKey) + { + return ECUtil.generatePrivateKeyParameter(privateKey); + } + } + catch (Exception e) + { + throw new InvalidKeyException("cannot identify EC private key: " + e.toString()); + } + } + + throw new InvalidKeyException("can't identify EC private key."); + } + + public static ASN1ObjectIdentifier getNamedCurveOid( + String name) + { + ASN1ObjectIdentifier oid = X962NamedCurves.getOID(name); + + if (oid == null) + { + oid = SECNamedCurves.getOID(name); + if (oid == null) + { + oid = NISTNamedCurves.getOID(name); + } + if (oid == null) + { + oid = TeleTrusTNamedCurves.getOID(name); + } + if (oid == null) + { + oid = ECGOST3410NamedCurves.getOID(name); + } + } + + return oid; + } + + public static X9ECParameters getNamedCurveByOid( + ASN1ObjectIdentifier oid) + { + X9ECParameters params = CustomNamedCurves.getByOID(oid); + + if (params == null) + { + params = X962NamedCurves.getByOID(oid); + if (params == null) + { + params = SECNamedCurves.getByOID(oid); + } + if (params == null) + { + params = NISTNamedCurves.getByOID(oid); + } + if (params == null) + { + params = TeleTrusTNamedCurves.getByOID(oid); + } + } + + return params; + } + + public static String getCurveName( + ASN1ObjectIdentifier oid) + { + String name = X962NamedCurves.getName(oid); + + if (name == null) + { + name = SECNamedCurves.getName(oid); + if (name == null) + { + name = NISTNamedCurves.getName(oid); + } + if (name == null) + { + name = TeleTrusTNamedCurves.getName(oid); + } + if (name == null) + { + name = ECGOST3410NamedCurves.getName(oid); + } + } + + return name; + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/ExtendedInvalidKeySpecException.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/ExtendedInvalidKeySpecException.java new file mode 100644 index 00000000..3f39d39e --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/ExtendedInvalidKeySpecException.java @@ -0,0 +1,21 @@ +package org.spongycastle.jcajce.provider.asymmetric.util; + +import java.security.spec.InvalidKeySpecException; + +public class ExtendedInvalidKeySpecException + extends InvalidKeySpecException +{ + private Throwable cause; + + public ExtendedInvalidKeySpecException(String msg, Throwable cause) + { + super(msg); + + this.cause = cause; + } + + public Throwable getCause() + { + return cause; + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/GOST3410Util.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/GOST3410Util.java new file mode 100644 index 00000000..c331e3c0 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/GOST3410Util.java @@ -0,0 +1,52 @@ +package org.spongycastle.jcajce.provider.asymmetric.util; + +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; + +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.GOST3410Parameters; +import org.spongycastle.crypto.params.GOST3410PrivateKeyParameters; +import org.spongycastle.crypto.params.GOST3410PublicKeyParameters; +import org.spongycastle.jce.interfaces.GOST3410PrivateKey; +import org.spongycastle.jce.interfaces.GOST3410PublicKey; +import org.spongycastle.jce.spec.GOST3410PublicKeyParameterSetSpec; + +/** + * utility class for converting jce/jca GOST3410-94 objects + * objects into their org.spongycastle.crypto counterparts. + */ +public class GOST3410Util +{ + static public AsymmetricKeyParameter generatePublicKeyParameter( + PublicKey key) + throws InvalidKeyException + { + if (key instanceof GOST3410PublicKey) + { + GOST3410PublicKey k = (GOST3410PublicKey)key; + GOST3410PublicKeyParameterSetSpec p = k.getParameters().getPublicKeyParameters(); + + return new GOST3410PublicKeyParameters(k.getY(), + new GOST3410Parameters(p.getP(), p.getQ(), p.getA())); + } + + throw new InvalidKeyException("can't identify GOST3410 public key: " + key.getClass().getName()); + } + + static public AsymmetricKeyParameter generatePrivateKeyParameter( + PrivateKey key) + throws InvalidKeyException + { + if (key instanceof GOST3410PrivateKey) + { + GOST3410PrivateKey k = (GOST3410PrivateKey)key; + GOST3410PublicKeyParameterSetSpec p = k.getParameters().getPublicKeyParameters(); + + return new GOST3410PrivateKeyParameters(k.getX(), + new GOST3410Parameters(p.getP(), p.getQ(), p.getA())); + } + + throw new InvalidKeyException("can't identify GOST3410 private key."); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/IESUtil.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/IESUtil.java new file mode 100644 index 00000000..fb93838e --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/IESUtil.java @@ -0,0 +1,32 @@ +package org.spongycastle.jcajce.provider.asymmetric.util; + +import org.spongycastle.crypto.engines.IESEngine; +import org.spongycastle.jce.spec.IESParameterSpec; + +public class IESUtil +{ + public static IESParameterSpec guessParameterSpec(IESEngine engine) + { + if (engine.getCipher() == null) + { + return new IESParameterSpec(null, null, 128); + } + else if (engine.getCipher().getUnderlyingCipher().getAlgorithmName().equals("DES") || + engine.getCipher().getUnderlyingCipher().getAlgorithmName().equals("RC2") || + engine.getCipher().getUnderlyingCipher().getAlgorithmName().equals("RC5-32") || + engine.getCipher().getUnderlyingCipher().getAlgorithmName().equals("RC5-64")) + { + return new IESParameterSpec(null, null, 64, 64); + } + else if (engine.getCipher().getUnderlyingCipher().getAlgorithmName().equals("SKIPJACK")) + { + return new IESParameterSpec(null, null, 80, 80); + } + else if (engine.getCipher().getUnderlyingCipher().getAlgorithmName().equals("GOST28147")) + { + return new IESParameterSpec(null, null, 256, 256); + } + + return new IESParameterSpec(null, null, 128, 128); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/KeyUtil.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/KeyUtil.java new file mode 100644 index 00000000..d1eb6841 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/KeyUtil.java @@ -0,0 +1,72 @@ +package org.spongycastle.jcajce.provider.asymmetric.util; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; + +public class KeyUtil +{ + public static byte[] getEncodedSubjectPublicKeyInfo(AlgorithmIdentifier algId, ASN1Encodable keyData) + { + try + { + return getEncodedSubjectPublicKeyInfo(new SubjectPublicKeyInfo(algId, keyData)); + } + catch (Exception e) + { + return null; + } + } + + public static byte[] getEncodedSubjectPublicKeyInfo(AlgorithmIdentifier algId, byte[] keyData) + { + try + { + return getEncodedSubjectPublicKeyInfo(new SubjectPublicKeyInfo(algId, keyData)); + } + catch (Exception e) + { + return null; + } + } + + public static byte[] getEncodedSubjectPublicKeyInfo(SubjectPublicKeyInfo info) + { + try + { + return info.getEncoded(ASN1Encoding.DER); + } + catch (Exception e) + { + return null; + } + } + + public static byte[] getEncodedPrivateKeyInfo(AlgorithmIdentifier algId, ASN1Encodable privKey) + { + try + { + PrivateKeyInfo info = new PrivateKeyInfo(algId, privKey.toASN1Primitive()); + + return getEncodedPrivateKeyInfo(info); + } + catch (Exception e) + { + return null; + } + } + + public static byte[] getEncodedPrivateKeyInfo(PrivateKeyInfo info) + { + try + { + return info.getEncoded(ASN1Encoding.DER); + } + catch (Exception e) + { + return null; + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/PKCS12BagAttributeCarrierImpl.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/PKCS12BagAttributeCarrierImpl.java new file mode 100644 index 00000000..5ae93fdb --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/PKCS12BagAttributeCarrierImpl.java @@ -0,0 +1,124 @@ +package org.spongycastle.jcajce.provider.asymmetric.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1InputStream; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OutputStream; +import org.spongycastle.jce.interfaces.PKCS12BagAttributeCarrier; + +public class PKCS12BagAttributeCarrierImpl + implements PKCS12BagAttributeCarrier +{ + private Hashtable pkcs12Attributes; + private Vector pkcs12Ordering; + + PKCS12BagAttributeCarrierImpl(Hashtable attributes, Vector ordering) + { + this.pkcs12Attributes = attributes; + this.pkcs12Ordering = ordering; + } + + public PKCS12BagAttributeCarrierImpl() + { + this(new Hashtable(), new Vector()); + } + + public void setBagAttribute( + ASN1ObjectIdentifier oid, + ASN1Encodable attribute) + { + if (pkcs12Attributes.containsKey(oid)) + { // preserve original ordering + pkcs12Attributes.put(oid, attribute); + } + else + { + pkcs12Attributes.put(oid, attribute); + pkcs12Ordering.addElement(oid); + } + } + + public ASN1Encodable getBagAttribute( + ASN1ObjectIdentifier oid) + { + return (ASN1Encodable)pkcs12Attributes.get(oid); + } + + public Enumeration getBagAttributeKeys() + { + return pkcs12Ordering.elements(); + } + + int size() + { + return pkcs12Ordering.size(); + } + + Hashtable getAttributes() + { + return pkcs12Attributes; + } + + Vector getOrdering() + { + return pkcs12Ordering; + } + + public void writeObject(ObjectOutputStream out) + throws IOException + { + if (pkcs12Ordering.size() == 0) + { + out.writeObject(new Hashtable()); + out.writeObject(new Vector()); + } + else + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ASN1OutputStream aOut = new ASN1OutputStream(bOut); + + Enumeration e = this.getBagAttributeKeys(); + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); + + aOut.writeObject(oid); + aOut.writeObject((ASN1Encodable)pkcs12Attributes.get(oid)); + } + + out.writeObject(bOut.toByteArray()); + } + } + + public void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException + { + Object obj = in.readObject(); + + if (obj instanceof Hashtable) + { + this.pkcs12Attributes = (Hashtable)obj; + this.pkcs12Ordering = (Vector)in.readObject(); + } + else + { + ASN1InputStream aIn = new ASN1InputStream((byte[])obj); + + ASN1ObjectIdentifier oid; + + while ((oid = (ASN1ObjectIdentifier)aIn.readObject()) != null) + { + this.setBagAttribute(oid, aIn.readObject()); + } + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java new file mode 100644 index 00000000..1d1dc8c9 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java @@ -0,0 +1,395 @@ +package org.spongycastle.jcajce.provider.asymmetric.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PushbackInputStream; +import java.security.cert.CRL; +import java.security.cert.CRLException; +import java.security.cert.CertPath; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactorySpi; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import org.spongycastle.asn1.ASN1InputStream; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.SignedData; +import org.spongycastle.asn1.x509.Certificate; +import org.spongycastle.asn1.x509.CertificateList; + +/** + * class for dealing with X509 certificates. + * <p> + * At the moment this will deal with "-----BEGIN CERTIFICATE-----" to "-----END CERTIFICATE-----" + * base 64 encoded certs, as well as the BER binaries of certificates and some classes of PKCS#7 + * objects. + */ +public class CertificateFactory + extends CertificateFactorySpi +{ + private static final PEMUtil PEM_CERT_PARSER = new PEMUtil("CERTIFICATE"); + private static final PEMUtil PEM_CRL_PARSER = new PEMUtil("CRL"); + + private ASN1Set sData = null; + private int sDataObjectCount = 0; + private InputStream currentStream = null; + + private ASN1Set sCrlData = null; + private int sCrlDataObjectCount = 0; + private InputStream currentCrlStream = null; + + private java.security.cert.Certificate readDERCertificate( + ASN1InputStream dIn) + throws IOException, CertificateParsingException + { + ASN1Sequence seq = (ASN1Sequence)dIn.readObject(); + + if (seq.size() > 1 + && seq.getObjectAt(0) instanceof ASN1ObjectIdentifier) + { + if (seq.getObjectAt(0).equals(PKCSObjectIdentifiers.signedData)) + { + sData = SignedData.getInstance(ASN1Sequence.getInstance( + (ASN1TaggedObject)seq.getObjectAt(1), true)).getCertificates(); + + return getCertificate(); + } + } + + return new X509CertificateObject( + Certificate.getInstance(seq)); + } + + private java.security.cert.Certificate getCertificate() + throws CertificateParsingException + { + if (sData != null) + { + while (sDataObjectCount < sData.size()) + { + Object obj = sData.getObjectAt(sDataObjectCount++); + + if (obj instanceof ASN1Sequence) + { + return new X509CertificateObject( + Certificate.getInstance(obj)); + } + } + } + + return null; + } + + private java.security.cert.Certificate readPEMCertificate( + InputStream in) + throws IOException, CertificateParsingException + { + ASN1Sequence seq = PEM_CERT_PARSER.readPEMObject(in); + + if (seq != null) + { + return new X509CertificateObject( + Certificate.getInstance(seq)); + } + + return null; + } + + protected CRL createCRL(CertificateList c) + throws CRLException + { + return new X509CRLObject(c); + } + + private CRL readPEMCRL( + InputStream in) + throws IOException, CRLException + { + ASN1Sequence seq = PEM_CRL_PARSER.readPEMObject(in); + + if (seq != null) + { + return createCRL( + CertificateList.getInstance(seq)); + } + + return null; + } + + private CRL readDERCRL( + ASN1InputStream aIn) + throws IOException, CRLException + { + ASN1Sequence seq = (ASN1Sequence)aIn.readObject(); + + if (seq.size() > 1 + && seq.getObjectAt(0) instanceof ASN1ObjectIdentifier) + { + if (seq.getObjectAt(0).equals(PKCSObjectIdentifiers.signedData)) + { + sCrlData = SignedData.getInstance(ASN1Sequence.getInstance( + (ASN1TaggedObject)seq.getObjectAt(1), true)).getCRLs(); + + return getCRL(); + } + } + + return createCRL( + CertificateList.getInstance(seq)); + } + + private CRL getCRL() + throws CRLException + { + if (sCrlData == null || sCrlDataObjectCount >= sCrlData.size()) + { + return null; + } + + return createCRL( + CertificateList.getInstance( + sCrlData.getObjectAt(sCrlDataObjectCount++))); + } + + /** + * Generates a certificate object and initializes it with the data + * read from the input stream inStream. + */ + public java.security.cert.Certificate engineGenerateCertificate( + InputStream in) + throws CertificateException + { + if (currentStream == null) + { + currentStream = in; + sData = null; + sDataObjectCount = 0; + } + else if (currentStream != in) // reset if input stream has changed + { + currentStream = in; + sData = null; + sDataObjectCount = 0; + } + + try + { + if (sData != null) + { + if (sDataObjectCount != sData.size()) + { + return getCertificate(); + } + else + { + sData = null; + sDataObjectCount = 0; + return null; + } + } + + PushbackInputStream pis = new PushbackInputStream(in); + int tag = pis.read(); + + if (tag == -1) + { + return null; + } + + pis.unread(tag); + + if (tag != 0x30) // assume ascii PEM encoded. + { + return readPEMCertificate(pis); + } + else + { + return readDERCertificate(new ASN1InputStream(pis)); + } + } + catch (Exception e) + { + throw new ExCertificateException(e); + } + } + + /** + * Returns a (possibly empty) collection view of the certificates + * read from the given input stream inStream. + */ + public Collection engineGenerateCertificates( + InputStream inStream) + throws CertificateException + { + java.security.cert.Certificate cert; + List certs = new ArrayList(); + + while ((cert = engineGenerateCertificate(inStream)) != null) + { + certs.add(cert); + } + + return certs; + } + + /** + * Generates a certificate revocation list (CRL) object and initializes + * it with the data read from the input stream inStream. + */ + public CRL engineGenerateCRL( + InputStream inStream) + throws CRLException + { + if (currentCrlStream == null) + { + currentCrlStream = inStream; + sCrlData = null; + sCrlDataObjectCount = 0; + } + else if (currentCrlStream != inStream) // reset if input stream has changed + { + currentCrlStream = inStream; + sCrlData = null; + sCrlDataObjectCount = 0; + } + + try + { + if (sCrlData != null) + { + if (sCrlDataObjectCount != sCrlData.size()) + { + return getCRL(); + } + else + { + sCrlData = null; + sCrlDataObjectCount = 0; + return null; + } + } + + PushbackInputStream pis = new PushbackInputStream(inStream); + int tag = pis.read(); + + if (tag == -1) + { + return null; + } + + pis.unread(tag); + + if (tag != 0x30) // assume ascii PEM encoded. + { + return readPEMCRL(pis); + } + else + { // lazy evaluate to help processing of large CRLs + return readDERCRL(new ASN1InputStream(pis, true)); + } + } + catch (CRLException e) + { + throw e; + } + catch (Exception e) + { + throw new CRLException(e.toString()); + } + } + + /** + * Returns a (possibly empty) collection view of the CRLs read from + * the given input stream inStream. + * + * The inStream may contain a sequence of DER-encoded CRLs, or + * a PKCS#7 CRL set. This is a PKCS#7 SignedData object, with the + * only signficant field being crls. In particular the signature + * and the contents are ignored. + */ + public Collection engineGenerateCRLs( + InputStream inStream) + throws CRLException + { + CRL crl; + List crls = new ArrayList(); + + while ((crl = engineGenerateCRL(inStream)) != null) + { + crls.add(crl); + } + + return crls; + } + + public Iterator engineGetCertPathEncodings() + { + return PKIXCertPath.certPathEncodings.iterator(); + } + + public CertPath engineGenerateCertPath( + InputStream inStream) + throws CertificateException + { + return engineGenerateCertPath(inStream, "PkiPath"); + } + + public CertPath engineGenerateCertPath( + InputStream inStream, + String encoding) + throws CertificateException + { + return new PKIXCertPath(inStream, encoding); + } + + public CertPath engineGenerateCertPath( + List certificates) + throws CertificateException + { + Iterator iter = certificates.iterator(); + Object obj; + while (iter.hasNext()) + { + obj = iter.next(); + if (obj != null) + { + if (!(obj instanceof X509Certificate)) + { + throw new CertificateException("list contains non X509Certificate object while creating CertPath\n" + obj.toString()); + } + } + } + return new PKIXCertPath(certificates); + } + + private class ExCertificateException + extends CertificateException + { + private Throwable cause; + + public ExCertificateException(Throwable cause) + { + this.cause = cause; + } + + public ExCertificateException(String msg, Throwable cause) + { + super(msg); + + this.cause = cause; + } + + public Throwable getCause() + { + return cause; + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/ExtCRLException.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/ExtCRLException.java new file mode 100644 index 00000000..af366acf --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/ExtCRLException.java @@ -0,0 +1,20 @@ +package org.spongycastle.jcajce.provider.asymmetric.x509; + +import java.security.cert.CRLException; + +class ExtCRLException + extends CRLException +{ + Throwable cause; + + ExtCRLException(String message, Throwable cause) + { + super(message); + this.cause = cause; + } + + public Throwable getCause() + { + return cause; + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/KeyFactory.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/KeyFactory.java new file mode 100644 index 00000000..38597048 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/KeyFactory.java @@ -0,0 +1,95 @@ +package org.spongycastle.jcajce.provider.asymmetric.x509; + +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactorySpi; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.jce.provider.BouncyCastleProvider; + +public class KeyFactory + extends KeyFactorySpi +{ + + protected PrivateKey engineGeneratePrivate( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof PKCS8EncodedKeySpec) + { + try + { + PrivateKeyInfo info = PrivateKeyInfo.getInstance(((PKCS8EncodedKeySpec)keySpec).getEncoded()); + PrivateKey key = BouncyCastleProvider.getPrivateKey(info); + + if (key != null) + { + return key; + } + + throw new InvalidKeySpecException("no factory found for OID: " + info.getPrivateKeyAlgorithm().getAlgorithm()); + } + catch (Exception e) + { + throw new InvalidKeySpecException(e.toString()); + } + } + + throw new InvalidKeySpecException("Unknown KeySpec type: " + keySpec.getClass().getName()); + } + + protected PublicKey engineGeneratePublic( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof X509EncodedKeySpec) + { + try + { + SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(((X509EncodedKeySpec)keySpec).getEncoded()); + PublicKey key = BouncyCastleProvider.getPublicKey(info); + + if (key != null) + { + return key; + } + + throw new InvalidKeySpecException("no factory found for OID: " + info.getAlgorithm().getAlgorithm()); + } + catch (Exception e) + { + throw new InvalidKeySpecException(e.toString()); + } + } + + throw new InvalidKeySpecException("Unknown KeySpec type: " + keySpec.getClass().getName()); + } + + protected KeySpec engineGetKeySpec(Key key, Class keySpec) + throws InvalidKeySpecException + { + if (keySpec.isAssignableFrom(PKCS8EncodedKeySpec.class) && key.getFormat().equals("PKCS#8")) + { + return new PKCS8EncodedKeySpec(key.getEncoded()); + } + else if (keySpec.isAssignableFrom(X509EncodedKeySpec.class) && key.getFormat().equals("X.509")) + { + return new X509EncodedKeySpec(key.getEncoded()); + } + + throw new InvalidKeySpecException("not implemented yet " + key + " " + keySpec); + } + + protected Key engineTranslateKey(Key key) + throws InvalidKeyException + { + throw new InvalidKeyException("not implemented yet " + key); + } +}
\ No newline at end of file diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/PEMUtil.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/PEMUtil.java new file mode 100644 index 00000000..4eb704d9 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/PEMUtil.java @@ -0,0 +1,88 @@ +package org.spongycastle.jcajce.provider.asymmetric.x509; + +import java.io.IOException; +import java.io.InputStream; + +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.util.encoders.Base64; + +public class PEMUtil +{ + private final String _header1; + private final String _header2; + private final String _footer1; + private final String _footer2; + + PEMUtil( + String type) + { + _header1 = "-----BEGIN " + type + "-----"; + _header2 = "-----BEGIN X509 " + type + "-----"; + _footer1 = "-----END " + type + "-----"; + _footer2 = "-----END X509 " + type + "-----"; + } + + private String readLine( + InputStream in) + throws IOException + { + int c; + StringBuffer l = new StringBuffer(); + + do + { + while (((c = in.read()) != '\r') && c != '\n' && (c >= 0)) + { + l.append((char)c); + } + } + while (c >= 0 && l.length() == 0); + + if (c < 0) + { + return null; + } + + return l.toString(); + } + + ASN1Sequence readPEMObject( + InputStream in) + throws IOException + { + String line; + StringBuffer pemBuf = new StringBuffer(); + + while ((line = readLine(in)) != null) + { + if (line.startsWith(_header1) || line.startsWith(_header2)) + { + break; + } + } + + while ((line = readLine(in)) != null) + { + if (line.startsWith(_footer1) || line.startsWith(_footer2)) + { + break; + } + + pemBuf.append(line); + } + + if (pemBuf.length() != 0) + { + try + { + return ASN1Sequence.getInstance(Base64.decode(pemBuf.toString())); + } + catch (Exception e) + { + throw new IOException("malformed PEM data encountered"); + } + } + + return null; + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/PKIXCertPath.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/PKIXCertPath.java new file mode 100644 index 00000000..08fd6b0b --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/PKIXCertPath.java @@ -0,0 +1,372 @@ +package org.spongycastle.jcajce.provider.asymmetric.x509; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.security.NoSuchProviderException; +import java.security.cert.CertPath; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import javax.security.auth.x500.X500Principal; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1InputStream; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERSet; +import org.spongycastle.asn1.pkcs.ContentInfo; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.SignedData; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.util.io.pem.PemObject; +import org.spongycastle.util.io.pem.PemWriter; + +/** + * CertPath implementation for X.509 certificates. + * <br /> + **/ +public class PKIXCertPath + extends CertPath +{ + static final List certPathEncodings; + + static + { + List encodings = new ArrayList(); + encodings.add("PkiPath"); + encodings.add("PEM"); + encodings.add("PKCS7"); + certPathEncodings = Collections.unmodifiableList(encodings); + } + + private List certificates; + + /** + * @param certs + */ + private List sortCerts( + List certs) + { + if (certs.size() < 2) + { + return certs; + } + + X500Principal issuer = ((X509Certificate)certs.get(0)).getIssuerX500Principal(); + boolean okay = true; + + for (int i = 1; i != certs.size(); i++) + { + X509Certificate cert = (X509Certificate)certs.get(i); + + if (issuer.equals(cert.getSubjectX500Principal())) + { + issuer = ((X509Certificate)certs.get(i)).getIssuerX500Principal(); + } + else + { + okay = false; + break; + } + } + + if (okay) + { + return certs; + } + + // find end-entity cert + List retList = new ArrayList(certs.size()); + List orig = new ArrayList(certs); + + for (int i = 0; i < certs.size(); i++) + { + X509Certificate cert = (X509Certificate)certs.get(i); + boolean found = false; + + X500Principal subject = cert.getSubjectX500Principal(); + + for (int j = 0; j != certs.size(); j++) + { + X509Certificate c = (X509Certificate)certs.get(j); + if (c.getIssuerX500Principal().equals(subject)) + { + found = true; + break; + } + } + + if (!found) + { + retList.add(cert); + certs.remove(i); + } + } + + // can only have one end entity cert - something's wrong, give up. + if (retList.size() > 1) + { + return orig; + } + + for (int i = 0; i != retList.size(); i++) + { + issuer = ((X509Certificate)retList.get(i)).getIssuerX500Principal(); + + for (int j = 0; j < certs.size(); j++) + { + X509Certificate c = (X509Certificate)certs.get(j); + if (issuer.equals(c.getSubjectX500Principal())) + { + retList.add(c); + certs.remove(j); + break; + } + } + } + + // make sure all certificates are accounted for. + if (certs.size() > 0) + { + return orig; + } + + return retList; + } + + PKIXCertPath(List certificates) + { + super("X.509"); + this.certificates = sortCerts(new ArrayList(certificates)); + } + + /** + * Creates a CertPath of the specified type. + * This constructor is protected because most users should use + * a CertificateFactory to create CertPaths. + **/ + PKIXCertPath( + InputStream inStream, + String encoding) + throws CertificateException + { + super("X.509"); + try + { + if (encoding.equalsIgnoreCase("PkiPath")) + { + ASN1InputStream derInStream = new ASN1InputStream(inStream); + ASN1Primitive derObject = derInStream.readObject(); + if (!(derObject instanceof ASN1Sequence)) + { + throw new CertificateException("input stream does not contain a ASN1 SEQUENCE while reading PkiPath encoded data to load CertPath"); + } + Enumeration e = ((ASN1Sequence)derObject).getObjects(); + certificates = new ArrayList(); + CertificateFactory certFactory = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME); + while (e.hasMoreElements()) + { + ASN1Encodable element = (ASN1Encodable)e.nextElement(); + byte[] encoded = element.toASN1Primitive().getEncoded(ASN1Encoding.DER); + certificates.add(0, certFactory.generateCertificate( + new ByteArrayInputStream(encoded))); + } + } + else if (encoding.equalsIgnoreCase("PKCS7") || encoding.equalsIgnoreCase("PEM")) + { + inStream = new BufferedInputStream(inStream); + certificates = new ArrayList(); + CertificateFactory certFactory= CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME); + Certificate cert; + while ((cert = certFactory.generateCertificate(inStream)) != null) + { + certificates.add(cert); + } + } + else + { + throw new CertificateException("unsupported encoding: " + encoding); + } + } + catch (IOException ex) + { + throw new CertificateException("IOException throw while decoding CertPath:\n" + ex.toString()); + } + catch (NoSuchProviderException ex) + { + throw new CertificateException("BouncyCastle provider not found while trying to get a CertificateFactory:\n" + ex.toString()); + } + + this.certificates = sortCerts(certificates); + } + + /** + * Returns an iteration of the encodings supported by this + * certification path, with the default encoding + * first. Attempts to modify the returned Iterator via its + * remove method result in an UnsupportedOperationException. + * + * @return an Iterator over the names of the supported encodings (as Strings) + **/ + public Iterator getEncodings() + { + return certPathEncodings.iterator(); + } + + /** + * Returns the encoded form of this certification path, using + * the default encoding. + * + * @return the encoded bytes + * @exception java.security.cert.CertificateEncodingException if an encoding error occurs + **/ + public byte[] getEncoded() + throws CertificateEncodingException + { + Iterator iter = getEncodings(); + if (iter.hasNext()) + { + Object enc = iter.next(); + if (enc instanceof String) + { + return getEncoded((String)enc); + } + } + return null; + } + + /** + * Returns the encoded form of this certification path, using + * the specified encoding. + * + * @param encoding the name of the encoding to use + * @return the encoded bytes + * @exception java.security.cert.CertificateEncodingException if an encoding error + * occurs or the encoding requested is not supported + * + **/ + public byte[] getEncoded(String encoding) + throws CertificateEncodingException + { + if (encoding.equalsIgnoreCase("PkiPath")) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + ListIterator iter = certificates.listIterator(certificates.size()); + while (iter.hasPrevious()) + { + v.add(toASN1Object((X509Certificate)iter.previous())); + } + + return toDEREncoded(new DERSequence(v)); + } + else if (encoding.equalsIgnoreCase("PKCS7")) + { + ContentInfo encInfo = new ContentInfo(PKCSObjectIdentifiers.data, null); + + ASN1EncodableVector v = new ASN1EncodableVector(); + for (int i = 0; i != certificates.size(); i++) + { + v.add(toASN1Object((X509Certificate)certificates.get(i))); + } + + SignedData sd = new SignedData( + new ASN1Integer(1), + new DERSet(), + encInfo, + new DERSet(v), + null, + new DERSet()); + + return toDEREncoded(new ContentInfo( + PKCSObjectIdentifiers.signedData, sd)); + } + else if (encoding.equalsIgnoreCase("PEM")) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PemWriter pWrt = new PemWriter(new OutputStreamWriter(bOut)); + + try + { + for (int i = 0; i != certificates.size(); i++) + { + pWrt.writeObject(new PemObject("CERTIFICATE", ((X509Certificate)certificates.get(i)).getEncoded())); + } + + pWrt.close(); + } + catch (Exception e) + { + throw new CertificateEncodingException("can't encode certificate for PEM encoded path"); + } + + return bOut.toByteArray(); + } + else + { + throw new CertificateEncodingException("unsupported encoding: " + encoding); + } + } + + /** + * Returns the list of certificates in this certification + * path. The List returned must be immutable and thread-safe. + * + * @return an immutable List of Certificates (may be empty, but not null) + **/ + public List getCertificates() + { + return Collections.unmodifiableList(new ArrayList(certificates)); + } + + /** + * Return a DERObject containing the encoded certificate. + * + * @param cert the X509Certificate object to be encoded + * + * @return the DERObject + **/ + private ASN1Primitive toASN1Object( + X509Certificate cert) + throws CertificateEncodingException + { + try + { + return new ASN1InputStream(cert.getEncoded()).readObject(); + } + catch (Exception e) + { + throw new CertificateEncodingException("Exception while encoding certificate: " + e.toString()); + } + } + + private byte[] toDEREncoded(ASN1Encodable obj) + throws CertificateEncodingException + { + try + { + return obj.toASN1Primitive().getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + throw new CertificateEncodingException("Exception thrown: " + e); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java new file mode 100644 index 00000000..780cad8c --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java @@ -0,0 +1,318 @@ +package org.spongycastle.jcajce.provider.asymmetric.x509; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.cert.CRLException; +import java.security.cert.X509CRLEntry; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Set; + +import javax.security.auth.x500.X500Principal; + +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Enumerated; +import org.spongycastle.asn1.ASN1InputStream; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.util.ASN1Dump; +import org.spongycastle.asn1.x500.X500Name; +import org.spongycastle.asn1.x509.CRLReason; +import org.spongycastle.asn1.x509.Extension; +import org.spongycastle.asn1.x509.Extensions; +import org.spongycastle.asn1.x509.GeneralName; +import org.spongycastle.asn1.x509.GeneralNames; +import org.spongycastle.asn1.x509.TBSCertList; +import org.spongycastle.asn1.x509.X509Extension; + +/** + * The following extensions are listed in RFC 2459 as relevant to CRL Entries + * + * ReasonCode Hode Instruction Code Invalidity Date Certificate Issuer + * (critical) + */ +public class X509CRLEntryObject extends X509CRLEntry +{ + private TBSCertList.CRLEntry c; + + private X500Name certificateIssuer; + private int hashValue; + private boolean isHashValueSet; + + protected X509CRLEntryObject(TBSCertList.CRLEntry c) + { + this.c = c; + this.certificateIssuer = null; + } + + /** + * Constructor for CRLEntries of indirect CRLs. If <code>isIndirect</code> + * is <code>false</code> {@link #getCertificateIssuer()} will always + * return <code>null</code>, <code>previousCertificateIssuer</code> is + * ignored. If this <code>isIndirect</code> is specified and this CRLEntry + * has no certificate issuer CRL entry extension + * <code>previousCertificateIssuer</code> is returned by + * {@link #getCertificateIssuer()}. + * + * @param c + * TBSCertList.CRLEntry object. + * @param isIndirect + * <code>true</code> if the corresponding CRL is a indirect + * CRL. + * @param previousCertificateIssuer + * Certificate issuer of the previous CRLEntry. + */ + protected X509CRLEntryObject( + TBSCertList.CRLEntry c, + boolean isIndirect, + X500Name previousCertificateIssuer) + { + this.c = c; + this.certificateIssuer = loadCertificateIssuer(isIndirect, previousCertificateIssuer); + } + + /** + * Will return true if any extensions are present and marked as critical as + * we currently don't handle any extensions! + */ + public boolean hasUnsupportedCriticalExtension() + { + Set extns = getCriticalExtensionOIDs(); + + return extns != null && !extns.isEmpty(); + } + + private X500Name loadCertificateIssuer(boolean isIndirect, X500Name previousCertificateIssuer) + { + if (!isIndirect) + { + return null; + } + + Extension ext = getExtension(Extension.certificateIssuer); + if (ext == null) + { + return previousCertificateIssuer; + } + + try + { + GeneralName[] names = GeneralNames.getInstance(ext.getParsedValue()).getNames(); + for (int i = 0; i < names.length; i++) + { + if (names[i].getTagNo() == GeneralName.directoryName) + { + return X500Name.getInstance(names[i].getName()); + } + } + return null; + } + catch (Exception e) + { + return null; + } + } + + public X500Principal getCertificateIssuer() + { + if (certificateIssuer == null) + { + return null; + } + try + { + return new X500Principal(certificateIssuer.getEncoded()); + } + catch (IOException e) + { + return null; + } + } + + private Set getExtensionOIDs(boolean critical) + { + Extensions extensions = c.getExtensions(); + + if (extensions != null) + { + Set set = new HashSet(); + Enumeration e = extensions.oids(); + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) e.nextElement(); + Extension ext = extensions.getExtension(oid); + + if (critical == ext.isCritical()) + { + set.add(oid.getId()); + } + } + + return set; + } + + return null; + } + + public Set getCriticalExtensionOIDs() + { + return getExtensionOIDs(true); + } + + public Set getNonCriticalExtensionOIDs() + { + return getExtensionOIDs(false); + } + + private Extension getExtension(ASN1ObjectIdentifier oid) + { + Extensions exts = c.getExtensions(); + + if (exts != null) + { + return exts.getExtension(oid); + } + + return null; + } + + public byte[] getExtensionValue(String oid) + { + Extension ext = getExtension(new ASN1ObjectIdentifier(oid)); + + if (ext != null) + { + try + { + return ext.getExtnValue().getEncoded(); + } + catch (Exception e) + { + throw new RuntimeException("error encoding " + e.toString()); + } + } + + return null; + } + + /** + * Cache the hashCode value - calculating it with the standard method. + * @return calculated hashCode. + */ + public int hashCode() + { + if (!isHashValueSet) + { + hashValue = super.hashCode(); + isHashValueSet = true; + } + + return hashValue; + } + + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (o instanceof X509CRLEntryObject) + { + X509CRLEntryObject other = (X509CRLEntryObject)o; + + return this.c.equals(other.c); + } + + return super.equals(this); + } + + public byte[] getEncoded() + throws CRLException + { + try + { + return c.getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + throw new CRLException(e.toString()); + } + } + + public BigInteger getSerialNumber() + { + return c.getUserCertificate().getValue(); + } + + public Date getRevocationDate() + { + return c.getRevocationDate().getDate(); + } + + public boolean hasExtensions() + { + return c.getExtensions() != null; + } + + public String toString() + { + StringBuffer buf = new StringBuffer(); + String nl = System.getProperty("line.separator"); + + buf.append(" userCertificate: ").append(this.getSerialNumber()).append(nl); + buf.append(" revocationDate: ").append(this.getRevocationDate()).append(nl); + buf.append(" certificateIssuer: ").append(this.getCertificateIssuer()).append(nl); + + Extensions extensions = c.getExtensions(); + + if (extensions != null) + { + Enumeration e = extensions.oids(); + if (e.hasMoreElements()) + { + buf.append(" crlEntryExtensions:").append(nl); + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); + Extension ext = extensions.getExtension(oid); + if (ext.getExtnValue() != null) + { + byte[] octs = ext.getExtnValue().getOctets(); + ASN1InputStream dIn = new ASN1InputStream(octs); + buf.append(" critical(").append(ext.isCritical()).append(") "); + try + { + if (oid.equals(X509Extension.reasonCode)) + { + buf.append(CRLReason.getInstance(ASN1Enumerated.getInstance(dIn.readObject()))).append(nl); + } + else if (oid.equals(X509Extension.certificateIssuer)) + { + buf.append("Certificate issuer: ").append(GeneralNames.getInstance(dIn.readObject())).append(nl); + } + else + { + buf.append(oid.getId()); + buf.append(" value = ").append(ASN1Dump.dumpAsString(dIn.readObject())).append(nl); + } + } + catch (Exception ex) + { + buf.append(oid.getId()); + buf.append(" value = ").append("*****").append(nl); + } + } + else + { + buf.append(nl); + } + } + } + } + + return buf.toString(); + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java new file mode 100644 index 00000000..d387c440 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java @@ -0,0 +1,627 @@ +package org.spongycastle.jcajce.provider.asymmetric.x509; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Principal; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.CRLException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509CRL; +import java.security.cert.X509CRLEntry; +import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import javax.security.auth.x500.X500Principal; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1InputStream; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.util.ASN1Dump; +import org.spongycastle.asn1.x500.X500Name; +import org.spongycastle.asn1.x509.CRLDistPoint; +import org.spongycastle.asn1.x509.CRLNumber; +import org.spongycastle.asn1.x509.CertificateList; +import org.spongycastle.asn1.x509.Extension; +import org.spongycastle.asn1.x509.Extensions; +import org.spongycastle.asn1.x509.GeneralNames; +import org.spongycastle.asn1.x509.IssuingDistributionPoint; +import org.spongycastle.asn1.x509.TBSCertList; +import org.spongycastle.jce.X509Principal; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.provider.RFC3280CertPathUtilities; +import org.spongycastle.util.encoders.Hex; + +/** + * The following extensions are listed in RFC 2459 as relevant to CRLs + * + * Authority Key Identifier + * Issuer Alternative Name + * CRL Number + * Delta CRL Indicator (critical) + * Issuing Distribution Point (critical) + */ +public class X509CRLObject + extends X509CRL +{ + private CertificateList c; + private String sigAlgName; + private byte[] sigAlgParams; + private boolean isIndirect; + private boolean isHashCodeSet = false; + private int hashCodeValue; + + static boolean isIndirectCRL(X509CRL crl) + throws CRLException + { + try + { + byte[] idp = crl.getExtensionValue(Extension.issuingDistributionPoint.getId()); + return idp != null + && IssuingDistributionPoint.getInstance(ASN1OctetString.getInstance(idp).getOctets()).isIndirectCRL(); + } + catch (Exception e) + { + throw new ExtCRLException( + "Exception reading IssuingDistributionPoint", e); + } + } + + protected X509CRLObject( + CertificateList c) + throws CRLException + { + this.c = c; + + try + { + this.sigAlgName = X509SignatureUtil.getSignatureName(c.getSignatureAlgorithm()); + + if (c.getSignatureAlgorithm().getParameters() != null) + { + this.sigAlgParams = ((ASN1Encodable)c.getSignatureAlgorithm().getParameters()).toASN1Primitive().getEncoded(ASN1Encoding.DER); + } + else + { + this.sigAlgParams = null; + } + + this.isIndirect = isIndirectCRL(this); + } + catch (Exception e) + { + throw new CRLException("CRL contents invalid: " + e); + } + } + + /** + * Will return true if any extensions are present and marked + * as critical as we currently dont handle any extensions! + */ + public boolean hasUnsupportedCriticalExtension() + { + Set extns = getCriticalExtensionOIDs(); + + if (extns == null) + { + return false; + } + + extns.remove(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT); + extns.remove(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR); + + return !extns.isEmpty(); + } + + private Set getExtensionOIDs(boolean critical) + { + if (this.getVersion() == 2) + { + Extensions extensions = c.getTBSCertList().getExtensions(); + + if (extensions != null) + { + Set set = new HashSet(); + Enumeration e = extensions.oids(); + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); + Extension ext = extensions.getExtension(oid); + + if (critical == ext.isCritical()) + { + set.add(oid.getId()); + } + } + + return set; + } + } + + return null; + } + + public Set getCriticalExtensionOIDs() + { + return getExtensionOIDs(true); + } + + public Set getNonCriticalExtensionOIDs() + { + return getExtensionOIDs(false); + } + + public byte[] getExtensionValue(String oid) + { + Extensions exts = c.getTBSCertList().getExtensions(); + + if (exts != null) + { + Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid)); + + if (ext != null) + { + try + { + return ext.getExtnValue().getEncoded(); + } + catch (Exception e) + { + throw new IllegalStateException("error parsing " + e.toString()); + } + } + } + + return null; + } + + public byte[] getEncoded() + throws CRLException + { + try + { + return c.getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + throw new CRLException(e.toString()); + } + } + + public void verify(PublicKey key) + throws CRLException, NoSuchAlgorithmException, + InvalidKeyException, NoSuchProviderException, SignatureException + { + verify(key, BouncyCastleProvider.PROVIDER_NAME); + } + + public void verify(PublicKey key, String sigProvider) + throws CRLException, NoSuchAlgorithmException, + InvalidKeyException, NoSuchProviderException, SignatureException + { + if (!c.getSignatureAlgorithm().equals(c.getTBSCertList().getSignature())) + { + throw new CRLException("Signature algorithm on CertificateList does not match TBSCertList."); + } + + Signature sig; + + if (sigProvider != null) + { + sig = Signature.getInstance(getSigAlgName(), sigProvider); + } + else + { + sig = Signature.getInstance(getSigAlgName()); + } + + sig.initVerify(key); + sig.update(this.getTBSCertList()); + + if (!sig.verify(this.getSignature())) + { + throw new SignatureException("CRL does not verify with supplied public key."); + } + } + + public int getVersion() + { + return c.getVersionNumber(); + } + + public Principal getIssuerDN() + { + return new X509Principal(X500Name.getInstance(c.getIssuer().toASN1Primitive())); + } + + public X500Principal getIssuerX500Principal() + { + try + { + return new X500Principal(c.getIssuer().getEncoded()); + } + catch (IOException e) + { + throw new IllegalStateException("can't encode issuer DN"); + } + } + + public Date getThisUpdate() + { + return c.getThisUpdate().getDate(); + } + + public Date getNextUpdate() + { + if (c.getNextUpdate() != null) + { + return c.getNextUpdate().getDate(); + } + + return null; + } + + private Set loadCRLEntries() + { + Set entrySet = new HashSet(); + Enumeration certs = c.getRevokedCertificateEnumeration(); + + X500Name previousCertificateIssuer = null; // the issuer + while (certs.hasMoreElements()) + { + TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)certs.nextElement(); + X509CRLEntryObject crlEntry = new X509CRLEntryObject(entry, isIndirect, previousCertificateIssuer); + entrySet.add(crlEntry); + if (isIndirect && entry.hasExtensions()) + { + Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer); + + if (currentCaName != null) + { + previousCertificateIssuer = X500Name.getInstance(GeneralNames.getInstance(currentCaName.getParsedValue()).getNames()[0].getName()); + } + } + } + + return entrySet; + } + + public X509CRLEntry getRevokedCertificate(BigInteger serialNumber) + { + Enumeration certs = c.getRevokedCertificateEnumeration(); + + X500Name previousCertificateIssuer = null; // the issuer + while (certs.hasMoreElements()) + { + TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)certs.nextElement(); + + if (serialNumber.equals(entry.getUserCertificate().getValue())) + { + return new X509CRLEntryObject(entry, isIndirect, previousCertificateIssuer); + } + + if (isIndirect && entry.hasExtensions()) + { + Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer); + + if (currentCaName != null) + { + previousCertificateIssuer = X500Name.getInstance(GeneralNames.getInstance(currentCaName.getParsedValue()).getNames()[0].getName()); + } + } + } + + return null; + } + + public Set getRevokedCertificates() + { + Set entrySet = loadCRLEntries(); + + if (!entrySet.isEmpty()) + { + return Collections.unmodifiableSet(entrySet); + } + + return null; + } + + public byte[] getTBSCertList() + throws CRLException + { + try + { + return c.getTBSCertList().getEncoded("DER"); + } + catch (IOException e) + { + throw new CRLException(e.toString()); + } + } + + public byte[] getSignature() + { + return c.getSignature().getBytes(); + } + + public String getSigAlgName() + { + return sigAlgName; + } + + public String getSigAlgOID() + { + return c.getSignatureAlgorithm().getAlgorithm().getId(); + } + + public byte[] getSigAlgParams() + { + if (sigAlgParams != null) + { + byte[] tmp = new byte[sigAlgParams.length]; + + System.arraycopy(sigAlgParams, 0, tmp, 0, tmp.length); + + return tmp; + } + + return null; + } + + /** + * Returns a string representation of this CRL. + * + * @return a string representation of this CRL. + */ + public String toString() + { + StringBuffer buf = new StringBuffer(); + String nl = System.getProperty("line.separator"); + + buf.append(" Version: ").append(this.getVersion()).append( + nl); + buf.append(" IssuerDN: ").append(this.getIssuerDN()) + .append(nl); + buf.append(" This update: ").append(this.getThisUpdate()) + .append(nl); + buf.append(" Next update: ").append(this.getNextUpdate()) + .append(nl); + buf.append(" Signature Algorithm: ").append(this.getSigAlgName()) + .append(nl); + + byte[] sig = this.getSignature(); + + buf.append(" Signature: ").append( + new String(Hex.encode(sig, 0, 20))).append(nl); + for (int i = 20; i < sig.length; i += 20) + { + if (i < sig.length - 20) + { + buf.append(" ").append( + new String(Hex.encode(sig, i, 20))).append(nl); + } + else + { + buf.append(" ").append( + new String(Hex.encode(sig, i, sig.length - i))).append(nl); + } + } + + Extensions extensions = c.getTBSCertList().getExtensions(); + + if (extensions != null) + { + Enumeration e = extensions.oids(); + + if (e.hasMoreElements()) + { + buf.append(" Extensions: ").append(nl); + } + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) e.nextElement(); + Extension ext = extensions.getExtension(oid); + + if (ext.getExtnValue() != null) + { + byte[] octs = ext.getExtnValue().getOctets(); + ASN1InputStream dIn = new ASN1InputStream(octs); + buf.append(" critical(").append( + ext.isCritical()).append(") "); + try + { + if (oid.equals(Extension.cRLNumber)) + { + buf.append( + new CRLNumber(ASN1Integer.getInstance( + dIn.readObject()).getPositiveValue())) + .append(nl); + } + else if (oid.equals(Extension.deltaCRLIndicator)) + { + buf.append( + "Base CRL: " + + new CRLNumber(ASN1Integer.getInstance( + dIn.readObject()).getPositiveValue())) + .append(nl); + } + else if (oid + .equals(Extension.issuingDistributionPoint)) + { + buf.append( + IssuingDistributionPoint.getInstance(dIn.readObject())).append(nl); + } + else if (oid + .equals(Extension.cRLDistributionPoints)) + { + buf.append( + CRLDistPoint.getInstance(dIn.readObject())).append(nl); + } + else if (oid.equals(Extension.freshestCRL)) + { + buf.append( + CRLDistPoint.getInstance(dIn.readObject())).append(nl); + } + else + { + buf.append(oid.getId()); + buf.append(" value = ").append( + ASN1Dump.dumpAsString(dIn.readObject())) + .append(nl); + } + } + catch (Exception ex) + { + buf.append(oid.getId()); + buf.append(" value = ").append("*****").append(nl); + } + } + else + { + buf.append(nl); + } + } + } + Set set = getRevokedCertificates(); + if (set != null) + { + Iterator it = set.iterator(); + while (it.hasNext()) + { + buf.append(it.next()); + buf.append(nl); + } + } + return buf.toString(); + } + + /** + * Checks whether the given certificate is on this CRL. + * + * @param cert the certificate to check for. + * @return true if the given certificate is on this CRL, + * false otherwise. + */ + public boolean isRevoked(Certificate cert) + { + if (!cert.getType().equals("X.509")) + { + throw new RuntimeException("X.509 CRL used with non X.509 Cert"); + } + + Enumeration certs = c.getRevokedCertificateEnumeration(); + + X500Name caName = c.getIssuer(); + + if (certs.hasMoreElements()) + { + BigInteger serial = ((X509Certificate)cert).getSerialNumber(); + + while (certs.hasMoreElements()) + { + TBSCertList.CRLEntry entry = TBSCertList.CRLEntry.getInstance(certs.nextElement()); + + if (isIndirect && entry.hasExtensions()) + { + Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer); + + if (currentCaName != null) + { + caName = X500Name.getInstance(GeneralNames.getInstance(currentCaName.getParsedValue()).getNames()[0].getName()); + } + } + + if (entry.getUserCertificate().getValue().equals(serial)) + { + X500Name issuer; + + if (cert instanceof X509Certificate) + { + issuer = X500Name.getInstance(((X509Certificate)cert).getIssuerX500Principal().getEncoded()); + } + else + { + try + { + issuer = org.spongycastle.asn1.x509.Certificate.getInstance(cert.getEncoded()).getIssuer(); + } + catch (CertificateEncodingException e) + { + throw new RuntimeException("Cannot process certificate"); + } + } + + if (!caName.equals(issuer)) + { + return false; + } + + return true; + } + } + } + + return false; + } + + public boolean equals(Object other) + { + if (this == other) + { + return true; + } + + if (!(other instanceof X509CRL)) + { + return false; + } + + if (other instanceof X509CRLObject) + { + X509CRLObject crlObject = (X509CRLObject)other; + + if (isHashCodeSet) + { + boolean otherIsHashCodeSet = crlObject.isHashCodeSet; + if (otherIsHashCodeSet) + { + if (crlObject.hashCodeValue != hashCodeValue) + { + return false; + } + } + } + + return this.c.equals(crlObject.c); + } + + return super.equals(other); + } + + public int hashCode() + { + if (!isHashCodeSet) + { + isHashCodeSet = true; + hashCodeValue = super.hashCode(); + } + + return hashCodeValue; + } +} + diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java new file mode 100644 index 00000000..7acb7cad --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java @@ -0,0 +1,903 @@ +package org.spongycastle.jcajce.provider.asymmetric.x509; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Principal; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Security; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.security.auth.x500.X500Principal; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1InputStream; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OutputStream; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1String; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERIA5String; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.misc.MiscObjectIdentifiers; +import org.spongycastle.asn1.misc.NetscapeCertType; +import org.spongycastle.asn1.misc.NetscapeRevocationURL; +import org.spongycastle.asn1.misc.VerisignCzagExtension; +import org.spongycastle.asn1.util.ASN1Dump; +import org.spongycastle.asn1.x500.X500Name; +import org.spongycastle.asn1.x500.style.RFC4519Style; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.BasicConstraints; +import org.spongycastle.asn1.x509.Extension; +import org.spongycastle.asn1.x509.Extensions; +import org.spongycastle.asn1.x509.GeneralName; +import org.spongycastle.asn1.x509.KeyUsage; +import org.spongycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl; +import org.spongycastle.jce.X509Principal; +import org.spongycastle.jce.interfaces.PKCS12BagAttributeCarrier; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.provider.RFC3280CertPathUtilities; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.Integers; +import org.spongycastle.util.encoders.Hex; + +class X509CertificateObject + extends X509Certificate + implements PKCS12BagAttributeCarrier +{ + private org.spongycastle.asn1.x509.Certificate c; + private BasicConstraints basicConstraints; + private boolean[] keyUsage; + private boolean hashValueSet; + private int hashValue; + + private PKCS12BagAttributeCarrier attrCarrier = new PKCS12BagAttributeCarrierImpl(); + + public X509CertificateObject( + org.spongycastle.asn1.x509.Certificate c) + throws CertificateParsingException + { + this.c = c; + + try + { + byte[] bytes = this.getExtensionBytes("2.5.29.19"); + + if (bytes != null) + { + basicConstraints = BasicConstraints.getInstance(ASN1Primitive.fromByteArray(bytes)); + } + } + catch (Exception e) + { + throw new CertificateParsingException("cannot construct BasicConstraints: " + e); + } + + try + { + byte[] bytes = this.getExtensionBytes("2.5.29.15"); + if (bytes != null) + { + DERBitString bits = DERBitString.getInstance(ASN1Primitive.fromByteArray(bytes)); + + bytes = bits.getBytes(); + int length = (bytes.length * 8) - bits.getPadBits(); + + keyUsage = new boolean[(length < 9) ? 9 : length]; + + for (int i = 0; i != length; i++) + { + keyUsage[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0; + } + } + else + { + keyUsage = null; + } + } + catch (Exception e) + { + throw new CertificateParsingException("cannot construct KeyUsage: " + e); + } + } + + public void checkValidity() + throws CertificateExpiredException, CertificateNotYetValidException + { + this.checkValidity(new Date()); + } + + public void checkValidity( + Date date) + throws CertificateExpiredException, CertificateNotYetValidException + { + if (date.getTime() > this.getNotAfter().getTime()) // for other VM compatibility + { + throw new CertificateExpiredException("certificate expired on " + c.getEndDate().getTime()); + } + + if (date.getTime() < this.getNotBefore().getTime()) + { + throw new CertificateNotYetValidException("certificate not valid till " + c.getStartDate().getTime()); + } + } + + public int getVersion() + { + return c.getVersionNumber(); + } + + public BigInteger getSerialNumber() + { + return c.getSerialNumber().getValue(); + } + + public Principal getIssuerDN() + { + try + { + return new X509Principal(X500Name.getInstance(c.getIssuer().getEncoded())); + } + catch (IOException e) + { + return null; + } + } + + public X500Principal getIssuerX500Principal() + { + try + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ASN1OutputStream aOut = new ASN1OutputStream(bOut); + + aOut.writeObject(c.getIssuer()); + + return new X500Principal(bOut.toByteArray()); + } + catch (IOException e) + { + throw new IllegalStateException("can't encode issuer DN"); + } + } + + public Principal getSubjectDN() + { + return new X509Principal(X500Name.getInstance(c.getSubject().toASN1Primitive())); + } + + public X500Principal getSubjectX500Principal() + { + try + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ASN1OutputStream aOut = new ASN1OutputStream(bOut); + + aOut.writeObject(c.getSubject()); + + return new X500Principal(bOut.toByteArray()); + } + catch (IOException e) + { + throw new IllegalStateException("can't encode issuer DN"); + } + } + + public Date getNotBefore() + { + return c.getStartDate().getDate(); + } + + public Date getNotAfter() + { + return c.getEndDate().getDate(); + } + + public byte[] getTBSCertificate() + throws CertificateEncodingException + { + try + { + return c.getTBSCertificate().getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + throw new CertificateEncodingException(e.toString()); + } + } + + public byte[] getSignature() + { + return c.getSignature().getBytes(); + } + + /** + * return a more "meaningful" representation for the signature algorithm used in + * the certficate. + */ + public String getSigAlgName() + { + Provider prov = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME); + + if (prov != null) + { + String algName = prov.getProperty("Alg.Alias.Signature." + this.getSigAlgOID()); + + if (algName != null) + { + return algName; + } + } + + Provider[] provs = Security.getProviders(); + + // + // search every provider looking for a real algorithm + // + for (int i = 0; i != provs.length; i++) + { + String algName = provs[i].getProperty("Alg.Alias.Signature." + this.getSigAlgOID()); + if (algName != null) + { + return algName; + } + } + + return this.getSigAlgOID(); + } + + /** + * return the object identifier for the signature. + */ + public String getSigAlgOID() + { + return c.getSignatureAlgorithm().getAlgorithm().getId(); + } + + /** + * return the signature parameters, or null if there aren't any. + */ + public byte[] getSigAlgParams() + { + if (c.getSignatureAlgorithm().getParameters() != null) + { + try + { + return c.getSignatureAlgorithm().getParameters().toASN1Primitive().getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + return null; + } + } + else + { + return null; + } + } + + public boolean[] getIssuerUniqueID() + { + DERBitString id = c.getTBSCertificate().getIssuerUniqueId(); + + if (id != null) + { + byte[] bytes = id.getBytes(); + boolean[] boolId = new boolean[bytes.length * 8 - id.getPadBits()]; + + for (int i = 0; i != boolId.length; i++) + { + boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0; + } + + return boolId; + } + + return null; + } + + public boolean[] getSubjectUniqueID() + { + DERBitString id = c.getTBSCertificate().getSubjectUniqueId(); + + if (id != null) + { + byte[] bytes = id.getBytes(); + boolean[] boolId = new boolean[bytes.length * 8 - id.getPadBits()]; + + for (int i = 0; i != boolId.length; i++) + { + boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0; + } + + return boolId; + } + + return null; + } + + public boolean[] getKeyUsage() + { + return keyUsage; + } + + public List getExtendedKeyUsage() + throws CertificateParsingException + { + byte[] bytes = this.getExtensionBytes("2.5.29.37"); + + if (bytes != null) + { + try + { + ASN1InputStream dIn = new ASN1InputStream(bytes); + ASN1Sequence seq = (ASN1Sequence)dIn.readObject(); + List list = new ArrayList(); + + for (int i = 0; i != seq.size(); i++) + { + list.add(((ASN1ObjectIdentifier)seq.getObjectAt(i)).getId()); + } + + return Collections.unmodifiableList(list); + } + catch (Exception e) + { + throw new CertificateParsingException("error processing extended key usage extension"); + } + } + + return null; + } + + public int getBasicConstraints() + { + if (basicConstraints != null) + { + if (basicConstraints.isCA()) + { + if (basicConstraints.getPathLenConstraint() == null) + { + return Integer.MAX_VALUE; + } + else + { + return basicConstraints.getPathLenConstraint().intValue(); + } + } + else + { + return -1; + } + } + + return -1; + } + + public Collection getSubjectAlternativeNames() + throws CertificateParsingException + { + return getAlternativeNames(getExtensionBytes(Extension.subjectAlternativeName.getId())); + } + + public Collection getIssuerAlternativeNames() + throws CertificateParsingException + { + return getAlternativeNames(getExtensionBytes(Extension.issuerAlternativeName.getId())); + } + + public Set getCriticalExtensionOIDs() + { + if (this.getVersion() == 3) + { + Set set = new HashSet(); + Extensions extensions = c.getTBSCertificate().getExtensions(); + + if (extensions != null) + { + Enumeration e = extensions.oids(); + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); + Extension ext = extensions.getExtension(oid); + + if (ext.isCritical()) + { + set.add(oid.getId()); + } + } + + return set; + } + } + + return null; + } + + private byte[] getExtensionBytes(String oid) + { + Extensions exts = c.getTBSCertificate().getExtensions(); + + if (exts != null) + { + Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid)); + if (ext != null) + { + return ext.getExtnValue().getOctets(); + } + } + + return null; + } + + public byte[] getExtensionValue(String oid) + { + Extensions exts = c.getTBSCertificate().getExtensions(); + + if (exts != null) + { + Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid)); + + if (ext != null) + { + try + { + return ext.getExtnValue().getEncoded(); + } + catch (Exception e) + { + throw new IllegalStateException("error parsing " + e.toString()); + } + } + } + + return null; + } + + public Set getNonCriticalExtensionOIDs() + { + if (this.getVersion() == 3) + { + Set set = new HashSet(); + Extensions extensions = c.getTBSCertificate().getExtensions(); + + if (extensions != null) + { + Enumeration e = extensions.oids(); + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); + Extension ext = extensions.getExtension(oid); + + if (!ext.isCritical()) + { + set.add(oid.getId()); + } + } + + return set; + } + } + + return null; + } + + public boolean hasUnsupportedCriticalExtension() + { + if (this.getVersion() == 3) + { + Extensions extensions = c.getTBSCertificate().getExtensions(); + + if (extensions != null) + { + Enumeration e = extensions.oids(); + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); + String oidId = oid.getId(); + + if (oidId.equals(RFC3280CertPathUtilities.KEY_USAGE) + || oidId.equals(RFC3280CertPathUtilities.CERTIFICATE_POLICIES) + || oidId.equals(RFC3280CertPathUtilities.POLICY_MAPPINGS) + || oidId.equals(RFC3280CertPathUtilities.INHIBIT_ANY_POLICY) + || oidId.equals(RFC3280CertPathUtilities.CRL_DISTRIBUTION_POINTS) + || oidId.equals(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT) + || oidId.equals(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR) + || oidId.equals(RFC3280CertPathUtilities.POLICY_CONSTRAINTS) + || oidId.equals(RFC3280CertPathUtilities.BASIC_CONSTRAINTS) + || oidId.equals(RFC3280CertPathUtilities.SUBJECT_ALTERNATIVE_NAME) + || oidId.equals(RFC3280CertPathUtilities.NAME_CONSTRAINTS)) + { + continue; + } + + Extension ext = extensions.getExtension(oid); + + if (ext.isCritical()) + { + return true; + } + } + } + } + + return false; + } + + public PublicKey getPublicKey() + { + try + { + return BouncyCastleProvider.getPublicKey(c.getSubjectPublicKeyInfo()); + } + catch (IOException e) + { + return null; // should never happen... + } + } + + public byte[] getEncoded() + throws CertificateEncodingException + { + try + { + return c.getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + throw new CertificateEncodingException(e.toString()); + } + } + + public boolean equals( + Object o) + { + if (o == this) + { + return true; + } + + if (!(o instanceof Certificate)) + { + return false; + } + + Certificate other = (Certificate)o; + + try + { + byte[] b1 = this.getEncoded(); + byte[] b2 = other.getEncoded(); + + return Arrays.areEqual(b1, b2); + } + catch (CertificateEncodingException e) + { + return false; + } + } + + public synchronized int hashCode() + { + if (!hashValueSet) + { + hashValue = calculateHashCode(); + hashValueSet = true; + } + + return hashValue; + } + + private int calculateHashCode() + { + try + { + int hashCode = 0; + byte[] certData = this.getEncoded(); + for (int i = 1; i < certData.length; i++) + { + hashCode += certData[i] * i; + } + return hashCode; + } + catch (CertificateEncodingException e) + { + return 0; + } + } + + public void setBagAttribute( + ASN1ObjectIdentifier oid, + ASN1Encodable attribute) + { + attrCarrier.setBagAttribute(oid, attribute); + } + + public ASN1Encodable getBagAttribute( + ASN1ObjectIdentifier oid) + { + return attrCarrier.getBagAttribute(oid); + } + + public Enumeration getBagAttributeKeys() + { + return attrCarrier.getBagAttributeKeys(); + } + + public String toString() + { + StringBuffer buf = new StringBuffer(); + String nl = System.getProperty("line.separator"); + + buf.append(" [0] Version: ").append(this.getVersion()).append(nl); + buf.append(" SerialNumber: ").append(this.getSerialNumber()).append(nl); + buf.append(" IssuerDN: ").append(this.getIssuerDN()).append(nl); + buf.append(" Start Date: ").append(this.getNotBefore()).append(nl); + buf.append(" Final Date: ").append(this.getNotAfter()).append(nl); + buf.append(" SubjectDN: ").append(this.getSubjectDN()).append(nl); + buf.append(" Public Key: ").append(this.getPublicKey()).append(nl); + buf.append(" Signature Algorithm: ").append(this.getSigAlgName()).append(nl); + + byte[] sig = this.getSignature(); + + buf.append(" Signature: ").append(new String(Hex.encode(sig, 0, 20))).append(nl); + for (int i = 20; i < sig.length; i += 20) + { + if (i < sig.length - 20) + { + buf.append(" ").append(new String(Hex.encode(sig, i, 20))).append(nl); + } + else + { + buf.append(" ").append(new String(Hex.encode(sig, i, sig.length - i))).append(nl); + } + } + + Extensions extensions = c.getTBSCertificate().getExtensions(); + + if (extensions != null) + { + Enumeration e = extensions.oids(); + + if (e.hasMoreElements()) + { + buf.append(" Extensions: \n"); + } + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); + Extension ext = extensions.getExtension(oid); + + if (ext.getExtnValue() != null) + { + byte[] octs = ext.getExtnValue().getOctets(); + ASN1InputStream dIn = new ASN1InputStream(octs); + buf.append(" critical(").append(ext.isCritical()).append(") "); + try + { + if (oid.equals(Extension.basicConstraints)) + { + buf.append(BasicConstraints.getInstance(dIn.readObject())).append(nl); + } + else if (oid.equals(Extension.keyUsage)) + { + buf.append(KeyUsage.getInstance(dIn.readObject())).append(nl); + } + else if (oid.equals(MiscObjectIdentifiers.netscapeCertType)) + { + buf.append(new NetscapeCertType((DERBitString)dIn.readObject())).append(nl); + } + else if (oid.equals(MiscObjectIdentifiers.netscapeRevocationURL)) + { + buf.append(new NetscapeRevocationURL((DERIA5String)dIn.readObject())).append(nl); + } + else if (oid.equals(MiscObjectIdentifiers.verisignCzagExtension)) + { + buf.append(new VerisignCzagExtension((DERIA5String)dIn.readObject())).append(nl); + } + else + { + buf.append(oid.getId()); + buf.append(" value = ").append(ASN1Dump.dumpAsString(dIn.readObject())).append(nl); + //buf.append(" value = ").append("*****").append(nl); + } + } + catch (Exception ex) + { + buf.append(oid.getId()); + // buf.append(" value = ").append(new String(Hex.encode(ext.getExtnValue().getOctets()))).append(nl); + buf.append(" value = ").append("*****").append(nl); + } + } + else + { + buf.append(nl); + } + } + } + + return buf.toString(); + } + + public final void verify( + PublicKey key) + throws CertificateException, NoSuchAlgorithmException, + InvalidKeyException, NoSuchProviderException, SignatureException + { + Signature signature; + String sigName = X509SignatureUtil.getSignatureName(c.getSignatureAlgorithm()); + + try + { + signature = Signature.getInstance(sigName, BouncyCastleProvider.PROVIDER_NAME); + } + catch (Exception e) + { + signature = Signature.getInstance(sigName); + } + + checkSignature(key, signature); + } + + public final void verify( + PublicKey key, + String sigProvider) + throws CertificateException, NoSuchAlgorithmException, + InvalidKeyException, NoSuchProviderException, SignatureException + { + String sigName = X509SignatureUtil.getSignatureName(c.getSignatureAlgorithm()); + Signature signature = Signature.getInstance(sigName, sigProvider); + + checkSignature(key, signature); + } + + private void checkSignature( + PublicKey key, + Signature signature) + throws CertificateException, NoSuchAlgorithmException, + SignatureException, InvalidKeyException + { + if (!isAlgIdEqual(c.getSignatureAlgorithm(), c.getTBSCertificate().getSignature())) + { + throw new CertificateException("signature algorithm in TBS cert not same as outer cert"); + } + + ASN1Encodable params = c.getSignatureAlgorithm().getParameters(); + + // TODO This should go after the initVerify? + X509SignatureUtil.setSignatureParameters(signature, params); + + signature.initVerify(key); + + signature.update(this.getTBSCertificate()); + + if (!signature.verify(this.getSignature())) + { + throw new SignatureException("certificate does not verify with supplied key"); + } + } + + private boolean isAlgIdEqual(AlgorithmIdentifier id1, AlgorithmIdentifier id2) + { + if (!id1.getAlgorithm().equals(id2.getAlgorithm())) + { + return false; + } + + if (id1.getParameters() == null) + { + if (id2.getParameters() != null && !id2.getParameters().equals(DERNull.INSTANCE)) + { + return false; + } + + return true; + } + + if (id2.getParameters() == null) + { + if (id1.getParameters() != null && !id1.getParameters().equals(DERNull.INSTANCE)) + { + return false; + } + + return true; + } + + return id1.getParameters().equals(id2.getParameters()); + } + + private static Collection getAlternativeNames(byte[] extVal) + throws CertificateParsingException + { + if (extVal == null) + { + return null; + } + try + { + Collection temp = new ArrayList(); + Enumeration it = ASN1Sequence.getInstance(extVal).getObjects(); + while (it.hasMoreElements()) + { + GeneralName genName = GeneralName.getInstance(it.nextElement()); + List list = new ArrayList(); + list.add(Integers.valueOf(genName.getTagNo())); + switch (genName.getTagNo()) + { + case GeneralName.ediPartyName: + case GeneralName.x400Address: + case GeneralName.otherName: + list.add(genName.getEncoded()); + break; + case GeneralName.directoryName: + list.add(X500Name.getInstance(RFC4519Style.INSTANCE, genName.getName()).toString()); + break; + case GeneralName.dNSName: + case GeneralName.rfc822Name: + case GeneralName.uniformResourceIdentifier: + list.add(((ASN1String)genName.getName()).getString()); + break; + case GeneralName.registeredID: + list.add(ASN1ObjectIdentifier.getInstance(genName.getName()).getId()); + break; + case GeneralName.iPAddress: + byte[] addrBytes = DEROctetString.getInstance(genName.getName()).getOctets(); + final String addr; + try + { + addr = InetAddress.getByAddress(addrBytes).getHostAddress(); + } + catch (UnknownHostException e) + { + continue; + } + list.add(addr); + break; + default: + throw new IOException("Bad tag number: " + genName.getTagNo()); + } + + temp.add(Collections.unmodifiableList(list)); + } + if (temp.size() == 0) + { + return null; + } + return Collections.unmodifiableCollection(temp); + } + catch (Exception e) + { + throw new CertificateParsingException(e.getMessage()); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java new file mode 100644 index 00000000..6c988b8d --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java @@ -0,0 +1,138 @@ +package org.spongycastle.jcajce.provider.asymmetric.x509; + +import java.io.IOException; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.spec.PSSParameterSpec; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Null; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Sequence; +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; + +class X509SignatureUtil +{ + private static final ASN1Null derNull = DERNull.INSTANCE; + + static void setSignatureParameters( + Signature signature, + ASN1Encodable params) + throws NoSuchAlgorithmException, SignatureException, InvalidKeyException + { + if (params != null && !derNull.equals(params)) + { + AlgorithmParameters sigParams = AlgorithmParameters.getInstance(signature.getAlgorithm(), signature.getProvider()); + + try + { + sigParams.init(params.toASN1Primitive().getEncoded()); + } + catch (IOException e) + { + throw new SignatureException("IOException decoding parameters: " + e.getMessage()); + } + + if (signature.getAlgorithm().endsWith("MGF1")) + { + try + { + signature.setParameter(sigParams.getParameterSpec(PSSParameterSpec.class)); + } + catch (GeneralSecurityException e) + { + throw new SignatureException("Exception extracting parameters: " + e.getMessage()); + } + } + } + } + + static String getSignatureName( + AlgorithmIdentifier sigAlgId) + { + ASN1Encodable params = sigAlgId.getParameters(); + + if (params != null && !derNull.equals(params)) + { + if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS)) + { + RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params); + + return getDigestAlgName(rsaParams.getHashAlgorithm().getAlgorithm()) + "withRSAandMGF1"; + } + if (sigAlgId.getAlgorithm().equals(X9ObjectIdentifiers.ecdsa_with_SHA2)) + { + ASN1Sequence ecDsaParams = ASN1Sequence.getInstance(params); + + return getDigestAlgName((ASN1ObjectIdentifier)ecDsaParams.getObjectAt(0)) + "withECDSA"; + } + } + + return sigAlgId.getAlgorithm().getId(); + } + + /** + * Return the digest algorithm using one of the standard JCA string + * representations rather the the algorithm identifier (if possible). + */ + private static String getDigestAlgName( + ASN1ObjectIdentifier digestAlgOID) + { + if (PKCSObjectIdentifiers.md5.equals(digestAlgOID)) + { + return "MD5"; + } + else if (OIWObjectIdentifiers.idSHA1.equals(digestAlgOID)) + { + return "SHA1"; + } + else if (NISTObjectIdentifiers.id_sha224.equals(digestAlgOID)) + { + return "SHA224"; + } + else if (NISTObjectIdentifiers.id_sha256.equals(digestAlgOID)) + { + return "SHA256"; + } + else if (NISTObjectIdentifiers.id_sha384.equals(digestAlgOID)) + { + return "SHA384"; + } + else if (NISTObjectIdentifiers.id_sha512.equals(digestAlgOID)) + { + return "SHA512"; + } + else if (TeleTrusTObjectIdentifiers.ripemd128.equals(digestAlgOID)) + { + return "RIPEMD128"; + } + else if (TeleTrusTObjectIdentifiers.ripemd160.equals(digestAlgOID)) + { + return "RIPEMD160"; + } + else if (TeleTrusTObjectIdentifiers.ripemd256.equals(digestAlgOID)) + { + return "RIPEMD256"; + } + else if (CryptoProObjectIdentifiers.gostR3411.equals(digestAlgOID)) + { + return "GOST3411"; + } + else + { + return digestAlgOID.getId(); + } + } +} |