Encodage et décodage Base64 en Java 8

On se souviendra de Java 8 principalement pour l'introduction de lambdas, de flux, d'un nouveau modèle de date / heure et du moteur JavaScript Nashorn à Java. Certains se souviendront également de Java 8 pour avoir introduit diverses fonctionnalités petites mais utiles telles que l'API Base64. Qu'est-ce que Base64 et comment utiliser cette API? Cet article répond à ces questions.

Qu'est-ce que Base64?

Base64 est un schéma de codage binaire en texte qui représente des données binaires dans un format de chaîne ASCII imprimable en les traduisant en une représentation radix-64. Chaque chiffre Base64 représente exactement 6 bits de données binaires.

Demande Base64 pour les documents de commentaires

Base64 a été décrite pour la première fois (mais non nommée) dans la RFC 1421: Amélioration de la confidentialité pour le courrier électronique Internet: Partie I: Procédures de cryptage et d'authentification des messages. Plus tard, il a été officiellement présenté comme Base64 dans RFC 2045: Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies, puis revisité dans RFC 4648: The Base16, Base32, and Base64 Data Encodings.

Base64 est utilisé pour empêcher les données d'être modifiées pendant leur transit via des systèmes d'information, tels que le courrier électronique, qui peuvent ne pas être propres 8 bits (ils peuvent déformer les valeurs 8 bits). Par exemple, vous joignez une image à un e-mail et souhaitez que l'image arrive à l'autre extrémité sans être déformée. Votre logiciel de messagerie Base64 encode l'image et insère le texte équivalent dans le message, comme illustré ci-dessous:

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'illustration montre que cette image codée commence par /et se termine par =. Le ...indique le texte que je n'ai pas montré par souci de concision. Notez que l'encodage complet pour cet exemple ou tout autre exemple est environ 33% plus grand que les données binaires d'origine.

Le logiciel de messagerie du destinataire décodera en Base64 l'image textuelle encodée pour restaurer l'image binaire d'origine. Pour cet exemple, l'image serait affichée en ligne avec le reste du message.

Encodage et décodage Base64

Base64 repose sur de simples algorithmes d'encodage et de décodage. Ils fonctionnent avec un sous-ensemble de 65 caractères d'US-ASCII où chacun des 64 premiers caractères correspond à une séquence binaire équivalente de 6 bits. Voici l'alphabet:

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

Le 65e caractère ( =) est utilisé pour remplir le texte encodé en Base64 à une taille intégrale, comme expliqué brièvement.

Propriété de sous-ensemble

Ce sous-ensemble a la propriété importante qu'il est représenté de manière identique dans toutes les versions d'ISO 646, y compris US-ASCII, et tous les caractères du sous-ensemble sont également représentés de manière identique dans toutes les versions d'EBCDIC.

L'algorithme de codage reçoit un flux d'entrée d'octets de 8 bits. Ce flux est supposé être ordonné avec le bit le plus significatif en premier: le premier bit est le bit de poids fort du premier octet, le huitième bit est le bit de poids faible de cet octet, et ainsi de suite.

De gauche à droite, ces octets sont organisés en groupes de 24 bits. Chaque groupe est traité comme quatre groupes de 6 bits concaténés. Chaque groupe de 6 bits indexe dans un tableau des 64 caractères imprimables; le caractère résultant est sorti.

Lorsque moins de 24 bits sont disponibles à la fin des données en cours de codage, zéro bit est ajouté (à droite) pour former un nombre entier de groupes de 6 bits. Ensuite, un ou deux =caractères de pad peuvent être émis. Il y a deux cas à considérer:

  • Un octet restant: quatre bits de zéro sont ajoutés à cet octet pour former deux groupes de 6 bits. Chaque groupe indexe le tableau et un caractère résultant est généré. Après ces deux caractères, deux =caractères de remplissage sont émis.
  • Deux octets restants: Deux bits de zéro sont ajoutés au deuxième octet pour former trois groupes de 6 bits. Chaque groupe indexe le tableau et un caractère résultant est généré. Après ces trois caractères, un =caractère de remplissage est émis.

