views:

339

answers:

2

Here is the setup:

I create public/private key pair with .NET, I want to sign a string. I take a random string, get a byte[] from it, sign it, and take the signature in the java app. I want to verify it in java ( (!)I'm talking about Android's java).

The process to take the public key to Java environment: When I create the public key, I take the byte arrays for the public key (P, Q, G, Y) and create PublicKey in Java with those values. P, Q, G, Y in .NET are byte[], I convert them to sbyte[] and use these sbyte[] in Java, creating big integers:

byte[] byteP = new byte[] { -34, ...... -117 };

...

BigInteger p = new BigInteger(1,byteP);

...

new DSAPublicKeySpec(y, p, q, g);

To test the process, I take the signature byte[] from C#, convert it to sbyte[] and then use it in Java.

The problem is, I cannot verify the signature string later. I got

java.security.SignatureException: signature bytes have invalid encoding

Any ideas appreciated! (Like, a better, entirely different way to do the whole thing ;) )

A: 

Not sure if I am throwing you off on a goose chase here, but (Sun's!) BigInteger uses the 1 you pass in the constructor as the signum of the number - so it could have an impact on the resulting signature calculation ... I've had issues with this in the past using RSA ...

nahojkap
Yes, I know. Without passing the signum to BigInteger(), the app throws exception "bad p". I assume that it must be positive, but I'm not sure that this is the way I should convert the byte[] c# is giving me in Java.
Danail
+1  A: 

A DSA signature is actually two numbers and there is no real standard for how to format this as a bytearray.

Java chooses to encode it as the DER-encoding of an ASN.1-sequence containing two ASN.1-integers.

.NET chooses to prepend zeroes to the two numbers so they are exactly 20 bytes long and concatenate them.

To convert from .NET to Java format do something like this (untested, but should be mostly correct):

public byte[] ConvertToDsaSignatureToJavaEncoding(byte[] dsa){
  if(dsa.Length!=40)
    throw new ArgumentException("dsa", "DSA signature should always be 40 bytes long");
  // Split into r and s.
  byte[] r = new byte[20];
  Array.Copy(dsa, 0, r, 0, 20);
  byte[] s = new byte[20];
  Array.Copy(dsa, 20, s, 0, 20);

  // Convert to complement-2
  byte[] complementTwoR = ToComplementTwo(r);
  byte[] complementTwoS = ToComplementTwo(s);

  // Build the result
  byte[] res = new byte[complementTwoR.Length + complementTwoS.Length + 6];
  // Sequence{
  res[0] = 0x30;
  res[1] = (byte) (complementTwoR.Length + complementTwoS.Length + 4);
  // Integer (R)
  res[2] = 0x02;
  res[3] = (byte) complementTwoR.Length;
  Array.Copy(complementTwoR, 0, res, 4, complementTwoR.Length);
  // Integer (S)
  res[complementTwoR.Length + 4] = 0x02;
  res[complementTwoR.Length + 5] = (byte) complementTwoS.Length;
  Array.Copy(complementTwoS, 0, res, complementTwoR.Length + 6, complementTwoS.Length);

  return res;
}

public byte[] ToComplementTwo(byte[] d){
// Ensure the top-bit is zero, otherwise remove unneeded zeroes
// - Find non-zero byte
int i = 0;
while (i < d.Length && d[i] == 0) i++;
// - Do we need an extra byte
int extraByte = (d[i] & 0x80) == 1 ? 1 : 0;
// - Build the result
byte[] res = new byte[d.Length-i+extraByte];
Array.Copy(d, i, res, extraByte, d.Length-i);
return res;

}

Rasmus Faber
Great! Thanks! Still cannot verify the byte array successfully, but I don't get the exception anymore! One tiny error left: Array.Copy(complementTwoS, 0, res, 6, complementTwoS.Length) - 6 should be 26. 10x again!
Danail