Kodiranje i dekodiranje Base64 u Javi 8

Java 8 ostat će zapamćen uglavnom po uvođenju lambda, streamova, novog modela datuma / vremena i JavaScript mehanizma Nashorn u Javu. Neki će se Java 8 sjećati i po tome što je predstavio razne male, ali korisne značajke poput Base64 API-ja. Što je Base64 i kako mogu koristiti ovaj API? Ovaj post odgovara na ova pitanja.

Što je Base64?

Base64 je shema kodiranja binarnog u tekst koja predstavlja binarne podatke u formatu niza za ispis ASCII prevodeći ih u prikaz radix-64. Svaka znamenka Base64 predstavlja točno 6 bitova binarnih podataka.

Zahtjev Base64 za komentare

Base64 je prvi put opisan (ali nije imenovan) u RFC 1421: Poboljšanje privatnosti za internetsku elektroničku poštu: I dio: Postupci šifriranja i provjere autentičnosti. Kasnije je službeno predstavljen kao Base64 u RFC 2045: Višenamjenska proširenja internetske pošte (MIME), prvi dio: Format internetskih tijela za poruke, a zatim je ponovno pregledan u RFC 4648: Kodiranje podataka Base16, Base32 i Base64.

Base64 se koristi za sprječavanje promjene podataka tijekom prolaska kroz informacijske sustave, poput e-pošte, koji možda nisu 8-bitni čisti (mogu prekriviti 8-bitne vrijednosti). Na primjer, e-poruci priložite sliku i želite da slika stigne na drugi kraj, a da se ne pokvari. Vaš softver za e-poštu Base64 kodira sliku i ubacuje ekvivalentan tekst u poruku, kao što je prikazano u nastavku:

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

Ilustracija pokazuje da ova kodirana slika započinje s /i završava s =. ...Pokazuje tekst koji ne pokazuju za sažetosti. Imajte na umu da je cjelokupno kodiranje za ovaj ili bilo koji drugi primjer oko 33 posto veće od izvornih binarnih podataka.

Primateljev softver za e-poštu Base64 dekodira kodiranu tekstualnu sliku kako bi obnovio izvornu binarnu sliku. U ovom se primjeru slika prikazuje u retku s ostatkom poruke.

Kodiranje i dekodiranje Base64

Base64 se oslanja na jednostavne algoritme kodiranja i dekodiranja. Oni rade s podskupom US-ASCII od 65 znakova, gdje se svaki od prva 64 znaka preslikava u ekvivalentnu 6-bitnu binarnu sekvencu. Evo abecede:

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

65. znak ( =) koristi se za umetanje Base64 kodiranog teksta u integralnu veličinu, kako je kratko objašnjeno.

Svojstvo podskupa

Ovaj podskup ima važno svojstvo da je identično predstavljen u svim inačicama ISO 646, uključujući US-ASCII, a svi su znakovi u podskupu također identično predstavljeni u svim inačicama EBCDIC-a.

Algoritam kodiranja prima ulazni tok od 8-bitnih bajtova. Pretpostavlja se da je ovaj tok poredan s najznačajnijim bitom prvi: prvi bit je bit visokog reda u prvom bajtu, osmi bit je bit nižeg reda u ovom bajtu itd.

S lijeva na desno, ti su bajtovi organizirani u 24-bitne skupine. Svaka se skupina tretira kao četiri povezane 6-bitne skupine. Svaka 6-bitna grupa indeksira se u niz od 64 znaka za ispis; izlazi rezultirajući znak.

Kad je na kraju podataka koji se kodiraju dostupno manje od 24 bita, dodaje se nula bitova (s desne strane) kako bi se formirao integralni broj 6-bitnih grupa. Tada se =mogu izvesti jedan ili dva znaka jastučića. Postoje dva slučaja koja treba razmotriti:

  • Jedan preostali bajt: Četiri nulta bita dodana su ovom bajtu kako bi se stvorile dvije 6-bitne grupe. Svaka grupa indeksira niz i rezultirajući znak izlazi. Nakon ova dva znaka =izlaze dva znaka jastučića.
  • Dva preostala bajta: Dva nulta bita dodana su drugom bajtu da bi se stvorile tri 6-bitne grupe. Svaka grupa indeksira niz i rezultirajući znak izlazi. Nakon ova tri znaka, =izlazi jedan znak pločice.

