Codifica e decodifica Base64 in Java 8

Java 8 verrà ricordato principalmente per aver introdotto lambda, flussi, un nuovo modello data / ora e il motore JavaScript Nashorn in Java. Alcuni ricorderanno anche Java 8 per aver introdotto varie piccole ma utili funzionalità come l'API Base64. Cos'è Base64 e come si usa questa API? Questo post risponde a queste domande.

Cos'è Base64?

Base64 è uno schema di codifica da binario a testo che rappresenta i dati binari in un formato di stringa ASCII stampabile traducendolo in una rappresentazione radix-64. Ogni cifra Base64 rappresenta esattamente 6 bit di dati binari.

Richiesta Base64 per documenti di commento

Base64 è stato descritto per la prima volta (ma non nominato) in RFC 1421: Miglioramento della privacy per posta elettronica Internet: Parte I: Crittografia dei messaggi e procedure di autenticazione. Successivamente, è stato presentato ufficialmente come Base64 in RFC 2045: Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies, e successivamente rivisitato in RFC 4648: The Base16, Base32 e Base64 Data Encodings.

Base64 viene utilizzato per impedire la modifica dei dati durante il transito attraverso sistemi informativi, come la posta elettronica, che potrebbero non essere puliti a 8 bit (potrebbero alterare i valori a 8 bit). Ad esempio, alleghi un'immagine a un messaggio di posta elettronica e desideri che l'immagine arrivi dall'altra parte senza essere alterata. Il tuo software di posta elettronica Base64 codifica l'immagine e inserisce il testo equivalente nel messaggio, come illustrato di seguito:

Content-Disposition: inline; filename=IMG_0006.JPG Content-Transfer-Encoding: base64 /9j/4R/+RXhpZgAATU0AKgAAAAgACgEPAAIAAAAGAAAAhgEQAAIAAAAKAAAAjAESAAMAAAABAAYA AAEaAAUAAAABAAAAlgEbAAUAAAABAAAAngEoAAMAAAABAAIAAAExAAIAAAAHAAAApgEyAAIAAAAU AAAArgITAAMAAAABAAEAAIdpAAQAAAABAAAAwgAABCRBcHBsZQBpUGhvbmUgNnMAAAAASAAAAAEA ... NOMbnDUk2bGh26x2yiJcsoBIrvtPe3muBbTRGMdeufmH+Nct4chUXpwSPk/qK9GtJRMWWVFbZ0JH I4rf2dkZSbOjt7hhEzwcujA4I7Gust75pYVwAPpXn+kzNLOVYD7xFegWEKPkHsM/pU1F0NKbNS32 o24sSCOlaaFYLUhjky4x9PSsKL5bJsdWkAz3xirH2dZLy1DM2C44zx1FZqL2PTXY/9k=

L'illustrazione mostra che questa immagine codificata inizia con /e finisce con =. Il ...indica un testo che non ho mostrato per brevità. Nota che l'intera codifica per questo o qualsiasi altro esempio è circa il 33% più grande dei dati binari originali.

Il software di posta elettronica del destinatario decodificherà in Base64 l'immagine testuale codificata per ripristinare l'immagine binaria originale. Per questo esempio, l'immagine verrebbe mostrata in linea con il resto del messaggio.

Codifica e decodifica Base64

Base64 si basa su semplici algoritmi di codifica e decodifica. Funzionano con un sottoinsieme di 65 caratteri di US-ASCII in cui ciascuno dei primi 64 caratteri viene mappato a una sequenza binaria a 6 bit equivalente. Ecco l'alfabeto:

Value Encoding Value Encoding Value Encoding Value Encoding 0 A 17 R 34 i 51 z 1 B 18 S 35 j 52 0 2 C 19 T 36 k 53 1 3 D 20 U 37 l 54 2 4 E 21 V 38 m 55 3 5 F 22 W 39 n 56 4 6 G 23 X 40 o 57 5 7 H 24 Y 41 p 58 6 8 I 25 Z 42 q 59 7 9 J 26 a 43 r 60 8 10 K 27 b 44 s 61 9 11 L 28 c 45 t 62 + 12 M 29 d 46 u 63 / 13 N 30 e 47 v 14 O 31 f 48 w (pad) = 15 P 32 g 49 x 16 Q 33 h 50 y

Il 65 ° carattere ( =) viene utilizzato per riempire il testo con codifica Base64 a una dimensione integrale come spiegato brevemente.

Proprietà del sottoinsieme

