Java Bouncy Castle ECDSA生成不正确的签名

Java Bouncy Castle ECDSA生成不正确的签名,java,cryptography,bouncycastle,signing,ecdsa,Java,Cryptography,Bouncycastle,Signing,Ecdsa,我正在尝试使用curve secp256k1和RFC6979生成的确定性K来实现ECDSA,用于在java中为steem区块链签名事务。我不是密码巫师,但据我所知,Bouncy Castle 1.5拥有我所需要的一切 在上下文中,我在LinuxMint18.2上使用OpenJDK1.8.0191。本项目的IDE为Intellij 2018.3.5社区版。我还使用javafx为我的应用程序制作GUI 我正在通过JSON RPC向远程steemd节点发送HTTPS请求。从节点获取数据一点问题都没有,

我正在尝试使用curve secp256k1和RFC6979生成的确定性K来实现ECDSA,用于在java中为steem区块链签名事务。我不是密码巫师,但据我所知,Bouncy Castle 1.5拥有我所需要的一切

在上下文中,我在LinuxMint18.2上使用OpenJDK1.8.0191。本项目的IDE为Intellij 2018.3.5社区版。我还使用javafx为我的应用程序制作GUI

我正在通过JSON RPC向远程steemd节点发送HTTPS请求。从节点获取数据一点问题都没有,但我无法为广播操作事务生成有效的签名,在本例中是投票操作。签名在形式上显然是有效的,但解码为不正确的公钥,因此节点不接受我的事务

我遵循一个用python编写的指南,可以找到它,并且使用了完整的源代码。不过,导游大概3岁了。我不确定从那以后这个过程发生了什么变化(如果有的话),而且我很难找到steem的文档资源

这是我的控制器类中的相关代码

Digest sha256 = new SHA256Digest();//BouncyCastle SHA256 object
sha256.update(ser, 0, ser.length);//Ser is the serialized transaction, I'm fairly confident I serialize correctly
byte[] digest = new byte[32];//this byte[] holds the hash of ser
sha256.doFinal(digest, 0);
String wif = pwdPost.getText().trim();//Gets the private key, represented as wif string, from javafx password textbox
byte[] keybytes = Utils.wif2key(wif);//Utils is my own static class of helper functions, I'm fairly certain wif2key is correct too
byte[][] signout = Utils.signTransaction(digest , keybytes, sha256);//signTransaction is where I think the error is
byte[] r, s;//The rest is just putting the signature data into a string for the payload
r = Arrays.copyOfRange(signout[0], 0, signout[0].length);
s = Arrays.copyOfRange(signout[1], 0, signout[1].length);
byte[] sigbytes = new byte[r.length + s.length + 1];
sigbytes[0] = signout[2][0];
for (int i = 0; i < r.length; i++){
    sigbytes[i+1] = r[i];
}
for(int i = 0; i < s.length; i++){
    sigbytes[i+r.length+1] = s[i];
}
String sig = DatatypeConverter.printHexBinary(sigbytes);
Digest sha256=新的SHA256Digest()//BouncyCastle SHA256对象
更新(序列号,0,序列长度)//Ser是序列化的事务,我很有信心序列化正确
字节[]摘要=新字节[32]//此字节[]包含ser的哈希值
sha256.doFinal(摘要,0);
字符串wif=pwdPost.getText().trim()//从javafx密码文本框获取私钥,表示为wif字符串
byte[]keybytes=Utils.wif2key(wif)//Utils是我自己的静态助手函数类,我相当肯定wif2key也是正确的
byte[][]signout=Utils.signTransaction(摘要,keybytes,sha256)//signTransaction是我认为错误的地方
字节[]r,s//其余的只是将签名数据放入有效负载的字符串中
r=Arrays.copyOfRange(签出[0],0,签出[0]。长度);
s=Arrays.copyOfRange(签出[1],0,签出[1]。长度);
字节[]sigbytes=新字节[r.length+s.length+1];
sigbytes[0]=签出[2][0];
对于(int i=0;i
这是我尝试对数据签名的函数

public static byte[][] signTransaction(byte[] data, byte[] privateKey, Digest d) {
    try {
        byte[] r;
        byte[] s;
        BigInteger[] sig;
        LinkedList<byte[]> sigout = new LinkedList<byte[]>();
        Security.addProvider(new BouncyCastleProvider());
        ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
        do {
            HMacDSAKCalculator kmaker = new HMacDSAKCalculator(d);
            ECDSASigner ecdsaSigner = new ECDSASigner(kmaker);
            ECDomainParameters domain = new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN());
            ECPrivateKeyParameters privateKeyParms = new ECPrivateKeyParameters(new BigInteger(1, privateKey), domain);
            ParametersWithRandom params = new ParametersWithRandom(privateKeyParms);
            ecdsaSigner.init(true, params);
            sig = ecdsaSigner.generateSignature(data);
            r = sig[0].toByteArray();
            s = sig[1].toByteArray();
        }while(r.length != 32 || s.length != 32);
        byte[] publicKey = getPublicKey(privateKey);
        byte recoveryId = getRecoveryId(r, s, data, publicKey);
        for (BigInteger sigChunk : sig) {
            sigout.add(sigChunk.toByteArray());
        }
        sigout.add(new byte[]{(byte)(recoveryId + 31)});
        return sigout.toArray(new byte[][]{});
    } catch (Exception e) {
        StringWriter errors = new StringWriter();
        e.printStackTrace(new PrintWriter(errors));
        return new byte[0][0];
    }
}
公共静态字节[][]签名事务(字节[]数据,字节[]私钥,摘要d){
试一试{
字节[]r;
字节[]s;
biginger[]sig;
LinkedList sigout=新建LinkedList();
addProvider(新的BouncyCastleProvider());
ECNamedCurveParameterSpec=ECNamedCurveTable.getParameterSpec(“secp256k1”);
做{
HMacDSAKCalculator kmaker=新的HMacDSAKCalculator(d);
ECDSASigner ECDSASigner=新的ECDSASigner(kmaker);
ECDomainParameters域=新的ECDomainParameters(spec.getCurve()、spec.getG()、spec.getN());
ECPrivateKeyParameters privateKeyParms=新的ECPrivateKeyParameters(新的BigInteger(1,privateKey),域);
参数swithrandom params=新参数swithrandom(privateKeyParms);
ecdsaSigner.init(true,参数);
sig=ecdsaSigner.generateSignature(数据);
r=sig[0].toByteArray();
s=sig[1].toByteArray();
}而(r.length!=32 | | s.length!=32);
字节[]publicKey=getPublicKey(privateKey);
字节recoveryId=getRecoveryId(r、s、数据、公钥);
for(biginger-sigcunk:sig){
添加(sigcunk.toByteArray());
}
添加(新字节[]{(字节)(recoveryId+31)});
返回sigout.toArray(新字节[][]{});
}捕获(例外e){
StringWriter错误=新建StringWriter();
e、 printStackTrace(新的PrintWriter(错误));
返回新字节[0][0];
}
}
signTransaction函数调用代码中的另外两个函数。为了完整起见,我将包括它们以防万一,但我认为问题不在这里

