androidsignatureverificationecdsaspongycastle

Why ECDSA verification fails in Android using Spongy Castle?


I am trying to verify a signature in Android using spongy castle. This dependency has been added to gradle as:

implementation group: 'com.madgag.spongycastle', name: 'bcpkix-jdk15on', version: '1.58.0.0'

When running below code in Android Flamingo I always get false for verified with different public keys, and signatures. But all of them are valid and verified according to this site. Here is my Android code (I need to use secp256k1 and hex values for key, message, and signature):

String publicKeyHex = "04954ed5969f7d4cc2baa1f0345f037a4758e84058d202feb64885c32dfc62123af4c8ccfba16088fc9cc2bd84594d810ad470706cfddfc17999d291b038f4c9f1";
String messageHex   = "12ca8b3b17607820279c7e0095d655d9ce289e90cccc0e99ae00f5e29f8d6615";
String signatureHex = "3045022100affcf495d11d02fcde6f9cd502975b78dd18b4b50127edef56683ca8534575cf02206f3d92bb26d515d122754c88e92c1d7d8bfc2b35876608b9c75ee9cbcd6fdde9";

byte[] publicKeyBytes = hexStringToByteArray(publicKeyHex);
byte[] messageBytes   = hexStringToByteArray(messageHex);
byte[] signatureBytes = hexStringToByteArray(signatureHex);

final ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
final ECNamedCurveSpec params = new ECNamedCurveSpec("secp256k1", spec.getCurve(), spec.getG(),spec.getN());

KeyFactory keyFactory = KeyFactory.getInstance("EC");
ECPoint ecPoint = ECPointUtil.decodePoint(params.getCurve(), publicKeyBytes);
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(ecPoint, params);
ECPublicKey publicKey = (ECPublicKey) keyFactory.generatePublic(pubKeySpec);

Signature signature = Signature.getInstance("Sha256withECDSA");
signature.initVerify(publicKey);
signature.update(messageBytes);
boolean verified = signature.verify(signatureBytes);

and this is the definition of hexStringToByteArray method:

public static final byte[] hexStringToByteArray(final String str) {
    int len = str.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i += 2)
        data[i / 2] = (byte) ((Character.digit(str.charAt(i), 16) << 4) + Character.digit(str.charAt(i+1), 16));
    return data;
}

what's wrong with this code?


Solution

  • The verification is successful if the ASCII (or UTF-8) encoding of messageHex is applied and not the hex decoded messageHex, i.e. with

    byte[] messageBytes = messageHex.getBytes(StandardCharsets.US_ASCII);
    

    verification is successful.

    This means that the ASCII encoding and not the hex decoding of messageHex was signed.