I having trouble verifying an ECDSA signature signed using client side javascript with Java/BouncyCastle.

The javascript signing function source:

sign: function (hash, priv) {
        var d = priv;
        var n = ecparams.getN();
        var e = BigInteger.fromByteArrayUnsigned(hash);

        do {
            var k = ECDSA.getBigRandom(n);
            var G = ecparams.getG();
            var Q = G.multiply(k);
            var r = Q.getX().toBigInteger().mod(n);
        } while (r.compareTo(BigInteger.ZERO) <= 0);

        var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n);

  return ECDSA.serializeSig(r, s);

serializeSig: function (r, s) {
        var rBa = r.toByteArrayUnsigned();
        var sBa = s.toByteArrayUnsigned();

        var sequence = [];
        sequence.push(0x02); // INTEGER
        sequence = sequence.concat(rBa);

        sequence.push(0x02); // INTEGER
        sequence = sequence.concat(sBa);

        sequence.unshift(0x30) // SEQUENCE

        return sequence;

The server side verify function source:

     * Verifies the given ASN.1 encoded ECDSA signature against a hash using the public key.
     * @param data      Hash of the data to verify.
     * @param signature ASN.1 encoded signature.
     * @param pub       The public key bytes to use.
    public static boolean verify(byte[] data, byte[] signature, byte[] pub) {
        ECDSASigner signer = new ECDSASigner();
        ECPublicKeyParameters params = new ECPublicKeyParameters(ecParams.getCurve().decodePoint(pub), ecParams);
        signer.init(false, params);
        try {
            ASN1InputStream decoder = new ASN1InputStream(signature);
            DERSequence seq = (DERSequence) decoder.readObject();
            DERInteger r = (DERInteger) seq.getObjectAt(0);
            DERInteger s = (DERInteger) seq.getObjectAt(1);
            return signer.verifySignature(data, r.getValue(), s.getValue());
        } catch (IOException e) {
            throw new RuntimeException(e);

Does anyone know what format the javascript signature is in (DER?) and if so how to convert it to ASN.1?

I tried:

public static byte[] toASN1(byte[] data) {
            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream(400);
                ASN1OutputStream encoder = new ASN1OutputStream(baos);

                return baos.toByteArray();
            } catch (IOException e) {
                throw new RuntimeException(e);  // Cannot happen, writing to memory stream.

but validation still fails.

  • 4
    $\begingroup$ This question appears to be off-topic because it ask about using/implementing a crypto-lib (here: BouncyCastle) in an application. Like the help of Crypto.SE states: “If your question is about using a cryptographic library (e.g. Crypto++, OpenSSL etc) and using it in your application, you probably need Stack Overflow. (A good indicator is the existence of source code in your question or most good answers).” $\endgroup$ – e-sushi Mar 4 '14 at 23:28

Disclaimer: I don't know Javascript and I do not practice BouncyCastle. However, I do know Java, and ASN.1.

ASN.1 is a notation for structured data, and DER is a set of rules for transforming a data structure (described in ASN.1) into a sequence of bytes, and back.

This is ASN.1, namely the description of the structure which an ECDSA signature exhibits:

ECDSASignature ::= SEQUENCE {
    r   INTEGER,
    s   INTEGER

When encoded in DER, this becomes the following sequence of bytes:

0x30 b1 0x02 b2 (vr) 0x02 b3 (vs)


  • b1 is a single byte value, equal to the length, in bytes, of the remaining list of bytes (from the first 0x02 to the end of the encoding);
  • b2 is a single byte value, equal to the length, in bytes, of (vr);
  • b3 is a single byte value, equal to the length, in bytes, of (vs);
  • (vr) is the signed big-endian encoding of the value "$r$", of minimal length;
  • (vs) is the signed big-endian encoding of the value "$s$", of minimal length.

"Signed big-endian encoding of minimal length" means that the numerical value must be encoded as a sequence of bytes, such that the least significant byte comes last (that's what "big endian" means), the total length is the shortest possible to represent the value (that's "minimal length"), and the first bit of the first byte specifies the sign of the value (that's "signed"). For ECDSA, the $r$ and $s$ values are positive integers, so the first bit of the first byte must be a 0; i.e. the first byte of (vr) (respectively (vs)) must have a value between 0x00 and 0x7F.

For instance, if we were to encode the numerical value 117, it would use a single byte 0x75, and not a two-byte sequence 0x00 0x75 (because of minimality). However, the value 193 must be encoded as two bytes 0x00 0xC1, because a single byte 0xC1, by itself, would denote a negative integer, since the first (aka "leftmost") bit of 0xC1 is a 1 (a single byte of value 0xC1 represents the value -63).

I insist on these details because my guess is that it is what the Javascript code does incorrectly. The Javascript code invokes a method called toByteArrayUnsigned; that name is evocative of conversion to an unsigned representation (i.e. always positive, even if the first bit is a 1), and that's wrong for DER. I invite you to save the raw signature in a file and decode it "by hand" to see if it matches the fine details of ASN.1 encoding, as explained above (the openssl asn1parse command may help, too).

(If my guess is right, you should have a probability of about 1/4 of getting a correct signature nonetheless -- when the values $r$ and $s$ happen to be short enough to have a high bit set to 0 when using unsigned encoding, i.e. a situation where the bug is benign.)

| improve this answer | |
  • $\begingroup$ Thank you for excellent answer. You were right on the money about validation passing every 1 in 4 times. I tried changing toByteArrayUnsigned to toByteArray but kept getting a DER decoding error. However if I pass the r & s values directly without using serialiseSig it validates correctly. $\endgroup$ – Ben Reeves Feb 2 '12 at 9:34
  • $\begingroup$ do you happen to know what is actually signed in TLS in the ServerKeyExchange? I'm guessing the server params in DER ( curve type | named curve | pubkey length | pubkey) $\endgroup$ – David 天宇 Wong May 12 '15 at 18:28
  • $\begingroup$ It is specified in the standard for DHE parameters, and in the other standard for ECDHE parameters. In the former case (DHE), it is the concatenation of the client random, the server random, and the encoded DH values (TLS encoding, not ASN.1/DER). With ECDHE, this is an encoding of the curve description and the server ECDH public value, this time without the client and server randoms. $\endgroup$ – Thomas Pornin May 12 '15 at 18:34
  • $\begingroup$ in python the numbers are written in big endian as well it seems $\endgroup$ – David 天宇 Wong May 12 '15 at 19:47
  • $\begingroup$ TLS signature is over the same scope; maybe you were thrown by a notation change from 1.0-and-1.1 to 1.2. 2246 and 4346 say Signature in ServerKeyExchange is digitally-signed of MD5 and SHA1 hashes with RSA or just SHA1 with DSA; either way hash input is the two randoms and ServerParams (which includes server pubkey). 4492 is based on 4346 and says digitally-signed{sha_hash} where sha_hash covers as above. 5246 added sender-chosen explicit hash into the digitally-signed construct in 4.7 so now 7.4.3 and 7.4.8 directly specify hash inputs. $\endgroup$ – dave_thompson_085 Jan 7 '16 at 22:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.