public static byte[] getPublicKey(byte[] privateKey) {
    try {
        ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
        ECPoint pointQ = spec.getG().multiply(new BigInteger(1, privateKey));
        return pointQ.getEncoded(false);
    } catch (Exception e) {
        StringWriter errors = new StringWriter();
        e.printStackTrace(new PrintWriter(errors));
        return new byte[0];
    }
}
public static byte getRecoveryId(byte[] sigR, byte[] sigS, byte[] message, byte[] publicKey) {
    ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
    BigInteger pointN = spec.getN();
    for (int recoveryId = 0; recoveryId < 2; recoveryId++) {
        try {
            BigInteger pointX = new BigInteger(1, sigR);

            X9IntegerConverter x9 = new X9IntegerConverter();
            byte[] compEnc = x9.integerToBytes(pointX, 1 + x9.getByteLength(spec.getCurve()));
            compEnc[0] = (byte) ((recoveryId & 1) == 1 ? 0x03 : 0x02);
            ECPoint pointR = spec.getCurve().decodePoint(compEnc);
            if (!pointR.multiply(pointN).isInfinity()) {
                continue;
            }

            BigInteger pointE = new BigInteger(1, message);
            BigInteger pointEInv = BigInteger.ZERO.subtract(pointE).mod(pointN);
            BigInteger pointRInv = new BigInteger(1, sigR).modInverse(pointN);
            BigInteger srInv = pointRInv.multiply(new BigInteger(1, sigS)).mod(pointN);
            BigInteger pointEInvRInv = pointRInv.multiply(pointEInv).mod(pointN);
            ECPoint pointQ = ECAlgorithms.sumOfTwoMultiplies(spec.getG(), pointEInvRInv, pointR, srInv);
            byte[] pointQBytes = pointQ.getEncoded(false);
            boolean matchedKeys = true;
            for (int j = 0; j < publicKey.length; j++) {
                if (pointQBytes[j] != publicKey[j]) {
                    matchedKeys = false;
                    break;
                }
            }
            if (!matchedKeys) {
                continue;
            }
            return (byte) (0xFF & recoveryId);
        } catch (Exception e) {
            StringWriter errors = new StringWriter();
            e.printStackTrace(new PrintWriter(errors));
            //logger.error(errors.toString());
            continue;
        }
    }

    return (byte) 0xFF;
}
公共静态字节[]getPublicKey(字节[]privateKey){
试一试{
ECNamedCurveParameterSpec=ECNamedCurveTable.getParameterSpec(“secp256k1”);
ECPoint pointQ=spec.getG().multiply(新的BigInteger(1,privateKey));
返回点q.getEncoded(false);
}捕获(例外e){
StringWriter错误=新建StringWriter();
e、 printStackTrace(新的PrintWriter(错误));
返回新字节[0];
}
}
公共静态字节getRecoveryId(字节[]sigR,字节[]sigS,字节[]消息,字节[]公钥){
ECNamedCurveParameterSpec=ECNamedCurveTable.getParameterSpec(“secp256k1”);
BigInteger pointN=spec.getN();
对于(int-recoveryId=0;recoveryId<2;recoveryId++){
试一试{
BigInteger pointX=新的BigInteger(1,sigR);
X9IntegerConverter x9=新的X9IntegerConverter();
字节[]compEnc=x9.integerToBytes(pointX,1+x9.getbytellength(spec.getCurve());
compEnc[0]=(字节)((recoveryId&1)==1?0x03:0x02);
ECPoint pointR=spec.getCurve().decodePoint(compEnc);
if(!pointR.multiply(pointN).isInfinity()){
继续;
}
BigInteger pointE=新的BigInteger(1,消息);
biginger pointenv=biginger.ZERO.subtract(pointE.mod(pointN);
biginger pointRInv=新的biginger(1,sigR).modInverse(pointN);
biginger srInv=pointRInv.multiply(新的biginger(1,sigS)).mod(pointN);
BigInteger pointEInvRInv=pointRInv.multiply(pointEInv.mod)(pointN);
ECPoint pointQ=ECAlgorithms.sumOfTwoMultiplies(规范getG(),pointEInvRInv,pointR,srInv);
byte[]pointQBytes=pointQ.getEncoded(false);
布尔匹配键=真;
对于(int j=0;j