我在项目中使用了完整的示例解决方案,因此它可能对其他人有用。它使用EC-Keypair(曲线“ secp256r1”)和ECDH进行KeyExchange。您需要BouncyCastle并注意没有适当的异常处理,并且现有密钥库将被覆盖,恕不另行通知
我目前正在编写一个加密的Java程序,并为其实现了密钥交换,以便拥有程序实例(不必同时运行)的两个用户可以就AES加密的共享密钥达成一致。我计划为此使用Diffie Hellman密钥交换协议。
因此,我通常遵循this example by Oracle,除了在程序的不同方法中实现Alice和Bob的部分。在此示例中,Alice和Bob交换的是他们的已编码公钥]
byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();
byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded();
分别。为了传输这些编码的公共密钥,我将这些字节数组保存为文件,以供每个用户传输给其他用户。
现在,我想处理这样的情况,即启动交换密钥的用户(例如Alice)在等待其他用户的响应的同时关闭程序,并将其编码后的公共密钥作为文件发送回。重新启动程序时,Alice希望根据从Bob接收到的公共密钥和她自己的私有密钥来计算共享密钥,后者在关闭程序时必须存储在某个地方。因为我的程序已经使用PKCS12-KeyStore,所以我认为可以将Diffie-Hellman密钥对保存到该KeyStore。
因此,我跟随the answer to this question使用自签名X509证书存储RSA密钥对的方法。但是,这显然会引发RSA签名算法的错误org.bouncycastle.operator.OperatorCreationException: cannot create signer: Supplied key (com.sun.crypto.provider.DHPrivateKey) is not a RSAPrivateKey instance
:
String signatureAlgorithm = "SHA256WithRSA";
ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithm)
.setProvider(bcProvider).build(keyPair.getPrivate());
我用]初始化密钥对后>
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("DH"); keyPairGen.initialize(bitLength); KeyPair keyPair = keyPairGen.generateKeyPair();
现在要解决这个问题,有没有办法:
我目前正在编写一个实现了密钥交换的加密Java程序,以便两个拥有该程序实例的运行用户(不必同时运行)可以就...达成一致]]] >>
我在项目中使用了完整的示例解决方案,因此它可能对其他人有用。它使用EC-Keypair(曲线“ secp256r1”)和ECDH进行KeyExchange。您需要BouncyCastle并注意没有适当的异常处理,并且现有密钥库将被覆盖,恕不另行通知
import org.bouncycastle.asn1.x500.X500Name;
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;
import javax.crypto.KeyAgreement;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
public class StoreEcdhKeyInPKCS12KeystoreSO {
public static void main(String[] args) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException, OperatorCreationException, CertificateException, KeyStoreException, IOException, UnrecoverableKeyException, InvalidKeySpecException, InvalidKeyException {
System.out.println("Storing a ECDH Keypair in a PKCS12 Keystore");
// you need BouncyCastle, get it from https://www.bouncycastle.org/latest_releases.html
Security.addProvider(new BouncyCastleProvider());
System.out.println("\nJava version: " + Runtime.version() + " BouncyCastle Version: " + Security.getProvider("BC"));
// ### WARNING: no exception handling, existing keystores will be overwritten without notice ###
String ecCurvename = "secp256r1";
// alice's credentials
String aliceKeystoreFilename = "alicekeystore.p12";
char[] aliceKeystoreEntryPassword = "aliceEntryPassword".toCharArray();
String aliceKeypairAlias = "aliceKeypairAlias";
char[] aliceKeypairPassword = "aliceKeypairPassword".toCharArray();
KeyPair aliceKeyPairGenerated;
PrivateKey alicePrivateKeyLoaded;
byte[] aliceReceivedPublicKeyFromBob;
byte[] aliceSharedSecret;
// bob's credentials
String bobKeystoreFilename = "bobkeystore.p12";
char[] bobKeystoreEntryPassword = "bobEntryPassword".toCharArray();
String bobKeypairAlias = "bobKeypairAlias";
char[] bobKeypairPassword = "bobKeypairPassword".toCharArray();
KeyPair bobKeyPairGenerated;
PrivateKey bobPrivateKeyLoaded;
byte[] bobReceivedPublicKeyFromAlice;
byte[] bobSharedSecret;
// alice start keypair generation, comment out if you still have a keystore with the keys
aliceKeyPairGenerated = generateEcdhKeyPair(ecCurvename);
// save keypair
storeEcdhKeypairInPKCS12Keystore(aliceKeystoreFilename, aliceKeystoreEntryPassword, aliceKeypairAlias, aliceKeypairPassword, aliceKeyPairGenerated);
System.out.println("alice has a new keystore: " + aliceKeystoreFilename);
// bob start keypair generation, comment out if you still have a keystore with the keys
bobKeyPairGenerated = generateEcdhKeyPair(ecCurvename);
storeEcdhKeypairInPKCS12Keystore(bobKeystoreFilename, bobKeystoreEntryPassword, bobKeypairAlias, bobKeypairPassword, bobKeyPairGenerated);
System.out.println("bob has a new keystore: " + bobKeystoreFilename);
// alice sends her public key to bob -e.g. you could code it with base64, here we're just cloning the key
bobReceivedPublicKeyFromAlice = aliceKeyPairGenerated.getPublic().getEncoded().clone();
// later on - bob received the the public key from alice and loads his key from keystore
bobPrivateKeyLoaded = loadEcdhPrivateKeyFromPKCS12Keystore(bobKeystoreFilename, bobKeystoreEntryPassword, bobKeypairAlias, bobKeypairPassword);
// bob creates the shared secret with public key from alice
bobSharedSecret = createEcdhSharedSecret(bobPrivateKeyLoaded, bobReceivedPublicKeyFromAlice);
System.out.println("SharedSecret Bob: " + bytesToHex(bobSharedSecret));
// bob sends his public key to alice -e.g. you could code it with base64, here we're just cloning the key
aliceReceivedPublicKeyFromBob = loadEcdhPublicKeyFromPKCS12Keystore(bobKeystoreFilename, bobKeystoreEntryPassword, bobKeypairAlias, bobKeypairPassword).getEncoded().clone();
// alice loads her private key from keystore and generates the SecretShare
alicePrivateKeyLoaded = loadEcdhPrivateKeyFromPKCS12Keystore(aliceKeystoreFilename, aliceKeystoreEntryPassword, aliceKeypairAlias, aliceKeypairPassword);
aliceSharedSecret = createEcdhSharedSecret(alicePrivateKeyLoaded, aliceReceivedPublicKeyFromBob);
System.out.println("SharedSecret Alice: " + bytesToHex(aliceSharedSecret));
// check that both SecretShare's are equal
System.out.println("Compare aliceSharedSecret and bobSharedSecret: " + Arrays.equals(aliceSharedSecret, bobSharedSecret));
// do what ever you want with your SharedSecret, e.g. shorten it using SHA256 for a AES encryption key
}
public static KeyPair generateEcdhKeyPair(String curvenameString) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "SunEC");
ECGenParameterSpec ecParameterSpec = new ECGenParameterSpec(curvenameString);
keyPairGenerator.initialize(ecParameterSpec);
return keyPairGenerator.genKeyPair();
}
public static void storeEcdhKeypairInPKCS12Keystore(String filename, char[] entryPassword, String keypairAlias, char[] keypairPassword, KeyPair keypair) throws CertificateException, OperatorCreationException, KeyStoreException, IOException, NoSuchAlgorithmException {
// --- create the self signed cert
java.security.cert.Certificate cert = createSelfSigned(keypair);
// --- create a new pkcs12 key store in memory
KeyStore pkcs12 = KeyStore.getInstance("PKCS12");
pkcs12.load(null, null);
// --- create entry in PKCS12
pkcs12.setKeyEntry(keypairAlias, keypair.getPrivate(), keypairPassword, new Certificate[]{cert});
// --- store PKCS#12 as file
try (FileOutputStream p12 = new FileOutputStream(filename)) {
pkcs12.store(p12, entryPassword);
} catch (NoSuchAlgorithmException | IOException e) {
e.printStackTrace();
}
}
public static PrivateKey loadEcdhPrivateKeyFromPKCS12Keystore(String filename, char[] entryPassword, String keypairAlias, char[] keypairPassword) throws CertificateException, OperatorCreationException, KeyStoreException, IOException, NoSuchAlgorithmException, UnrecoverableKeyException {
// --- read PKCS#12 as file
KeyStore pkcs12 = KeyStore.getInstance("PKCS12");
try (FileInputStream p12 = new FileInputStream(filename)) {
pkcs12.load(p12, entryPassword);
}
// --- retrieve private key
return (PrivateKey) pkcs12.getKey(keypairAlias, keypairPassword);
}
public static PublicKey loadEcdhPublicKeyFromPKCS12Keystore(String filename, char[] entryPassword, String keypairAlias, char[] keypairPassword) throws CertificateException, OperatorCreationException, KeyStoreException, IOException, NoSuchAlgorithmException, UnrecoverableKeyException {
// --- read PKCS#12 as file
KeyStore pkcs12 = KeyStore.getInstance("PKCS12");
try (FileInputStream p12 = new FileInputStream(filename)) {
pkcs12.load(p12, entryPassword);
}
// --- retrieve public key
Certificate cert = pkcs12.getCertificate(keypairAlias);
return cert.getPublicKey();
}
public static byte[] createEcdhSharedSecret(PrivateKey privateKey, byte[] publicKeyByte) throws NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException {
KeyAgreement keyAgree = KeyAgreement.getInstance("ECDH");
keyAgree.init(privateKey);
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyByte);
KeyFactory keyFactory = KeyFactory.getInstance("EC");
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
keyAgree.doPhase(publicKey, true);
return keyAgree.generateSecret();
}
private static X509Certificate createSelfSigned(KeyPair pair) throws OperatorCreationException, CertificateException {
// source: https://stackoverflow.com/a/50801856/8166854, author Maarten Bodewes
X500Name dnName = new X500Name("CN=publickeystorageonly");
BigInteger certSerialNumber = BigInteger.ONE;
Date startDate = new Date(); // now
Calendar calendar = Calendar.getInstance();
calendar.setTime(startDate);
calendar.add(Calendar.YEAR, 100); // 100 years validity
Date endDate = calendar.getTime();
ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithECDSA").build(pair.getPrivate());
JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(dnName, certSerialNumber, startDate, endDate, dnName, pair.getPublic());
return new JcaX509CertificateConverter().getCertificate(certBuilder.build(contentSigner));
}
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();
}
}
这是小输出:
Storing a ECDH Keypair in a PKCS12 Keystore
Java version: 11.0.6+8-b520.43 BouncyCastle Version: BC version 1.65
alice has a new keystore: alicekeystore.p12
bob has a new keystore: bobkeystore.p12
SharedSecret Bob: ab457b66687fcaefca6d648d428a66b1642355be6c8fb5190624043a7de2215c
SharedSecret Alice: ab457b66687fcaefca6d648d428a66b1642355be6c8fb5190624043a7de2215c
Compare aliceSharedSecret and bobSharedSecret: true
我在项目中使用了完整的示例解决方案,因此它可能对其他人有用。它使用EC-Keypair(曲线“ secp256r1”)和ECDH进行KeyExchange。您需要BouncyCastle并注意没有适当的异常处理,并且现有密钥库将被覆盖,恕不另行通知