17

I am trying to encrypt strings in .NET by using a RSA algorithm and decrypt the result in Java. At the moment, I have been able to do the opposite (Encrypt in Java, Decrypt in .NET). Here I have my code that actually works (JAVA encryption):

byte[] modulusBytes = Base64.decode("2rRVVVFJRbH/wAPDtnwZwu+nxU+AZ6uXxh/sW+AMCBogg7vndZsnRiHoLttYYPqOyOhfgaBOQogrIfrKL4lipK4m52SBzw/FfcM9DsKs/rYR83tBLiIAfgdnVjF27tZID+HJMFTiI30mALjr7+tfp+2lIACXA1RIKTk7S9pDmX8=");
byte[] exponentBytes = Base64.decode("AQAB");
BigInteger modulus = new BigInteger(1, modulusBytes );
BigInteger exponent = new BigInteger(1, exponentBytes);

RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(rsaPubKey);

Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);

byte[] plainBytes = new String("big kitty dancing").getBytes("UTF-8");
byte[] cipherData = cipher.doFinal( plainBytes );
String encryptedString = Base64.encode(cipherData);
return encryptedString;

And (.NET decryption)

const int PROVIDER_RSA_FULL = 1;
const string CONTAINER_NAME = "Tracker";

CspParameters cspParams;
cspParams = new CspParameters(PROVIDER_RSA_FULL);
cspParams.KeyContainerName = CONTAINER_NAME;
RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider(cspParams);
rsa1.FromXmlString("<RSAKeyValue><Modulus>2rRVVVFJRbH/wAPDtnwZwu+nxU+AZ6uXxh/sW+AMCBogg7vndZsnRiHoLttYYPqOyOhfgaBOQogrIfrKL4lipK4m52SBzw/FfcM9DsKs/rYR83tBLiIAfgdnVjF27tZID+HJMFTiI30mALjr7+tfp+2lIACXA1RIKTk7S9pDmX8=</Modulus><Exponent>AQAB</Exponent><P>+lXMCEwIN/7+eMpBrq87kQppxu3jJBTwztGTfXNaPUTx+A6uqRwug5oHBbSpYXKNDNCBzVm/0VxB3bo4FJx+ZQ==</P><Q>yasOGaJaE9xlF9T2xRuKeG9ZxCiyjhYaYB/mbtL+SIbtkRLi/AxaU4g2Il/UxhxhSXArKxIzV28zktispPJx1Q==</Q><DP>ueRgQIEFUV+fY979a1RgrVHIPpqEI1URhOMH3Q59oiXCcOumM5njyIHmWQxRAzXnG+7xlKXi1PrnRll0L4oOKQ==</DP><DQ>dfEMNgG1HJhwpxdtmqkYuoakwQvsIRzcIAuIAJh1DoWaupWJGk8/JEstHb1d+t7uJrzrAi2KyT/HscH2diE0YQ==</DQ><InverseQ>YoYF9PF6FiC0YngVeaC/eqt/ea8wMYNN3YO1LuzWpcy2exPRj2U0ZbWMvHXMUb4ea2qmhZGx1QlK4ULAuWKpXQ==</InverseQ><D>g1WAWI4pEK9TA7CA2Yyy/2FzzNiu0uQCuE2TZYRNiomo96KQXpxwqAzZLw+VDXfJMypwDMAVZe/SqzSJnFEtZxjdxaEo3VLcZ1mnbIL0vS7D6iFeYutF9kF231165qGd3k2tgymNMMpY7oYKjS11Y6JqWDU0WE5hjS2X35iG6mE=</D></RSAKeyValue>");

string data2Decrypt = "BaB21vY+RD/jiY3AAsb269fIWTEH38s0xLUfJ7CoVUgaQ6vYzB0tiJ1Ag9HNEdCcuZdGchhqnms8jpsqsHC1iKrz6QCLsgUU7VNWDfQqZYR6Rl/GwR0biK2STnOL+g06f/JUdixHOHOgROify1m8qppYo5plpOVMqYFzEMREMkM=";

byte[] encyrptedBytes = Convert.FromBase64String(data2Decrypt);

