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 'pkix/src/main/java/org/spongycastle/eac')
-rw-r--r--pkix/src/main/java/org/spongycastle/eac/EACCertificateBuilder.java83
-rw-r--r--pkix/src/main/java/org/spongycastle/eac/EACCertificateHolder.java88
-rw-r--r--pkix/src/main/java/org/spongycastle/eac/EACCertificateRequestHolder.java88
-rw-r--r--pkix/src/main/java/org/spongycastle/eac/EACException.java27
-rw-r--r--pkix/src/main/java/org/spongycastle/eac/EACIOException.java29
-rw-r--r--pkix/src/main/java/org/spongycastle/eac/jcajce/DefaultEACHelper.java14
-rw-r--r--pkix/src/main/java/org/spongycastle/eac/jcajce/EACHelper.java11
-rw-r--r--pkix/src/main/java/org/spongycastle/eac/jcajce/JcaPublicKeyConverter.java168
-rw-r--r--pkix/src/main/java/org/spongycastle/eac/jcajce/NamedEACHelper.java22
-rw-r--r--pkix/src/main/java/org/spongycastle/eac/jcajce/ProviderEACHelper.java22
-rw-r--r--pkix/src/main/java/org/spongycastle/eac/operator/EACSignatureVerifier.java30
-rw-r--r--pkix/src/main/java/org/spongycastle/eac/operator/EACSigner.java27
-rw-r--r--pkix/src/main/java/org/spongycastle/eac/operator/jcajce/DefaultEACHelper.java14
-rw-r--r--pkix/src/main/java/org/spongycastle/eac/operator/jcajce/EACHelper.java39
-rw-r--r--pkix/src/main/java/org/spongycastle/eac/operator/jcajce/EACUtil.java5
-rw-r--r--pkix/src/main/java/org/spongycastle/eac/operator/jcajce/JcaEACSignatureVerifierBuilder.java181
-rw-r--r--pkix/src/main/java/org/spongycastle/eac/operator/jcajce/JcaEACSignerBuilder.java234
-rw-r--r--pkix/src/main/java/org/spongycastle/eac/operator/jcajce/NamedEACHelper.java22
-rw-r--r--pkix/src/main/java/org/spongycastle/eac/operator/jcajce/ProviderEACHelper.java22
19 files changed, 1126 insertions, 0 deletions
diff --git a/pkix/src/main/java/org/spongycastle/eac/EACCertificateBuilder.java b/pkix/src/main/java/org/spongycastle/eac/EACCertificateBuilder.java
new file mode 100644
index 00000000..b3ca48e8
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/eac/EACCertificateBuilder.java
@@ -0,0 +1,83 @@
+package org.spongycastle.eac;
+
+import java.io.OutputStream;
+
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.DERApplicationSpecific;
+import org.spongycastle.asn1.eac.CVCertificate;
+import org.spongycastle.asn1.eac.CertificateBody;
+import org.spongycastle.asn1.eac.CertificateHolderAuthorization;
+import org.spongycastle.asn1.eac.CertificateHolderReference;
+import org.spongycastle.asn1.eac.CertificationAuthorityReference;
+import org.spongycastle.asn1.eac.EACTags;
+import org.spongycastle.asn1.eac.PackedDate;
+import org.spongycastle.asn1.eac.PublicKeyDataObject;
+import org.spongycastle.eac.operator.EACSigner;
+
+public class EACCertificateBuilder
+{
+ private static final byte [] ZeroArray = new byte [] {0};
+
+ private PublicKeyDataObject publicKey;
+ private CertificateHolderAuthorization certificateHolderAuthorization;
+ private PackedDate certificateEffectiveDate;
+ private PackedDate certificateExpirationDate;
+ private CertificateHolderReference certificateHolderReference;
+ private CertificationAuthorityReference certificationAuthorityReference;
+
+ public EACCertificateBuilder(
+ CertificationAuthorityReference certificationAuthorityReference,
+ PublicKeyDataObject publicKey,
+ CertificateHolderReference certificateHolderReference,
+ CertificateHolderAuthorization certificateHolderAuthorization,
+ PackedDate certificateEffectiveDate,
+ PackedDate certificateExpirationDate)
+ {
+ this.certificationAuthorityReference = certificationAuthorityReference;
+ this.publicKey = publicKey;
+ this.certificateHolderReference = certificateHolderReference;
+ this.certificateHolderAuthorization = certificateHolderAuthorization;
+ this.certificateEffectiveDate = certificateEffectiveDate;
+ this.certificateExpirationDate = certificateExpirationDate;
+ }
+
+ private CertificateBody buildBody()
+ {
+ DERApplicationSpecific certificateProfileIdentifier;
+
+ certificateProfileIdentifier = new DERApplicationSpecific(
+ EACTags.INTERCHANGE_PROFILE, ZeroArray);
+
+ CertificateBody body = new CertificateBody(
+ certificateProfileIdentifier,
+ certificationAuthorityReference,
+ publicKey,
+ certificateHolderReference,
+ certificateHolderAuthorization,
+ certificateEffectiveDate,
+ certificateExpirationDate);
+
+ return body;
+ }
+
+ public EACCertificateHolder build(EACSigner signer)
+ throws EACException
+ {
+ try
+ {
+ CertificateBody body = buildBody();
+
+ OutputStream vOut = signer.getOutputStream();
+
+ vOut.write(body.getEncoded(ASN1Encoding.DER));
+
+ vOut.close();
+
+ return new EACCertificateHolder(new CVCertificate(body, signer.getSignature()));
+ }
+ catch (Exception e)
+ {
+ throw new EACException("unable to process signature: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/eac/EACCertificateHolder.java b/pkix/src/main/java/org/spongycastle/eac/EACCertificateHolder.java
new file mode 100644
index 00000000..edc75a6d
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/eac/EACCertificateHolder.java
@@ -0,0 +1,88 @@
+package org.spongycastle.eac;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1ParsingException;
+import org.spongycastle.asn1.eac.CVCertificate;
+import org.spongycastle.asn1.eac.PublicKeyDataObject;
+import org.spongycastle.eac.operator.EACSignatureVerifier;
+
+public class EACCertificateHolder
+{
+ private CVCertificate cvCertificate;
+
+ private static CVCertificate parseBytes(byte[] certEncoding)
+ throws IOException
+ {
+ try
+ {
+ return CVCertificate.getInstance(certEncoding);
+ }
+ catch (ClassCastException e)
+ {
+ throw new EACIOException("malformed data: " + e.getMessage(), e);
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new EACIOException("malformed data: " + e.getMessage(), e);
+ }
+ catch (ASN1ParsingException e)
+ {
+ if (e.getCause() instanceof IOException)
+ {
+ throw (IOException)e.getCause();
+ }
+ else
+ {
+ throw new EACIOException("malformed data: " + e.getMessage(), e);
+ }
+ }
+ }
+
+ public EACCertificateHolder(byte[] certEncoding)
+ throws IOException
+ {
+ this(parseBytes(certEncoding));
+ }
+
+ public EACCertificateHolder(CVCertificate cvCertificate)
+ {
+ this.cvCertificate = cvCertificate;
+ }
+
+ /**
+ * Return the underlying ASN.1 structure for the certificate in this holder.
+ *
+ * @return a X509CertificateStructure object.
+ */
+ public CVCertificate toASN1Structure()
+ {
+ return cvCertificate;
+ }
+
+ public PublicKeyDataObject getPublicKeyDataObject()
+ {
+ return cvCertificate.getBody().getPublicKey();
+ }
+
+ public boolean isSignatureValid(EACSignatureVerifier verifier)
+ throws EACException
+ {
+ try
+ {
+ OutputStream vOut = verifier.getOutputStream();
+
+ vOut.write(cvCertificate.getBody().getEncoded(ASN1Encoding.DER));
+
+ vOut.close();
+
+ return verifier.verify(cvCertificate.getSignature());
+ }
+ catch (Exception e)
+ {
+ throw new EACException("unable to process signature: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/eac/EACCertificateRequestHolder.java b/pkix/src/main/java/org/spongycastle/eac/EACCertificateRequestHolder.java
new file mode 100644
index 00000000..77b01e4d
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/eac/EACCertificateRequestHolder.java
@@ -0,0 +1,88 @@
+package org.spongycastle.eac;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1ParsingException;
+import org.spongycastle.asn1.eac.CVCertificateRequest;
+import org.spongycastle.asn1.eac.PublicKeyDataObject;
+import org.spongycastle.eac.operator.EACSignatureVerifier;
+
+public class EACCertificateRequestHolder
+{
+ private CVCertificateRequest request;
+
+ private static CVCertificateRequest parseBytes(byte[] requestEncoding)
+ throws IOException
+ {
+ try
+ {
+ return CVCertificateRequest.getInstance(requestEncoding);
+ }
+ catch (ClassCastException e)
+ {
+ throw new EACIOException("malformed data: " + e.getMessage(), e);
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new EACIOException("malformed data: " + e.getMessage(), e);
+ }
+ catch (ASN1ParsingException e)
+ {
+ if (e.getCause() instanceof IOException)
+ {
+ throw (IOException)e.getCause();
+ }
+ else
+ {
+ throw new EACIOException("malformed data: " + e.getMessage(), e);
+ }
+ }
+ }
+
+ public EACCertificateRequestHolder(byte[] certEncoding)
+ throws IOException
+ {
+ this(parseBytes(certEncoding));
+ }
+
+ public EACCertificateRequestHolder(CVCertificateRequest request)
+ {
+ this.request = request;
+ }
+
+ /**
+ * Return the underlying ASN.1 structure for the certificate in this holder.
+ *
+ * @return a X509CertificateStructure object.
+ */
+ public CVCertificateRequest toASN1Structure()
+ {
+ return request;
+ }
+
+ public PublicKeyDataObject getPublicKeyDataObject()
+ {
+ return request.getPublicKey();
+ }
+
+ public boolean isInnerSignatureValid(EACSignatureVerifier verifier)
+ throws EACException
+ {
+ try
+ {
+ OutputStream vOut = verifier.getOutputStream();
+
+ vOut.write(request.getCertificateBody().getEncoded(ASN1Encoding.DER));
+
+ vOut.close();
+
+ return verifier.verify(request.getInnerSignature());
+ }
+ catch (Exception e)
+ {
+ throw new EACException("unable to process signature: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/eac/EACException.java b/pkix/src/main/java/org/spongycastle/eac/EACException.java
new file mode 100644
index 00000000..d8e1612a
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/eac/EACException.java
@@ -0,0 +1,27 @@
+package org.spongycastle.eac;
+
+/**
+ * General checked Exception thrown in the cert package and its sub-packages.
+ */
+public class EACException
+ extends Exception
+{
+ private Throwable cause;
+
+ public EACException(String msg, Throwable cause)
+ {
+ super(msg);
+
+ this.cause = cause;
+ }
+
+ public EACException(String msg)
+ {
+ super(msg);
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/eac/EACIOException.java b/pkix/src/main/java/org/spongycastle/eac/EACIOException.java
new file mode 100644
index 00000000..857a6f6a
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/eac/EACIOException.java
@@ -0,0 +1,29 @@
+package org.spongycastle.eac;
+
+import java.io.IOException;
+
+/**
+ * General IOException thrown in the cert package and its sub-packages.
+ */
+public class EACIOException
+ extends IOException
+{
+ private Throwable cause;
+
+ public EACIOException(String msg, Throwable cause)
+ {
+ super(msg);
+
+ this.cause = cause;
+ }
+
+ public EACIOException(String msg)
+ {
+ super(msg);
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/eac/jcajce/DefaultEACHelper.java b/pkix/src/main/java/org/spongycastle/eac/jcajce/DefaultEACHelper.java
new file mode 100644
index 00000000..703e1405
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/eac/jcajce/DefaultEACHelper.java
@@ -0,0 +1,14 @@
+package org.spongycastle.eac.jcajce;
+
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+
+class DefaultEACHelper
+ implements EACHelper
+{
+ public KeyFactory createKeyFactory(String type)
+ throws NoSuchAlgorithmException
+ {
+ return KeyFactory.getInstance(type);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/eac/jcajce/EACHelper.java b/pkix/src/main/java/org/spongycastle/eac/jcajce/EACHelper.java
new file mode 100644
index 00000000..c6f3ffb2
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/eac/jcajce/EACHelper.java
@@ -0,0 +1,11 @@
+package org.spongycastle.eac.jcajce;
+
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+
+interface EACHelper
+{
+ KeyFactory createKeyFactory(String type)
+ throws NoSuchProviderException, NoSuchAlgorithmException;
+}
diff --git a/pkix/src/main/java/org/spongycastle/eac/jcajce/JcaPublicKeyConverter.java b/pkix/src/main/java/org/spongycastle/eac/jcajce/JcaPublicKeyConverter.java
new file mode 100644
index 00000000..9e6a7eca
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/eac/jcajce/JcaPublicKeyConverter.java
@@ -0,0 +1,168 @@
+package org.spongycastle.eac.jcajce;
+
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECField;
+import java.security.spec.ECFieldFp;
+import java.security.spec.EllipticCurve;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPublicKeySpec;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.eac.EACObjectIdentifiers;
+import org.spongycastle.asn1.eac.ECDSAPublicKey;
+import org.spongycastle.asn1.eac.PublicKeyDataObject;
+import org.spongycastle.asn1.eac.RSAPublicKey;
+import org.spongycastle.eac.EACException;
+import org.spongycastle.jce.spec.ECParameterSpec;
+import org.spongycastle.jce.spec.ECPublicKeySpec;
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECPoint;
+
+public class JcaPublicKeyConverter
+{
+ private EACHelper helper = new DefaultEACHelper();
+
+ public JcaPublicKeyConverter setProvider(String providerName)
+ {
+ this.helper = new NamedEACHelper(providerName);
+
+ return this;
+ }
+
+ public JcaPublicKeyConverter setProvider(Provider provider)
+ {
+ this.helper = new ProviderEACHelper(provider);
+
+ return this;
+ }
+
+ public PublicKey getKey(PublicKeyDataObject publicKeyDataObject)
+ throws EACException, InvalidKeySpecException
+ {
+ if (publicKeyDataObject.getUsage().on(EACObjectIdentifiers.id_TA_ECDSA))
+ {
+ return getECPublicKeyPublicKey((ECDSAPublicKey)publicKeyDataObject);
+ }
+ else
+ {
+ RSAPublicKey pubKey = (RSAPublicKey)publicKeyDataObject;
+ RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(pubKey.getModulus(), pubKey.getPublicExponent());
+
+ try
+ {
+ KeyFactory factk = helper.createKeyFactory("RSA");
+
+ return factk.generatePublic(pubKeySpec);
+ }
+ catch (NoSuchProviderException e)
+ {
+ throw new EACException("cannot find provider: " + e.getMessage(), e);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ throw new EACException("cannot find algorithm ECDSA: " + e.getMessage(), e);
+ }
+ }
+ }
+
+ private PublicKey getECPublicKeyPublicKey(ECDSAPublicKey key)
+ throws EACException, InvalidKeySpecException
+ {
+ ECParameterSpec spec = getParams(key);
+ ECCurve curve = spec.getCurve();
+
+ ECPoint point = curve.decodePoint(key.getPublicPointY());
+ ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, spec);
+
+ KeyFactory factk;
+ try
+ {
+ factk = helper.createKeyFactory("ECDSA");
+ }
+ catch (NoSuchProviderException e)
+ {
+ throw new EACException("cannot find provider: " + e.getMessage(), e);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ throw new EACException("cannot find algorithm ECDSA: " + e.getMessage(), e);
+ }
+
+ return factk.generatePublic(pubKeySpec);
+ }
+
+ private ECParameterSpec getParams(ECDSAPublicKey key)
+ {
+ if (!key.hasParameters())
+ {
+ throw new IllegalArgumentException("Public key does not contains EC Params");
+ }
+
+ BigInteger p = key.getPrimeModulusP();
+ ECCurve.Fp curve = new ECCurve.Fp(p, key.getFirstCoefA(), key.getSecondCoefB());
+
+ ECPoint G = curve.decodePoint(key.getBasePointG());
+
+ BigInteger order = key.getOrderOfBasePointR();
+ BigInteger coFactor = key.getCofactorF();
+ // TODO: update to use JDK 1.5 EC API
+ ECParameterSpec ecspec = new ECParameterSpec(curve, G, order, coFactor);
+
+ return ecspec;
+ }
+
+ public PublicKeyDataObject getPublicKeyDataObject(ASN1ObjectIdentifier usage, PublicKey publicKey)
+ {
+ if (publicKey instanceof java.security.interfaces.RSAPublicKey)
+ {
+ java.security.interfaces.RSAPublicKey pubKey = (java.security.interfaces.RSAPublicKey)publicKey;
+
+ return new RSAPublicKey(usage, pubKey.getModulus(), pubKey.getPublicExponent());
+ }
+ else
+ {
+ ECPublicKey pubKey = (ECPublicKey)publicKey;
+ java.security.spec.ECParameterSpec params = pubKey.getParams();
+
+ return new ECDSAPublicKey(
+ usage,
+ ((ECFieldFp)params.getCurve().getField()).getP(),
+ params.getCurve().getA(), params.getCurve().getB(),
+ convertPoint(convertCurve(params.getCurve()), params.getGenerator(), false).getEncoded(),
+ params.getOrder(),
+ convertPoint(convertCurve(params.getCurve()), pubKey.getW(), false).getEncoded(),
+ params.getCofactor());
+ }
+ }
+
+ private static org.spongycastle.math.ec.ECPoint convertPoint(
+ ECCurve curve,
+ java.security.spec.ECPoint point,
+ boolean withCompression)
+ {
+ return curve.createPoint(point.getAffineX(), point.getAffineY(), withCompression);
+ }
+
+ private static ECCurve convertCurve(
+ EllipticCurve ec)
+ {
+ ECField field = ec.getField();
+ BigInteger a = ec.getA();
+ BigInteger b = ec.getB();
+
+ if (field instanceof ECFieldFp)
+ {
+ return new ECCurve.Fp(((ECFieldFp)field).getP(), a, b);
+ }
+ else
+ {
+ throw new IllegalStateException("not implemented yet!!!");
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/eac/jcajce/NamedEACHelper.java b/pkix/src/main/java/org/spongycastle/eac/jcajce/NamedEACHelper.java
new file mode 100644
index 00000000..7e85142d
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/eac/jcajce/NamedEACHelper.java
@@ -0,0 +1,22 @@
+package org.spongycastle.eac.jcajce;
+
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+
+class NamedEACHelper
+ implements EACHelper
+{
+ private final String providerName;
+
+ NamedEACHelper(String providerName)
+ {
+ this.providerName = providerName;
+ }
+
+ public KeyFactory createKeyFactory(String type)
+ throws NoSuchProviderException, NoSuchAlgorithmException
+ {
+ return KeyFactory.getInstance(type, providerName);
+ }
+} \ No newline at end of file
diff --git a/pkix/src/main/java/org/spongycastle/eac/jcajce/ProviderEACHelper.java b/pkix/src/main/java/org/spongycastle/eac/jcajce/ProviderEACHelper.java
new file mode 100644
index 00000000..2cda60a2
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/eac/jcajce/ProviderEACHelper.java
@@ -0,0 +1,22 @@
+package org.spongycastle.eac.jcajce;
+
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+
+class ProviderEACHelper
+ implements EACHelper
+{
+ private final Provider provider;
+
+ ProviderEACHelper(Provider provider)
+ {
+ this.provider = provider;
+ }
+
+ public KeyFactory createKeyFactory(String type)
+ throws NoSuchAlgorithmException
+ {
+ return KeyFactory.getInstance(type, provider);
+ }
+} \ No newline at end of file
diff --git a/pkix/src/main/java/org/spongycastle/eac/operator/EACSignatureVerifier.java b/pkix/src/main/java/org/spongycastle/eac/operator/EACSignatureVerifier.java
new file mode 100644
index 00000000..3dd967b9
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/eac/operator/EACSignatureVerifier.java
@@ -0,0 +1,30 @@
+package org.spongycastle.eac.operator;
+
+import java.io.OutputStream;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+
+public interface EACSignatureVerifier
+{
+ /**
+ * Return the usage OID specifying the signature type.
+ *
+ * @return algorithm oid.
+ */
+ ASN1ObjectIdentifier getUsageIdentifier();
+
+ /**
+ * Returns a stream that will accept data for the purpose of calculating
+ * a signature for later verification. Use org.spongycastle.util.io.TeeOutputStream if you want to accumulate
+ * the data on the fly as well.
+ *
+ * @return an OutputStream
+ */
+ OutputStream getOutputStream();
+
+ /**
+ * @param expected expected value of the signature on the data.
+ * @return true if the signature verifies, false otherwise
+ */
+ boolean verify(byte[] expected);
+} \ No newline at end of file
diff --git a/pkix/src/main/java/org/spongycastle/eac/operator/EACSigner.java b/pkix/src/main/java/org/spongycastle/eac/operator/EACSigner.java
new file mode 100644
index 00000000..9a53685a
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/eac/operator/EACSigner.java
@@ -0,0 +1,27 @@
+package org.spongycastle.eac.operator;
+
+import java.io.OutputStream;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+
+public interface EACSigner
+{
+ ASN1ObjectIdentifier getUsageIdentifier();
+
+ /**
+ * Returns a stream that will accept data for the purpose of calculating
+ * a signature. Use org.spongycastle.util.io.TeeOutputStream if you want to accumulate
+ * the data on the fly as well.
+ *
+ * @return an OutputStream
+ */
+ OutputStream getOutputStream();
+
+ /**
+ * Returns a signature based on the current data written to the stream, since the
+ * start or the last call to getSignature().
+ *
+ * @return bytes representing the signature.
+ */
+ byte[] getSignature();
+}
diff --git a/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/DefaultEACHelper.java b/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/DefaultEACHelper.java
new file mode 100644
index 00000000..3aab0585
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/DefaultEACHelper.java
@@ -0,0 +1,14 @@
+package org.spongycastle.eac.operator.jcajce;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+
+class DefaultEACHelper
+ extends EACHelper
+{
+ protected Signature createSignature(String type)
+ throws NoSuchAlgorithmException
+ {
+ return Signature.getInstance(type);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/EACHelper.java b/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/EACHelper.java
new file mode 100644
index 00000000..21369981
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/EACHelper.java
@@ -0,0 +1,39 @@
+package org.spongycastle.eac.operator.jcajce;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Signature;
+import java.util.Hashtable;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.eac.EACObjectIdentifiers;
+
+abstract class EACHelper
+{
+ private static final Hashtable sigNames = new Hashtable();
+
+ static
+ {
+ sigNames.put(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_1, "SHA1withRSA");
+ sigNames.put(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_256, "SHA256withRSA");
+ sigNames.put(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_1, "SHA1withRSAandMGF1");
+ sigNames.put(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_256, "SHA256withRSAandMGF1");
+ sigNames.put(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_512, "SHA512withRSA");
+ sigNames.put(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_512, "SHA512withRSAandMGF1");
+
+ sigNames.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, "SHA1withECDSA");
+ sigNames.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_224, "SHA224withECDSA");
+ sigNames.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_256, "SHA256withECDSA");
+ sigNames.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_384, "SHA384withECDSA");
+ sigNames.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_512, "SHA512withECDSA");
+ }
+
+ public Signature getSignature(ASN1ObjectIdentifier oid)
+ throws NoSuchProviderException, NoSuchAlgorithmException
+ {
+ return createSignature((String)sigNames.get(oid));
+ }
+
+ protected abstract Signature createSignature(String type)
+ throws NoSuchProviderException, NoSuchAlgorithmException;
+}
diff --git a/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/EACUtil.java b/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/EACUtil.java
new file mode 100644
index 00000000..c21178a5
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/EACUtil.java
@@ -0,0 +1,5 @@
+package org.spongycastle.eac.operator.jcajce;
+
+class EACUtil
+{
+}
diff --git a/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/JcaEACSignatureVerifierBuilder.java b/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/JcaEACSignatureVerifierBuilder.java
new file mode 100644
index 00000000..7ffdb79a
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/JcaEACSignatureVerifierBuilder.java
@@ -0,0 +1,181 @@
+package org.spongycastle.eac.operator.jcajce;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.eac.EACObjectIdentifiers;
+import org.spongycastle.eac.operator.EACSignatureVerifier;
+import org.spongycastle.operator.OperatorCreationException;
+import org.spongycastle.operator.OperatorStreamException;
+import org.spongycastle.operator.RuntimeOperatorException;
+
+public class JcaEACSignatureVerifierBuilder
+{
+ private EACHelper helper = new DefaultEACHelper();
+
+ public JcaEACSignatureVerifierBuilder setProvider(String providerName)
+ {
+ this.helper = new NamedEACHelper(providerName);
+
+ return this;
+ }
+
+ public JcaEACSignatureVerifierBuilder setProvider(Provider provider)
+ {
+ this.helper = new ProviderEACHelper(provider);
+
+ return this;
+ }
+
+ public EACSignatureVerifier build(final ASN1ObjectIdentifier usageOid, PublicKey pubKey)
+ throws OperatorCreationException
+ {
+ Signature sig;
+ try
+ {
+ sig = helper.getSignature(usageOid);
+
+ sig.initVerify(pubKey);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ throw new OperatorCreationException("unable to find algorithm: " + e.getMessage(), e);
+ }
+ catch (NoSuchProviderException e)
+ {
+ throw new OperatorCreationException("unable to find provider: " + e.getMessage(), e);
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new OperatorCreationException("invalid key: " + e.getMessage(), e);
+ }
+
+ final SignatureOutputStream sigStream = new SignatureOutputStream(sig);
+
+ return new EACSignatureVerifier()
+ {
+ public ASN1ObjectIdentifier getUsageIdentifier()
+ {
+ return usageOid;
+ }
+
+ public OutputStream getOutputStream()
+ {
+ return sigStream;
+ }
+
+ public boolean verify(byte[] expected)
+ {
+ try
+ {
+ if (usageOid.on(EACObjectIdentifiers.id_TA_ECDSA))
+ {
+ try
+ {
+ byte[] reencoded = derEncode(expected);
+
+ return sigStream.verify(reencoded);
+ }
+ catch (Exception e)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return sigStream.verify(expected);
+ }
+ }
+ catch (SignatureException e)
+ {
+ throw new RuntimeOperatorException("exception obtaining signature: " + e.getMessage(), e);
+ }
+ }
+ };
+ }
+
+ private static byte[] derEncode(byte[] rawSign) throws IOException
+ {
+ int len = rawSign.length / 2;
+
+ byte[] r = new byte[len];
+ byte[] s = new byte[len];
+ System.arraycopy(rawSign, 0, r, 0, len);
+ System.arraycopy(rawSign, len, s, 0, len);
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(new ASN1Integer(new BigInteger(1, r)));
+ v.add(new ASN1Integer(new BigInteger(1, s)));
+
+ DERSequence seq = new DERSequence(v);
+ return seq.getEncoded();
+ }
+
+ private class SignatureOutputStream
+ extends OutputStream
+ {
+ private Signature sig;
+
+ SignatureOutputStream(Signature sig)
+ {
+ this.sig = sig;
+ }
+
+ public void write(byte[] bytes, int off, int len)
+ throws IOException
+ {
+ try
+ {
+ sig.update(bytes, off, len);
+ }
+ catch (SignatureException e)
+ {
+ throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
+ }
+ }
+
+ public void write(byte[] bytes)
+ throws IOException
+ {
+ try
+ {
+ sig.update(bytes);
+ }
+ catch (SignatureException e)
+ {
+ throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
+ }
+ }
+
+ public void write(int b)
+ throws IOException
+ {
+ try
+ {
+ sig.update((byte)b);
+ }
+ catch (SignatureException e)
+ {
+ throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
+ }
+ }
+
+ boolean verify(byte[] expected)
+ throws SignatureException
+ {
+ return sig.verify(expected);
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/JcaEACSignerBuilder.java b/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/JcaEACSignerBuilder.java
new file mode 100644
index 00000000..460bb5cf
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/JcaEACSignerBuilder.java
@@ -0,0 +1,234 @@
+package org.spongycastle.eac.operator.jcajce;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.util.Arrays;
+import java.util.Hashtable;
+
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.eac.EACObjectIdentifiers;
+import org.spongycastle.eac.operator.EACSigner;
+import org.spongycastle.operator.OperatorCreationException;
+import org.spongycastle.operator.OperatorStreamException;
+import org.spongycastle.operator.RuntimeOperatorException;
+
+public class JcaEACSignerBuilder
+{
+ private static final Hashtable sigNames = new Hashtable();
+
+ static
+ {
+ sigNames.put("SHA1withRSA", EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_1);
+ sigNames.put("SHA256withRSA", EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_256);
+ sigNames.put("SHA1withRSAandMGF1", EACObjectIdentifiers.id_TA_RSA_PSS_SHA_1);
+ sigNames.put("SHA256withRSAandMGF1", EACObjectIdentifiers.id_TA_RSA_PSS_SHA_256);
+ sigNames.put("SHA512withRSA", EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_512);
+ sigNames.put("SHA512withRSAandMGF1", EACObjectIdentifiers.id_TA_RSA_PSS_SHA_512);
+
+ sigNames.put("SHA1withECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_1);
+ sigNames.put("SHA224withECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_224);
+ sigNames.put("SHA256withECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_256);
+ sigNames.put("SHA384withECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_384);
+ sigNames.put("SHA512withECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_512);
+ }
+
+ private EACHelper helper = new DefaultEACHelper();
+
+ public JcaEACSignerBuilder setProvider(String providerName)
+ {
+ this.helper = new NamedEACHelper(providerName);
+
+ return this;
+ }
+
+ public JcaEACSignerBuilder setProvider(Provider provider)
+ {
+ this.helper = new ProviderEACHelper(provider);
+
+ return this;
+ }
+
+ public EACSigner build(String algorithm, PrivateKey privKey)
+ throws OperatorCreationException
+ {
+ return build((ASN1ObjectIdentifier)sigNames.get(algorithm), privKey);
+ }
+
+ public EACSigner build(final ASN1ObjectIdentifier usageOid, PrivateKey privKey)
+ throws OperatorCreationException
+ {
+ Signature sig;
+ try
+ {
+ sig = helper.getSignature(usageOid);
+
+ sig.initSign(privKey);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ throw new OperatorCreationException("unable to find algorithm: " + e.getMessage(), e);
+ }
+ catch (NoSuchProviderException e)
+ {
+ throw new OperatorCreationException("unable to find provider: " + e.getMessage(), e);
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new OperatorCreationException("invalid key: " + e.getMessage(), e);
+ }
+
+ final SignatureOutputStream sigStream = new SignatureOutputStream(sig);
+
+ return new EACSigner()
+ {
+ public ASN1ObjectIdentifier getUsageIdentifier()
+ {
+ return usageOid;
+ }
+
+ public OutputStream getOutputStream()
+ {
+ return sigStream;
+ }
+
+ public byte[] getSignature()
+ {
+ try
+ {
+ byte[] signature = sigStream.getSignature();
+
+ if (usageOid.on(EACObjectIdentifiers.id_TA_ECDSA))
+ {
+ return reencode(signature);
+ }
+
+ return signature;
+ }
+ catch (SignatureException e)
+ {
+ throw new RuntimeOperatorException("exception obtaining signature: " + e.getMessage(), e);
+ }
+ }
+ };
+ }
+
+ public static int max(int el1, int el2)
+ {
+ return el1 > el2 ? el1 : el2;
+ }
+
+ private static byte[] reencode(byte[] rawSign)
+ {
+ ASN1Sequence sData = ASN1Sequence.getInstance(rawSign);
+
+ BigInteger r = ASN1Integer.getInstance(sData.getObjectAt(0)).getValue();
+ BigInteger s = ASN1Integer.getInstance(sData.getObjectAt(1)).getValue();
+
+ byte[] rB = r.toByteArray();
+ byte[] sB = s.toByteArray();
+
+ int rLen = unsignedIntLength(rB);
+ int sLen = unsignedIntLength(sB);
+
+ byte[] ret;
+ int len = max(rLen, sLen);
+
+ ret = new byte[len * 2];
+ Arrays.fill(ret, (byte)0);
+
+ copyUnsignedInt(rB, ret, len - rLen);
+ copyUnsignedInt(sB, ret, 2 * len - sLen);
+
+ return ret;
+ }
+
+ private static int unsignedIntLength(byte[] i)
+ {
+ int len = i.length;
+ if (i[0] == 0)
+ {
+ len--;
+ }
+
+ return len;
+ }
+
+ private static void copyUnsignedInt(byte[] src, byte[] dst, int offset)
+ {
+ int len = src.length;
+ int readoffset = 0;
+ if (src[0] == 0)
+ {
+ len--;
+ readoffset = 1;
+ }
+
+ System.arraycopy(src, readoffset, dst, offset, len);
+ }
+
+ private class SignatureOutputStream
+ extends OutputStream
+ {
+ private Signature sig;
+
+ SignatureOutputStream(Signature sig)
+ {
+ this.sig = sig;
+ }
+
+ public void write(byte[] bytes, int off, int len)
+ throws IOException
+ {
+ try
+ {
+ sig.update(bytes, off, len);
+ }
+ catch (SignatureException e)
+ {
+ throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
+ }
+ }
+
+ public void write(byte[] bytes)
+ throws IOException
+ {
+ try
+ {
+ sig.update(bytes);
+ }
+ catch (SignatureException e)
+ {
+ throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
+ }
+ }
+
+ public void write(int b)
+ throws IOException
+ {
+ try
+ {
+ sig.update((byte)b);
+ }
+ catch (SignatureException e)
+ {
+ throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
+ }
+ }
+
+ byte[] getSignature()
+ throws SignatureException
+ {
+ return sig.sign();
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/NamedEACHelper.java b/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/NamedEACHelper.java
new file mode 100644
index 00000000..129356c2
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/NamedEACHelper.java
@@ -0,0 +1,22 @@
+package org.spongycastle.eac.operator.jcajce;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Signature;
+
+class NamedEACHelper
+ extends EACHelper
+{
+ private final String providerName;
+
+ NamedEACHelper(String providerName)
+ {
+ this.providerName = providerName;
+ }
+
+ protected Signature createSignature(String type)
+ throws NoSuchProviderException, NoSuchAlgorithmException
+ {
+ return Signature.getInstance(type, providerName);
+ }
+} \ No newline at end of file
diff --git a/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/ProviderEACHelper.java b/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/ProviderEACHelper.java
new file mode 100644
index 00000000..e222dbdb
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/eac/operator/jcajce/ProviderEACHelper.java
@@ -0,0 +1,22 @@
+package org.spongycastle.eac.operator.jcajce;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.Signature;
+
+class ProviderEACHelper
+ extends EACHelper
+{
+ private final Provider provider;
+
+ ProviderEACHelper(Provider provider)
+ {
+ this.provider = provider;
+ }
+
+ protected Signature createSignature(String type)
+ throws NoSuchAlgorithmException
+ {
+ return Signature.getInstance(type, provider);
+ }
+} \ No newline at end of file