Nodejs和C#使用ECDH算法交换秘钥

转载于:https://bkssl.com/document/nodejs-csharp-ecdh.html

nodejs的ECDH算法在进行computeSecret的时候不会自动进行HASH运算,但C#的ECDH算法必须指定HASH算法。

两边算法必须使用相同的椭圆曲线和Hash算法,例如下面用例都是用的SHA256。

双方交换公钥,计算得到共享秘钥:SharedSecret。

用例使用readline去交换公钥,实际一般运行在其他协议中,像SSH2等。

单纯秘钥交换并不安全,需要增加其他手段保证不被中间人监听、修改,例如ssh2使用serverhostkey对秘钥交换的数据进行签名。

nodejs测试代码

javascript 复制代码
import readline from 'readline';

import crypto  from 'crypto';

function computeHash(data, alg){
    const hashAlg = crypto.createHash(alg || 'SHA256');
    hashAlg.update(data);
    return hashAlg.digest();
}

const reader = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

const ecdh = crypto.createECDH('prime256v1');

const e = ecdh.generateKeys();

console.log("Local Public Key:" + e.toString('base64'));

reader.question('Please Input Remote PublicKey:', key => {
    console.log("Remote Public Key:" + key);
    let sharedSecret = ecdh.computeSecret(Buffer.from(key, 'base64'));

    //计算共享秘钥,需要先HASH运算,以兼容C#
    sharedSecret = computeHash(sharedSecret);
    console.log("Shared Secret: " + sharedSecret.toString('base64'));
})


// 控制台输出
// Local Public Key:BGLb5Sddo8N5sd1/+a7vhwb66QFMqAJtNFvEISecSzrBP3DjEr3R6vHeefI100yVDCnPQLNmgv56ampL+tNBc2c=
// Please Input Remote PublicKey:BASkBjbrTNysfXrcqyCOWuVgWVRF8a3VTufPLl99PFdY4+oj2d2kImu1OoZLMMoRKwul02gP27dWRoMCGicHjuQ=
// Remote Public Key:BASkBjbrTNysfXrcqyCOWuVgWVRF8a3VTufPLl99PFdY4+oj2d2kImu1OoZLMMoRKwul02gP27dWRoMCGicHjuQ=
// Shared Secret: EvhnVuNRa3dBqIFDyfSQfYmRF4/fPgJHDRZG5xB6HnA=

C#测试代码

cs 复制代码
var ecdh = ECDiffieHellmanHelper.New();
byte[] localPublicKey = ECDiffieHellmanHelper.CreateKeyExchange(ecdh);

Console.WriteLine("Local Public Key: {0}", localPublicKey.ToBase64String());


Console.WriteLine("Please Input Remote Public Key:");
string remotePublicKey = Console.ReadLine();

Console.WriteLine("Remote Public Key: {0}", remotePublicKey);

byte[] sharedSecret = ECDiffieHellmanHelper.DecryptKeyExchange(ecdh, remotePublicKey.AsBase64String());

Console.WriteLine("Shared Secret: {0}", sharedSecret.ToBase64String());

// 控制台输出
// Local Public Key: BASkBjbrTNysfXrcqyCOWuVgWVRF8a3VTufPLl99PFdY4+oj2d2kImu1OoZLMMoRKwul02gP27dWRoMCGicHjuQ=
// Please Input Remote Public Key:
// BGLb5Sddo8N5sd1/+a7vhwb66QFMqAJtNFvEISecSzrBP3DjEr3R6vHeefI100yVDCnPQLNmgv56ampL+tNBc2c=
// Remote Public Key: BGLb5Sddo8N5sd1/+a7vhwb66QFMqAJtNFvEISecSzrBP3DjEr3R6vHeefI100yVDCnPQLNmgv56ampL+tNBc2c=
// Shared Secret: EvhnVuNRa3dBqIFDyfSQfYmRF4/fPgJHDRZG5xB6HnA=

C#的ECDiffieHellmanHelper类实现

cs 复制代码
internal class ECPublicKey : ECDiffieHellmanPublicKey
{
    private readonly ECParameters parameters;

    public ECPublicKey(ECParameters parameters)
    {
        this.parameters = parameters;
    }

    public override ECParameters ExportParameters() => parameters;

    public override ECParameters ExportExplicitParameters() => parameters;

}

public class ECDiffieHellmanHelper
{
    //可以选择其他的曲线
    private static readonly ECCurve curve = ECCurve.NamedCurves.nistP256;


    public static ECDiffieHellman New(ECCurve curve) => ECDiffieHellman.Create(curve);
    public static ECDiffieHellman New() => ECDiffieHellman.Create(curve);
    public static byte[] CreateKeyExchange(ECDiffieHellman ecdh)
    {
        var args = ecdh.PublicKey.ExportParameters();

        int blockSize = args.Q.X.Length;
        byte[] buffer = new byte[blockSize * 2 + 1];
        buffer[0] = 0x4;
        args.Q.X.CopyTo(buffer, 1);
        args.Q.Y.CopyTo(buffer, blockSize + 1);

        return buffer;
    }

    public static byte[] DecryptKeyExchange(ECDiffieHellman ecdh, byte[] serverPublicKey)
    {
        ReadOnlySpan<byte> bytes = new(serverPublicKey);

        int length = (serverPublicKey.Length - 1) / 2;

        ECPublicKey thirdPublicKey = new (new ECParameters
        {
            Curve = curve,
            D = null,
            Q = new ECPoint
            {
                X = bytes.Slice(1, length).ToArray(),
                Y = bytes.Slice(1 + length, length).ToArray(),
            }
        });

        return ecdh.DeriveKeyFromHash(thirdPublicKey, HashAlgorithmName.SHA256);
    }
}
相关推荐
张人玉2 小时前
C# TCP 服务器和客户端
服务器·tcp/ip·c#
睡前要喝豆奶粉3 小时前
.NET Core Web API中数据库相关配置
数据库·c#·.netcore
周杰伦fans3 小时前
C# 中 Entity Framework (EF) 和 EF Core 里的 `AsNoTracking` 方法
开发语言·c#
西部森林牧歌3 小时前
Arbess零基础学习 - 使用Arbess+GitLab实现Node.js项目自动化构建/主机部署
ci/cd·node.js·gitlab·arbess·tiklab devops
她说彩礼65万4 小时前
C#设计模式 单例模式实现方式
单例模式·设计模式·c#
Aevget6 小时前
界面控件DevExpress WPF v25.1新版亮点:AI功能的全面升级
c#·.net·wpf·界面控件·devexpress·ui开发
Archy_Wang_17 小时前
Hangfire 入门与实战:在 .NET Core 中实现可靠后台任务处理
c#·.netcore
Smile_2542204187 小时前
nodered 下载 excel 文件
node.js·excel
二进制星轨9 小时前
在 Ubuntu 上快速配置 Node.js 环境(附问题说明)
linux·ubuntu·node.js
黄毛火烧雪下9 小时前
Node.js 自动替换脚本工具:一键完成多项目批量修改与资源替换
node.js·ci