我有一个nodejs片段,我想用C#实现它.

const sha256 = require('sha256');
const EC = require('elliptic').ec;
const ec = new EC("secp256k1");

// Serialize a number into an 8-byte array. This is a copy/paste primitive, not worth
// getting into the details.
function uvarint64ToBuf (uint) {
    const result = [];

    while (uint >= 0x80) {
        result.push((uint & 0xFF) | 0x80);
        uint >>>= 7;
    }

    result.push(uint | 0);

    return new Buffer(result);
}

// Sign transaction with seed
function signTransaction (seed, txnHex) {
    const privateKey = ec.keyFromPrivate(seed);
    const transactionBytes = new Buffer(txnHex, 'hex');
    const transactionHash = new Buffer(sha256.x2(transactionBytes), 'hex');
    const signature = privateKey.sign(transactionHash);
    const signatureBytes = new Buffer(signature.toDER());
    const signatureLength = uvarint64ToBuf(signatureBytes.length);
    const signedTransactionBytes = Buffer.concat([
        transactionBytes.slice(0, -1),
        signatureLength,
        signatureBytes
    ])
    return signedTransactionBytes.toString('hex');
}

示例事务十六进制

01efa5060f5fdae5ed9d90e5f076b2c328a64e347d0c87e8e3317daec5a44fe7c8000102bd53e48625f49e60ff6b7a934e3871b54cc2a93a8737352e8320549e42e2322bab0405270000167b22426f6479223a2248656c6c6f20576f726c64227de807d461f4df9efad8a0f3f716002102bd53e48625f49e60ff6b7a934e3871b54cc2a93a8737352e8320549e42e2322b0000

javascript函数返回的签名事务十六进制示例

01efa5060f5fdae5ed9d90e5f076b2c328a64e347d0c87e8e3317daec5a44fe7c8000102bd53e48625f49e60ff6b7a934e3871b54cc2a93a8737352e8320549e42e2322bab0405270000167b22426f6479223a2248656c6c6f20576f726c64227de807d461f4df9efad8a0f3f716002102bd53e48625f49e60ff6b7a934e3871b54cc2a93a8737352e8320549e42e2322b00473045022100c36e2b80f2160304cf640b1296244e7a3873aacf2831098bca3727ad06f4c270022007d3697ceef266a05ad70219d92fbd570f6ec7a5731aaf02718213067d42d1cf

如果你注意到签名的十六进制是TransactionHex + 47 (length of signature) + 473045022100c36e2b80f2160304cf640b1296244e7a3873aacf2831098bca3727ad06f4c270022007d3697ceef266a05ad70219d92fbd570f6ec7a5731aaf02718213067d42d1cf (actual signature)

我试图使用Unity3d中的C#Bouncy Castle库生成签名部分,但没有成功.

这是C代码(source)

public string GetSignature(string privateKey, string message)
    {
        var curve = SecNamedCurves.GetByName("secp256k1");
        var domain = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H);
        var keyParameters = new ECPrivateKeyParameters(new BigInteger(privateKey, 16), domain);

        var signer = new ECDsaSigner(new HMacDsaKCalculator(new Sha256Digest()));
        signer.Init(true, keyParameters);

        var signature = signer.GenerateSignature(Encoding.UTF8.GetBytes(message));

        var r = signature[0];
        var s = signature[1];

        var otherS = curve.Curve.Order.Subtract(s);

        if (s.CompareTo(otherS) == 1)
        {
            s = otherS;
        }

        var derSignature = new DerSequence
        (
            new DerInteger(new BigInteger(1, r.ToByteArray())),
            new DerInteger(new BigInteger(1, s.ToByteArray()))
        )
        .GetDerEncoded();

        return Convert(derSignature);
    }

    public string Convert(byte[] input)
    {
        return string.Concat(input.Select(x => x.ToString("x2")));
    }

    public byte[] Convert(string input)
    {
        if (input.StartsWith("0x")) input = input.Remove(0, 2);

        return Enumerable.Range(0, input.Length / 2).Select(x => System.Convert.ToByte(input.Substring(x * 2, 2), 16)).ToArray();
    }

这确实会生成一个签名,但它与我上面提到的签名不同.

签名由GetSignature生成

3044022018a06b1f2b8a1e2f5ea2a78b0d6d98ec483b9fa4345821cfef892be6c825ff4702207958160e533c801dff5e50600206e1cd938d6df74ebfa4b0347a95de67dda986

P.S.Here相当于python对事务进行签名以供参考.

推荐答案

NodeJS代码中的sha256.x2()会散列两次,而C#代码中的ECDsaSigner根本不会散列.因此,在C#代码中,GetSignature()必须显式散列两次:

using Org.BouncyCastle.Crypto.Digests;
...
var messageBytes = Convert(message);
var hash = GetHash(GetHash(messageBytes));
var signature = signer.GenerateSignature(hash);
...

具有

private static byte[] GetHash(byte[] data)
{
    var digest = new Sha256Digest();
    var hash = new byte[digest.GetDigestSize()];
    digest.BlockUpdate(data, 0, data.Length);
    digest.DoFinal(hash, 0);
    return hash;
}

由于这两种代码都使用RFC6979规定的确定性ECDSA算法,因此相同输入数据的哈希相同,可以进行比较.

The overall result is the concatenation of txnHex 具有out the last byte, the hex encoded length of the signature and the signature in ASN.1/DER format; a possible implementation is:

var txnHex = "...";
var seed = "...";
string signature = GetSignature(seed, txnHex);
string signedTransactionBytes = txnHex.Substring(0, txnHex.Length - 2) + (signature.Length / 2).ToString("x") + signature;

请注意,uvarint64ToBuf()实现实际上更复杂,但可以根据此处使用的签名长度进行简化.

Csharp相关问答推荐

Microsoft. VisualBasic. FileIO. FileSystem. MoveFile()对话框有错误?

如何使用XmlSerializer反序列化字符串数组?

如何分配对象后的class的属性?

只有第一个LINQ.Count()语句有效

如何忽略API JSON响应中的空字符串?

使用HttpResponseMessage中的嵌套列表初始化JSON

ASP.NET Core AutoMapper:如何解决错误 CS0121调用在以下方法或属性之间不明确

CS1660无法将lambda表达式转换为类型INavigationBase,因为它不是委托类型

NET8 MAUI并部署到真实设备上进行测试

如何向事件添加成员

如果是,我怎么才能让这个加75,如果不是,我怎么才能减go 100?

将操作从编辑页重定向到带参数的索引页

使用CollectionView时在.NET Maui中显示数据时出现问题

如何在Akka.NET中重新启动执行元时清除邮箱

此异步方法在重写方法中缺少等待运算符警告

毛伊岛.NET 8图片不再适合按钮

具有嵌套属性的IGGroup

C#USB条形码 scanner 在第二次扫描时未写入行尾字符

GMap.NET中的暗模式映射

如何在Blazor 8 RC2静态模式下以编程方式导航到另一个页面