有弹性的城堡ECDSA生成不正确的签名

时间:2019-03-05 16:04:55

标签: java cryptography bouncycastle signing ecdsa

我正在尝试使用曲线secp256k1和RFC6979的确定性K生成来实现ECDSA,以对Java中的Steem区块链进行交易签名。我不是加密向导,但据我了解,Bouncy Castle 1.5具有我需要的一切。

对于上下文,我在Linux Mint 18.2上使用openjdk 1.8.0_191。该项目的IDE是Intellij 2018.3.5社区版。我还使用javafx为我的应用程序制作GUI。

我正在通过JSON RPC将HTTPS请求发送到远程Steemd节点。从节点获取数据根本没有问题,但是我很难为广播操作事务(在这种情况下为表决操作)生成有效签名。签名在形式上显然有效,但是会解码为错误的公钥,因此节点不接受我的交易。

我正在遵循用python编写的指南,可以在here中找到它,使用的完整源代码是here。该指南虽然大约3岁。从那以后,我不确定该过程发生了什么变化(如果有的话),并且我在查找Steem的文档资源时遇到了麻烦。

这是我的Controller类中的相关代码,

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);

这是我尝试对数据进行签名的功能,

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];
    }
}

那个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;
}

0 个答案:

没有答案