【ECDH java后端和javaScript前后端互通实现】

ECDH java后端和javaScript前后端互通实现

问题背景

web前端和后端之间的通信不一定可靠,如果不方便使用预共享密钥的方式完成身份的确认,那么可以使用ECDH密钥协商算法在前端和后端进行密钥间的协商。

ECDH算法

Elliptic-curve Diffie-Hellman(ECDH)是一种通过两方达成密钥协商的密钥生成算法,每一方都有一个椭圆双曲线的公私钥对,双方在不安全的通信通道间传递信息达成密钥生成协议。生成的密钥会被直接作为一个密钥或者是生成其他密钥的种子。最终生成的密钥将被对称密码学算法例如AESGCM作为密钥使用。
接下来的例子阐述了共享密钥是如何产生的:
Alice想和Bob建立一个共享密钥,但是唯一能够传递消息的通道被第三方监听。在初始时刻,Alice和Bob共享了些域的参数(p a,b,G,n,h),Alice在整数范围[t,n-1]中随机选择一个私钥d,并通过计算(Q=d·G)具体计算方式,得到了公钥Q,公私钥对为( d A d_A dA, Q A Q_A QA)。Bob用同样的计算方式得到了( d B d_B dB, Q B Q_B QB)。双方将自己的公钥发送给对方,分别计算:
( x k x_k xk, y k y_k yk)= d A d_A dA· Q B Q_B QB
( x k x_k xk, y k y_k yk)= d B d_B dB· Q A Q_A QA
( x k x_k xk, y k y_k yk)即为所得的私钥。
Alice仅仅公开了她的公钥信息,任何参与方想要计算出Alice的公钥需要解决椭圆曲线离散对数问题(这很难被计算出来)。因此我们认为Alice和Bob通过这样的方法协商密钥是安全的。除了Alice和Bob,没有谁能计算出他们协商的密钥。
在众多ECDH算法的计算标准中,Curve25519是使用最为广泛的密钥生成算法。

Java 实现

public class EcdhKeyPair{
    
    
private String privatekey;
private String publicKey;
public String getprivatekey(){
    
    
    return privatekey;
}
public String getPublicKey(){
    
    
    return publickey;
}
public class Ecdhservice{
    
    
private static final int ECDH_KEY_LEN= 32;

private static final int SHARED_KEY_LEN= 16;

private byte[] privatekey;

private byte[] publicKey;

private BigInteger pInteger;

private EcdhKeyPair ecdhKeyPair;

private static final SecureRandom RANDOM_GENERATOR;
static {
    
    
   try {
    
    
        RANDOM_GENERATOR = SecureRandom.getInstancestrong();
   } catch(NoSuchAlgorithmException e){
    
    
        log.error("SecureRandom init failed. ECDH encryption would be unavailable.");
EcdhService.");
        throw new InnerException(INTERNAL_ERROR, "Failed to init ECDHService.");
        }
   }
   private void generatetckeyPair() {
    
    
       privatekey = new byte[ECDH KEY_LEN];
       publicKey= new byte [ECDH KEY LEN];
       X25519.generatePrivateKey(RANDOM_GENERATOR, privateKey);
       X25519.generatePublickey(privatekey, 0, publicKey, 0);
   }
   private byte[] establisSharedKey(byte[] clientPublicKey){
    
    
       if (clientPublicKey.length != 32 ) {
    
    
          throw new InnerExecption(INVALID_REQUEST, "Client ECDH public key should be 256 bits/32 bytes");
       }
       BigInteger cInteger = new Biginteger(clientpublickey);
       if (cInteger.equals(BigInteger ZERO) || cInteger.equals(BigInteger.ONE) || cInteger.equals(pInteger)|| cInteger.equals(pInteger.subtract(BigInteges.ONE))){
    
    
          throw new InnerException(INVALID_REQUEST, "Client ECDH public key Cannot be specific value.");
         }
         try {
    
    
         byte[] agreement = new byte[ECDH_KEY_LEN];
         try {
    
    
           X25519.calculateAgreement(getprivatekey(), 0,clientPublickey,0, agreement, 0);
          }catch (RuntimetException e) {
    
    
            throw new InnerException(INVALID_REQUEST,
"Cannot calculate the agreement with client key, please check the request parameters.");
          }
          MessageDigest digester = MessageDigest.getInstance("SHA-256");
          return Arrays.copy0fRange(digester.digest(agreement),0, SHARED_KEY_LEN);
    } catch (NoSuchAIgorithmException e) {
    
    
         throw new InnerException(INTERNAL_ERROR,
"Failed to calculate shared key. " + e.getLocalizedMessage());
   }
}

前端JavaScript 实现

注意: 此处JavaScript代码没有将生成的sharedKey进行SHA256处理。

import {
    
     sharedkey, generateKeypair } from "curve25519-js"
* @description 生成公钥秘钥
* @param {
    
    Uint8Array} backPublickeyBase64 后端base64加密的的公钥
* @return 共享秘key:Uint&Arnay privatekey 私钥
*
export function generatekey(backPublicKeyBase64: string){
    
    
// 获取后端的公钥进行base64转成Uint8Array
const backPublickey=base64ToUint8Array(backPublicKeyBase64)
//符合密码学要求的安全的随机值 Uint8Array 32字节 8位无符号整型数组
const randomBytes = window.crypto.getRandomValues(new Uint8Array(32))
// 前端使用generatekeyPair得到自己的公钥和私钥,用自己前端的公钥去交换后端的公钥
const keyPair = generateKeyPair(randomBytes)
//将生成的公钥通过base64 加密发给后台
const publicKey =uint8arrayToBase64(keyPair.public)
// 根据后端的公钥用前端的松铜和后端的公钥生成前端的sharekey 
const sharekey = sharedkey(keypair.private, backPublicKey)
return {
    
    
  publicKey,
  shareKey
}

猜你喜欢

转载自blog.csdn.net/sinat_28199083/article/details/128280714