5

I need to encrypt String for project related purpose and was given the below code for the same by vendor.

public static string EncryptString(string StringToEncrypt)
{
    RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
    string xmlString = "<RSAKeyValue><Modulus>qqoWhMwGrrEBRr92VYud3j+iIEm7652Fs20HvNckH3tRDJIL465TLy7Cil8VYxJre69zwny1aUAPYItybg5pSbSORmP+hMp6Jhs+mg3qRPvHfNIl23zynb4kAi4Mx/yEkGwsa6L946lZKY8f9UjDkLJY7yXevMML1LT+h/a0a38=</Modulus><Exponent>AQAB</Exponent><P>20PwC7nSsfrfA9pzwSOnRYdbhOYivFSuERxvXHvNjCll5XdmFYYp1d2evXcXbyj3E1k8azce1avQ9njH85NMNQ==</P><Q>x0G0lWcQ13NDhEcWbA7R2W5LPUmRqcjQXo8qFIaHk7LZ7ps9fAk/kOxaCR6hvfczgut1xSpXv6rnQ5IGvxaHYw==</Q><DP>lyybF2qSEvYVxvFZt8MeM/jkJ5gIQPLdZJzHRutwx39PastMjfCHbZW0OYsflBuZZjSzTHSfhNBGbXjO22gmNQ==</DP><DQ>NJVLYa4MTL83Tx4vdZ7HlFi99FOI5ESBcKLZWQdTmg+14XkIVcZfBxDIheWWi3pEFsWqk7ij5Ynlc/iCXUVFvw==</DQ><InverseQ>X5Aw9YSQLSfTSXEykTt7QZe6SUA0QwGph3mUae6A2SaSTmIZTcmSUsJwhL7PLNZKbMKSWXfWoemj0EVUpZbZ3Q==</InverseQ><D>jQL4lEUYCGNMUK6GEezIRgiB5vfFg8ql3DjsOcXxnOmBcEeD913kcYnLSBWEUFW55Xp0xW/RXOOHURgnNnRF3Ty5UR73jPN3/8QgMSxV8OXFo3+QvX+KHNHzf2cjKQDVObJTKxHsHKy+L2qjfULA4e+1cSDNn5zIln2ov51Ou3E=</D></RSAKeyValue>";
    provider.FromXmlString(xmlString);
    return Convert.ToBase64String(provider.Encrypt(Encoding.ASCII.GetBytes(StringToEncrypt), false));
}

However I need to modify or translate it to JAVA. I have wrote the below method for the same purpose.

public static String EncryptString(String strToBeEncrypted) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException
{
    String modulusString = "qqoWhMwGrrEBRr92VYud3j+iIEm7652Fs20HvNckH3tRDJIL465TLy7Cil8VYxJre69zwny1aUAPYItybg5pSbSORmP+hMp6Jhs+mg3qRPvHfNIl23zynb4kAi4Mx/yEkGwsa6L946lZKY8f9UjDkLJY7yXevMML1LT+h/a0a38=";
    String publicExponentString = "AQAB";
    byte[] modulusBytes = Base64.decodeBase64(modulusString);
    byte[] exponentBytes = Base64.decodeBase64(publicExponentString);
    BigInteger modulus = new BigInteger(1, modulusBytes);
    BigInteger publicExponent = new BigInteger(1, exponentBytes);
    RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, publicExponent);
    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 = strToBeEncrypted.getBytes("US-ASCII");
    byte[] cipherData = cipher.doFinal(plainBytes);
    String encryptedStringBase64 = Base64.encodeBase64String(cipherData);

    return encryptedStringBase64;
}

But the sample results do not match.

String is "4111111111111111" and encrypted result should be:

PfU31ai9dSwWX4Im19TlikfO9JetkJbUE+btuvpBuNHTnnfrt4XdM4PmGA19z8rF+lPUC/kcOEXciUSxFrAPyuRJHifIDqWFbbJvPhatbf269BXUiAW31UBX3X5bBOqNWjh4LDitYY0BtarlTU4xzOFyb7vLpLJe9aHGWhzs6q0=

But the result from Java code is

