java - numeros - Descifre el texto codificado de C#RIJNDAEL
encriptar y desencriptar rsa en java (1)
Estoy implementando en Java la comunicación a una aplicación de terceros. Como parte del proceso de inicio de sesión, la aplicación de terceros está enviando una cadena encriptada que debo decodificar y enviar de vuelta. He pasado casi 2 días haciendo publicaciones y leyendo publicaciones, pero no encuentro la manera correcta de implementarlo.
Tengo un caso de prueba donde la cadena cifrada es "c1W2YO1vYQzu6czteEidrG0U4g5gT4h57vAlP7tdjcY =" que descifrado con la contraseña "GAT" debe devolver "101714994".
La documentación que tengo declara esto: la cadena de autorización fue encriptada con la siguiente configuración:
- Relleno de datos de entrada: PKCS * 7
- La matriz de bytes de la contraseña tiene 32 bytes de longitud. La cadena de contraseña se convierte en una matriz de bytes codificada en UTF-16 y la matriz de bytes se rellena con ceros de hasta 32 bytes de longitud. Las contraseñas más largas se truncan.
Este es el ejemplo de C # de cómo descifrar la cadena de autorización:
/// <summary>
/// Decrypts a string.
/// </summary>
/// <param name="content">The string to decrypt.</param>
/// <param name="password">The password to use.</param>
/// <returns>The decrypted string.</returns>
private static string DecryptString(string content, string password) {
Rijndael aes;
byte[] retVal = null;
byte[] contentBytes;
byte[] passwordBytes;
byte[] ivBytes;
try {
//Get the content as byte[]
contentBytes = Convert.FromBase64String(content);
//Create the password and initial vector bytes
passwordBytes = new byte[32];
ivBytes = new byte[16];
Array.Copy(Encoding.Unicode.GetBytes(password), passwordBytes, Encoding.Unicode.GetBytes(password).Length);
Array.Copy(passwordBytes, ivBytes, 16);
//Create the cryptograpy
object aes = Rijndael.Create();
aes.Key = passwordBytes;
aes.IV = ivBytes;
aes.Padding = PaddingMode.PKCS7;
//Decrypt
retVal = aes.CreateDecryptor().TransformFinalBlock(contentBytes, 0, contentBytes.Length);
}
catch {
}
aes = null;
contentBytes = null;
passwordBytes = null;
ivBytes = null;
return Encoding.Unicode.GetString(retVal)
}
Este es mi procedimiento de Java para descifrar la cadena:
private String decryptAuthorizationString(String authString, String password) {
try {
//Force the test string
authString = "c1W2YO1vYQzu6czteEidrG0U4g5gT4h57vAlP7tdjcY=";
//Force the test password
password = "GAT";
//Create the password and initial vector bytes
byte[] passwordBytes= new byte[32];
byte[] b= password.getBytes("UTF-8");
int len= b.length;
if (len > passwordBytes.length) len = passwordBytes.length;
System.arraycopy(b, 0, passwordBytes, 0, len);
byte[] ivBytes= new byte[16];
System.arraycopy(passwordBytes, 0, ivBytes, 0, 16);
//Get the authString as byte[]
byte[] authBytes = new BASE64Decoder().decodeBuffer(authString);
InputStream inputStream = new ByteArrayInputStream(authBytes);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// If you have Bouncycastle library installed, you can use
// Rijndael/CBC/PKCS7PADDING directly.
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("Rijndael/CBC/PKCS7PADDING", "BC");
// convertedSecureString and initVector must be byte[] with correct length
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(passwordBytes, "AES"), new IvParameterSpec(ivBytes));
CipherInputStream cryptoStream = new CipherInputStream(inputStream, cipher);
byte[] buffer = new byte[1024];
len = cryptoStream.read(buffer, 0, buffer.length);
while (len > 0) {
outputStream.write(buffer, 0, len);
len = cryptoStream.read(buffer, 0, buffer.length);
}
outputStream.flush();
cryptoStream.close();
String resStr = outputStream.toString("UTF-8");
return resStr; //<<--- resStr must be "101714994"
} catch (Throwable t) {
}
return null;
}
El procedimiento se ejecuta sin ningún error pero el resultado no es el que necesito. Cualquier ayuda será muy apreciada.
No necesita el BouncyCastleProvider para esto, ya que AES ya está incluido en Java. Sin embargo, el relleno PKCS # 7 está incorrectamente indicado por "PKCS5Padding"
, por lo que "AES/CBC/PKCS7Padding"
no se puede indicar sin Bouncy Castle.
La codificación Unicode
predeterminada de .NET es en realidad más compatible con UTF-16LE. Deja que Microsoft no se quede con los nombres estándar (aunque es posible que lo hayan precedido).
Java JCE no se basa realmente en la transmisión como las clases C #, por lo que es mejor evitar la transmisión por completo.
He reescrito tu código de muestra para mostrar cómo codificarlo correctamente en Java (sin embargo, necesitarás ser compatible con Java 7). No coloque excepciones debajo de la tabla, AssertError
en AssertError
o RuntimeException
.
He usado el descodificador Bouncy Castle Base 64, ya que ese fue el disponible tanto para ti como para mí (pero por lo demás esto es independiente de Bouncy). Java 8 tiene una clase base 64 incluida.
Así que sin más preámbulos:
import static java.nio.charset.StandardCharsets.UTF_16LE;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.util.encoders.Base64;
public class AuthenticationStringDecrypter {
private static final String AES_CBC_PKCS5PADDING = "AES/CBC/PKCS5PADDING";
private static final int KEY_SIZE = 256;
public static void main(final String[] args) throws Exception {
System.out.println(decryptAuthorizationString(
"c1W2YO1vYQzu6czteEidrG0U4g5gT4h57vAlP7tdjcY=", "GAT"));
}
private static String decryptAuthorizationString(final String authString,
final String password) {
try {
// --- check if AES-256 is available
if (Cipher.getMaxAllowedKeyLength(AES_CBC_PKCS5PADDING) < KEY_SIZE) {
throw new IllegalStateException("Unlimited crypto files not present in this JRE");
}
// --- create cipher
final Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5PADDING);
// --- create the key and initial vector bytes
final byte[] passwordEncoded = password.getBytes(UTF_16LE);
final byte[] keyData = Arrays.copyOf(passwordEncoded, KEY_SIZE
/ Byte.SIZE);
final byte[] ivBytes = Arrays.copyOf(keyData, cipher.getBlockSize());
// --- init cipher
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyData, "AES"),
new IvParameterSpec(ivBytes));
// --- decode & decrypt authentication string
final byte[] authBytes = Base64.decode(authString);
final byte[] decryptedData = cipher.doFinal(authBytes);
// WARNING: may still decrypt to wrong string if
// authString or password are incorrect -
// BadPaddingException may *not* be thrown
return new String(decryptedData, UTF_16LE);
} catch (BadPaddingException | IllegalBlockSizeException e) {
// failure to authenticate
return null;
} catch (final GeneralSecurityException e) {
throw new IllegalStateException(
"Algorithms or unlimited crypto files not available", e);
}
}
}