byte[] plain = rsa1.Decrypt(encyrptedBytes, false);
string decryptedString = System.Text.Encoding.UTF8.GetString(plain);
Console.WriteLine("SALIDA: " + decryptedString);

Now I want to do the opposite... But I get some errors like (the size of the key should be 128 bytes... etc) How should I do it?

Here I add the current non working code:

.NET

public string Encrypt(string text)
{
    const int PROVIDER_RSA_FULL = 1;
    const string CONTAINER_NAME = "Tracker";

    CspParameters cspParams;
    cspParams = new CspParameters(PROVIDER_RSA_FULL);
    cspParams.KeyContainerName = CONTAINER_NAME;
    RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider(cspParams);
    rsa1.FromXmlString("<RSAKeyValue><Modulus>2rRVVVFJRbH/wAPDtnwZwu+nxU+AZ6uXxh/sW+AMCBogg7vndZsnRiHoLttYYPqOyOhfgaBOQogrIfrKL4lipK4m52SBzw/FfcM9DsKs/rYR83tBLiIAfgdnVjF27tZID+HJMFTiI30mALjr7+tfp+2lIACXA1RIKTk7S9pDmX8=</Modulus><Exponent>AQAB</Exponent><P>92jJJyzFBSx6gL4Y1YpALmc5CNjoE/wETjqb3ci2v0+3rZWvJKmKy1ZEdlXpyuvXVksJ6cMdUpNAkMknUk9pTQ==</P><Q>4kxkABZOXyDLryYGCGY0b8N0FIdu5BTCFDYEdcatxl/f7ZGDS1NgHJpUWxkVXFfHy2Y/GuDOIbpcwlsO739H+w==</Q><DP>5bNFvrdUHF+VRN45VFjNCcgQLeSkY5mBrdfASoNFGA29LM5iE5nNIMfxPCS7sQiRnq6Af6YFHVtVgJchiMvtqQ==</DP><DQ>j+ng1qVY5epnXlWiFIla45C7K6sNfIMvAcdwgq39KWEjeWPGyYqWXtpOtzh2eylf6Bx4GVHKBW0NPJTIJMsfLQ==</DQ><InverseQ>8uu0dfPVDqB2qFM1Vdi8hl+2uZtN7gjT2co1cEWy29HVYBZD0k9KKCf2PbkeuSfpgFpE70wW5Hrp8V7l/SwSOw==</InverseQ><D>MM/c18zroJ2Iqi9s5/asvUBF3pjO3NSEbFjFpP/NT6WdKimvECWPz2xT6NlV0Vc6tQaAAmtn7Bt+HPhfVdrA4/ysYVe3/6TWkPjW+bvAhMWu/ZqISx11/jPYSGD9g3ZXgUiqcQM8UbOjlswoq4fpheEXTB0xdVutDLpO3qgHN6k=</D></RSAKeyValue>");

    System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
    byte[] textBytes = encoding.GetBytes(text);
    byte[] encryptedOutput = rsa1.Encrypt(textBytes, false);
    string outputB64 = Convert.ToBase64String(encryptedOutput);
    Console.WriteLine(outputB64);
    return outputB64;
}

Java

public static String Decrypt(String encodedString) throws IllegalBlockSizeException, UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, BadPaddingException
{
    byte[] modulusBytes = Base64.decode("2rRVVVFJRbH/wAPDtnwZwu+nxU+AZ6uXxh/sW+AMCBogg7vndZsnRiHoLttYYPqOyOhfgaBOQogrIfrKL4lipK4m52SBzw/FfcM9DsKs/rYR83tBLiIAfgdnVjF27tZID+HJMFTiI30mALjr7+tfp+2lIACXA1RIKTk7S9pDmX8=");
    byte[] exponentBytes = Base64.decode("AQAB");
    BigInteger modulus = new BigInteger(1, modulusBytes );
    BigInteger exponent = new BigInteger(1, exponentBytes);

    RSAPrivateKeySpec rsaPrivKey = new RSAPrivateKeySpec(modulus, exponent);
    KeyFactory fact = KeyFactory.getInstance("RSA");
    PrivateKey privKey = fact.generatePrivate(rsaPrivKey);

    Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    cipher.init(Cipher.DECRYPT_MODE, privKey);

    byte[] base64String = Base64.decode(encodedString);
    byte[] plainBytes = new String(base64String).getBytes("UTF-8");
    byte[] cipherData = cipher.doFinal(plainBytes);

    System.out.println(cipherData);
    return cipherData.toString();
}
CSchulz
  • 10,882
  • 11
  • 60
  • 114