Questo sottoinsieme ha l'importante proprietà di essere rappresentato in modo identico in tutte le versioni di ISO 646, incluso US-ASCII, e tutti i caratteri nel sottoinsieme sono anche rappresentati in modo identico in tutte le versioni di EBCDIC.

L'algoritmo di codifica riceve un flusso di input di byte a 8 bit. Si presume che questo flusso sia ordinato con il bit più significativo per primo: il primo bit è il bit di ordine superiore nel primo byte, l'ottavo bit è il bit di ordine inferiore in questo byte e così via.

Da sinistra a destra, questi byte sono organizzati in gruppi a 24 bit. Ciascun gruppo viene considerato come quattro gruppi concatenati a 6 bit. Ogni gruppo a 6 bit viene indicizzato in una matrice di 64 caratteri stampabili; il carattere risultante viene emesso.

Quando sono disponibili meno di 24 bit alla fine dei dati da codificare, vengono aggiunti zero bit (a destra) per formare un numero intero di gruppi di 6 bit. Quindi, =possono essere emessi uno o due caratteri del pad. Ci sono due casi da considerare:

  • Un byte rimanente: quattro bit zero vengono aggiunti a questo byte per formare due gruppi a 6 bit. Ogni gruppo indicizza la matrice e viene emesso un carattere risultante. Dopo questi due caratteri, =vengono emessi due caratteri del pad.
  • Due byte rimanenti: due bit zero vengono aggiunti al secondo byte per formare tre gruppi a 6 bit. Ogni gruppo indicizza la matrice e viene emesso un carattere risultante. Dopo questi tre caratteri, =viene emesso un carattere di riempimento.

Consideriamo tre esempi per imparare come funziona l'algoritmo di codifica. Per prima cosa, supponiamo di voler codificare @!*:

Source ASCII bit sequences with prepended 0 bits to form 8-bit bytes: @ ! * 01000000 00100001 00101010 Dividing this 24-bit group into four 6-bit groups yields the following: 010000 | 000010 | 000100 | 101010 These bit patterns equate to the following indexes: 16 2 4 42 Indexing into the Base64 alphabet shown earlier yields the following encoding: QCEq

Continueremo accorciando la sequenza di input a @!:

Source ASCII bit sequences with prepended 0 bits to form 8-bit bytes: @ ! 01000000 00100001 Two zero bits are appended to make three 6-bit groups: 010000 | 000010 | 000100 These bit patterns equate to the following indexes: 16 2 4 Indexing into the Base64 alphabet shown earlier yields the following encoding: QCE An = pad character is output, yielding the following final encoding: QCE=

L'esempio finale riduce la sequenza di input a @:

Source ASCII bit sequence with prepended 0 bits to form 8-bit byte: @ 01000000 Four zero bits are appended to make two 6-bit groups: 010000 | 000000 These bit patterns equate to the following indexes: 16 0 Indexing into the Base64 alphabet shown earlier yields the following encoding: QA Two = pad characters are output, yielding the following final encoding: QA==

L'algoritmo di decodifica è l'inverso dell'algoritmo di codifica. Tuttavia, è libero di intraprendere l'azione appropriata al rilevamento di un carattere non nell'alfabeto Base64 o di un numero errato di caratteri del pad.

Varianti di Base64

