Diffie-Hellman密钥协商算法探究

作者 | 魔王赵二狗

导读

隐私核算(Privacy-preserving computation)是指在确保数据提供方不走漏原始数据的前提下,对数据进行分析核算的一系列信息技术,确保数据在流通与融合进程中的可用不行见。而Diffie–Hellman密钥洽谈是一种安全协议。它能够让两边在彻底没有对方任何预先信息的条件下经过不安全信道创立起一个密钥。这个密钥能够在后续的通讯中作为对称秘钥讯内容。

全文6088字,估计阅读时间16分钟。

01 什么是DH密钥洽谈算法

1.1 DH由来

DH密钥洽谈是Whitefield与Martin Hellman在1976年提出了一个的密钥交流协议。

1.2 处理什么问题

首先咱们先看一个场景:

小明要在网络上给小红发一篇情书,小明呢,比较害臊,不想让其他人知道情书的内容。

那么显而易见,小明必须要对内容进行加密,这个时候就需求挑选加密的办法,咱们知道关于非对称加密对内容的长度是有限制的,而小明写的情书内容又十分多,那只好选用AES对称加密。

Diffie-Hellman密钥协商算法探究

咱们知道,AES对称加密和解密是需求密钥key的,那么咱们假定小明和小红之间需求传递密钥,那么怎么确保密钥key的安全性?这时候你或许会说,把密钥key用RSA非对称加密不就好了(数字信封的概念),但咱们是否有其他更好的办法处理问题?

这时候,DH密钥洽谈算法就应运而生,他处理的便是对称加密的密钥无需进行传输,并使小明、小红运用的AES密钥是一致的,那么这是怎么完结的呢。

1.3 完结原理

DH算法处理了密钥在两边不直接传递密钥的情况下完结密钥交流,这个神奇的交流原理彻底由数学理论支持。

咱们来看DH算法交流密钥的步骤。假设小明、小红两边需求传递密钥,他们之间能够这么做:

Diffie-Hellman密钥协商算法探究

  1. 小明首选挑选一个素数pp,例如:97,底数ggpp的一个原根,例如:5,随机数,例如:123,然后核算A=gaA=g^a ,然后,小红发送p=97p=97g=5g=5A=34A=34给小红;

  2. 小红收到后,也挑选一个随机数bb,例如:456,然后核算B=gamodp=75B=g^a mod p=75 ,小红再一起核算s2=Abmodp=22s2=A^b mod p=22

  3. 小红把核算的B=75B=75发给小明,小明核算s1=Bamodp=22s1=B^a mod p=22,核算成果与乙算出的成果相同,都是22。

所以终究两边洽谈出的密钥s=22s=22。注意到这个密钥ss并没有在网络上传输。而经过网络传输pp,ggAABB是无法推算出ss的,由于实践算法挑选的素数是十分大的。

所以,更确切地说,DH算法是一个密钥洽谈算法,两边终究洽谈出一个一起的密钥,而这个密钥不会经过网络传输,来确保了密钥的安全性。此时,小明和小红都露出了开心的笑脸。

02 公式推导

本着谨慎的情绪,现在咱们对s1s1s2s2是否恒等做公式推导。一般来说,书上是如下解说:

Diffie-Hellman密钥协商算法探究

这儿是运用了求余的运算规矩,也便是说以下等式默认是建立的:

Diffie-Hellman密钥协商算法探究

其实当成定理记住也就OK的,可是想要证明这个公式也很简单:将求余运算转换为加减乘除运算,然后使用二项式打开公式便能够得到答案。

推导进程:

Diffie-Hellman密钥协商算法探究

依据①②可得

Diffie-Hellman密钥协商算法探究

Diffie-Hellman密钥协商算法探究

将③带入上式,可得

Diffie-Hellman密钥协商算法探究

运用二项式打开公式将打开,则

Diffie-Hellman密钥协商算法探究

从这个表达式能够看出,前项每一项都是的整数倍,因而 运算必定为0,因而:

Diffie-Hellman密钥协商算法探究

所以

Diffie-Hellman密钥协商算法探究

建立。

03 使用完结

注:本示例以Java服务端作为小明,Android客户端作为小红,下图为履行次序。

Diffie-Hellman密钥协商算法探究

1.客户端建议恳求

获取服务端的p,g,serverNum

 // 获取服务器的p,g,serverNum
Request request = new Request.Builder()
        .get()
        .url("https://xxxxx/dh/getdhbasedata")
        .build();
Call call = mHttpClient.newCall(request);
Response res = call.execute();

2. 服务端创立信息

创立DHServer类

public class DHServer {
    /** 用来生成大素数p */
    private static final String SOURCE = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF";
    /** 大素数p */
    private BigInteger mP;
    /** 原根g */
    private BigInteger mG;
    /** 服务端随机数值 */
    private int mServerNum
}

DHServer中添加初始化办法:

 /**
 * 初始化p g serverNum 以及核算服务端的processedServerNum
 * @return HashMap<String, String> 回来初始化创立好的p g serverNum 以及核算服务端的processedServerNum
 */
public HashMap<String, String> init() {
    generateBaseInfo();
    HashMap<String, String> baseData = new HashMap<>();
    baseData.put("p", mP.toString());
    baseData.put("g", mG.toString());
    baseData.put("serverNumr", mServerNum + "");
    baseData.put("processedServerNum", processServerKey());
    return baseData;
}

