[JAVA加解密]DH算法

来源:互联网 发布:视音频切换矩阵 编辑:程序博客网 时间:2024/06/15 18:40

一、算法描述:

1.简介:

Diffie-Hellman:一种确保共享KEY安全穿越不安全网络的方法。Whitefield与Martin Hellman在1976年提出了一个奇妙的密钥交换协议,称为Diffie-Hellman密钥交换协议/算法(Diffie-Hellman Key Exchange/Agreement Algorithm).这个机制的巧妙在于需要安全通信的双方可以用这个方法确定对称密钥。然后可以用这个密钥进行加密和解密。但是注意,这个密钥交换协议/算法只能用于密钥的交换,而不能进行消息的加密和解密。双方确定要用的密钥后,要使用其他对称密钥操作加密算法实际加密和解密消息。

就是说,DH算法是用来安全地交换密钥的

2.算法描述:

基于原根的定义及性质,可以定义Diffie-Hellman密钥交换算法.该算法描述如下:
1,有两个全局公开的参数,一个素数q和一个整数a,a是q的一个原根.
2,假设用户A和B希望交换一个密钥,用户A选择一个作为私有密钥的随机数XA(XA<q),并计算公开密钥YA=a^XA mod q。A对XA的值保密存放而使YA能被B公开获得。类似地,用户B选择一个私有的随机数XB<q,并计算公开密钥YB=a^XB mod q。B对XB的值保密存放而使YB能被A公开获得.
3,用户A产生共享秘密密钥的计算方式是K = (YB)^XA mod q.同样,用户B产生共享秘密密钥的计算是K = (YA)^XB mod q.这两个计算产生相同的结果: K = (YB)^XA mod q = (a^XB mod q)^XA mod q = (a^XB)^XA mod q (根据取模运算规则得到) = a^(XBXA) mod q = (a^XA)^XB mod q = (a^XA mod q)^XB mod q = (YA)^XB mod q 因此相当于双方已经交换了一个相同的秘密密钥.
4,因为XA和XB是保密的,一个敌对方可以利用的参数只有q,a,YA和YB.因而敌对方被迫取离散对数来确定密钥.例如,要获取用户B的秘密密钥,敌对方必须先计算 XB = inda,q(YB) 然后再使用用户B采用的同样方法计算其秘密密钥K. Diffie-Hellman密钥交换算法的安全性依赖于这样一个事实:虽然计算以一个素数为模的指数相对容易,但计算离散对数却很困难.对于大的素数,计算出离散对数几乎是不可能的. 下面给出例子.密钥交换基于素数q = 97和97的一个原根a = 5.A和B分别选择私有密钥XA = 36和XB = 58.每人计算其公开密钥 YA = 5^36 = 50 mod 97 YB = 5^58 = 44 mod 97 在他们相互获取了公开密钥之后,各自通过计算得到双方共享的秘密密钥如下: K = (YB)^XA mod 97 = 44^36 = 75 mod 97 K = (YA)^XB mod 97 = 50^58 = 75 mod 97 从|50,44|出发,攻击者要计算出75很不容易. 下图给出了一个利用Diffie-Hellman计算的简单协议.


二、算法JAVA实现:

开始之前声明一下有一点还是不太明白:

假设甲方给乙方传数据,算法的实现要求乙方构建密钥时必须用甲方公钥构建乙方密钥。我自己是这样理解的:在实际实现的过程中,很难传递双方共享素数q及其元根α,因此采取这样的措施。

那么我们在接下的实现约定以下几个原则:

1.本地密钥:加密数据用的密钥

2.密钥=公钥+私钥,甲乙有自己的不相同的密钥,用来加密本地密钥

3.一方的密钥由Init()直接产生,另一方的密钥构建需用到已构建方的公钥


首先,对全局变量声明:

<span style="white-space:pre"></span>private static final String KEY_ALGORITHM = "DH";private static final String SECRET_ALGORITHM = "AES";private static final int KEY_SIZE=512;private static final String PUBLIC_KEY="DHPublicKey";private static final String PRIVATE_KEY="DHPrivateKey";

复习一下对称加密中产生密钥的三种方法:

产生secretKey的三种方法:a.凭空产生:KeyGenerator类对象获得SecretKey类对象:init()->generatorKey() b.通过byte[]数组产生:密钥工厂利用密钥材料生成SecretKey:secretKeyFactory.generateSecretKey(dks);c.通过byte[]数组产生:密钥材料类初始化成父类SecretKey


