views:

516

answers:

3

Hi guys,

In my program (server side - Java) I've created keystore file, with command:

keytool -genkey -alias myalias -keyalg RSA -validity 10000 -keystore my.keystore

and exported related X509 certificate with:

keytool -export -alias myalias -file cert.cer -keystore my.keystore

After I saved cert.cer on client side (C#) and I write this code:

X509Certificate2 x509 = new X509Certificate2();
byte[] rawData = ReadFile("mycert.cer");
x509.Import(rawData);

RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)x509.PublicKey.Key;
byte[] plainbytes = System.Text.Encoding.ASCII.GetBytes("My Secret");
byte[] cipherbytes = rsa.Encrypt(plainbytes, true);
String cipherHex = convertToHex(cipherContent);
byte[] byteArray = encoding.GetBytes(cipherHex);

....

I write this Java code on server side:

keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream("C:\\my.keystore"), "mypass".toCharArray());
Key key = keyStore.getKey("myalias", "mypass".toCharArray());
if (key instanceof PrivateKey) {
    Certificate cert = keyStore.getCertificate("myalias");
    PublicKey pubKey = cert.getPublicKey();
    privKey = (PrivateKey)key;
}
byte[] toDecodeBytes = new BigInteger(encodeMessageHex, 16).toByteArray();
Cipher decCipher = Cipher.getInstance("RSA");
decCipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] decodeMessageBytes = decCipher.doFinal(toDecodeBytes);
String decodeMessageString = new String(decodeMessageBytes);

I receive this error:

javax.crypto.BadPaddingException: Data must start with zero

Can you help me, please? Thanks thanks,

+1  A: 

Hm, I haven't done exactly what you are here, I had an DES one I had to do, and I was getting the same error. The trick was I had to get compatible CipherModes and PaddingModes on both sides. For mine it was PaddingMode.PKCS7 and CipherMode.CBC on the csharp side, and on the java side I used the DESede/CBC/PKCS5Padding xform.

hth Mike

mezmo
You are correct. Code should never rely on the default values for mode and padding. Those should always be explicitly defined.
laz
Oh yeah Laz :)But can I set my values Padding and CipherMode on C# side when I use RSA algorithm?In my code, I try to set RSA/ECB/NoPadding and I don't receive some exceptions but my string is completely different from original.How can I resolve my problem?Thanks
Cecco
A: 

Your method of hex-decoding in Java using BigInteger will produce the wrong result much of the time because the Java BigInteger class encodes a value whose high-order byte is >= 128 with an extra zero byte at the front. Use the Apache commons codec for hex en/de-coding.

EDIT: Your C# code is not correct. There is a .NET class System.Runtime.Remoting.Metadata.W3cXsd2001.SoapHexBinary that will do the hex en/de-coding for your C# code. The following C# code fragment should work better

public static String execute(String content)
{
    ASCIIEncoding encoding = new ASCIIEncoding();
    byte[] plainbytes = System.Text.Encoding.ASCII.GetBytes(content);
    byte[] cipherbytes = rsa.Encrypt(plainbytes, false);
    SoapHexBinary hexBinary = new SoapHexBinary(cipherbytes);
    String cipherHex = hexBinary.ToString();

    // This String is used on java side to decrypt
    Console.WriteLine("CIPHER HEX: " + cipherHex);
    return cipherHex;
}

EDIT 2:

Seems that SoapHexBinary class might have had a short life in .NET. There are a number of good solutions to the problem at this msdn link.

GregS
Hi GregS, thanks for your answer.I have used Apache Commons Codec in java, but I don't know where I can downloads these for C# side, where I encrypt my data.Also, I tryed to change this line on Java side (Server):Cipher decCipher = Cipher.getInstance("RSA");with:Cipher decCipher = Cipher.getInstance("RSA/ECB/NoPadding");In this case, I don't receive some exceptions but String decode il completely different from original. Can you help me?Thanks
Cecco
You shouldn't need anything on the C# side, assuming convertToHex is correct. On the Java side, the correct padding should be one of the OAEP paddings, I'm not sure which one. Try OAEPWITHSHA1ANDMGF1PADDING
GregS
Thanks GregS, I try this method ;)One question: If I change this line byte[] cipherbytes = rsa.Encrypt(plainbytes, true) in byte[] cipherbytes = rsa.Encrypt(plainbytes, false) I have a PKCS1 v1.5 right?How I manage this on java side? Thanks
Cecco
@Cecco: If you do this, then on the Java side make the padding PKCS1PADDING.
GregS
Hi GregS,I try with this settings: rsa.Encrypt(plainbytes, true) on C# side and Cipher.getInstance("RSA/ECB/OAEPWITHSHA1ANDMGF1PADDING") with org.apache.commons.codec.binary.Hex.decodeHex(encodeMessageHex.toCharArray()).But I have always same error: BadPaddingException: Data must start with zero. If I use RSA/ECB/NoPadding I don't have some exception, but I have a string totally different, please say me why, I'm going to crazy!I don't understand what I have to do to solve my terrible problem ...
Cecco
Sorry Greg, but also with PKCS1PADDING I have BadPaddingException. I think that I have some error on C# side, but I don't understand where is it....
Cecco
The BadPadding exception can also be caused by using the wrong private key. Sorry my advice didn't help. Usually we need a small self contained complete example. That way we can run the code ourselves.
GregS
Ok, tomorrow I going to upload a .rar package that contains my keystore and certificate that I have used in my application.So you can download it and you can run this code.Thanks Greg :)
Cecco
Downloads my scripts at: http://www.megaupload.com/?d=ZB5BPVMJSay me if you download it correctly. Thanks GregS.
Cecco
I was able to download your code, which does not match what you posted above. Any reason for the discrepancy?
GregS
I have changed some thinks in uploaded code. I try to change encrypt mode without result :(
Cecco
FANTASTIC!!!! With your SoapHexBinary works correctly!!! Thanks GregS!!! Now I post solution for everybody :)
Cecco
A: 

Thanks to GregS answers I found solution of my problem. Now I post this.

C# SIDE (Client-Side)

X509Certificate2 x509 = new X509Certificate2();
byte[] rawData = ReadFile("mycert.cer");
x509.Import(rawData);

After I loads my X509Certificate I call my execute method:

public static String execute(String content)
{
    ASCIIEncoding encoding = new ASCIIEncoding();
    byte[] plainbytes = System.Text.Encoding.ASCII.GetBytes(content);
    byte[] cipherbytes = rsa.Encrypt(plainbytes, false);
    SoapHexBinary hexBinary = new SoapHexBinary(cipherbytes);
    String cipherHex = hexBinary.ToString();

    // This String is used on java side to decrypt
    Console.WriteLine("CIPHER HEX: " + cipherHex);
    return cipherHex;
}

JAVA SIDE (Server-Side)

keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream("C:\\my.keystore"), "mypass".toCharArray());
Key key = keyStore.getKey("myalias", "mypass".toCharArray());
if (key instanceof PrivateKey) {
    Certificate cert = keyStore.getCertificate("myalias");
    PublicKey pubKey = cert.getPublicKey();
    privKey = (PrivateKey)key;
}
byte[] toDecodeBytes = new BigInteger(encodeMessageHex, 16).toByteArray();
Cipher decCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
decCipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] decodeMessageBytes = decCipher.doFinal(toDecodeBytes);
String decodeMessageString = new String(decodeMessageBytes);

The problem was in Hex-Encryption on C# Side that are completely different on Java side. Thanks GregS, you are the best ;)

Cecco