length - En Java, ¿cómo convierto una matriz de bytes a una cadena de dígitos hexadecimales mientras mantengo los ceros a la izquierda?
java string hex to array (27)
¿Es eso una solución defectuosa? (Android java)
// Create MD5 Hash
MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
digest.update(s.getBytes());
byte[] md5sum = digest.digest();
BigInteger bigInt = new BigInteger(1, md5sum);
String stringMD5 = bigInt.toString(16);
// Fill to 32 chars
stringMD5 = String.format("%32s", stringMD5).replace('' '', ''0'');
return stringMD5;
Entonces, básicamente, reemplaza espacios con 0.
Estoy trabajando con un ejemplo de código Java para hacer md5 hashes. Una parte convierte los resultados de bytes en una cadena de dígitos hexadecimales:
byte messageDigest[] = algorithm.digest();
StringBuffer hexString = new StringBuffer();
for (int i=0;i<messageDigest.length;i++) {
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
}
Sin embargo, no funciona, ya que toHexString aparentemente deja caer ceros a la izquierda. Entonces, ¿cuál es la forma más sencilla de pasar de la matriz de bytes a la cadena hexadecimal que mantiene los ceros a la izquierda?
¿Y cómo se puede volver a convertir de ascii a array de bytes?
Seguí el siguiente código para convertir a ascii dado por Jemenake.
public static String toHexString(byte[] bytes) {
char[] hexArray = {''0'',''1'',''2'',''3'',''4'',''5'',''6'',''7'',''8'',''9'',''A'',''B'',''C'',''D'',''E'',''F''};
char[] hexChars = new char[bytes.length * 2];
int v;
for ( int j = 0; j < bytes.length; j++ ) {
v = bytes[j] & 0xFF;
hexChars[j*2] = hexArray[v/16];
hexChars[j*2 + 1] = hexArray[v%16];
}
return new String(hexChars);
}
Consulte Apache Commons Codec Hex.encodeHex . El tipo de devolución es char[]
, que se puede convertir trivialmente en String
. Asi que:
String hexString = new String(Hex.encodeHex(messageDigest));
En mi humilde opinión, todas las soluciones anteriores que proporcionan fragmentos para eliminar los ceros a la izquierda son incorrectas.
byte messageDigest[] = algorithm.digest();
for (int i = 0; i < messageDigest.length; i++) {
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
}
De acuerdo con este fragmento, se toman 8 bits del conjunto de bytes en una iteración, se convierten en un entero (ya que la función Integer.toHexString toma int como argumento) y luego ese entero se convierte al valor hash correspondiente. Entonces, por ejemplo, si tiene 00000001 00000001 en binario, de acuerdo con el código, la variable hexString tendría 0x11 como valor hexadecimal, mientras que el valor correcto debería ser 0x0101. Por lo tanto, al calcular MD5 podemos obtener hashes de longitud <32 bytes (debido a ceros perdidos) que pueden no satisfacer las propiedades criptográficas únicas que MD5 hace.
La solución al problema es reemplazar el fragmento de código anterior por el siguiente fragmento:
byte messageDigest[] = algorithm.digest();
for (int i = 0; i < messageDigest.length; i++) {
int temp=0xFF & messageDigest[i];
String s=Integer.toHexString(temp);
if(temp<=0x0F){
s="0"+s;
}
hexString.append(s);
}
Encontré que Integer.toHexString es un poco lento. Si está convirtiendo muchos bytes, puede considerar construir una matriz de cadenas que contenga "00" .. "FF" y usar el número entero como índice. Es decir
hexString.append(hexArray[0xFF & messageDigest[i]]);
Esto es más rápido y asegura la longitud correcta. Solo requiere la matriz de cadenas:
String[] hexArray = {
"00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
"10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
"20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
"30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
"40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
"50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
"60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
"70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
"80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
"90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
"A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
"B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
"C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
"D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
"E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
"F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
Esta solución es un poco más vieja escuela, y debe ser eficiente en la memoria.
public static String toHexString(byte bytes[]) {
if (bytes == null) {
return null;
}
StringBuffer sb = new StringBuffer();
for (int iter = 0; iter < bytes.length; iter++) {
byte high = (byte) ( (bytes[iter] & 0xf0) >> 4);
byte low = (byte) (bytes[iter] & 0x0f);
sb.append(nibble2char(high));
sb.append(nibble2char(low));
}
return sb.toString();
}
private static char nibble2char(byte b) {
byte nibble = (byte) (b & 0x0f);
if (nibble < 10) {
return (char) (''0'' + nibble);
}
return (char) (''a'' + nibble - 10);
}
Esta solución no requiere desplazamiento de bits o máscara, tablas de búsqueda o bibliotecas externas, y es lo más corta que puedo obtener:
byte[] digest = new byte[16];
Formatter fmt = new Formatter();
for (byte b : digest) {
fmt.format("%02X", b);
}
fmt.toString()
Esto dará cadena larga de dos caracteres para un byte.
public String toString(byte b){
final char[] Hex = new String("0123456789ABCDEF").toCharArray();
return "0x"+ Hex[(b & 0xF0) >> 4]+ Hex[(b & 0x0F)];
}
Esto es lo que estoy usando para hashes MD5:
public static String getMD5(String filename)
throws NoSuchAlgorithmException, IOException {
MessageDigest messageDigest =
java.security.MessageDigest.getInstance("MD5");
InputStream in = new FileInputStream(filename);
byte [] buffer = new byte[8192];
int len = in.read(buffer, 0, buffer.length);
while (len > 0) {
messageDigest.update(buffer, 0, len);
len = in.read(buffer, 0, buffer.length);
}
in.close();
return new BigInteger(1, messageDigest.digest()).toString(16);
}
EDITAR: He probado y me he dado cuenta de que con esto también se cortan los ceros al final. Pero esto solo puede ocurrir al principio, por lo que puedes compararlo con la longitud esperada y el parche en consecuencia.
Esto también es equivalente pero más conciso utilizando Apache util HexBin donde el código se reduce a
HexBin.encode(messageDigest).toLowerCase();
He estado buscando lo mismo ... algunas buenas ideas aquí, pero ejecuté algunos micro benchmarks. Encontré que el siguiente es el más rápido (modificado a partir de Ayman''s arriba y aproximadamente 2 veces más rápido, y aproximadamente 50% más rápido que el de Steve justo encima de este):
public static String hash(String text, String algorithm)
throws NoSuchAlgorithmException {
byte[] hash = MessageDigest.getInstance(algorithm).digest(text.getBytes());
return new BigInteger(1, hash).toString(16);
}
Edit: Ups: omitimos que esto es esencialmente lo mismo que kgiannakakis y, por lo tanto, podemos despojar a un líder 0. Sin embargo, al modificar esto al siguiente, sigue siendo el más rápido:
public static String hash(String text, String algorithm)
throws NoSuchAlgorithmException {
byte[] hash = MessageDigest.getInstance(algorithm).digest(text.getBytes());
BigInteger bi = new BigInteger(1, hash);
String result = bi.toString(16);
if (result.length() % 2 != 0) {
return "0" + result;
}
return result;
}
Me gustaron las presentaciones de Steve, pero podría haberlo hecho sin un par de variables y haber guardado varias líneas en el proceso.
public static String toHexString(byte[] bytes) {
char[] hexArray = {''0'',''1'',''2'',''3'',''4'',''5'',''6'',''7'',''8'',''9'',''A'',''B'',''C'',''D'',''E'',''F''};
char[] hexChars = new char[bytes.length * 2];
int v;
for ( int j = 0; j < bytes.length; j++ ) {
v = bytes[j] & 0xFF;
hexChars[j*2] = hexArray[v/16];
hexChars[j*2 + 1] = hexArray[v%16];
}
return new String(hexChars);
}
Lo que me gusta de esto es que es fácil ver exactamente lo que está haciendo (en lugar de confiar en alguna conversión mágica de cuadro negro BigInteger) y también estás libre de tener que preocuparte por los casos de esquina como los ceros iniciales y demás. Esta rutina toma cada mordisco de 4 bits y lo convierte en un char de hex. Y está usando una búsqueda de tabla, por lo que probablemente sea rápido. Probablemente podría ser más rápido si reemplaza v / 16 y v% 16 con cambios en modo bit y AND, pero soy demasiado perezoso para probarlo ahora mismo.
Me sorprende que a nadie se le ocurrió la siguiente solución:
StringWriter sw = new StringWriter();
com.sun.corba.se.impl.orbutil.HexOutputStream hex = new com.sun.corba.se.impl.orbutil.HexOutputStream(sw);
hex.write(byteArray);
System.out.println(sw.toString());
Otra opción
public static String toHexString(byte[]bytes) {
StringBuilder sb = new StringBuilder(bytes.length*2);
for(byte b: bytes)
sb.append(Integer.toHexString(b+0x800).substring(1));
return sb.toString();
}
Para mantener los ceros a la izquierda, aquí hay una pequeña variación de lo que ha sugerido Paul (por ejemplo, md5 hash):
public static String MD5hash(String text) throws NoSuchAlgorithmException {
byte[] hash = MessageDigest.getInstance("MD5").digest(text.getBytes());
return String.format("%032x",new BigInteger(1, hash));
}
Vaya, esto parece peor de lo que es propuesto por Ayman, lo siento por eso
Parece que las funciones de concat y append pueden ser muy lentas. Lo siguiente fue MUCHO más rápido para mí (que mi publicación anterior). Cambiar a una matriz de caracteres para construir la salida fue el factor clave para acelerarlo. No he comparado con Hex.encodeHex sugerido por Brandon DuRette.
public static String toHexString(byte[] bytes) {
char[] hexArray = {''0'',''1'',''2'',''3'',''4'',''5'',''6'',''7'',''8'',''9'',''A'',''B'',''C'',''D'',''E'',''F''};
char[] hexChars = new char[10000000];
int c = 0;
int v;
for ( j = 0; j < bytes.length; j++ ) {
v = bytes[j] & 0xFF;
hexChars[c] = hexArray[v/16];
c++;
hexChars[c] = hexArray[v%16];
c++;
}
return new String(hexChars, 0, c); }
Puede obtenerlo escribiendo menos sin bibliotecas externas:
String hex = (new HexBinaryAdapter()).marshal(md5.digest(YOUR_STRING.getBytes()))
Puedes usar el que está abajo. Probé esto con cero bytes adelantados y con bytes negativos iniciales también
public static String toHex(byte[] bytes) {
BigInteger bi = new BigInteger(1, bytes);
return String.format("%0" + (bytes.length << 1) + "X", bi);
}
Si quiere dígitos hexadecimales en minúscula, use "x"
en el formato String.
Un enfoque simple sería verificar cuántos dígitos son Integer.toHexString()
por Integer.toHexString()
y agregar un cero inicial a cada byte si es necesario. Algo como esto:
public static String toHexString(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(0xFF & bytes[i]);
if (hex.length() == 1) {
hexString.append(''0'');
}
hexString.append(hex);
}
return hexString.toString();
}
Usaría algo como esto para longitud fija, como hashes:
md5sum = String.format("%032x", new BigInteger(1, md.digest()));
El 0
en la máscara hace el relleno ...
Utilice DatatypeConverter.printHexBinary()
. Puede leer su documentación en http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/DatatypeConverter.html
Por ejemplo:
byte bytes[] = {(byte)0, (byte)0, (byte)134, (byte)0, (byte)61};
System.out.println(javax.xml.bind.DatatypeConverter.printHexBinary(bytes));
Resultará en:
000086003D
mi variante
StringBuilder builder = new StringBuilder();
for (byte b : bytes)
{
builder.append(Character.forDigit(b/16, 16));
builder.append(Character.forDigit(b % 16, 16));
}
System.out.println(builder.toString());
esto funciona para mi.
Guava hace bastante simple también:
BaseEncoding.base16().encode( bytes );
Es una buena alternativa cuando Apache Commons no está disponible. También tiene algunos controles agradables de la salida como:
byte[] bytes = new byte[] { 0xa, 0xb, 0xc, 0xd, 0xe, 0xf };
BaseEncoding.base16().lowerCase().withSeparator( ":", 2 ).encode( bytes );
// "0a:0b:0c:0d:0e:0f"
String result = String.format("%0" + messageDigest.length + "s", hexString.toString())
Esa es la solución más corta dado lo que ya tienes. Si puede convertir la matriz de bytes a un valor numérico, String.format
puede convertirla en una cadena hexadecimal al mismo tiempo.
byte messageDigest[] = algorithm.digest();
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < messageDigest.length; i++) {
String hexByte = Integer.toHexString(0xFF & messageDigest[i]);
int numDigits = 2 - hexByte.length();
while (numDigits-- > 0) {
hexString.append(''0'');
}
hexString.append(hexByte);
}
static String toHex(byte[] digest) {
String digits = "0123456789abcdef";
StringBuilder sb = new StringBuilder(digest.length * 2);
for (byte b : digest) {
int bi = b & 0xff;
sb.append(digits.charAt(bi >> 4));
sb.append(digits.charAt(bi & 0xf));
}
return sb.toString();
}
static String toHex(byte[] digest) {
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%1$02X", b));
}
return sb.toString();
}