Sono state ideate diverse varianti di Base64. Alcune varianti richiedono che il flusso di output codificato sia diviso in più righe di lunghezza fissa con ogni riga che non superi un certo limite di lunghezza e (ad eccezione dell'ultima riga) sia separata dalla riga successiva tramite un separatore di riga (ritorno \ra capo seguito da un avanzamento riga \n). Descrivo le tre varianti supportate dall'API Base64 di Java 8. Controlla la voce Base64 di Wikipedia per un elenco completo delle varianti.

Di base

RFC 4648 descrive una variante Base64 nota come Basic . Questa variante utilizza l'alfabeto Base64 presentato nella tabella 1 di RFC 4648 e RFC 2045 (e mostrato in precedenza in questo post) per la codifica e la decodifica. Il codificatore tratta il flusso di output codificato come una riga; non vengono emessi separatori di riga. Il decoder rifiuta una codifica che contiene caratteri al di fuori dell'alfabeto Base64. Notare che queste e altre disposizioni possono essere ignorate.

MIMO

RFC 2045 descrive una variante Base64 nota come MIME . Questa variante utilizza l'alfabeto Base64 presentato nella tabella 1 dell'RFC 2045 per la codifica e la decodifica. Il flusso di output codificato è organizzato in righe di non più di 76 caratteri; ogni riga (tranne l'ultima riga) è separata dalla riga successiva tramite un separatore di riga. Tutti i separatori di riga o altri caratteri non trovati nell'alfabeto Base64 vengono ignorati durante la decodifica.

URL e nome file sicuro

RFC 4648 descrive una variante Base64 nota come URL e filename Safe . Questa variante utilizza l'alfabeto Base64 presentato nella tabella 2 della RFC 4648 per la codifica e la decodifica. L'alfabeto è identico all'alfabeto mostrato in precedenza tranne che -sostituisce +e _sostituisce /. Non vengono emessi separatori di riga. Il decoder rifiuta una codifica che contiene caratteri al di fuori dell'alfabeto Base64.

La codifica Base64 è utile nel contesto di lunghi dati binari e richieste HTTP GET. L'idea è codificare questi dati e quindi aggiungerli all'URL GET HTTP. Se è stata utilizzata la variante Basic o MIME, qualsiasi carattere +o /nei dati codificati dovrebbe essere codificato in URL in sequenze esadecimali ( +diventa %2Be /diventa %2F). La stringa dell'URL risultante sarebbe leggermente più lunga. Sostituendo +con -e /con _, URL e Filename Safe evitano la necessità di codificatori / decodificatori di URL (e il loro impatto sulla lunghezza dei valori codificati). Inoltre, questa variante è utile quando i dati codificati devono essere utilizzati per un nome di file perché i nomi di file Unix e Windows non possono contenere /.

Lavorare con l'API Base64 di Java

Java 8 introduced a Base64 API consisting of the java.util.Base64 class along with its Encoder and Decoder nested static classes. Base64 presents several static methods for obtaining encoders and decoders:

  • Base64.Encoder getEncoder(): Return an encoder for the Basic variant.
  • Base64.Decoder getDecoder(): Return a decoder for the Basic variant.
  • Base64.Encoder getMimeEncoder(): Return an encoder for the MIME variant.
  • Base64.Encoder getMimeEncoder(int lineLength, byte[] lineSeparator): Return an encoder for a modified MIME variant with the given lineLength (rounded down to the nearest multiple of 4 -- output not separated into lines when lineLength<= 0) and lineSeparator. It throws java.lang.IllegalArgumentException when lineSeparator includes any Base64 alphabet character presented in Table 1 of RFC 2045.

    RFC 2045's encoder, which is returned from the noargument getMimeEncoder() method, is rather rigid. For example, that encoder creates encoded text with fixed line lengths (except for the last line) of 76 characters. If you want an encoder to support RFC 1421, which dicates a fixed line length of 64 characters, you need to use getMimeEncoder(int lineLength, byte[] lineSeparator).

  • Base64.Decoder getMimeDecoder(): Return a decoder for the MIME variant.
  • Base64.Encoder getUrlEncoder(): Return an encoder for the URL and Filename Safe variant.
  • Base64.Decoder getUrlDecoder(): Return a decoder for the URL and Filename Safe variant.

Base64.Encoder presents several threadsafe instance methods for encoding byte sequences. Passing the null reference to one of the following methods results in java.lang.NullPointerException:

  • byte[] encode(byte[] src): Encode all bytes in src to a newly-allocated byte array, which this method returns.
  • int encode(byte[] src, byte[] dst): Encode all bytes in src to dst (starting at offset 0). If dst isn't big enough to hold the encoding, IllegalArgumentException is thrown. Otherwise, the number of bytes written to dst is returned.
  • ByteBuffer encode(ByteBuffer buffer): Encode all remaining bytes in buffer to a newly-allocated java.nio.ByteBuffer object. Upon return, buffer's position will be updated to its limit; its limit won't have been changed. The returned output buffer's position will be zero and its limit will be the number of resulting encoded bytes.
  • String encodeToString(byte[] src): Encode all bytes in src to a string, which is returned. Invoking this method is equivalent to executing new String(encode(src), StandardCharsets.ISO_8859_1).
  • Base64.Encoder withoutPadding(): Return an encoder that encodes equivalently to this encoder, but without adding any padding character at the end of the encoded byte data.
  • OutputStream wrap(OutputStream os): Avvolge un flusso di output per la codifica dei dati byte. Si consiglia di chiudere prontamente il flusso di output restituito dopo l'uso, durante il quale scaricherà tutti i possibili byte rimanenti nel flusso di output sottostante. La chiusura del flusso di output restituito chiuderà il flusso di output sottostante.

Base64.Decoderpresenta diversi metodi di istanza threadsafe per la decodifica di sequenze di byte. Il passaggio del riferimento null a uno dei seguenti metodi comporta NullPointerException: