密码技术学习(6.2.1)-非对称加密在Java中的使用-DH(密钥交换算法)

版权声明:转载请注明出处 https://blog.csdn.net/laozhaishaozuo/article/details/81901455
  • 非对称加密在Java中的使用-DH(密钥交换算法)

之前介绍了对称加密在Java中使用,现在我们来介绍如何在Java中使用非对称加密。之前的文章中介绍过非对称加密的相关信息,如有疑问可以去阅读之前的相关文章。

DH(密钥交换算法)

在这里首先介绍一种算法DH(全称Diffie-Hellman,即密钥交换协议/算法),

它是由Whitfield Diffie与Martin Hellman在1976年提出的一个奇妙的密钥交换协议,这个机制的巧妙在于需要安全通信的双方可以用这个方法确定对称密钥,然后可以用这个密钥进行加密和解密。

它可以说是非对称加密算法的来源。

DHCoder工具类

import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * DH 密钥交换算法
 * 
 * @author shaozuo
 * @date 2018/07/30
 */
public class DHCoder {
    public static final String ALGORITHM_NAME = "DH";

    /**
     * 本地密钥算法<br>
     * 可选 DES、AES、DESede
     */
    public static final String SECRET_ALGORITHM = "DES";

    /**
     * 密钥长度 1024<br>
     * 必须是 64 的倍数,512~1024之间
     */
    private static final int KEY_SIZE = 1024;

    private static final String PUBLIC_KEY = "public_key";
    private static final String PRIVATE_KEY = "private_key";

    /**
     * 初始化甲方公钥材料
     * 
     * @return Map 甲方密钥map
     * @throws Exception
     */
    public static Map<String, Object> initKey() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM_NAME);
        keyPairGenerator.initialize(KEY_SIZE);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
        DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();

        Map<String, Object> keyMap = new HashMap<>();
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

    /**
     * 初始化乙方公钥材料
     * 
     * @param encodedKey
     *            甲方公密
     * @return Map 乙方密钥map
     * @throws Exception
     */
    public static Map<String, Object> initKey(byte[] encodedKey) throws Exception {
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_NAME);
        DHPublicKey publicKey4A = (DHPublicKey) keyFactory.generatePublic(keySpec);
        DHParameterSpec dhParameterSpec = publicKey4A.getParams();
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyFactory.getAlgorithm());
        keyPairGenerator.initialize(dhParameterSpec);

        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
        DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();

        Map<String, Object> keyMap = new HashMap<>();
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

    /**
     * 加密
     * 
     * @param data
     *            要加密的数据
     * @param key
     *            私钥
     * @return byte[] 解密数据
     * @throws Exception
     */
    public static byte[] encypt(byte[] data, byte[] key) throws Exception {
        SecretKey secretKey = new SecretKeySpec(key, SECRET_ALGORITHM);
        Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        return cipher.doFinal(data);
    }

    /**
     * 解密
     * 
     * @param data
     *            要解密的数据
     * @param key
     *            私钥
     * @return byte[] 原始
     * @throws Exception
     */
    public static byte[] decypt(byte[] data, byte[] key) throws Exception {
        SecretKey secretKey = new SecretKeySpec(key, SECRET_ALGORITHM);
        Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        return cipher.doFinal(data);
    }

    /**
     * 
     * @param encodedPublicKey
     * @param encodedPrivateKey
     * @return
     * @see http://www.oracle.com/technetwork/java/javase/8u161-relnotes-4021379.html#JDK-8185292
     * @throws Exception
     */
    public static byte[] getSecretKey(byte[] encodedPublicKey, byte[] encodedPrivateKey)
            throws Exception {
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedPublicKey);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_NAME);
        DHPublicKey publicKey4A = (DHPublicKey) keyFactory.generatePublic(keySpec);
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        KeyAgreement keyAgreement = KeyAgreement.getInstance(keyFactory.getAlgorithm());
        keyAgreement.init(privateKey);
        keyAgreement.doPhase(publicKey4A, true);

        SecretKey secretKey = keyAgreement.generateSecret(SECRET_ALGORITHM);
        return secretKey.getEncoded();
    }

    public static byte[] getPrivateKey(Map<String, Object> keyMap) {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return key.getEncoded();
    }

    public static byte[] getPublicKey(Map<String, Object> keyMap) {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return key.getEncoded();
    }
}

DHTester

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;

import java.util.Map;

import org.apache.commons.codec.binary.Base64;
import org.junit.Before;
import org.junit.Test;

public class DHTester {
    // 甲方公钥
    private byte[] publicKey4A;
    // 甲方私钥
    private byte[] privateKey4A;
    // 甲方本地密钥
    private byte[] secretKey4A;
    // 乙方公钥
    private byte[] publicKey4B;
    // 乙方私钥
    private byte[] privateKey4B;
    // 乙方本地密钥
    private byte[] secretKey4B;

    /**
     * 需要设置 -Djdk.crypto.KeyAgreement.legacyKDF=true
     * 
     * 
     * http://www.oracle.com/technetwork/java/javase/8u161-relnotes-4021379.html#JDK-8185292
     * 
     * @throws Exception
     */
    @Before
    public final void initKey() throws Exception {
        Map<String, Object> keyMap4A = DHCoder.initKey();
        publicKey4A = DHCoder.getPublicKey(keyMap4A);
        privateKey4A = DHCoder.getPrivateKey(keyMap4A);

        System.out.println("甲方公钥: " + Base64.encodeBase64String(publicKey4A));
        System.out.println("甲方私钥: " + Base64.encodeBase64String(privateKey4A));

        // 通过甲方公钥 产生乙方
        Map<String, Object> keyMap4B = DHCoder.initKey(publicKey4A);
        publicKey4B = DHCoder.getPublicKey(keyMap4B);
        privateKey4B = DHCoder.getPrivateKey(keyMap4B);

        System.out.println("乙方公钥: " + Base64.encodeBase64String(publicKey4B));
        System.out.println("乙方私钥: " + Base64.encodeBase64String(privateKey4B));

        secretKey4A = DHCoder.getSecretKey(publicKey4B, privateKey4A);
        System.out.println("甲方本地密钥: " + Base64.encodeBase64String(secretKey4A));
        secretKey4B = DHCoder.getSecretKey(publicKey4A, privateKey4B);
        System.out.println("乙方本地密钥: " + Base64.encodeBase64String(secretKey4B));

        assertArrayEquals(secretKey4A, secretKey4B);
    }

    @Test
    public void test() throws Exception {
        System.out.println("甲方向乙方发送加密数据");
        String input = "待加密数据";
        System.out.println("原文:" + input);

        byte[] encodeData = DHCoder.encypt(input.getBytes(), secretKey4A);
        System.out.println("加密数据: " + Base64.encodeBase64String(encodeData));

        byte[] decodeData = DHCoder.decypt(encodeData, secretKey4B);
        String output = new String(decodeData);
        System.out.println("解密数据:" + output);

        assertEquals(input, output);
    }
}

参考资料

关于本章内容,参考了一下书籍和文章

  1. Java加密与解密的艺术 链接
  2. Diffie-Hellman密钥协议算法
  3. 进阶阅读

本系列其他文章

密码技术学习系列文章

猜你喜欢

转载自blog.csdn.net/laozhaishaozuo/article/details/81901455