Reixons
  • 227
  • 2
  • 3
  • 7
  • Give us some stack traces. Are you specifying the same padding schemes? I'm able to encrypt in .Net decrypt in Java, and vice versa, no problem. Though I use PKCS5Padding(Java) and PKCS7 (.Net) and do not hard code keys. – Petey B Jul 04 '11 at 15:05
  • I cannot give any trace because the errors I get are varying (as I said the size of the key, padding exceptions as: javax.crypto.BadPaddingException: Data must start with zero) ... I can post the code that does not work, but I am working on it by now. If you could please post how you do it, I would be very grateful because it is important for me to solve this problem in the next few days... – Reixons Jul 04 '11 at 15:12

3 Answers3

10

The last few lines of your Java decrypt code do not make sense. These lines are:

byte[] base64String = Base64.decode(encodedString);
byte[] plainBytes = new String(base64String).getBytes("UTF-8");
byte[] cipherData = cipher.doFinal(plainBytes);

System.out.println(cipherData);
return cipherData.toString();

You have to reverse the order of the steps you used to encrypt in .NET. First, you should Base64 decode the encoded string to get the cipher bytes. You did that, but you mislabeled the result as base64String. You probably should call this result cipherData. Second, you need to decrypt cipherData to get plain text. Third, you should create a string from plainbytes using the two-arg String constructor with the Charset for the second argument. Here is what the code should look like, or close to it.

byte[] cipherData = Base64.decode(encodedString);
byte[] plainBytes = cipher.doFinal(cipherData);

return new String(plainBytes, "UTF-8");

Finally, in Java every object has a toString() method but it doesn't always do what you want. For arrays the toString() method simply returns a representation of object id for that array, sort of the JVM equivalent of a memory address.

EDIT:

I missed that you are also using the wrong key in your decrypt code. Your are using the RSA public key, but you must instead use the RSA private key.

President James K. Polk
  • 40,516
  • 21
  • 95
  • 125
  • I am using the private key but in order to generate it, I am using the same modulus and exponent I use to generate the public one... isn't that correct? – Reixons Jul 05 '11 at 14:41
  • you were right, the problem with my first code was that I was also using the exponent to generate the private key and I should use the "D" part of the key. Now it works. Thanks everybody!! – Reixons Jul 05 '11 at 22:21
  • I'm doing something very similar but as a result of decrypt, I'm getting a Null input buffer: java.lang.IllegalArgumentException: Null input buffer somewhere in the code. I printed out my private key and it seems to be something like this: Sun RSA private key, 1024 bits modulus: 17117003084657245951324093201042962541622648817557411074899862199275295356525279994079227 private exponent: 994733020251546255267347320447594310775787731259309481548242001068408479929746858409912925575444823141577188360613158181121242481 I took out parts of it because it was too long. Any idea why this is happening – C0D3 Mar 27 '13 at 22:54
  • I noticed that after doing byte[] cipherData = Base64.decode(encodedString); the cipherData is null. Why could this be? Basically: return new String(cipherData, "UTF-8"); is returning null – C0D3 Mar 27 '13 at 23:21
  • And now finally this is what I'm getting: Data must start with zero: javax.crypto.BadPaddingException: Data must start with zero – C0D3 Mar 27 '13 at 23:36
  • @c0d3Junk13: Please ask a question in the normal manner, not in the comments. – President James K. Polk Mar 28 '13 at 00:09
  • Can we take this to chat if you have a few moments? – C0D3 Mar 28 '13 at 00:15
  • It used to offer if we kept commenting on something – C0D3 Mar 28 '13 at 00:16
  • @c0d3Junk13: Ok, let's do a couple more rounds. – President James K. Polk Mar 28 '13 at 00:17
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/27053/discussion-between-c0d3junk13-and-gregs) – C0D3 Mar 28 '13 at 00:17
0

As you requested are hare some code snippets. RSA keys are from x509 certs.

