1

I need to know what encoding

https://msdn.microsoft.com/en-us/library/bb347054(v=vs.110).aspx

ECDsaCng.SignData Method (Byte[])

is using for it's byte array by default and how to convert it to a DER format accepted by

https://www.openssl.org/docs/manmaster/crypto/i2d_ECDSA_SIG.html

so that I can verify my C# generated ECDSA signature in OpenSSL using the method ECDSA_do_verify.

I know its SHA1 and I know how to load that digest, I just don't know what the byte encoding is for the ECDsaCng.SignData method, nor how to convert it to DER format, if I even need to do that.

Martin Harrigan
  • 1,044
  • 11
  • 28
Akumaburn
  • 530
  • 1
  • 5
  • 17
  • You've mentioned DSA in C# and ECDSA in OpenSSL; which algorithm are you actually trying to use? They're not the same... – bartonjs Aug 01 '16 at 15:15
  • Ah sorry, ECDsaCng.SignData Method (Byte[]) is what mean.. – Akumaburn Aug 01 '16 at 15:22
  • Note also for anyone reading this the correct function is SignHash, as ECDSA_do_verify expects a known digest, and SignData will re-hash with whatever is the .NET default and this is what was causing me so much trouble. There is an implementation of OpenSSL verify using C# keys i've made in one of the questions under my profile you may also want to check out. Also this may be helpful: http://stackoverflow.com/questions/24251336/import-a-public-key-from-somewhere-else-to-cngkey – Akumaburn Aug 09 '16 at 20:29
  • 1
    Ah, I'd missed that subtlety; I thought you were using [ECDsa.SignData(byte[], HashAlgorithmName)](https://msdn.microsoft.com/en-us/library/mt591029(v=vs.110).aspx). The one you called uses whatever algorithm comes from ECDsaCng.HashAlgorithm. .net 4.6.1 moved SignData to the ECDsa base class and added the hash algorithm parameter. – bartonjs Aug 09 '16 at 20:34

2 Answers2

2

An ECDSA signature is the value-pair (r, s).

Windows CNG emits this as a concatenation of the two values, and since .NET does not reinterpret the data, this is the de facto .NET format. (r is the first half of the array, s is the second half)

OpenSSL expects a DER encoded structure of SEQUENCE(INTEGER(r), INTEGER(s)). This loosely means { 0x30, payload_length, 0x02, r_length, r_bytes[0]...r_bytes[r_length-1], 0x02, s_length, s_bytes[0]...s_bytes[s_length-1] }; though it's slightly trickier than that because padding bytes are required when r_bytes[0] or s_bytes[0] >= 0x80 (since that makes it appear as a negative number).

Unfortunately, there's not a general purpose DER encoder exposed in the framework by default.

If you're trying to run on Linux, you might be better served by using .NET Core, since the ECDSA implementation for Linux already does this data translation.

bartonjs
  • 30,352
  • 2
  • 71
  • 111
  • Is it exactly half of the array? that is are r and s equal in size? if not how do i determine when it(r or s) starts and ends? – Akumaburn Aug 05 '16 at 20:18
  • Well, .NET Core certainly interprets it as always equal in size (thus the blob should always have an even length). Since Windows also wouldn't have enough information to know if extra (or deficient) bytes were from r or s it seems quite logical that they use rigid output. – bartonjs Aug 05 '16 at 20:29
1

Here's TLSSigAPI.cs the code of generating sign with .Net and verifying with OpenSSL.

Maybe help you.

byte[] rawDataHash = SHA256(rawData);
byte[] rawSig = ecdsa.SignHash(rawDataHash);
int halfLength = rawSig.Length / 2;
byte[][] rEncoded = SegmentedEncodeUnsignedInteger(rawSig, 0, halfLength);
byte[][] sEncoded = SegmentedEncodeUnsignedInteger(rawSig, halfLength, halfLength);
List<byte[][]> items = new List<byte[][]>() { rEncoded, sEncoded };
byte[] opensslSig = ConstructSequence(items);
Wendell
  • 21
  • 3