ECDSA signature Java vs Go

风流意气都作罢 提交于 2019-12-06 07:24:31

Managed to get this to work. So just to document it for myself and anyone interested..

As pointed by in comments, the signature from Java is in ASN1 format. Found a nice description of the format here: https://crypto.stackexchange.com/questions/1795/how-can-i-convert-a-der-ecdsa-signature-to-asn-1.

I also found some good examples on how to do SHAxx with ECDSA in Go at https://github.com/gtank/cryptopasta (sign.go and sign_test.go). Just need to run the relevant SHA function before the ECDSA code.

Found example code for building the public keys from parameters in Go at http://codrspace.com/supcik/golang-jwt-ecdsa/.

I paste the relevant code below, if someone finds an issue, please let me know..

Relevant Java code:

public static PublicKey bytesToPublicKey(byte[] x509key) throws GeneralSecurityException {
    X509EncodedKeySpec spec = new X509EncodedKeySpec(x509key);
    KeyFactory factory = KeyFactory.getInstance("EC");
    ECPublicKey publicKey = (ECPublicKey) factory.generatePublic(spec);
    //We should be able to use these X and Y in Go to build the public key
    BigInteger x = publicKey.getW().getAffineX();
    BigInteger y = publicKey.getW().getAffineY();
    System.out.println(publicKey.toString());
    return publicKey;
}

//we can either use the Java standard signature ANS1 format output, or just take the R and S parameters from it, and pass those to Go
//https://stackoverflow.com/questions/48783809/ecdsa-sign-with-bouncycastle-and-verify-with-crypto
public static BigInteger extractR(byte[] signature) throws Exception {
    int startR = (signature[1] & 0x80) != 0 ? 3 : 2;
    int lengthR = signature[startR + 1];
    return new BigInteger(Arrays.copyOfRange(signature, startR + 2, startR + 2 + lengthR));
}

public static BigInteger extractS(byte[] signature) throws Exception {
    int startR = (signature[1] & 0x80) != 0 ? 3 : 2;
    int lengthR = signature[startR + 1];
    int startS = startR + 2 + lengthR;
    int lengthS = signature[startS + 1];
    return new BigInteger(Arrays.copyOfRange(signature, startS + 2, startS + 2 + lengthS));
}

public static byte[] signMsg(String msg, PrivateKey priv) throws Exception {
    Signature ecdsa = Signature.getInstance("SHA1withECDSA");

    ecdsa.initSign(priv);

    byte[] strByte = msg.getBytes("UTF-8");
    ecdsa.update(strByte);

    byte[] realSig = ecdsa.sign();

    //this is the R and S we could also pass as the signature
    System.out.println("R: "+extractR(realSig));
    System.out.println("S: "+extractS(realSig));

    return realSig;
}

Relevant Go code:

func verifyMySig(pub *ecdsa.PublicKey, msg string, sig []byte) bool {
    //https://github.com/gtank/cryptopasta
    digest := sha1.Sum([]byte(msg))

    var esig ecdsaSignature
    asn1.Unmarshal(sig, &esig)
    //above is ASN1 decoding from the Java format. Alternatively, we can just transfer R and S parameters and set those
    //  esig.R.SetString("89498588918986623250776516710529930937349633484023489594523498325650057801271", 0)
    //  esig.S.SetString("67852785826834317523806560409094108489491289922250506276160316152060290646810", 0)
    fmt.Printf("R: %d , S: %d", esig.R, esig.S)
    println()
    return ecdsa.Verify(pub, digest[:], esig.R, esig.S)
}

func hexToPrivateKey(hexStr string)  *ecdsa.PrivateKey {
    bytes, err := hex.DecodeString(hexStr)
    print(err)

    k := new(big.Int)
    k.SetBytes(bytes)
    println("k:")
    fmt.Println(k.String())

    priv := new(ecdsa.PrivateKey)
    curve := elliptic.P256()
    priv.PublicKey.Curve = curve
    priv.D = k
    priv.PublicKey.X, priv.PublicKey.Y = curve.ScalarBaseMult(k.Bytes())
    //we can check these against the Java implementation to see if it matches to know key was transferred OK
    fmt.Printf("X: %d, Y: %d", priv.PublicKey.X, priv.PublicKey.Y)
    println()

    return priv
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!