Search code examples
opensslrsabouncycastlesignecdsa

Openssl ECDSA sign input as-is - without digest


I am trying to sign an existing digest with openssl.

Let's say I already have a digest 'mydigest'. With that said I dont want to use:

echo -n "mydigest" | openssl dgst -sha256 -sign key.pem | openssl enc -A -base64

I have ECDSA not rsa so I assume I should not use rsautl which is using the input as-is.

So my assumption is that I would need something which takes input as-is (mydigest) and sign it with my ECDSA private key.

I tried following in order to see if the size of hash created with different hash algorithm has any affect on sign result:

echo -n "mydigest" | openssl pkeyutl -sign -inkey key.pem | openssl enc -A -base64

and

echo -n "my-very-very-very-long-digest" | openssl pkeyutl -sign -inkey key.pem | openssl enc -A -base64

but the size of the outputs is same for both commands in terms of output length. I would assume for the large my-very..-long-digest it should return larger output (because it should take the input as-is without shortening (hashing).

========================================

EDIT.

Maybe the example below will help to understand what I am asking. This is an example with bouncycastle.

 // sign something
String messageToSign = "something_to_sign";
ECDomainParameters domain = new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN());
ECDSASigner signer = new ECDSASigner();
signer.init(true, new ECPrivateKeyParameters(privateKey, domain));
MessageDigest digest = MessageDigest.getInstance("Keccak-256");
byte[] hash = digest.digest(messageToSign.getBytes(StandardCharsets.UTF_8));
BigInteger[] signature = signer.generateSignature(hash);

Let's assume I have following:

  1. hash.
  2. keys.

And now I want to create signature with openssl which should take the hash and keys as an input without creating the hash. Basically I want to replace

BigInteger[] signature = signer.generateSignature(hash);

in the example with openssl.

openssl * ????? *

I assumed the size of the results should indicate if the hash (with different digest size) algorithm I use for hash has any affect on results.


Solution

  • openssl pkeyutl -sign -inkey ecprivkey.pem is entirely correct.

    I would assume for the large my-very..-long-digest it should return larger output (because it should take the input as-is without shortening (hashing).

    You assumed wrongly. An ECDSA signature mathematically consists of two integers (r,s) in the range 1 to the order of the curve subgroup; it is completely unaffected by the size of the hash used as (or on) the input. It is recommended to use a hash whose size matches the subgroup -- e.g. SHA256 (or your Keccak256) with P-256 aka secp256r1 -- because otherwise if the hash is too large it is truncated or if too small it is padded and either reduces security.

    The same is true for DSA, and mostly for RSA -- for RSA the signature is always the size of the RSA key, and an encoded-and-padded hash smaller than the RSA key is padded, but a too-large one is rejected as an error. (This is very rare, because RSA keys below 2048 bits are no longer considered acceptably secure for use, and no one uses hashes that big.)

    The size of an ECDSA signature representation -- or encoding -- can vary and there are several different standards. OpenSSL always uses the ASN.1 SEQUENCE of INTEGERs shown in rfc3279 2.2.3. So do the standard SunEC and BouncyCastle providers in Java by default. The length of the ASN.1 encoding depends, usually only slightly, on the values of the two integers which as you correctly note are controlled by a random value (k) and thus effectively pseudo-random numbers themselves. See (ADDED) Java ECDSAwithSHA256 signature with inconsistent length and (cross) https://crypto.stackexchange.com/questions/33095/shouldnt-a-signature-using-ecdsa-be-exactly-96-bytes-not-102-or-103 and https://crypto.stackexchange.com/questions/44988/length-of-ecdsa-signature

    The Bouncy provider also supports '{hash}with{PLAIN-,CVC-}ECDSA' algorithms which do the same mathematical signature but use the simpler representation defined by P1363, simply two (unsigned bigendian) integers of fixed size (equal to the subgroup order size) concatenated. EDIT: And the SunEC provider since Java 9 does so with the different name '{hash}withECDSAinP1363format' (somehow I missed this on first writing). JWS also uses this representation. Finally, the Bouncy LWAPI which you showed returns or takes BigInteger[] -- the mathematical value -- and leaves the encoding and decoding up to you.