SHA-1 e codifica Base64

La funzione di hash SHA-1 permette di avere in output un array di byte di dimensione fissa pari a 20. Ricordo che SHA-1 è una funzione di hash a 160 bit con un livello di sicurezza di 80 bit, ben al di sotto di un ipotetico margine di sicurezza posto a 128 bit come da standard. Basterebbero 2^80 operazioni per risalire al messaggio (attacco del compleanno…ma non è cosi semplice). In questo post mostro come la codifica Base64 amplifica il messaggio da 20 a 28 byte.

String testString = "abcdefghilmnopqrst99";
System.out.println("testString.length() = " + testString.length());
byte[] testBase64 = Base64.encodeBase64(testString.getBytes());
for (int i = 0; i < testBase64.length; i++) {
System.out.println("testBase64[" + i + "] = " + testBase64[i] +
  + "; char-->" + (char)testBase64[i]);
}
System.out.println("testBase64.length = " + testBase64.length);

Come sappiamo Base64 codifica i byte in gruppi di 6 bit e aggiunge se necessario il simbolo ‘=’ per ogni coppia di bit necessaria al padding.

20 byte * 8 = 160 bit

160 bit / 6 bit = 26 simboli (per difetto)

26 simboli + 6 bit = 156 bit avanzano 4 bit => 2 bit padding

Quindi avrò 156 + 4 +2 = 162 bit per la codifica in Base64

162 bit / 6 bit = 27 simboli + 1 simbolo per il padding = 28 simboli

Ma un simbolo Base64 non è altro che un carattere ASCII, quindi 28 simboli = 28 byte.

Infatti se osserviamo l’output del codice sopra esposto vediamo che gli utlimi due caratteri (simboli) sono ‘k=’; questo perché 9 = 00001001 ma abbiamo visto che avanzano 4 bit quindi vanno aggiunti a 1001 due bit 00 (1 coppia di bit per il padding implica aggiungere un solo simbolo ‘=’ alla fine dell’ array) per arrivare a 6 bit. In Base64 100100=’k’.

Commenti (1)

XML characters allowed

Di seguito riporto due metodi che controllano se un determinato carattere è valido secondo lo standard XML 1.0

Metodo A

  /**
     * This method ensures that the output String has only
     * valid XML unicode characters as specified by the
     * XML 1.0 standard. For reference, please see
     * <a href="http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char">the
     * standard</a>. This method will return an empty
     * String if the input is null or empty.
     *
     * @param in The String whose non-valid characters we want to remove.
     * @return The in String, stripped of non-valid characters.
     */
    public String stripNonValidXMLCharacters(String in) {
        StringBuffer out = new StringBuffer(); // Used to hold the output.
        char current; // Used to reference the current character.

        if (in == null || ("".equals(in))) return ""; // vacancy test.
        for (int i = 0; i < in.length(); i++) {
            current = in.charAt(i); // NOTE: No IndexOutOfBoundsException caught here;
                                    //it should not happen.
            if ((current == 0x9) ||
                (current == 0xA) ||
                (current == 0xD) ||
                ((current >= 0x20) && (current <= 0xD7FF)) ||
                ((current >= 0xE000) && (current <= 0xFFFD)) ||
                ((current >= 0x10000) && (current <= 0x10FFFF)))
                out.append(current);
        }
        return out.toString();
    }   

Metodo B

public static boolean iSValidXMLText(String xml) {
boolean valid = true;

if( xml != null ) {
valid = xml.matches("^([\\x09\\x0A\\x0D\\x20-\\x7E]|" //# ASCII
+ "[\\xC2-\\xDF][\\x80-\\xBF]|" //# non-overlong 2-byte
+ "\\xE0[\\xA0-\\xBF][\\x80-\\xBF]|" //# excluding overlongs
+ "[\\xE1-\\xEC\\xEE\\xEF][\\x80-\\xBF]{2}|" //# straight 3-byte
+ "\\xED[\\x80-\\x9F][\\x80-\\xBF]|" //# excluding surrogates
+ "\\xF0[\\x90-\\xBF][\\x80-\\xBF]{2}|" //# planes 1-3
+ "[\\xF1-\\xF3][\\x80-\\xBF]{3}|" //# planes 4-15
+ "\\xF4[\\x80-\\x8F][\\x80-\\xBF]{2})*$"); //# plane 16
}

return valid;
}

Commenti

Java encoding

Quando abbiamo la necessità di leggere uno stream di byte è molto importante definire il tipo di codifica se i byte rappresentano un flusso di caratteri. La codifica viene applicata alle classi InputStream, OutputStream (orientati ai byte), poiché le classi Reader, Writer sono di per sé orientate ai caratteri. Sono di grande aiuto le classi wrapper InputStreamReader, OutputStreamWriter.

Di seguito riporto un esempio di codice corretto:

BufferedReader br = new BufferedReader(new InputStreamReader(msg,"UTF-8"));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(baos, "UTF-8");
Writer w = new BufferedWriter(osw);
char[] buffer = new char[4096]; // 2^12
int len;
while ((len = br.read(buffer)) != -1) {
    w.write(buffer, 0, len);
}
br.close();
w.close();

In questo modo la lettura e scrittura è codificata in UTF-8 e le performance sono ottimizzate dalle classi BufferedReader e BufferedWriter

N.B. I charset che Java riconosce sono solo quelli IANA (http://www.iana.org/assignments/character-sets). Per osesrvare i Charset accettati vedere qui. Per esempio per settare la codifica utf-8 è necessario settare “UTF-8″; ma se chiediamo a Java ..getEncoding()=UTF8!

Un piccolo trucco quando si incontra il carattere € (non lo vedi? è l’Euro).. per salvarlo su db con codifica non AL32UTF8 ma WE8ISO8859P1 è questo:

// eventuale inserimento del carattere euro
byte[] bytes = <StringaConEuro>.getBytes("ISO-8859-15");
String stringBytes = new String(bytes, "ISO-8859-15");

byte[] bytesEuro = new byte[1];
bytesEuro[0] = -92;
String euro = new String(bytesEuro,"ISO-8859-15");

stringBytes = stringBytes.replaceAll(euro, " Euro");

Commenti

« Post precedenti