Skip to content
Draft
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
6 changes: 6 additions & 0 deletions cmdline/src/main/java/io/opentdf/platform/Command.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

import java.security.Security;
import java.text.ParseException;
import com.google.gson.JsonSyntaxException;
import io.opentdf.platform.sdk.AssertionConfig;
Expand All @@ -19,6 +20,7 @@
import io.opentdf.platform.sdk.SDK;
import io.opentdf.platform.sdk.SDKBuilder;
import nl.altindag.ssl.SSLFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import picocli.CommandLine;
import picocli.CommandLine.HelpCommand;
import picocli.CommandLine.Option;
Expand Down Expand Up @@ -63,6 +65,10 @@ class Versions {
+ "\",\"tdfSpecVersion\":\"" + Versions.TDF_SPEC + "\"}")
class Command {

static {
Security.addProvider(new BouncyCastleProvider());
}

@Option(names = { "-V", "--version" }, versionHelp = true, description = "display version info")
boolean versionInfoRequested;

Expand Down
133 changes: 46 additions & 87 deletions sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java
Original file line number Diff line number Diff line change
@@ -1,49 +1,37 @@
package io.opentdf.platform.sdk;

import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X962Parameters;
import org.bouncycastle.asn1.x9.X9ECPoint;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.generators.HKDFBytesGenerator;
import org.bouncycastle.crypto.params.HKDFParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.KeyPairGeneratorSpi;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.interfaces.ECPrivateKey;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.openssl.PEMException;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.util.io.pem.*;
import org.bouncycastle.util.io.pem.PemReader;
import org.bouncycastle.jce.spec.ECPublicKeySpec;

import javax.crypto.KeyAgreement;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.io.*;
import java.security.*;
import java.security.spec.*;
import java.util.Objects;
// https://www.bouncycastle.org/latest_releases.html

public class ECKeyPair {

private static final int SHA256_BYTES = 32;

static {
Security.addProvider(new BouncyCastleProvider());
}

private final ECCurve curve;

public enum ECAlgorithm {
ECDH,
ECDSA
}

private static final BouncyCastleProvider BOUNCY_CASTLE_PROVIDER = new BouncyCastleProvider();

private KeyPair keyPair;

public ECKeyPair() {
Expand All @@ -58,9 +46,9 @@ public ECKeyPair(ECCurve curve, ECAlgorithm algorithm) {
// Should this just use the algorithm vs use ECDH only for ECDH and ECDSA for
// everything else.
if (algorithm == ECAlgorithm.ECDH) {
generator = KeyPairGeneratorSpi.getInstance(ECAlgorithm.ECDH.name(), BOUNCY_CASTLE_PROVIDER);
generator = KeyPairGenerator.getInstance(ECAlgorithm.ECDH.name());
} else {
generator = KeyPairGeneratorSpi.getInstance(ECAlgorithm.ECDSA.name(), BOUNCY_CASTLE_PROVIDER);
generator = KeyPairGenerator.getInstance(ECAlgorithm.ECDSA.name());
}
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
Expand Down Expand Up @@ -122,77 +110,60 @@ public int keySize() {
}

public byte[] compressECPublickey() {
return ((ECPublicKey) this.keyPair.getPublic()).getQ().getEncoded(true);
return getCompressedECPublicKey(this.keyPair.getPublic());
}

public static String getPEMPublicKeyFromX509Cert(String pemInX509Format) {
try {
PEMParser parser = new PEMParser(new StringReader(pemInX509Format));
X509CertificateHolder x509CertificateHolder = (X509CertificateHolder) parser.readObject();
parser.close();
SubjectPublicKeyInfo publicKeyInfo = x509CertificateHolder.getSubjectPublicKeyInfo();
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(BOUNCY_CASTLE_PROVIDER);
ECPublicKey publicKey = null;
try {
publicKey = (ECPublicKey) converter.getPublicKey(publicKeyInfo);
} catch (PEMException e) {
throw new RuntimeException(e);
}
private static byte[] getCompressedECPublicKey(PublicKey publicKey) {
SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded());
X962Parameters params = X962Parameters.getInstance(publicKeyInfo.getAlgorithm().getParameters());
if (params.isImplicitlyCA()) {
throw new IllegalArgumentException("Implicitly CA parameters are not supported.");
}

// EC public key to pem formated.
StringWriter writer = new StringWriter();
PemWriter pemWriter = new PemWriter(writer);
org.bouncycastle.math.ec.ECCurve bcCurve =
ECNamedCurveTable.getByOID((ASN1ObjectIdentifier) params.getParameters()).getCurve();
org.bouncycastle.math.ec.ECPoint p =
bcCurve.decodePoint(publicKeyInfo.getPublicKeyData().getOctets());

pemWriter.writeObject(new PemObject("PUBLIC KEY", publicKey.getEncoded()));
pemWriter.flush();
pemWriter.close();
return writer.toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
return new X9ECPoint(p, true).getPointEncoding();
}

public static byte[] compressECPublickey(String pemECPubKey) {
try {
KeyFactory ecKeyFac = KeyFactory.getInstance("EC", "BC");
KeyFactory ecKeyFac = KeyFactory.getInstance("EC");
PemReader pemReader = new PemReader(new StringReader(pemECPubKey));
PemObject pemObject = pemReader.readPemObject();
PublicKey pubKey = ecKeyFac.generatePublic(new X509EncodedKeySpec(pemObject.getContent()));
return ((ECPublicKey) pubKey).getQ().getEncoded(true);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException(e);
} catch (NoSuchProviderException e) {
return getCompressedECPublicKey(pubKey);
} catch (NoSuchAlgorithmException | IOException | InvalidKeySpecException e) {
throw new RuntimeException(e);
}
}

public static String publicKeyFromECPoint(byte[] ecPoint, String curveName) {
try {
org.bouncycastle.math.ec.ECPoint point =
ECNamedCurveTable.getByName(curveName).getCurve().decodePoint(ecPoint);
java.security.spec.ECPoint jpoint = new java.security.spec.ECPoint(
point.getAffineXCoord().toBigInteger(), point.getAffineYCoord().toBigInteger());

// Create EC Public key
ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(curveName);
ECPoint point = ecSpec.getCurve().decodePoint(ecPoint);
ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(point, ecSpec);
KeyFactory keyFactory = KeyFactory.getInstance("ECDSA", "BC");
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
AlgorithmParameters algorithmParameters = AlgorithmParameters.getInstance("EC");
algorithmParameters.init(new ECGenParameterSpec(curveName));
ECParameterSpec ecParameterSpec = algorithmParameters.getParameterSpec(ECParameterSpec.class);

ECPublicKeySpec spec = new ECPublicKeySpec(jpoint, ecParameterSpec);
KeyFactory keyFactory = KeyFactory.getInstance("EC");
PublicKey publicKey = keyFactory.generatePublic(spec);

// EC Public keu to pem format.
// EC Public key to pem format.
StringWriter writer = new StringWriter();
PemWriter pemWriter = new PemWriter(writer);
pemWriter.writeObject(new PemObject("PUBLIC KEY", publicKey.getEncoded()));
pemWriter.flush();
pemWriter.close();
return writer.toString();
} catch (InvalidKeySpecException e) {
throw new RuntimeException(e);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (NoSuchProviderException e) {
throw new RuntimeException(e);
} catch (IOException e) {
} catch (InvalidKeySpecException | NoSuchAlgorithmException | IOException | InvalidParameterSpecException e) {
throw new RuntimeException(e);
}
}
Expand All @@ -203,7 +174,7 @@ public static ECPublicKey publicKeyFromPem(String pemEncoding) {
SubjectPublicKeyInfo publicKeyInfo = (SubjectPublicKeyInfo) parser.readObject();
parser.close();

JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(BOUNCY_CASTLE_PROVIDER);
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
return (ECPublicKey) converter.getPublicKey(publicKeyInfo);
} catch (IOException e) {
throw new RuntimeException(e);
Expand All @@ -216,7 +187,7 @@ public static ECPrivateKey privateKeyFromPem(String pemEncoding) {
PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) parser.readObject();
parser.close();

JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(BOUNCY_CASTLE_PROVIDER);
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
return (ECPrivateKey) converter.getPrivateKey(privateKeyInfo);
} catch (IOException e) {
throw new RuntimeException(e);
Expand All @@ -225,11 +196,11 @@ public static ECPrivateKey privateKeyFromPem(String pemEncoding) {

public static byte[] computeECDHKey(ECPublicKey publicKey, ECPrivateKey privateKey) {
try {
KeyAgreement aKeyAgree = KeyAgreement.getInstance("ECDH", "BC");
KeyAgreement aKeyAgree = KeyAgreement.getInstance("ECDH");
aKeyAgree.init(privateKey);
aKeyAgree.doPhase(publicKey, true);
return aKeyAgree.generateSecret();
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidKeyException e) {
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException(e);
}
}
Expand All @@ -250,35 +221,23 @@ public static byte[] calculateHKDF(byte[] salt, byte[] secret) {

public static byte[] computeECDSASig(byte[] digest, ECPrivateKey privateKey) {
try {
Signature ecdsaSign = Signature.getInstance("SHA256withECDSA", "BC");
Signature ecdsaSign = Signature.getInstance("SHA256withECDSA");
ecdsaSign.initSign(privateKey);
ecdsaSign.update(digest);
return ecdsaSign.sign();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (NoSuchProviderException e) {
throw new RuntimeException(e);
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
} catch (SignatureException e) {
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
throw new RuntimeException(e);
}
}

public static Boolean verifyECDSAig(byte[] digest, byte[] signature, ECPublicKey publicKey) {
try {
Signature ecdsaVerify = Signature.getInstance("SHA256withECDSA", "BC");
Signature ecdsaVerify = Signature.getInstance("SHA256withECDSA");
ecdsaVerify.initVerify(publicKey);
ecdsaVerify.update(digest);
return ecdsaVerify.verify(signature);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (NoSuchProviderException e) {
throw new RuntimeException(e);
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
} catch (SignatureException e) {
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
throw new RuntimeException(e);
}
}
}
}
2 changes: 1 addition & 1 deletion sdk/src/main/java/io/opentdf/platform/sdk/TDF.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -22,6 +21,7 @@
import java.nio.channels.SeekableByteChannel;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.ECPublicKey;
import java.text.ParseException;
import java.util.*;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.opentdf.platform.sdk;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;

import java.security.Security;

public class CryptoProviderSetupExtension implements BeforeAllCallback {
private BouncyCastleProvider securityProvider;

@Override
public synchronized void beforeAll(ExtensionContext extensionContext) throws Exception {
if (this.securityProvider == null) {
Security.addProvider(this.securityProvider = new BouncyCastleProvider());
}
}
}
Loading
Loading