Java 加解密技術系列之 DH

概念

 

DH,全稱爲「Diffie-Hellman」,他是一種確保共享KEY安全穿越不安全網絡的方法,也就是常說的密鑰一致協議。由公開密鑰密碼體制的奠定人Diffie和Hellman所提出的一種思想。java

 

簡單的說就是容許兩名用戶在公開媒體上交換信息以生成「一致」的、能夠共享的密鑰。也就是由甲方產出一對密鑰(公鑰、私鑰),乙方依照甲方公鑰產生乙方密鑰對(公鑰、私鑰)。算法

 

以此爲基線,做爲數據傳輸保密基礎,同時雙方使用同一種對稱加密算法構建本地密鑰(SecretKey)對數據加密。這樣,在互通了本地密鑰(SecretKey)算法後,甲乙雙方公開本身的公鑰,使用對方的公鑰和剛纔產生的私鑰加密數據,同時可使用對方的公鑰和本身的私鑰對數據解密。不僅僅是甲乙雙方兩方,能夠擴展爲多方共享數據通信,這樣就完成了網絡交互數據的安全通信!數組

 

 

原理

 

  • 通訊方A和通訊方B約定一個初始數g,g是公開的,如g=5;
  • A生成一個隨機數a,a是保密的,如a=6;
  • A計算g^a發送給B,g^a=5^6;
  • B生成一個隨機數b,b是保密的,如b=15;
  • B計算g^b發送給A,g^b=5^15;
  • A接收到g^b後,再使用保密的a,計算(g^b)^a=g^(a*b)=5^(6*15) ;
  • B接收到g^a後,再使用保密的b,計算(g^a)^b=g^(a*b)=5^(6*15);
  • 這樣通訊方A和B獲得一個相同的「密鑰」g^(a*b)=5^(6*15)。

 

整個通訊過程當中g、g^a、g^b是公開的,但因爲g、a、b都是整數,經過g和g^a獲得a仍是比較容易的,b也是如此,因此最終的「密鑰」g^(a*b)仍是能夠被計算出來的。因此實際的過程還須要在基本原理上加入新的計算——模運算。安全

 

  • 通訊方A和通訊方B約定一個初始數g,如g=5,一個質數p,如p=23,g和p是公開的 ;
  • A生成一個隨機數a,a是保密的,如a=6 ;
  • A計算g^a%p發送給B,g^a%p=5^6%23=8 ;
  • B生成一個隨機數b,b是保密的,如b=15 ;
  • B計算g^b%p發送給A,g^b%p=5^15%23=19 ;
  • A接收到g^b%p後,再使用保密的a,計算(g^b%p)^a%p=19^6%23=2 ;
  • B接收到g^a%p後,再使用保密的b,計算(g^a%p)^b%p=8^15%23=2 ;
  • 這樣通訊方A和B獲得一個相同的密鑰:2。

 

(g^b%p)^a%p=(g^a%p)^b%p 證實:網絡

 

若是a=2:工具

 

  • (g^b%p)^a%p=(g^b%p)^2%p=(g^b-n*p)^2%p=(g^(2*b)-2*g^b*n*p+(n*p)^2)%p=g^(2*b)%p ;
  • 能夠看出(g^b-n*p)^2展開後除g^(2*b)外,其它都是p的倍數,因此整個算式的結果是g^(2*b)%p ;
  • 同理對(g^b-n*p)^a展開後除g^(a*b)外,其它都是p的倍數,因此整個算式的結果是g^(a*b)%p ;
  • 一樣能夠得出(g^a%p)^b%p=g^(a*b)%p ;
  • 因此(g^b%p)^a%p=(g^a%p)^b%p 。

 

整個通訊過程當中g、p、g^a%p、g^b%p是公開的,這時經過g、p、g^a%p獲得a比較難,一樣經過g、p、g^b%p獲得b比較難,因此最終的密鑰是比較安全的。測試

 

以g=五、p=2三、g^a%p=8計算a爲例,a=log(5, (8+23*n)),這個只能將n的可能值逐個帶入公式試驗才能獲得a的值。若是a、p是比較大的數那麼計算更加困難。優化

 

須要注意的是,爲了防止應用優化算法計算上述問題,質數p不是隨便選擇的,須要符合必定的條件。隨機數a、b的生成算法也必需注意,應使結果儘量隨機,不能出現可預測的規律,不然會使破解變的容易。google

package com.test.dh;

import com.google.common.collect.Maps;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.*;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Map;

/**
 * Created by xiang.li on 2015/3/4.
 * DH 加解密工具類
 */
public class DH {
    /**
     * 定義加密方式
     */
    private static final String KEY_DH = "DH";
    /**
     * 默認密鑰字節數
     */
    private static final int KEY_SIZE = 1024;
    /**
     * DH加密下須要一種對稱加密算法對數據加密,這裏咱們使用DES,也可使用其餘對稱加密算法
     */
    private static final String KEY_DH_DES = "DES";
    private static final String KEY_DH_PUBLICKEY = "DHPublicKey";
    private static final String KEY_DH_PRIVATEKEY = "DHPrivateKey";

