The generation of a sharedSecret is done with the two components of (your own) privateKey and the (third party) public key - here the public key
is taken from the (third party) certificate. To get the sharedSecret you don't rebuild a keyPair (because they aren't a pair)
but use a KeyAgreement with instance "ECDH".
Below you find a full working sample program that takes the private key and certificate in PEM-format for both parties (usually
named as 'alice' and 'bob'). On both sides the PrivateKey and the PublicKey were rebuild and in the end the sharedSecret is generated
with "mixed" keys (first shared secret: private key from alice and public key from bob, second shared secret: private key from bob and public key from alice).
Both sharedSecrets are identical and are good for a AES-encryption with a key length 32 byte/256 bit.
Another note: this example runs without Bouncy Castle dependencies.
There is no proper exception handling and the using of fixed (build in) private key is UNSECURE.
The keys were generated with Java keytool and exported with openssl into PEM-format:
keytool -genkeypair -keysize 256 -sigalg SHA256withECDSA -keyalg EC -dname "cn=Alice" -alias alice -keypass apassword -keystore alice_keystore.pkcs12 -storetype pkcs12 -storepass apassword -validity 3650
openssl pkcs12 -in alice_keystore.pkcs12 -nokeys -out alice_crt.pem
openssl pkcs12 -in alice_keystore.pkcs12 -nodes -nocerts -out alice_privatekey.pem
[password: apassword]
result:
Example Key Exchange EC Curves without BC
alice crt: [
Version: V3
Subject: CN=Alice
Signature Algorithm: SHA256withECDSA, OID = 1.2.840.10045.4.3.2
Key: Sun EC public key, 256 bits
public x coord: 53193002007562396012681859968974283073474881849394375511693405764629717680344
public y coord: 67114127612689688829965470157066506514867660192432128210506355717815080266028
parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)
Validity: [From: Sat Aug 08 18:16:47 CEST 2020,
To: Tue Aug 06 18:16:47 CEST 2030]
Issuer: CN=Alice
SerialNumber: [ 57c8f4ae]
...
bob crt: [
Version: V3
Subject: CN=Bob
Signature Algorithm: SHA256withECDSA, OID = 1.2.840.10045.4.3.2
Key: Sun EC public key, 256 bits
public x coord: 53218839282383347527983259020322984395517469483615530947798768625444790545484
public y coord: 50373539334339208285989254642248018747424962690612261530706567696443441910381
parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)
Validity: [From: Sat Aug 08 20:22:39 CEST 2020,
To: Tue Aug 06 20:22:39 CEST 2030]
Issuer: CN=Bob
SerialNumber: [ 5c814597]
...
alice & bob sharedSecret equals: true
sharedSecret length: 32 data: c4adaea9b1f3cdf8bede4f3f51546c8bb5a33158f8299e6692c20c2de6109c16
code:
import javax.crypto.KeyAgreement;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.regex.Pattern;
public class EllipticCurveKeyExchange {
public static void main(String[] args) throws GeneralSecurityException, IOException {
System.out.println("Example Key Exchange EC Curves without BC");
// all keys are sample keys
// certificate and private key for alice
String alice_cert_pem = "-----BEGIN CERTIFICATE-----\n" +
"MIIBNjCB26ADAgECAgRXyPSuMAwGCCqGSM49BAMCBQAwEDEOMAwGA1UEAxMFQWxp\n" +
"Y2UwHhcNMjAwODA4MTYxNjQ3WhcNMzAwODA2MTYxNjQ3WjAQMQ4wDAYDVQQDEwVB\n" +
"bGljZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHWaLBYzJU0/WTsIWOXhQrNG\n" +
"NFypOpTI1UOO3k+S0YDYlGFABWPdALUfjk00SAsW5xpgEW2WRsnKgjO6IVMLKSyj\n" +
"ITAfMB0GA1UdDgQWBBQN9LQY8o7xlOWtJVmtAg84SZdz7jAMBggqhkjOPQQDAgUA\n" +
"A0gAMEUCIGwjEeUFk9Qv0dRvUsnJgmokpAfQ9ISfofkskanz8L9CAiEAvh7SbBd5\n" +
"A/50QbKp+73M+Sy1jmXqD+IEfkI0YDWtDac=\n" +
"-----END CERTIFICATE-----";
String alice_privateKey_pem = "-----BEGIN PRIVATE KEY-----\n" +
"MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCB+NAwfARVZ6KymEgd4\n" +
"D4DQjX0WomJDKZ7fUTDrNJtOqQ==\n" +
"-----END PRIVATE KEY-----";
CertificateFactory cf = CertificateFactory.getInstance("X.509");
ByteArrayInputStream bis_alice = new ByteArrayInputStream(loadPEM(alice_cert_pem));
X509Certificate alice_cert = (X509Certificate) cf.generateCertificate(bis_alice);
System.out.println("alice crt: " + alice_cert);
PublicKey alice_publicKey = alice_cert.getPublicKey();
KeyFactory kf = KeyFactory.getInstance("EC");
PrivateKey alice_privateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(loadPEM(alice_privateKey_pem)));
System.out.println("alice publicKey: " + alice_publicKey);
System.out.println("alice privateKey: " + alice_privateKey);
// certificate and private key for bob
String bob_cert_pem = "-----BEGIN CERTIFICATE-----\n" +
"MIIBMzCB16ADAgECAgRcgUWXMAwGCCqGSM49BAMCBQAwDjEMMAoGA1UEAxMDQm9i\n" +
"MB4XDTIwMDgwODE4MjIzOVoXDTMwMDgwNjE4MjIzOVowDjEMMAoGA1UEAxMDQm9i\n" +
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdajLq/yS6RMr+Pj745/mZ3gEDin8\n" +
"Idea+8VfIMepLExvXmmzTun18700D8tFJAhRk7PgkoRhztGgbzrXIaOSbaMhMB8w\n" +
"HQYDVR0OBBYEFDJqdrcRZD7noaNlKcYncfO+YdDIMAwGCCqGSM49BAMCBQADSQAw\n" +
"RgIhAOW7Asmky4fHL4/luoc0F1IuDUhXtS8iASKAaYHL72m8AiEA5reQE+JK93l9\n" +
"5+oPb2wxhD7QINZxShleE7qjBngT6io=\n" +
"-----END CERTIFICATE-----";
String bob_privateKey_pem = "-----BEGIN PRIVATE KEY-----\n" +
"MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCDv9KwdBlhxGukLkXp5\n" +
"UfMTddh+h00pEpMcvR3qsZYVyA==\n" +
"-----END PRIVATE KEY-----";
ByteArrayInputStream bis_bob = new ByteArrayInputStream(loadPEM(bob_cert_pem));
X509Certificate bob_cert = (X509Certificate) cf.generateCertificate(bis_bob);
System.out.println("bob crt: " + bob_cert);
PublicKey bob_publicKey = bob_cert.getPublicKey();
PrivateKey bob_privateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(loadPEM(bob_privateKey_pem)));
System.out.println("bob publicKey: " + bob_publicKey);
System.out.println("bob privateKey: " + bob_privateKey);
// generate sharedSecret for alice & bob
byte[] alice_sharedSecret = createEcdhSharedSecret(alice_privateKey, bob_publicKey);
byte[] bob_sharedSecret = createEcdhSharedSecret(bob_privateKey, alice_publicKey);
System.out.println("\nalice & bob sharedSecret equals: " + Arrays.equals(alice_sharedSecret, bob_sharedSecret));
System.out.println("sharedSecret length: " + alice_sharedSecret.length + " data: " + bytesToHex(alice_sharedSecret));
}
// https://stackoverflow.com/a/49753179/8166854
private static byte[] loadPEM (String resource) throws IOException {
byte[] input = resource.getBytes(StandardCharsets.ISO_8859_1);
// URL url = getClass().getResource(resource);
// InputStream in = url.openStream();
ByteArrayInputStream in = new ByteArrayInputStream(input);
String pem = new String(in.readAllBytes(), StandardCharsets.ISO_8859_1);
Pattern parse = Pattern.compile("(?m)(?s)^---*BEGIN.*---*$(.*)^---*END.*---*$.*");
String encoded = parse.matcher(pem).replaceFirst("$1");
return Base64.getMimeDecoder().decode(encoded);
}
public static byte[] createEcdhSharedSecret(PrivateKey privateKey, PublicKey publicKey) throws NoSuchAlgorithmException, InvalidKeyException {
KeyAgreement keyAgree = KeyAgreement.getInstance("ECDH");
keyAgree.init(privateKey);
keyAgree.doPhase(publicKey, true);
return keyAgree.generateSecret();
}
private static String bytesToHex(byte[] bytes) {
StringBuffer result = new StringBuffer();
for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
return result.toString();
}
}