Java RSA/AES:

// symmetric algorithm for data encryption
final String ALGORITHM = "AES";
// Padding for symmetric algorithm
final String PADDING_MODE = "/CBC/PKCS5Padding";
// character encoding
final String CHAR_ENCODING = "UTF-8";
// provider for the crypto
final String CRYPTO_PROVIDER = "Entrust";
// RSA algorithm used to encrypt symmetric key
final String RSA_ALGORITHM = "RSA/ECB/PKCS1Padding";
// symmetric key size (128, 192, 256) if using 192+ you must have the Java
// Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files
// installed
int AES_KEY_SIZE = 256;

private byte[] encryptWithRSA(byte[] aesKey, X509Certificate cert)
        throws NoSuchAlgorithmException, NoSuchPaddingException,
        InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
    // get the public key from the encryption certificate to encrypt with
    PublicKey pubKey = cert.getPublicKey();

    // get an instance of the RSA Cipher
    Cipher rsaCipher = Cipher.getInstance(RSA_ALGORITHM);

    // set the cipher to use the public key
    rsaCipher.init(Cipher.ENCRYPT_MODE, pubKey);

    // encrypt the aesKey
    return rsaCipher.doFinal(aesKey);
}

private AESEncryptedContents encryptWithAes(byte[] dataToEncrypt)
        throws NoSuchAlgorithmException, NoSuchPaddingException,
        InvalidKeyException, IllegalBlockSizeException,
        BadPaddingException, NoSuchProviderException {
    // get the symmetric key generator
    KeyGenerator keyGen = KeyGenerator.getInstance(ALGORITHM);
    keyGen.init(AES_KEY_SIZE); // set the key size

    // generate the key
    SecretKey skey = keyGen.generateKey();

    // convert to binary
    byte[] rawAesKey = skey.getEncoded();

    // initialize the secret key with the appropriate algorithm
    SecretKeySpec skeySpec = new SecretKeySpec(rawAesKey, ALGORITHM);

    // get an instance of the symmetric cipher
    Cipher aesCipher = Cipher.getInstance(ALGORITHM + PADDING_MODE,
            CRYPTO_PROVIDER);

    // set it to encrypt mode, with the generated key
    aesCipher.init(Cipher.ENCRYPT_MODE, skeySpec);

    // get the initialization vector being used (to be returned)
    byte[] aesIV = aesCipher.getIV();

    // encrypt the data
    byte[] encryptedData = aesCipher.doFinal(dataToEncrypt);

    // package the aes key, IV, and encrypted data and return them
    return new AESEncryptedContents(rawAesKey, aesIV, encryptedData);
}

private byte[] decryptWithAES(byte[] aesKey, byte[] aesIV,
        byte[] encryptedData) throws NoSuchAlgorithmException,
        NoSuchPaddingException, InvalidKeyException,
        InvalidAlgorithmParameterException, IllegalBlockSizeException,
        BadPaddingException, UnsupportedEncodingException,
        NoSuchProviderException {
    // initialize the secret key with the appropriate algorithm
    SecretKeySpec skeySpec = new SecretKeySpec(aesKey, ALGORITHM);

    // get an instance of the symmetric cipher
    Cipher aesCipher = Cipher.getInstance(ALGORITHM + PADDING_MODE,
            CRYPTO_PROVIDER);

    // set it to decrypt mode with the AES key, and IV
    aesCipher.init(Cipher.DECRYPT_MODE, skeySpec,
            new IvParameterSpec(aesIV));

    // decrypt and return the data
    byte[] decryptedData = aesCipher.doFinal(encryptedData);

    return decryptedData;
}

private byte[] decryptWithRSA(byte[] encryptedAesKey, PrivateKey privKey)
        throws IllegalBlockSizeException, BadPaddingException,
        InvalidKeyException, NoSuchAlgorithmException,
        NoSuchPaddingException, NoSuchProviderException {
    // get an instance of the RSA Cipher
    Cipher rsaCipher = Cipher.getInstance(RSA_ALGORITHM, CRYPTO_PROVIDER);

    // set the cipher to use the public key
    rsaCipher.init(Cipher.DECRYPT_MODE, privKey);

    // encrypt the aesKey
    return rsaCipher.doFinal(encryptedAesKey);
}