Razmotrimo tri primjera kako bismo naučili kako funkcionira algoritam kodiranja. Prvo, pretpostavimo da želimo kodirati @!*:

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

Nastavit ćemo skraćivanjem ulaznog niza na @!:

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=

Posljednji primjer skraćuje ulazni niz na @:

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==

Algoritam dekodiranja je inverzan algoritmu kodiranja. Međutim, slobodno možete poduzeti odgovarajuće radnje nakon otkrivanja znaka koji nije u abecedi Base64 ili netočnog broja znakova jastučića.

Varijante Base64

Osmišljeno je nekoliko inačica Base64. Neke inačice zahtijevaju da se kodirani izlazni tok podijeli na više linija fiksne duljine, pri čemu svaki redak ne prelazi određeno ograničenje duljine i (osim zadnjeg retka) odvaja se od sljedećeg retka pomoću separatora crta (povratak prijevoza \rpraćen linijskim uvlačenjem) \n). Opisujem tri varijante koje podržava Base64 API Java 8. Potpuni popis inačica potražite u Wikipedijinom unosu Base64.

Osnovni, temeljni

RFC 4648 opisuje Base64 inačicu poznatu kao Basic . Ova varijanta koristi abecedu Base64 predstavljenu u tablici 1 RFC 4648 i RFC 2045 (i prikazanu ranije u ovom postu) za kodiranje i dekodiranje. Kodiranje tretira kodirani izlazni tok kao jedan redak; ne izlaze separatori linija. Dekoder odbija kodiranje koje sadrži znakove izvan abecede Base64. Imajte na umu da se ove i druge odredbe mogu poništiti.

MIME

RFC 2045 opisuje Base64 inačicu poznatu kao MIME . Ova varijanta za kodiranje i dekodiranje koristi abecedu Base64 predstavljenu u tablici 1 RFC 2045. Kodirani izlazni tok organiziran je u retke koji nemaju više od 76 znakova; svaki redak (osim posljednjeg retka) odvojen je od sljedećeg retka pomoću separatora redaka. Svi odvajači redaka ili drugi znakovi koji nisu pronađeni u abecedi Base64 zanemaruju se tijekom dekodiranja.

URL i naziv datoteke sigurni

RFC 4648 opisuje Base64 inačicu poznatu kao URL i Filename Safe . Ova varijanta za kodiranje i dekodiranje koristi abecedu Base64 predstavljenu u tablici 2 RFC 4648. Abeceda je identična abecedi prikazanoj ranije, osim što -zamjenjuje +i _zamjenjuje /. Ne izlaze separatori linija. Dekoder odbija kodiranje koje sadrži znakove izvan abecede Base64.

Kodiranje Base64 korisno je u kontekstu dugih binarnih podataka i HTTP GET zahtjeva. Ideja je kodirati te podatke, a zatim ih dodati HTTP GET URL-u. Ako se koristila osnovna ili MIME varijanta, bilo koji +ili /znakovi u kodiranim podacima morali bi biti kodirani URL-om u heksadecimalne sekvence ( +postaje %2Bi /postaje %2F). Rezultirajući niz URL-a bio bi nešto duži. Zamjenom +s -i /s _, URL i naziv datoteke sigurno uklanja potrebu za enkodera URL / dekoderi (i njihovi utjecaji na duljinama kodiranih vrijednosti). Također, ova je varijanta korisna kada se kodirani podaci koriste za naziv datoteke jer imena datoteka Unix i Windows ne mogu sadržavati /.

Rad s Java-ovim Base64 API-jem

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): Omotajte izlazni tok za kodiranje bajtnih podataka. Preporučuje se odmah zatvoriti vraćeni izlazni tok nakon upotrebe, tijekom kojeg će se isprazniti svi mogući preostali bajtovi u temeljni izlazni tok. Zatvaranjem vraćenog izlaznog toka zatvorit će se temeljni izlazni tok.

Base64.Decoderpredstavlja nekoliko metoda sigurnih niti za dekodiranje sekvenci bajtova. Prosljeđivanje null reference na jednu od sljedećih metoda rezultira NullPointerException: