Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric')
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/DH.java47
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/DSA.java70
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/DSTU4145.java42
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/EC.java110
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ECGOST.java39
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ElGamal.java46
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/GOST.java49
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/IES.java23
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/RSA.java197
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/X509.java31
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/AlgorithmParameterGeneratorSpi.java77
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/AlgorithmParametersSpi.java142
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/BCDHPrivateKey.java213
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/BCDHPublicKey.java204
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/IESCipher.java507
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java227
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyFactorySpi.java128
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyPairGeneratorSpi.java119
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java103
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/AlgorithmParametersSpi.java132
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/BCDSAPrivateKey.java167
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/BCDSAPublicKey.java171
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/DSASigner.java313
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/DSAUtil.java72
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/KeyFactorySpi.java117
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java82
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PrivateKey.java467
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PublicKey.java431
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/KeyFactorySpi.java166
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/KeyPairGeneratorSpi.java188
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/SignatureSpi.java221
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dstu/SignatureSpiLe.java69
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java462
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java454
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/IESCipher.java552
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java361
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java239
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java270
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java367
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java541
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java400
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/KeyFactorySpi.java166
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/KeyPairGeneratorSpi.java186
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ecgost/SignatureSpi.java218
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/AlgorithmParameterGeneratorSpi.java76
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/AlgorithmParametersSpi.java130
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/BCElGamalPrivateKey.java197
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/BCElGamalPublicKey.java172
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/CipherSpi.java340
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/ElGamalUtil.java66
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/KeyFactorySpi.java156
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/elgamal/KeyPairGeneratorSpi.java100
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/AlgorithmParameterGeneratorSpi.java65
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/AlgorithmParametersSpi.java138
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/BCGOST3410PrivateKey.java253
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/BCGOST3410PublicKey.java224
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/KeyFactorySpi.java121
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/KeyPairGeneratorSpi.java81
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/gost/SignatureSpi.java229
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ies/AlgorithmParametersSpi.java138
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/ies/CipherSpi.java363
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java265
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/BCRSAPrivateCrtKey.java241
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/BCRSAPrivateKey.java145
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/BCRSAPublicKey.java177
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java586
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/DigestSignatureSpi.java366
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/ISOSignatureSpi.java142
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/KeyFactorySpi.java171
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/KeyPairGeneratorSpi.java78
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/PSSSignatureSpi.java394
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/rsa/RSAUtil.java66
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/BaseCipherSpi.java216
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/BaseKeyFactorySpi.java77
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/DHUtil.java50
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/DSABase.java112
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/DSAEncoder.java13
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/EC5Util.java154
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/ECUtil.java291
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/ExtendedInvalidKeySpecException.java21
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/GOST3410Util.java52
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/IESUtil.java32
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/KeyUtil.java72
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/util/PKCS12BagAttributeCarrierImpl.java124
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java395
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/ExtCRLException.java20
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/KeyFactory.java95
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/PEMUtil.java88
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/PKIXCertPath.java372
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java318
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java627
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java903
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java138
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();
+ }
+ }
+}