如何解决DH公钥在服务端协商报参数不兼容的问题
=======================

#### 问题现象

HarmonyOS使用ArkTS生成的DH公钥，在服务端协商失败，报错Incompatible parameters。

class TestDh {
    public static void main(String\[\] args) throws Exception {
        _//_ _生成并初始化DH密钥_
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DH");
        keyPairGenerator.initialize(512);
        _/\* 服务端公私钥 \*/_
        KeyPair keyPair= keyPairGenerator.generateKeyPair();
        DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
        String serverPuk = Base64.getEncoder().encodeToString(publicKey.getEncoded());
        _//_ _HarmonyOS公钥_
        String pukClient = "替换为HarmonyOS的公钥";
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(pukClient));
        _//_ _根据DH算法获取KeyFactory_
        KeyFactory kf = KeyFactory.getInstance("DH");
        _//_ _通过KeyFactory创建公钥_
        PublicKey receivedPublicKey = kf.generatePublic(keySpec);
        _//_ _创建KeyAgreement对象_
        KeyAgreement keyAgreement = KeyAgreement.getInstance("DH");
        _//_ _初始化协议，设置我方密钥_
        DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
        keyAgreement.init(privateKey);
        _//_ _添加对方的公钥_
        keyAgreement.doPhase((DHPublicKey)receivedPublicKey, true);
        _//_ _生成协商密钥_
        byte\[\] sharedSecret = keyAgreement.generateSecret();
        _//_ _输出协商的密钥_
    }
}

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/3c/v3/VS0x-vEJTv6KCORPLMynUg/zh-cn_image_0000002658849153.png?HW-CC-KV=V1&HW-CC-Date=20260709T134200Z&HW-CC-Expire=86400&HW-CC-Sign=BE84C959B485B5B6603B76C615296E0464238EA0DB54C2F52E76D4B3C4ECAEB0 "点击放大")

#### 背景知识

[DH密钥协商算法](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/crypto-key-agreement-overview#dh)流程如下：

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/db/v3/0jsdf1ErT4KsXEZdG_BQ_Q/zh-cn_image_0000002628769786.png?HW-CC-KV=V1&HW-CC-Date=20260709T134200Z&HW-CC-Expire=86400&HW-CC-Sign=7F3E0843B5A4C86FD2BA6126FC397C1EE941A9BB44416952784B1B45024CFE5C "点击放大")

#### 定位思路

1.  **检查公钥：**
    
    从DH算法的生成流程看，最关键的核心是密钥对的生成和交换，首先检查公钥的解析是否正确，核心代码：
    
    _//_ _来自服务端的公钥serverPubString_
    let serverPubString ='替换下xxx';
    const serverPubArray: Uint8Array = new util.Base64Helper().decodeSync(serverPubString);
    
    检查方法：查看serverPubArray的值是否成功生成。
    
2.  **检查密钥：**
    
    检查密钥生成器的配置参数是否正确，即参数要写成“DH\_modp1536”。
    
    let keyGen = cryptoFramework.createAsyKeyGenerator('DH\_modp1536');
    
3.  **检查密钥协商传参是否传错：**
    
    公钥和私钥的前后顺序不能错。
    
    keyAgreement.generateSecret(clientPair.priKey, serverPubKey.pubKey)
    
    通过以上步骤分析，发现服务端代码，密钥位数为512，而HarmonyOS侧提高了安全性，要求最低密钥长度为1536，导致协商失败。
    

#### 分析结论

DH密钥交换算法，务必按照定位思路中的三个步骤，逐个检查是否合法，如果参数不合法，将出现协商失败的情况。

#### 修改建议

将服务端代码密钥长度设置为1536后重新协商，即keyPairGenerator的initialize参数为initialize(1536)。

协商成功的截图：

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/53/v3/KL14H8AGSZ67DuZj30BFxg/zh-cn_image_0000002628609890.png?HW-CC-KV=V1&HW-CC-Date=20260709T134200Z&HW-CC-Expire=86400&HW-CC-Sign=AB3653846E064EBE1C112433C2B80BC2EB756CF8BEA91BD849D4142215442162)

这里提供HarmonyOS密钥协商的正确代码实现：

import { util } from '@kit.ArkTS';
import { cryptoFramework } from '@kit.CryptoArchitectureKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

@Entry
@Component
struct Index {
  @State res: string = 'init value';

  build() {
    Column() {
      Button('启动测试')
        .onClick(() => {
          dhCreate().then((result: string) => {
            this.res = result;
          });
        })
      Text('Result:\\n' + this.res)
    }
    .height('100%')
    .width('100%')
  }
}

async function dhCreate(): Promise<string> {
  _// 生成客户端公私钥对_
  let keyGen = cryptoFramework.createAsyKeyGenerator('DH\_modp1536');
  let clientPair = await keyGen.generateKeyPair();
  let clientPub = clientPair.pubKey.getEncoded();
  let base64Helper = new util.Base64Helper();
  _// 传给服务端的公钥:toServerPuk_
  let toServerPuk = base64Helper.encodeToStringSync(clientPub.data);
  hilog.debug(1, 'Index', \`createKeyAgreement toServerPuk ${toServerPuk}\`);
  _// 来自服务端的公钥serverPubString，以下为模拟key，非真实值！！！_
  let serverPubString =
    ' ';
  const serverPubArray: Uint8Array = new util.Base64Helper().decodeSync(serverPubString);
  try {
    let serverPubKey = keyGen.convertKeySync({ data: serverPubArray }, null);
    let keyAgreement = cryptoFramework.createKeyAgreement('DH\_modp1536');
    _// 使用服务端的公钥和客户端的私钥进行密钥协商_
    return keyAgreement.generateSecret(clientPair.priKey, serverPubKey.pubKey).then((reslove) => {
      let strBase = new util.Base64Helper().encodeToStringSync(reslove.data);
      hilog.debug(1, 'Index', 'result is:' + strBase);
      return strBase;
    }).catch((err: BusinessError) => {
      hilog.error(1, 'Index', 'createKeyAgreement error', err);
      return 'createKeyAgreement error';
    });
  } catch (err) {
    hilog.error(1, 'Index', 'dhCreate error', err);
    return 'dhCreate error';
  }
}