library - java cryptography example code
Problemas al convertir la matriz de bytes en cadena y volver a la matriz de bytes (4)
Hay muchas preguntas con respecto a este tema, la misma solución, pero esto no funciona para mí. Tengo una prueba simple con una encriptación. El cifrado / descifrado en sí mismo funciona (siempre que maneje esta prueba con la matriz de bytes en sí y no como cadenas). El problema es que no quiero manejarlo como una matriz de bytes sino como una cadena, pero cuando codigo la matriz de bytes a la cadena y viceversa, la matriz de bytes resultante difiere de la matriz de bytes original, por lo que el descifrado ya no funciona. Probé los siguientes parámetros en los métodos de cadena correspondientes: UTF-8, UTF8, UTF-16, UTF8. Ninguno de ellos funciona. El conjunto de bytes resultante difiere del original. ¿Alguna idea de por qué esto es así?
Encriptador:
public class NewEncrypter
{
private String algorithm = "DESede";
private Key key = null;
private Cipher cipher = null;
public NewEncrypter() throws NoSuchAlgorithmException, NoSuchPaddingException
{
key = KeyGenerator.getInstance(algorithm).generateKey();
cipher = Cipher.getInstance(algorithm);
}
public byte[] encrypt(String input) throws Exception
{
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] inputBytes = input.getBytes("UTF-16");
return cipher.doFinal(inputBytes);
}
public String decrypt(byte[] encryptionBytes) throws Exception
{
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] recoveredBytes = cipher.doFinal(encryptionBytes);
String recovered = new String(recoveredBytes, "UTF-16");
return recovered;
}
}
Esta es la prueba donde lo intento:
public class NewEncrypterTest
{
@Test
public void canEncryptAndDecrypt() throws Exception
{
String toEncrypt = "FOOBAR";
NewEncrypter encrypter = new NewEncrypter();
byte[] encryptedByteArray = encrypter.encrypt(toEncrypt);
System.out.println("encryptedByteArray:" + encryptedByteArray);
String decoded = new String(encryptedByteArray, "UTF-16");
System.out.println("decoded:" + decoded);
byte[] encoded = decoded.getBytes("UTF-16");
System.out.println("encoded:" + encoded);
String decryptedText = encrypter.decrypt(encoded); //Exception here
System.out.println("decryptedText:" + decryptedText);
assertEquals(toEncrypt, decryptedText);
}
}
Ahora, encontré otra solución también ...
public class NewEncrypterTest
{
@Test
public void canEncryptAndDecrypt() throws Exception
{
String toEncrypt = "FOOBAR";
NewEncrypter encrypter = new NewEncrypter();
byte[] encryptedByteArray = encrypter.encrypt(toEncrypt);
String encoded = String.valueOf(Hex.encodeHex(encryptedByteArray));
byte[] byteArrayToDecrypt = Hex.decodeHex(encoded.toCharArray());
String decryptedText = encrypter.decrypt(byteArrayToDecrypt);
System.out.println("decryptedText:" + decryptedText);
assertEquals(toEncrypt, decryptedText);
}
}
La solución aceptada no funcionará si su String
tiene algunos caracteres no típicos como š, ž, ć, Ō, ō, Ū
, etc.
El siguiente código funcionó bien para mí.
byte[] myBytes = Something.getMyBytes();
String encodedString = Base64.encodeToString(bytes, Base64.NO_WRAP);
byte[] decodedBytes = Base64.decode(encodedString, Base64.NO_WRAP);
No es una buena idea almacenar datos encriptados en Strings porque son para texto legible por humanos, no para datos binarios arbitrarios. Para datos binarios, es mejor usar byte[]
.
Sin embargo, si debe hacerlo, debe usar una codificación que tenga una correspondencia de 1 a 1 entre bytes y caracteres, es decir, donde cada secuencia de bytes se pueda asignar a una secuencia única de caracteres, y viceversa. Una de estas codificaciones es ISO-8859-1 , es decir:
String decoded = new String(encryptedByteArray, "ISO-8859-1");
System.out.println("decoded:" + decoded);
byte[] encoded = decoded.getBytes("ISO-8859-1");
System.out.println("encoded:" + java.util.Arrays.toString(encoded));
String decryptedText = encrypter.decrypt(encoded);
Otras codificaciones comunes que no pierden datos son hexadecimal y base64 , pero lamentablemente se necesita una biblioteca de ayuda para ellos. La API estándar no define clases para ellos.
Con UTF-16 el programa fallaría por dos razones:
- String.getBytes ("UTF-16") agrega un carácter de marcador de orden de bytes a la salida para identificar el orden de los bytes. Debe usar UTF-16LE o UTF-16BE para que esto no suceda.
- No todas las secuencias de bytes se pueden asignar a caracteres en UTF-16. Primero, el texto codificado en UTF-16 debe tener un número par de bytes. En segundo lugar, UTF-16 tiene un mecanismo para codificar caracteres Unicode más allá de U + FFFF. Esto significa que, por ejemplo, hay secuencias de 4 bytes que se asignan a un solo carácter Unicode. Para que esto sea posible, los primeros 2 bytes de los 4 no codifican ningún carácter en UTF-16.
Su problema es que no puede construir una cadena UTF-16 (o cualquier otra codificación) desde una matriz de bytes arbitraria (vea UTF-16 en Wikipedia ). Depende de usted, sin embargo, serializar y deserializar la matriz de bytes cifrados sin pérdida, para, por ejemplo, persistir y utilizarla más adelante. Aquí está el código de cliente modificado que debería darle una idea de lo que está sucediendo realmente con las matrices de bytes:
public static void main(String[] args) throws Exception {
String toEncrypt = "FOOBAR";
NewEncrypter encrypter = new NewEncrypter();
byte[] encryptedByteArray = encrypter.encrypt(toEncrypt);
System.out.println("encryptedByteArray:" + Arrays.toString(encryptedByteArray));
String decoded = new String(encryptedByteArray, "UTF-16");
System.out.println("decoded:" + decoded);
byte[] encoded = decoded.getBytes("UTF-16");
System.out.println("encoded:" + Arrays.toString(encoded));
String decryptedText = encrypter.decrypt(encryptedByteArray); // NOT the "encoded" value!
System.out.println("decryptedText:" + decryptedText);
}
Este es el resultado:
encryptedByteArray:[90, -40, -39, -56, -90, 51, 96, 95, -65, -54, -61, 51, 6, 15, -114, 88]
decoded:<some garbage>
encoded:[-2, -1, 90, -40, -1, -3, 96, 95, -65, -54, -61, 51, 6, 15, -114, 88]
decryptedText:FOOBAR
decryptedText
es correcto, cuando se restaura desde el encryptedByteArray
original. Tenga en cuenta que el valor encoded
no es lo mismo que encryptedByteArray
, debido a la pérdida de datos durante la conversión byte[] -> String("UTF-16")->byte[]
.