构建DH甲方密钥对:凭空产生:keyGenerator -> keyPair ->publicKey,privateKey. 最后用map对象返回,分别对应键: PUBLIC_KEY,PRIVATE_KEY

<span style="white-space:pre"></span>public static Map<String,Object> initKey() throws NoSuchAlgorithmException{KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);keyPairGenerator.initialize(KEY_SIZE);KeyPair keyPair = keyPairGenerator.generateKeyPair();PublicKey publicKey = keyPair.getPublic();PrivateKey privateKey = keyPair.getPrivate();Map<String,Object> keyMap = new HashMap<String,Object>(2);keyMap.put(PUBLIC_KEY, publicKey);keyMap.put(PRIVATE_KEY, privateKey);return keyMap;}

构建DH乙方密钥对:由甲方公钥产生:由byte[]->密钥材料->密钥工厂还原甲方公钥->->密钥材料DHParameterSpec->keyPariGenerator初始化成keyPairGenerator

就是一个byte[]->材料->密钥->材料->密钥;2.3步为还原甲密钥,4.5步产生乙密钥

至此得出一个结论:密钥共厂可以把密钥材料转换成密钥

*Spec这样的材料类可以把byte[]或密钥转成密钥材料

public static Map<String,Object> initKey(byte[] key) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidAlgorithmParameterException{//拿到传进来的公钥X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(key);KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);PublicKey pubKey = keyFactory.generatePublic(x509EncodedKeySpec);//Cannot make a static reference to the non-static method generatePublic(KeySpec) from the type KeyFactory//传进来的公钥构造密钥材料DHParameterSpec dhParameter = ((DHPublicKey)pubKey).getParams();//产生乙方公钥,私钥KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyFactory.getAlgorithm());keyPairGenerator.initialize(dhParameter);KeyPair keyPair = keyPairGenerator.genKeyPair();DHPublicKey publicKey = (DHPublicKey)keyPair.getPublic();DHPrivateKey privateKey = (DHPrivateKey)keyPair.getPrivate();Map<String,Object> keyMap = new HashMap<String,Object>(2);keyMap.put(PUBLIC_KEY, publicKey);keyMap.put(PRIVATE_KEY, privateKey);return keyMap;}

构建本地密钥代码:

<span style="white-space:pre"></span>public static byte[] getSecretKey(byte[] publicKey,byte[] privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException{//通过KeyFactory还原密钥,注意公私钥的材料类//byte[] ->密钥材料->密钥KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey);PublicKey pubKey = keyFactory.generatePublic(x509EncodedKeySpec);PKCS8EncodedKeySpec  pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey);PrivateKey priKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);//实例化KeyAgreement keyAgree = KeyAgreement.getInstance(KEY_ALGORITHM);keyAgree.init(priKey);keyAgree.doPhase(pubKey, true);SecretKey secretKey = keyAgree.generateSecret(SECRET_ALGORITHM);return secretKey.getEncoded();}

keyAgreement类实现公钥密码学产生本地密钥SecretKey

keyAgree,init(私钥)->doPhase(公钥,true)->generateSecretKey()


DHCoder实现:

public abstract class DHCoder {private static final String KEY_ALGORITHM = "DH";private static final String SECRET_ALGORITHM = "AES";private static final int KEY_SIZE=512;private static final String PUBLIC_KEY="DHPublicKey";private static final String PRIVATE_KEY="DHPrivateKey";public static Map<String,Object> initKey() throws NoSuchAlgorithmException{KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);keyPairGenerator.initialize(KEY_SIZE);KeyPair keyPair = keyPairGenerator.generateKeyPair();PublicKey publicKey = keyPair.getPublic();PrivateKey privateKey = keyPair.getPrivate();Map<String,Object> keyMap = new HashMap<String,Object>(2);keyMap.put(PUBLIC_KEY, publicKey);keyMap.put(PRIVATE_KEY, privateKey);return keyMap;}public static Map<String,Object> initKey(byte[] key) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidAlgorithmParameterException{//拿到传进来的公钥X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(key);KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);PublicKey pubKey = keyFactory.generatePublic(x509EncodedKeySpec);//Cannot make a static reference to the non-static method generatePublic(KeySpec) from the type KeyFactory//传进来的公钥构造密钥材料DHParameterSpec dhParameter = ((DHPublicKey)pubKey).getParams();//产生乙方公钥,私钥KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyFactory.getAlgorithm());keyPairGenerator.initialize(dhParameter);KeyPair keyPair = keyPairGenerator.genKeyPair();DHPublicKey publicKey = (DHPublicKey)keyPair.getPublic();DHPrivateKey privateKey = (DHPrivateKey)keyPair.getPrivate();Map<String,Object> keyMap = new HashMap<String,Object>(2);keyMap.put(PUBLIC_KEY, publicKey);keyMap.put(PRIVATE_KEY, privateKey);return keyMap;}public static byte[] encrypt(byte[] data,byte[]key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{SecretKey secretKey = new SecretKeySpec(key,SECRET_ALGORITHM);Cipher cipher = Cipher.getInstance(SECRET_ALGORITHM);cipher.init(Cipher.ENCRYPT_MODE, secretKey);return cipher.doFinal(data);}public static byte[] decrypt(byte[] data,byte[]key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{SecretKey secretKey = new SecretKeySpec(key,SECRET_ALGORITHM);Cipher cipher = Cipher.getInstance(SECRET_ALGORITHM);cipher.init(Cipher.DECRYPT_MODE, secretKey);return cipher.doFinal(data);}public static byte[] getSecretKey(byte[] publicKey,byte[] privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException{//通过KeyFactory还原密钥,注意公私钥的材料类//byte[] ->密钥材料->密钥KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey);PublicKey pubKey = keyFactory.generatePublic(x509EncodedKeySpec);PKCS8EncodedKeySpec  pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey);PrivateKey priKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);//实例化KeyAgreement keyAgree = KeyAgreement.getInstance(KEY_ALGORITHM);keyAgree.init(priKey);keyAgree.doPhase(pubKey, true);SecretKey secretKey = keyAgree.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();}}


三、DH算法测试用例:

测试两个东西:

1.甲乙方本地密钥应相同;

2.加解密能否互相实现;

public class DHCoderTest {private byte[] publicKey1;private byte[] privateKey1;private byte[] key1;private byte[] publicKey2;private byte[] privateKey2;private byte[] key2;@Beforepublic final void initKey() throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidAlgorithmParameterException, InvalidKeyException{Map<String,Object> keyMap1 = DHCoder.initKey();publicKey1 = DHCoder.getPublicKey(keyMap1);privateKey1 = DHCoder.getPrivateKey(keyMap1);System.err.println("甲方公钥:\n"+Base64.encodeBase64String(publicKey1));System.err.println("甲方私钥:\n"+Base64.encodeBase64String(privateKey1));//*甲方公钥产生乙方公私钥Map<String,Object> keyMap2 = DHCoder.initKey(publicKey1);publicKey2 = DHCoder.getPublicKey(keyMap2);privateKey2 = DHCoder.getPrivateKey(keyMap2);System.err.println("乙方公钥:\n"+Base64.encodeBase64String(publicKey2));System.err.println("乙方私钥:\n"+Base64.encodeBase64String(privateKey2));key1 = DHCoder.getSecretKey(publicKey2, privateKey1);key2 = DHCoder.getSecretKey(publicKey1, privateKey2);System.err.println("甲方本地密钥:\n"+Base64.encodeBase64String(key1));System.err.println("乙方本地密钥:\n"+Base64.encodeBase64String(key2));assertArrayEquals(key1,key2);}@Testpublic final void test() throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {System.err.println("\n=====甲方向乙方发送加密数据=====");String input1="密钥交换算法";System.err.println("原文: "+input1);System.err.println("---使用甲方密钥对本地数据加密---");byte[] code1=DHCoder.encrypt(input1.getBytes(), key1);System.err.println("加密: "+Base64.encodeBase64String(code1));System.err.println("---使用乙方密钥对本地数据解密---");byte[] decode1 = DHCoder.decrypt(code1, key2);String output1= new String(decode1);System.err.println("解密:  "+output1);assertEquals(input1,output1);System.err.println("\n=====乙方向甲方发送加密数据=====");String input2="DH";System.err.println("原文: "+input2);System.err.println("---使用乙方密钥对本地数据加密---");byte[] code2=DHCoder.encrypt(input2.getBytes(), key2);System.err.println("加密: "+Base64.encodeBase64String(code2));System.err.println("---使用甲方密钥对本地数据解密---");byte[] decode2 = DHCoder.decrypt(code2, key1);String output2= new String(decode2);System.err.println("解密:  "+output2);assertEquals(input2,output2);}

输出:

甲方公钥:
MIHgMIGXBgkqhkiG9w0BAwEwgYkCQQD8poLOjhLKuibvzPcRDlJtsHiwXt7LzR60ogjzrhYXrgHzW5Gkfm32NBPF4S7QiZvNEyrNUNmRUb3EPuc3WS4XAkBnhHGyepz0TukaScUUfbGpqvJE8FpDTWSGkx0tFCcbnjUDC3H9c9oXkGmzLik1Yw4cIGI1TQ2iCmxBblC+eUykAgIBgANEAAJBAJv7XJrlaOYCIPZ1vZc2Ol5GnvYQNWWcoeNPjNbzPW5keT5p1rl3mwLJdDnOI1Qwh9pw3vmQirNsBMhacKvu0I4=
甲方私钥:
MIHSAgEAMIGXBgkqhkiG9w0BAwEwgYkCQQD8poLOjhLKuibvzPcRDlJtsHiwXt7LzR60ogjzrhYXrgHzW5Gkfm32NBPF4S7QiZvNEyrNUNmRUb3EPuc3WS4XAkBnhHGyepz0TukaScUUfbGpqvJE8FpDTWSGkx0tFCcbnjUDC3H9c9oXkGmzLik1Yw4cIGI1TQ2iCmxBblC+eUykAgIBgAQzAjEA9dZOTVebQERSfblRNYIRob8g6g24V2DINCnEXwJTlYVdHIV5PLyjbWzn48eb2NIg
乙方公钥:
MIHgMIGXBgkqhkiG9w0BAwEwgYkCQQD8poLOjhLKuibvzPcRDlJtsHiwXt7LzR60ogjzrhYXrgHzW5Gkfm32NBPF4S7QiZvNEyrNUNmRUb3EPuc3WS4XAkBnhHGyepz0TukaScUUfbGpqvJE8FpDTWSGkx0tFCcbnjUDC3H9c9oXkGmzLik1Yw4cIGI1TQ2iCmxBblC+eUykAgIBgANEAAJBAJZ304pW1u5XRkEgyasNNV3Vy74yPPjTCNU5tbqbSnGDKKsBhjagSoMdJ21HiAPeU0NFIeZdPPYI2vu55ETQJOA=
乙方私钥:
MIHSAgEAMIGXBgkqhkiG9w0BAwEwgYkCQQD8poLOjhLKuibvzPcRDlJtsHiwXt7LzR60ogjzrhYXrgHzW5Gkfm32NBPF4S7QiZvNEyrNUNmRUb3EPuc3WS4XAkBnhHGyepz0TukaScUUfbGpqvJE8FpDTWSGkx0tFCcbnjUDC3H9c9oXkGmzLik1Yw4cIGI1TQ2iCmxBblC+eUykAgIBgAQzAjEA2jSKn/s21Yirb9HBIxrH2l9B6HI59s7Nvk5FOafpr0VqpigbdFV6fLUDURUUWOdb
甲方本地密钥:
gJyxRUpxSSxl6ibLr6LfIs9rTr7k9XxFKwhGqaOFy5U=
乙方本地密钥:
gJyxRUpxSSxl6ibLr6LfIs9rTr7k9XxFKwhGqaOFy5U=


=====甲方向乙方发送加密数据=====
原文: 密钥交换算法
---使用甲方密钥对本地数据加密---
加密: VqM0WkF0M8j1UURMOd9QRg==
---使用乙方密钥对本地数据解密---
解密:  密钥交换算法


=====乙方向甲方发送加密数据=====
原文: DH
---使用乙方密钥对本地数据加密---
加密: faq8AO4Joy1s6k6/k3JVUw==
---使用甲方密钥对本地数据解密---
解密:  DH


这里选择的密钥长度是512位,若选择1024位就更长了

DH算法支持密钥长度为64倍数,512~1024.长度与安全强度正比,运算时间反比

对称加密算法可选择DES,DESede,AES 等

合理选择密钥长度及算法是构建DH算法密码系统关键。


四、System.out.println()与System.err.println();

System.out.println();是标准输出,输出黑色
System.err.println();是标准错误输出,输出红色


默认的话都是写往控制台,从操作系统的实现上讲
标准输出 一般有默认有缓存
标准错误输出 一般没有缓存


System.out.println 能重定向到别的输出流,这样的话你在屏幕上将看不到打印的东西了,
而System.err.println只能在屏幕上实现打印,即使你重定向了也一样。

0 0
原创粉丝点击