存储Diffie-Hellman密钥对以在Java的KeyStore中重用

问题描述 投票:0回答:1

我目前正在编写一个加密的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();

现在要解决这个问题,有没有办法:

  1. 以不同的方式签名X509证书,以便可以存储Diffie-Hellman密钥对吗?
  2. 使用其他方法将Diffie-Hellman密钥对存储在KeyStore中吗?
  3. 将Diffie-Hellman密钥对安全地存储在KeyStore之外的其他位置吗?
  4. 或使用另一种方式的密钥交换协议以及将中间值存储在KeyStore中的要求?

我目前正在编写一个实现了密钥交换的加密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
java cryptography keystore x509 diffie-hellman
1个回答
0
投票

我在项目中使用了完整的示例解决方案,因此它可能对其他人有用。它使用EC-Keypair(曲线“ secp256r1”)和ECDH进行KeyExchange。您需要BouncyCastle并注意没有适当的异常处理,并且现有密钥库将被覆盖,恕不另行通知

© www.soinside.com 2019 - 2024. All rights reserved.