diff options
Diffstat (limited to 'pkix/src/main/java/org/spongycastle/eac')
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 |