C# .Net:

public byte[] encryptData(byte[] data, out byte[] encryptedAesKey, out byte[] aesIV) {
    if (data == null)
        throw new ArgumentNullException("data");

    byte[] encryptedData; // data to return

    // begin AES key generation
    RijndaelManaged aesAlg = new RijndaelManaged();
    aesAlg.KeySize = AES_KEY_SIZE;
    aesAlg.GenerateKey();
    aesAlg.GenerateIV();
    aesAlg.Mode = CipherMode.CBC;
    aesAlg.Padding = PaddingMode.PKCS7;

    // aes Key to be encrypted
    byte[] aesKey = aesAlg.Key;

    // aes IV that is passed back by reference
    aesIV = aesAlg.IV;

    //get a new RSA crypto service provider to encrypt the AES key with the certificates public key
    using (RSACryptoServiceProvider rsaCSP = new RSACryptoServiceProvider())
    {
        //add the certificates public key to the RSA crypto provider
        rsaCSP.FromXmlString(encryptionCertificate.PublicKey.Key.ToXmlString(false));

        //encrypt AES key with RSA Public key
        //passed back by reference
        encryptedAesKey = rsaCSP.Encrypt(aesKey, false);

        //get an aes encryptor instance
        ICryptoTransform aesEncryptor = aesAlg.CreateEncryptor();

        encryptedData = encryptWithAes(aesEncryptor, data);
    }

    if (encryptedData == null)
        throw new CryptographicException(
                "Fatal error while encrypting with AES");

    return encryptedData;
}

private byte[] encryptWithAes(ICryptoTransform aesEncryptor, byte[] data) {
    MemoryStream memStream = null; // stream to write encrypted data to
    CryptoStream cryptoStream = null; // crypto stream to encrypted data

    try {
        memStream = new MemoryStream();

        // initiate crypto stream telling it to write the encrypted data to
        // the memory stream
        cryptoStream = new CryptoStream(memStream, aesEncryptor,
                CryptoStreamMode.Write);

        // write the data to the memory stream
        cryptoStream.Write(data, 0, data.Length);
    } catch (Exception ee) {
        // rethrow
        throw new Exception("Error while encrypting with AES: ", ee);
    } finally {
        // close 'em
        if (cryptoStream != null)
            cryptoStream.Close();
        if (memStream != null)
            memStream.Close();
    }

    // return the encrypted data
    return memStream.ToArray();
}
CSchulz
  • 10,882
  • 11
  • 60
  • 114
Petey B
  • 11,439
  • 25
  • 81
  • 101
  • Thanks a lot Petey! I am going to try the code...I hope it works and does not give me any problems with the byte[] to string conversion. I will try to adapt it (I am not using certs or AES) and I will post sth as soon as I try it. Thanks again! – Reixons Jul 04 '11 at 17:08
  • @Reixons, no problem, let me know if I can help at all. What will you be encrypting? If it's longer than your RSA key length you should use AES+RSA. – Petey B Jul 04 '11 at 18:23
  • Well, I have tested the code and I have some problems. I get: javax.crypto.BadPaddingException: Data must start with zero I will try to describe it better in 2h. I don't have enough reputation to do it now!! :( – Reixons Jul 04 '11 at 20:40
0

Here is the answer I couldn't post yesterday, related to the first answer to my post.

Well, I have tested the code and I have some problems. I have tried not to change anything unless it was completely necessary. First I get an error here:

Cipher rsaCipher = Cipher.getInstance(RSA_ALGORITHM, CRYPTO_PROVIDER);

The "Entrust" crypto provider is not recognized... So I left just the first parameter. Then I get this error:

javax.crypto.BadPaddingException: Data must start with zero

I have tried through a WebService written in .NET which returns always byte arrays. Maybe there is some kind of problem in the translation. I know that I have to use Base64 numbers and (if I don't use AES) I have to break my Strings into pieces with the size of 128 bytes (limited by the RSA key). I am still working on the problem to realize why I could encrypt in Java and decrypt in .NET but not the opposite.

Thanks again for your help!!

Reixons
  • 227
  • 2
  • 3
  • 7