Cxp5AIzTHEkrU6YWwYo5yYvpED2qg9IC/0ct+tRgDZi9fJb8LAk+E1l9ljEt7MFQ2KB/exo4NYwijnBKYPeLStXyfVO1Bj6S76zMeKygAlCtDukq1UhJaJKaCXY94wi9Kel09VTmj+VByIYvAGUFqZGaK1CyLnd8QXMcdcWi3sA=

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
RahulMuk07
  • 53
  • 6
  • 2
    What happens if you run your C# and Java code again? Do the ciphertexts change? If so, then a randomized padding is used (important) and you need to encrypt in one and decrypt in the other to check compatibility. – Artjom B. Apr 13 '16 at 18:35
  • Thank you @ArtjomB. I just checked the compatibility with vendor's decryption code and it works fine. – RahulMuk07 Apr 14 '16 at 07:02
  • @ArtjomB. For encryption randomized padding *must* be used. Encryption must be non-deterministic, otherwise you would get the same ciphertext for the same plaintext (the same as in symmetric ECB mode encryption really). Only textbook RSA is deterministic, and very insecure for all kinds or reasons. Signature generation may be deterministic though. Could you post an answer? – Maarten Bodewes Apr 14 '16 at 23:19
  • @MaartenBodewes PKCS#1 v1.5 type 1 padding is not randomized. The chance is slim that this is actually used, but it's still there. – Artjom B. Apr 15 '16 at 07:58
  • @ArtjomB. Good catch. I haven't seen it in practice, but I'll make sure that it isn't in followup questions. – Maarten Bodewes Apr 15 '16 at 08:34

1 Answers1

2

Every encryption algorithm needs to be randomized in order to provide semantic security. Otherwise, an attacker might notice that you've sent the same message again, just by observing ciphertexts. In symmetric ciphers, this property is achieved by a random IV. In RSA, this is achieved by a randomized padding (PKCS#1 v1.5 type 2 and PKCS#1 v2.x OAEP are randomized).

You can check whether the padding is randomized by running the encryption again with the same key and plaintext, and comparing the ciphertexts to previous ciphertexts. If the ciphertexts change in either C# or Java between executions, then you will not be able to tell whether the encryption is compatible, just by looking at the ciphertexts.

The proper way to check this, would be to encrypt something in one language and then decrypt in the other. For full compatibility, you should also try it the other way around.

Looking at your code, both seem equivalent, because false is passed as the second parameter into RSACryptoServiceProvider#Encrypt to use PKCS#1 v1.5 padding, and Cipher.getInstance("RSA/ECB/PKCS1PADDING") requests the same padding. The input/output encodings also seem equivalent. So, yes this code will be equivalent.


PKCS#1 v1.5 padding should not be used nowadays, because it is vulnerable against a Bleichenbacher attack (reference). You should use OAEP for encryption and PSS for signing, which are considered secure. C# and Java both support OAEP, but there may be differences in the default hash functions that are used (hash and MGF1).

Community
  • 1
  • 1
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • Hi, I am facing the same scenario in the quesiton. If randomized padding is used how do I check if a password enterd by the user is same as the password in the database without decrypting the password from the database? Because I have heard decrypting the password is not a good practice. We only check if both the encrypted value is same. However, here I don't see how can I do that. Only way seems to be decrypting the password from db. – nanospeck Jul 26 '16 at 07:21
  • @nanospeck You should never encrypt your user's passwords. You need to use hashing instead with some strong ones being PBKDF2, bcrypt, scrypt and Argon2. Since hash functions are one-way function, you won't be able to "decrypt" the hashes. In order to authenticate your user, you can run the password through the hash function again in order to compare with the hash that is stored in the database. See more: [How to securely hash passwords?](http://security.stackexchange.com/q/211/45523) RSA is definitely the wrong tool for the job. – Artjom B. Jul 26 '16 at 21:21
  • Your words really helped me on the topic. Unfortunately, I am porting a project already implemented in .NET using the RSACryptoServiceProvider to Java so I can't reimplement the password encryption. – nanospeck Jul 27 '16 at 01:19