diff options
Diffstat (limited to 'pkix/src/main/java/org/spongycastle/eac/operator')
9 files changed, 574 insertions, 0 deletions
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 |