Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions src/main/java/org/cryptacular/adapter/AbstractWrappedECKey.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
/* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.adapter;

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 org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
import org.bouncycastle.math.ec.ECCurve;

/**
* Base class for wrapped EC keys.
Expand Down Expand Up @@ -38,7 +42,7 @@ public ECParameterSpec getParams()
final ECDomainParameters params = delegate.getParameters();
return
new ECParameterSpec(
EC5Util.convertCurve(params.getCurve(), params.getSeed()),
convertCurve(params.getCurve(), params.getSeed()),
new ECPoint(
params.getG().normalize().getXCoord().toBigInteger(),
params.getG().normalize().getYCoord().toBigInteger()),
Expand All @@ -52,4 +56,29 @@ public String getAlgorithm()
{
return ALGORITHM;
}

private static EllipticCurve convertCurve(final ECCurve curve, final byte[] seed)
{
final ECField field;
if (curve instanceof ECCurve.Fp) {
field = new ECFieldFp(((ECCurve.Fp) curve).getQ());
} else {
final ECCurve.F2m f2m = (ECCurve.F2m) curve;
final int m = f2m.getM();
final int k1 = f2m.getK1();
final int k2 = f2m.getK2();
final int k3 = f2m.getK3();
if (k2 == 0) {
field = new ECFieldF2m(m, new int[]{k1});
} else {
field = new ECFieldF2m(m, new int[]{k3, k2, k1});
}
}
return new EllipticCurve(
field,
curve.getA().toBigInteger(),
curve.getB().toBigInteger(),
seed);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.DSAParameters;
import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.cryptacular.EncodingException;
import org.cryptacular.pbe.OpenSSLAlgorithm;
import org.cryptacular.pbe.OpenSSLEncryptionScheme;
Expand Down Expand Up @@ -129,7 +129,7 @@ private ECPrivateKeyParameters parseECPrivateKey(final ASN1Sequence seq)
final ASN1TaggedObject asn1Params = ASN1TaggedObject.getInstance(seq.getObjectAt(2));
final X9ECParameters params;
if (asn1Params.getBaseObject() instanceof ASN1ObjectIdentifier) {
params = ECUtil.getNamedCurveByOid(ASN1ObjectIdentifier.getInstance(asn1Params.getBaseObject()));
params = ECNamedCurveTable.getByOID(ASN1ObjectIdentifier.getInstance(asn1Params.getBaseObject()));
} else {
params = X9ECParameters.getInstance(asn1Params.getBaseObject());
}
Expand Down
46 changes: 31 additions & 15 deletions src/main/java/org/cryptacular/generator/KeyPairGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@

import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
import org.cryptacular.CryptUtil;
import org.cryptacular.util.CertUtil;

/**
* Static factory that generates various types of asymmetric key pairs.
Expand Down Expand Up @@ -33,10 +35,14 @@ public static KeyPair generateDSA(final SecureRandom random, final int bitLength
if (bitLength < 1) {
throw new IllegalArgumentException("Bit length must be positive");
}
final org.bouncycastle.jcajce.provider.asymmetric.dsa.KeyPairGeneratorSpi generator =
new org.bouncycastle.jcajce.provider.asymmetric.dsa.KeyPairGeneratorSpi();
generator.initialize(bitLength, random);
return generator.generateKeyPair();
try {
final java.security.KeyPairGenerator generator =
java.security.KeyPairGenerator.getInstance("DSA", CertUtil.bouncyCastleProvider());
generator.initialize(bitLength, random);
return generator.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("DSA algorithm not available", e);
}
}


Expand All @@ -54,10 +60,14 @@ public static KeyPair generateRSA(final SecureRandom random, final int bitLength
if (bitLength < 1) {
throw new IllegalArgumentException("Bit length must be positive");
}
final org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyPairGeneratorSpi generator =
new org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyPairGeneratorSpi();
generator.initialize(bitLength, random);
return generator.generateKeyPair();
try {
final java.security.KeyPairGenerator generator =
java.security.KeyPairGenerator.getInstance("RSA", CertUtil.bouncyCastleProvider());
generator.initialize(bitLength, random);
return generator.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("RSA algorithm not available", e);
}
}


Expand All @@ -75,10 +85,14 @@ public static KeyPair generateEC(final SecureRandom random, final int bitLength)
if (bitLength < 1) {
throw new IllegalArgumentException("Bit length must be positive");
}
final org.bouncycastle.jcajce.provider.asymmetric.ec.KeyPairGeneratorSpi.EC generator =
new org.bouncycastle.jcajce.provider.asymmetric.ec.KeyPairGeneratorSpi.EC();
generator.initialize(bitLength, random);
return generator.generateKeyPair();
try {
final java.security.KeyPairGenerator generator =
java.security.KeyPairGenerator.getInstance("EC", CertUtil.bouncyCastleProvider());
generator.initialize(bitLength, random);
return generator.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("EC algorithm not available", e);
}
}


Expand All @@ -94,13 +108,15 @@ public static KeyPair generateEC(final SecureRandom random, final String namedCu
{
CryptUtil.assertNotNullArg(random, "Secure random cannot be null");
CryptUtil.assertNotNullArg(namedCurve, "Named curve cannot be null");
final org.bouncycastle.jcajce.provider.asymmetric.ec.KeyPairGeneratorSpi.EC generator =
new org.bouncycastle.jcajce.provider.asymmetric.ec.KeyPairGeneratorSpi.EC();
try {
final java.security.KeyPairGenerator generator =
java.security.KeyPairGenerator.getInstance("EC", CertUtil.bouncyCastleProvider());
generator.initialize(new ECNamedCurveGenParameterSpec(namedCurve), random);
return generator.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("EC algorithm not available", e);
} catch (InvalidAlgorithmParameterException e) {
throw new IllegalArgumentException("Invalid EC curve " + namedCurve, e);
}
return generator.generateKeyPair();
}
}
27 changes: 24 additions & 3 deletions src/main/java/org/cryptacular/util/CertUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import java.nio.CharBuffer;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
Expand Down Expand Up @@ -37,7 +39,6 @@
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
Expand Down Expand Up @@ -596,7 +597,9 @@ public static X509Certificate generateX509Certificate(
final BigInteger serial = BigInteger.valueOf(now.toEpochMilli());

try {
final ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgo).build(keyPair.getPrivate());
final Provider provider = bouncyCastleProvider();
final ContentSigner contentSigner =
new JcaContentSignerBuilder(signatureAlgo).setProvider(provider).build(keyPair.getPrivate());
final X500Name x500Name = new X500Name(RFC4519Style.INSTANCE, dn);
final X509v3CertificateBuilder certificateBuilder =
new JcaX509v3CertificateBuilder(x500Name,
Expand All @@ -607,12 +610,30 @@ public static X509Certificate generateX509Certificate(
keyPair.getPublic())
.addExtension(Extension.basicConstraints, true, new BasicConstraints(true));
return new JcaX509CertificateConverter()
.setProvider(new BouncyCastleProvider()).getCertificate(certificateBuilder.build(contentSigner));
.setProvider(provider).getCertificate(certificateBuilder.build(contentSigner));
} catch (OperatorCreationException | CertIOException | CertificateException e) {
throw new RuntimeException("Certificate generation error", e);
}
}

public static Provider bouncyCastleProvider()
{
Provider p = Security.getProvider("BCFIPS");
if (p != null) {
return p;
}
p = Security.getProvider("BC");
if (p != null) {
return p;
}
try {
return (Provider) Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider")
.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new IllegalStateException("No BouncyCastle provider found (BC or BCFIPS)", e);
}
}

/**
* Describes the behavior of string formatting of X.500 distinguished names.
*/
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/cryptacular/util/CsrUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,8 @@ public static PKCS10CertificationRequest generateCsr(
throw new CryptoException("Error adding subject alt names to CSR", e);
}
}
final JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder(sigAlg);
final JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder(sigAlg)
.setProvider(CertUtil.bouncyCastleProvider());
try {
final ContentSigner signer = csBuilder.build(keyPair.getPrivate());
return p10Builder.build(signer);
Expand Down
14 changes: 9 additions & 5 deletions src/main/java/org/cryptacular/util/KeyPairUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.PrivateKey;
Expand All @@ -14,6 +15,8 @@
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
Expand All @@ -24,7 +27,8 @@
import org.bouncycastle.crypto.signers.DSASigner;
import org.bouncycastle.crypto.signers.ECDSASigner;
import org.bouncycastle.crypto.signers.RSADigestSigner;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.crypto.util.PublicKeyFactory;
import org.cryptacular.CryptUtil;
import org.cryptacular.EncodingException;
import org.cryptacular.StreamException;
Expand Down Expand Up @@ -235,13 +239,13 @@ public static boolean isKeyPair(final ECPublicKey pubKey, final ECPrivateKey pri
CryptUtil.assertNotNullArg(privKey, "Private key cannot be null");
final ECDSASigner signer = new ECDSASigner();
try {
signer.init(true, ECUtil.generatePrivateKeyParameter(privKey));
signer.init(true, PrivateKeyFactory.createKey(PrivateKeyInfo.getInstance(privKey.getEncoded())));

final BigInteger[] sig = signer.generateSignature(SIGN_BYTES);
signer.init(false, ECUtil.generatePublicKeyParameter(pubKey));
signer.init(false, PublicKeyFactory.createKey(SubjectPublicKeyInfo.getInstance(pubKey.getEncoded())));
return signer.verifySignature(SIGN_BYTES, sig[0], sig[1]);
} catch (Exception e) {
throw new org.cryptacular.CryptoException("Signature computation error", e);
} catch (IOException e) {
throw new org.cryptacular.CryptoException("Key encoding error", e);
}
}

Expand Down
110 changes: 110 additions & 0 deletions src/test/java/org/cryptacular/generator/KeyPairGeneratorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.generator;

import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.assertj.core.api.Assertions.*;

/**
* Unit test for {@link KeyPairGenerator} class.
*
* @author Middleware Services
*/
public class KeyPairGeneratorTest
{
private final SecureRandom random = new SecureRandom();

private Provider bc;

@BeforeMethod
public void registerProvider()
{
bc = new BouncyCastleProvider();
Security.addProvider(bc);
}

@AfterMethod
public void removeProvider()
{
Security.removeProvider("BC");
}

@DataProvider(name = "rsa-key-sizes")
public Object[][] getRsaKeySizes()
{
return new Object[][] {
new Object[] {1024},
new Object[] {2048},
};
}

@DataProvider(name = "dsa-key-sizes")
public Object[][] getDsaKeySizes()
{
return new Object[][] {
new Object[] {1024},
};
}

@DataProvider(name = "ec-key-sizes")
public Object[][] getEcKeySizes()
{
return new Object[][] {
new Object[] {256},
new Object[] {384},
};
}

@DataProvider(name = "ec-named-curves")
public Object[][] getEcNamedCurves()
{
return new Object[][] {
new Object[] {"P-256"},
new Object[] {"P-384"},
};
}

@Test(dataProvider = "rsa-key-sizes")
public void testGenerateRSA(final int bitLength)
{
final RSAPublicKey pub = (RSAPublicKey) KeyPairGenerator.generateRSA(random, bitLength).getPublic();
assertThat(pub.getModulus().bitLength()).isEqualTo(bitLength);
}

@Test(dataProvider = "dsa-key-sizes")
public void testGenerateDSA(final int bitLength)
{
final DSAPublicKey pub = (DSAPublicKey) KeyPairGenerator.generateDSA(random, bitLength).getPublic();
assertThat(pub.getParams().getP().bitLength()).isEqualTo(bitLength);
}

@Test(dataProvider = "ec-key-sizes")
public void testGenerateECByBitLength(final int bitLength)
{
final ECPublicKey pub = (ECPublicKey) KeyPairGenerator.generateEC(random, bitLength).getPublic();
assertThat(pub.getParams().getCurve().getField().getFieldSize()).isEqualTo(bitLength);
}

@Test(dataProvider = "ec-named-curves")
public void testGenerateECByNamedCurve(final String namedCurve)
{
assertThat(KeyPairGenerator.generateEC(random, namedCurve).getPublic()).isInstanceOf(ECPublicKey.class);
}

@Test
public void testGenerateECInvalidCurveThrows()
{
assertThatThrownBy(() -> KeyPairGenerator.generateEC(random, "not-a-curve"))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Invalid EC curve");
}
}
Loading