DHServer中添加创立根底信息办法:

/**
 * 生成根底信息,p,g,服务端随机数serverNum
 */
private void generateBaseInfo () {
    // 第一步:依据pSource生成服务器当时固定的p
    BigInteger p = new BigInteger(SOURCE, 16);
    BigInteger tempP;
    BigInteger g;
    BigInteger gFlag;
    while (true) {
        tempP = p.subtract(new BigInteger("1"));
        // 取一个2-p中间的随机数
        g = getBigIntegerRandomRange(new BigInteger("2"), tempP);
        gFlag = g.modPow(tempP, p);
        if (gFlag.toString().equals("1")) {
            break;
        }
    }
    Random serverNumRd = new Random();
    this.mServerNum = serverNumRd.nextInt(100000) + 100;
    this.mG = g;
    this.mP = p;
}

DHServer中核算服务端的数值processedServerNum

/**
 * 回来已处理的服务端processedServerNum
 * @return processedServerNum
 */
private String processServerKey() {
    return mG.modPow((new BigInteger(mServerNum + "")), mP).toString();
}

3. 客户端收到信息后进行核算

接纳服务端数据

JSONObject data = new JSONObject(res.body().string());
String p = data.getString("p");
String g = data.getString("g");
String processedServerNum = data.getString("processedServerNum");

创立DHClient类并在结构办法中生成客户端随机数mClientNum

public class TiDHClient {
    private final int mClientNum;
    private BigInteger mP;
    private BigInteger mG;
    private BigInteger mProcessedServerNum;
    private BigInteger mProcessedClientNum;
    private BigInteger mKey;
    public TiDHClient() {
        mClientNum = new Random().nextInt(99999 - 10000) + 10000;
    }
}

DHClient添加核算办法,核算出密钥key与客户端核算值mProcessedClientNum

 /**
 * 经过服务端获取的 p, g 和processedServerNum核算密钥key.
 * @param p 经过服务端获取的 p
 * @param g 经过服务端获取的 g
 * @param serverNum 经过服务端获取的 server number
 * @return 密钥字符串
 */
public String processKey(String p, String g, String processedServerNum) {
    mP = new BigInteger(p);
    mG = new BigInteger(g);
    mProcessedServerNum = new BigInteger(processedServerNum);
    mProcessedClientNum = mG.modPow(new BigInteger(String.valueOf(mClientNum)), mP);
    // 核算密钥key
    mPublicKey = mServerNumber.modPow(new BigInteger(String.valueOf(mClientNum)), mP);
    return mPublicKey.toString();
}

DHClient中添加get办法

/**
 * 获取 processedClientNum. 用于发送给服务端.
 * 假如未调用 processKey 将回来空字符串.
 * @return processedClientNum.
 */
public String getProcessedClientNum() {
    if (mProcessedClientNum == null) {
        return "";
    }
    return  mProcessedClientNum.toString();
}
/**
 * 回来密钥字符串.
 * 假如未调用processKey 将回来空字符串
 * @return public key
 */
public String getKey() {
    if (mKey == null) {
        return "";
    }
    return mKey.toString();
}

4.客户端将processedClientNum核算成果给服务端

// 依据processedServerNum,processedClientNum和p 核算出密钥K
TiDHClient dhClient = new TiDHClient();
mClientKey = dhClient.processKey(p, g, serverNumber);
// 将核算过后的processedClientNum发送给服务器
FormBody formBody = new FormBody
        .Builder()
        .add("processedClientNum",dhClient.getProcessedClientNum())
        .build();
request = new Request.Builder()
        .post(formBody)
        .url("https://xxxxxxxxxx/dh/postdhclientdata")
        .build();
call = mHttpClient.newCall(request);
res = call.execute();
data = new JSONObject(res.body().string());

5.服务端核算密钥key

DHServer中添加核算办法

 /**
 * 依据客户端传过来的processedClientNum 核算出key
 * @param processedClientNum 客户端传过来的processedClientNum
 * @param serverNum 上一次恳求随机生成的serverNum
 * @param p 上一次恳求的 p
 * @return String 密钥key
 */
public String computeShareKey (String processedClientNum, String serverNumber, String p) {
    BigInteger BigClientNumber = new BigInteger(processedClientNum);
    return BigClientNumber.modPow(new BigInteger(serverNumber + ""), new BigInteger(p)).toString();
}

怎么样,看到这儿是否感觉这像是握手的进程,其实HTTPS的TLS1.3版别也引入了DH的概念来确保安全性。此外,p,g的生成还能够用RSA的公钥和私钥,这时候就会演变成DH-RSA算法。一起p,g的生成是放在服务端仍是放在客户端其实各有优缺点,我们能够考虑下。

学会这些,咱们也能够在事务中仿写数据传输的工具SDK,只要在初始化阶段进行洽谈,那么就能得到一个无法被抓包和破解的加密key,希望对我们的实践有所协助。

——END——

推荐阅读:

贴吧低代码高性能规矩引擎规划

浅谈权限体系在多利熊事务使用

分布式体系要害路径推迟分析实践

百度工程师教你玩转规划形式(装修器形式)

百度工程师带你体会引擎中的nodejs

揭秘百度智能测验在测验定位领域实践