    /**
     * 初始化甲方密鑰
     * @return
     */
    public static Map<String, Object> init() {
        Map<String, Object> map = null;
        try {
            KeyPairGenerator generator = KeyPairGenerator.getInstance(KEY_DH);
            generator.initialize(KEY_SIZE);
            KeyPair keyPair = generator.generateKeyPair();
            // 甲方公鑰
            DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
            // 甲方私鑰
            DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
            map = Maps.newHashMap();
            map.put(KEY_DH_PUBLICKEY, publicKey);
            map.put(KEY_DH_PRIVATEKEY, privateKey);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * 初始化乙方密鑰
     * @param key 甲方密鑰
     * @return
     */
    public static Map<String, Object> init(String key) {
        Map<String, Object> map = null;
        try {
            // 解析甲方密鑰
            byte[] bytes = decryptBase64(key);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
            KeyFactory factory = KeyFactory.getInstance(KEY_DH);
            PublicKey publicKey = factory.generatePublic(keySpec);

            // 由甲方公鑰構建乙方密鑰
            DHParameterSpec spec = ((DHPublicKey) publicKey).getParams();
            KeyPairGenerator generator = KeyPairGenerator.getInstance(KEY_DH);
            generator.initialize(spec);
            KeyPair keyPair = generator.generateKeyPair();
            // 乙方公鑰
            DHPublicKey dhPublicKey = (DHPublicKey) keyPair.getPublic();
            // 乙方私鑰
            DHPrivateKey dhPrivateKey = (DHPrivateKey) keyPair.getPrivate();
            map = Maps.newHashMap();
            map.put(KEY_DH_PUBLICKEY, dhPublicKey);
            map.put(KEY_DH_PRIVATEKEY, dhPrivateKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * DH 加密
     * @param data 帶加密數據
     * @param publicKey 甲方公鑰
     * @param privateKey 乙方私鑰
     * @return
     */
    public static byte[] encryptDH(byte[] data, String publicKey, String privateKey) {
        byte[] bytes = null;
        try {
            // 生成本地密鑰
            SecretKey secretKey = getSecretKey(publicKey, privateKey);
            // 數據加密
            Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            bytes = cipher.doFinal(data);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }
        return bytes;
    }

    /**
     * DH 解密
     * @param data 待解密數據
     * @param publicKey 乙方公鑰
     * @param privateKey 甲方私鑰
     * @return
     */
    public static byte[] decryptDH(byte[] data, String publicKey, String privateKey) {
        byte[] bytes = null;
        try {
            // 生成本地密鑰
            SecretKey secretKey = getSecretKey(publicKey, privateKey);
            // 數據解密
            Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            bytes = cipher.doFinal(data);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }
        return bytes;
    }

    /**
     * 取得私鑰
     * @param map
     * @return
     */
    public static String getPrivateKey(Map<String, Object> map) {
        String str = "";
        try {
            Key key = (Key) map.get(KEY_DH_PRIVATEKEY);
            str = encryptBase64(key.getEncoded());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return str;
    }

    /**
     * 取得公鑰
     * @param map
     * @return
     */
    public static String getPublicKey(Map<String, Object> map) {
        String str = "";
        try {
            Key key = (Key) map.get(KEY_DH_PUBLICKEY);
            str = encryptBase64(key.getEncoded());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return str;
    }

    /**
     * 構建本地密鑰
     * @param publicKey 公鑰
     * @param privateKey 私鑰
     * @return
     */
    private static SecretKey getSecretKey(String publicKey, String privateKey) {
        SecretKey secretKey = null;
        try {
            // 初始化公鑰
            byte[] publicBytes = decryptBase64(publicKey);
            KeyFactory factory = KeyFactory.getInstance(KEY_DH);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
            PublicKey localPublicKey = factory.generatePublic(keySpec);

            // 初始化私鑰
            byte[] privateBytes = decryptBase64(privateKey);
            PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privateBytes);
            PrivateKey localPrivateKey = factory.generatePrivate(spec);

            KeyAgreement agreement = KeyAgreement.getInstance(factory.getAlgorithm());
            agreement.init(localPrivateKey);
            agreement.doPhase(localPublicKey, true);

            // 生成本地密鑰
            secretKey = agreement.generateSecret(KEY_DH_DES);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return secretKey;
    }

    /**
     * BASE64 解密
     * @param key 須要解密的字符串
     * @return 字節數組
     * @throws Exception
     */
    public static byte[] decryptBase64(String key) throws Exception {
        return (new BASE64Decoder()).decodeBuffer(key);
    }

    /**
     * BASE64 加密
     * @param key 須要加密的字節數組
     * @return 字符串
     * @throws Exception
     */
    public static String encryptBase64(byte[] key) throws Exception {
        return (new BASE64Encoder()).encodeBuffer(key);
    }

    /**
     * 測試方法
     * @param args
     */
    public static void main(String[] args) {
        // 生成甲方密鑰對
        Map<String, Object> mapA = init();
        String publicKeyA = getPublicKey(mapA);
        String privateKeyA = getPrivateKey(mapA);
        System.out.println("甲方公鑰:\n" + publicKeyA);
        System.out.println("甲方私鑰:\n" + privateKeyA);

        // 由甲方公鑰產生本地密鑰對
        Map<String, Object> mapB = init(publicKeyA);
        String publicKeyB = getPublicKey(mapB);
        String privateKeyB = getPrivateKey(mapB);
        System.out.println("乙方公鑰:\n" + publicKeyB);
        System.out.println("乙方私鑰:\n" + privateKeyB);

        String word = "abc";
        System.out.println("原文: " + word);

        // 由甲方公鑰,乙方私鑰構建密文
        byte[] encWord = encryptDH(word.getBytes(), publicKeyA, privateKeyB);

        // 由乙方公鑰,甲方私鑰解密
        byte[] decWord = decryptDH(encWord, publicKeyB, privateKeyA);
        System.out.println("解密: " + new String(decWord));

    }
}
相關文章
相關標籤/搜索