zoukankan      html  css  js  c++  java
  • 廖雪峰Java10加密与安全-4加密算法-4密钥交换算法

    1DH算法

    1.1.原根公式:g^i mod P 条件:1<g<P,0<i<P

    原根:介于[1, p-1]之间的任意2个数i,j(p为素数,i≠j)的结果不相等,即
    g^i mod p ≠ g^j mod p ,则g为p的原根。
    同余式:正整数a,b对p取模,它们的余数相同,记做 或者a ≡ b (mod p)
    示例:模为7
    设a= 2,由于2^3=8≡1(mod 7),26=64≡1(mod7),23≡2^6(mod7),所以 2 不是模 7 的一个原根。
    设a= 3,由于3^1≡3(mod 7),3^2≡2(mod 7),3^3≡6(mod 7),3^4≡4(mod 7),3^5≡5(mod 7),3^6≡1(mod 7),所以 3 是模 7 的一个原根。
    总之:

    • 假设一个数g是P的原根,那么g^i mod P的结果两两不同
    • g^(P-1)≡1 (mod P)等同于 g^(p-1) mod P = 1当且仅当指数为P-1的时候成立.(这里P是素数)。

    1.2.素数:大于1的自然数

    1.3Diffie-Hellman算法

    基于原根的定义及性质,可以定义PH密钥交换算法

    • 1.有2个全部公开的参数,一个素数q和一个整数a,a是q的一个原根。
    • 2.假设用户A和B希望交换一个密钥,用户A选择一个作为私有密钥的随机数XA(XA < q),并计算公开密钥 YA = a^XA mod q。用户A对XA的值保密,而使YA能被B公开获得。
      类似用户B选择一个私有的随机数XB(XB < q),并计算公开密钥 YB = a^XB mod q。B对XB的值保密,而是YB能被A公开获得。
    • 3.用户A产生共享密钥的计算方式是:KA = (YB)^XA mod q。同样B产生共同密钥的计算方式是KB = (YA)^XB mod q。K值是相同的。
    KA  = (YB)^XA mod q 
        = (a^XB mod q)^XA mod q //把a替换为n*q+x:((n*q+x)^XB mod q)^XA mod q = (x^XB)^XA mod q = x^XA^XB。
        //内外层都存在mod q,内层去除mod q,则为(n*q+x)^XB mod q,最终结果依然为x^XB^XA,而外层存在mod q,因此内层增加、去除mod q不会对结果造成影响。内层去除mod q后,为(a^XB)^XA mod q
        = (a^XB)^XA mod q
        =(a^XA)^XB mod q
        =(a^XA mod q)^XB mod q
        =YA^XB mod q =KB
    

    2.密钥交换算法

    在使用对称加密算法的时候,加减密使用的是同一个密钥,以AES加密为例,当加密明文,需要使用一个随机生成的key作为密钥进行加减密。
    encrypt(key,message) -> s
    decrypt(key,s)->message
    问题:如何传递密钥?
    不给对方密钥,对方就不能解密;而直接传递密钥,会被黑客监听。
    所以问题变成了:如果在不安全的信道上安全的传递密钥?
    密钥交换算法,Differ-Hellman算法,就是为了解决这个问题的。最后的K就是密钥,而K并未在网上传输。

    #注意,python脚本
    #!/user/env/python3
    #coding:utf-8
    class DH():
    	def get(self,base,factorial,divider):
    		return base ** factorial % divider
    	#XA为随机数,获取YA
    	def getData(self,a,q,XA,XB):
    		YA = self.get(a,XA,q)
    		print("甲->乙传递:",a,q,YA)
    		#XB为随机数,获取YB
    		YB = self.get(a,XB,q)
    		print("乙->甲传递:",YB)
    		KA=self.get(YB,XA,q)
    		KB=self.get(YA,XB,q)
    		print("K值对比:",KA==KB,KA,KB)
    sh = DH()
    sh.getData(5,509,123,456)
    
    甲乙双方拥有各自的私钥,并生成公钥返回给对方。而根据DH原理,使用对方的公钥和自己的私钥生成的K值是相同的。

    3.代码示例

    package com.testList;
    
    import javax.crypto.Cipher;
    import javax.crypto.KeyAgreement;
    import javax.crypto.SecretKey;
    import java.io.IOException;
    import java.math.BigInteger;
    import java.security.*;
    import java.security.spec.X509EncodedKeySpec;
    import java.util.Base64;
    
    public class Person {
        public final String name;//发送方、接受方的名字
        public PublicKey publicKey;//公钥
        private PrivateKey privateKey;//私钥
        private SecretKey secretKey;//密钥
        public Person(String name){
            this.name=name;
        }
        public void generateKeyPair(){//生成公钥和私钥
            try{
                KeyPairGenerator kpGen = KeyPairGenerator.getInstance("DH");//创建一个KeyPairGenerator,用于DH算法
                kpGen.initialize(512);//创建一个512位的KeyPair
                KeyPair kp = kpGen.generateKeyPair();//通过generateKeyPair()获取一个KeyPair对象
                this.privateKey = kp.getPrivate();//通过getPrivate()获取私钥
                this.publicKey = kp.getPublic();//通过getPublic()获取公钥
            }catch (GeneralSecurityException e){
                throw new RuntimeException(e);
            }
        }
        public void generateSecretKey(byte[] receviedPubKeyBytes){//当通信双方把public
            try{
                X509EncodedKeySpec keySpec = new X509EncodedKeySpec(receviedPubKeyBytes);//从收到的公钥数组恢复公钥
                KeyFactory kf = KeyFactory.getInstance("DH");
                PublicKey receviedPublicKey = kf.generatePublic(keySpec);
                //生成本地密钥
                KeyAgreement keyAgreement = KeyAgreement.getInstance("DH");
                keyAgreement.init(this.privateKey);//传入自己的私钥
                keyAgreement.doPhase(receviedPublicKey,true);//传入接受到的公钥
                this.secretKey = keyAgreement.generateSecret("AES");//生成AES密钥
            }catch (GeneralSecurityException e){
                throw new RuntimeException(e);
            }
        }
        public void printKeys(){//将用户名、公钥、私钥、密钥全部打印
            System.out.printf("Name:%s
    ",this.name);
            System.out.printf("Private key:%s
    ",new BigInteger(1,this.privateKey.getEncoded()));
            System.out.printf("Public key:%s
    ",new BigInteger(1,this.publicKey.getEncoded()));
            System.out.printf("SecretKey:%s
    ",new BigInteger(1,this.secretKey.getEncoded()));
        }
        public String sendMessage(String message){//测试AES加密
            try{
                Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
                cipher.init(Cipher.ENCRYPT_MODE,this.secretKey);
                byte[] data = cipher.doFinal(message.getBytes("utf-8"));
                return Base64.getEncoder().encodeToString(data);
            }catch (GeneralSecurityException|IOException e){
                throw new RuntimeException(e);
            }
        }
        public String receivedMessage(String message){//接受消息,并解密
            try{
                Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
                cipher.init(Cipher.DECRYPT_MODE,this.secretKey);
                byte[] data = cipher.doFinal(Base64.getDecoder().decode(message));
                return new String(data,"utf-8");
            }catch (GeneralSecurityException|IOException e){
                throw new RuntimeException(e);
            }
        }
    }
    
    package com.testList;
    
    
    public class DH {
        public static void main(String[] args){
            //创建2个Person对象,作为发送方、接收方
            Person bob = new Person("Bob");
            Person alice = new Person("Alice");
            //生成各自的keyPair
            bob.generateKeyPair();
            alice.generateKeyPair();
            //双方交换各自的publickey,并生成自己的本地密钥
            bob.generateSecretKey(alice.publicKey.getEncoded());
            alice.generateSecretKey(bob.publicKey.getEncoded());
            //检查双方的本地密钥是否相同
            bob.printKeys();
            alice.printKeys();
            //发送
            String msgBobToAlice = bob.sendMessage("Hello,Alice");
            System.out.println("Bob->Alice:"+msgBobToAlice);
            //接受
            String aliceDecrypted = alice.receivedMessage(msgBobToAlice);
            System.out.println("Alice decrypted:"+aliceDecrypted);
        }
    }
    

    4.总结:

    • DH算法是一种密钥交换协议,通信双方通过不安全的信道协商密钥,然后进行对称加密传输
    • DH算法没有解决中间人攻击问题。
      * 如果黑客假冒乙与甲交换密钥,同时冒充甲与乙交换密钥,就可以成功的进行攻击。
  • 相关阅读:
    windows 7下matlab R2010a输入乱码的解决方案
    用 Microsoft Visual C++ 创建一个使用 wpcap.dll 的应用程序,
    E: oss4dkms: 子进程 脚本出错postinstallation 安装升级更新时出错的解决方法
    关于linux下面挂载Windows硬盘,但是无法在Windows下看到数据
    如何读取多个文件,文件后缀名不一致,不过类似source.1 source.2 source.3等
    Fedora 12 13 14基础环境配置
    linux内核空间与用户空间信息交互方法
    HDU 1232 畅通工程(最小生成树+并查集)
    hdu 2647 Reward(拓扑排序,反着来)
    HDU 1532 Drainage Ditches (最大网络流)
  • 原文地址:https://www.cnblogs.com/csj2018/p/10861597.html
Copyright © 2011-2022 走看看