Considérons trois exemples pour apprendre comment fonctionne l'algorithme d'encodage. Tout d'abord, supposons que nous souhaitons encoder @!*:

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

Nous continuerons en raccourcissant la séquence d'entrée à @!:

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=

Le dernier exemple raccourcit la séquence d'entrée à @:

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'algorithme de décodage est l'inverse de l'algorithme de codage. Cependant, il est libre de prendre les mesures appropriées lors de la détection d'un caractère ne figurant pas dans l'alphabet Base64 ou d'un nombre incorrect de caractères de remplissage.

Variantes Base64

Plusieurs variantes de Base64 ont été conçues. Certaines variantes exigent que le flux de sortie codé soit divisé en plusieurs lignes de longueur fixe, chaque ligne ne dépassant pas une certaine limite de longueur et (sauf pour la dernière ligne) étant séparée de la ligne suivante via un séparateur de ligne (retour chariot \rsuivi d'un saut de ligne \n). Je décris les trois variantes prises en charge par l'API Base64 de Java 8. Consultez l'entrée Base64 de Wikipedia pour une liste complète des variantes.

De base

La RFC 4648 décrit une variante Base64 connue sous le nom de Basic . Cette variante utilise l'alphabet Base64 présenté dans le tableau 1 de RFC 4648 et RFC 2045 (et montré plus tôt dans ce post) pour le codage et le décodage. L'encodeur traite le flux de sortie codé comme une ligne; aucun séparateur de ligne n'est sorti. Le décodeur rejette un encodage qui contient des caractères en dehors de l'alphabet Base64. Notez que ces stipulations et d'autres peuvent être annulées.

MIME

La RFC 2045 décrit une variante Base64 connue sous le nom de MIME . Cette variante utilise l'alphabet Base64 présenté dans le tableau 1 de la RFC 2045 pour l'encodage et le décodage. Le flux de sortie codé est organisé en lignes ne dépassant pas 76 caractères; chaque ligne (sauf la dernière ligne) est séparée de la ligne suivante via un séparateur de ligne. Tous les séparateurs de ligne ou autres caractères non trouvés dans l'alphabet Base64 sont ignorés lors du décodage.

URL et nom de fichier sécurisés

La RFC 4648 décrit une variante Base64 connue sous le nom d' URL et de nom de fichier sécurisé . Cette variante utilise l'alphabet Base64 présenté dans le tableau 2 de la RFC 4648 pour le codage et le décodage. L'alphabet est identique à l'alphabet montré précédemment sauf qu'il -remplace +et _remplace /. Aucun séparateur de ligne n'est sorti. Le décodeur rejette un encodage qui contient des caractères en dehors de l'alphabet Base64.

L'encodage Base64 est utile dans le contexte de longues données binaires et de requêtes HTTP GET. L'idée est d'encoder ces données, puis de les ajouter à l'URL HTTP GET. Si la variante de base ou MIME était utilisée, l'un des caractères +ou /des données codées devrait être codé en URL en séquences hexadécimales ( +devient %2Bet /devient %2F). La chaîne URL résultante serait un peu plus longue. En remplaçant +par -et /par _, URL et Filename Safe élimine le besoin d'encodeurs / décodeurs d'URL (et leurs impacts sur la longueur des valeurs encodées). De plus, cette variante est utile lorsque les données encodées doivent être utilisées pour un nom de fichier car les noms de fichiers Unix et Windows ne peuvent pas contenir /.

Travailler avec l'API Base64 de 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): Enveloppe un flux de sortie pour encoder des données d'octet. Il est recommandé de fermer rapidement le flux de sortie renvoyé après utilisation, pendant lequel il videra tous les octets restants possibles dans le flux de sortie sous-jacent. La fermeture du flux de sortie renvoyé fermera le flux de sortie sous-jacent.

Base64.Decoderprésente plusieurs méthodes d'instance threadsafe pour décoder des séquences d'octets. La transmission de la référence null à l'une des méthodes suivantes entraîne NullPointerException: