Перевод кода C# RSACryptoServiceProvider в Java
Мне нужно зашифровать строку в целях, связанных с проектом, и мне был предоставлен код ниже для того же поставщика.
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));
}
Однако мне нужно изменить или перевести его на JAVA. Я написал ниже метод для той же цели.
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;
}
Но результаты выборки не совпадают.
Строка "4111111111111111" и зашифрованный результат должен быть:
PfU31ai9dSwWX4Im19TlikfO9JetkJbUE + btuvpBuNHTnnfrt4XdM4PmGA19z8rF + lPUC / kcOEXciUSxFrAPyuRJHifIDqWFbbJvPhatbf269BXUiAW31UBX3X5bBOqNWjh4LDitYY0BtarlTU4xzOFyb7vLpLJe9aHGWhzs6q0 =
Но результат из кода Java
Cxp5AIzTHEkrU6YWwYo5yYvpED2qg9IC/0ct+tRgDZi9fJb8LAk+E1l9ljEt7MFQ2KB/exo4NYwijnBKYPeLStXyfVO1Bj6S76zMeKygAlCtDukq1UhJaJKaCXY94wi9Kel09VTmj+VByIYvAGUFqZGaK1CyLnd8QXMcdcWi3sA=
1 ответ
Каждый алгоритм шифрования должен быть рандомизирован для обеспечения семантической безопасности. В противном случае злоумышленник может заметить, что вы снова отправили то же сообщение, просто наблюдая за зашифрованными текстами. В симметричных шифрах это свойство достигается случайным IV. В RSA это достигается рандомизированным заполнением (PKCS#1 v1.5 type 2 и PKCS#1 v2.x OAEP рандомизированы).
Вы можете проверить, является ли заполнение случайным образом, снова запустив шифрование с тем же ключом и открытым текстом и сравнив шифротексты с предыдущими шифротекстами. Если зашифрованные тексты изменяются в C# или Java между выполнениями, вы не сможете определить, совместимо ли шифрование, просто взглянув на зашифрованные тексты.
Правильный способ проверить это - зашифровать что-то на одном языке, а затем расшифровать на другом. Для полной совместимости, вы должны попробовать и наоборот.
Глядя на ваш код, оба кажутся эквивалентными, потому что false
передается в качестве второго параметра в RSACryptoServiceProvider#Encrypt
использовать PKCS#1 v1.5 padding и Cipher.getInstance("RSA/ECB/PKCS1PADDING")
просит тот же отступ. Кодировки ввода / вывода также кажутся эквивалентными. Итак, да, этот код будет эквивалентен.
Заполнение PKCS # 1 v1.5 в настоящее время не должно использоваться, потому что оно уязвимо для атаки Bleichenbacher ( ссылка). Вы должны использовать OAEP для шифрования и PSS для подписи, которые считаются безопасными. C# и Java поддерживают OAEP, но могут быть различия в используемых хэш-функциях по умолчанию (hash и MGF1).