base64 - Codifica Dati Binari con ASCII

Scopo: Il modulo base64 contiene funzioni per traslare dati binari in un sottoinsieme di valori ASCII adatti per la trasmissione utilizzando protocolli a testo semplice.

Le codifiche base64, base32, base16 e base85 convertono byte ad 8 bit in valori che rientrano nell'intervallo di caratteri stampabili ASCII, a scapito dell'utilizzo di più bit, per rappresentare dati con compatibilità con sistemi che supportano solo dati ASCII, tipo SMTP. I valori base corrispondono alla lunghezza dell'alfabeto usato in ciascuna codifica. Ci sono anche varianti delle codifiche originali per preservare gli URL che usano alfabeti leggermente differenti.

Codifica Base 64

Questo è un esempio base di codifica di un testo.

# base64_b64encode.py
# -*- coding: utf-8 -*-
#end_pymotw_header

import base64
import textwrap

# Carica questo file sorgente ed elimina l'intestazione
with open(__file__, 'r', encoding='utf-8') as input:
    raw = input.read()
    initial_data = raw.split('#end_pymotw_header')[1]

byte_string = initial_data.encode('utf-8')
encoded_data = base64.b64encode(byte_string)
wrapped_data = textwrap.fill(str(encoded_data), width=50)

num_initial = len(byte_string)

padding = 3 - (num_initial % 3)

print('{} byte prima della codifica'.format(num_initial))
print('Attesi {} byte di riempimento'.format(padding))
print('{} byte dopo la codifica\n'.format(len(encoded_data)))
print(wrapped_data)

L'input deve essere un stringa di byte, per questo la stringa unicode viene prima codificata in UTF-8. L'output mostra i 195 bye della sorgente UTF-8 espansa a 260 byte dopo la codifica.

Non ci sono ritorni a capo nei dati codificati prodotti dalla libreria, ma l'output è stato allineato artificialmente per meglio accomodarlo nella pagina.
$ python3 base64_b64encode.py

195 byte prima della codifica
Attesi 3 byte di riempimento
260 byte dopo la codifica

b'CgppbXBvcnQgYmFzZTY0CmltcG9ydCB0ZXh0d3JhcAoKIyBD
YXJpY2EgcXVlc3RvIGZpbGUgc29yZ2VudGUgZWQgZWxpbWluYS
BsJ2ludGVzdGF6aW9uZQp3aXRoIG9wZW4oX19maWxlX18sICdy
JywgZW5jb2Rpbmc9J3V0Zi04JykgYXMgaW5wdXQ6CiAgICByYX
cgPSBpbnB1dC5yZWFkKCkKICAgIGluaXRpYWxfZGF0YSA9IHJh
dy5zcGxpdCgn'

Decodifica Base 64

b64decode() converte una stringa codificata nella sua forma originale, prendendo quattro byte e convertendoli nei tre originali, usando una tabella di associazione.

# base64_b64decode.py

import base64

encoded_data = b'UXVlc3RpIHNvbm8gaSBkYXRpLCBpbiBjaGlhcm8='
decoded_data = base64.b64decode(encoded_data)
print('Codificati   :', encoded_data)
print('Decodificati :', decoded_data)

Il processo di decodifica cerca ciascuna sequenza di 24 bit nell'input (tre byte) e codifica quegli stessi 24 bit spandendoli nei quattro byte dell'output. I simboli di uguale alla fine dell'output sono inseriti come riempimento visto che il numero di bit nella stringa originale non era equamente divisibile per 24, in questo esempio.

$ python3 base64_b64decode.py

Codificati   : b'UXVlc3RpIHNvbm8gaSBkYXRpLCBpbiBjaGlhcm8='
Decodificati : b'Questi sono i dati, in chiaro'

Il valore ritornato da b64decode() è una stringa di byte. Se si sa che il contenuto è testo, la stringa di byte può essere convertita in un oggetto unicode. Comunque lo scopo di utilizzare la codifica base 64 è di poter trasmettere dati binari, quindi non è sempre sicuro assumere che il valore decodificato sia testo.

Variazioni a prova di URL

Visto che l'alfabeto predefinito base64 potrebbe usare + e /, caratteri che sono usati negli URL, è spesso necessario usare una codifica alternativa con sostituti per quei caratteri.

# base64_urlsafe.py

import base64

encodes_with_pluses = b'\xfb\xef'
encodes_with_slashes = b'\xff\xff'

for original in [encodes_with_pluses, encodes_with_slashes]:
    print('Originale              :', repr(original))
    print('Codifica standard      :',
          base64.standard_b64encode(original))
    print('Codifica a prova di URL:',
          base64.urlsafe_b64encode(original))
    print()

Il + è sostituito da un - e / da un _. Per il resto, l'alfabeto è lo stesso.

$ python3 base64_urlsafe.py

Originale              : b'\xfb\xef'
Codifica standard      : b'++8='
Codifica a prova di URL: b'--8='

Originale              : b'\xff\xff'
Codifica standard      : b'//8='
Codifica a prova di URL: b'__8='

Altre Codifiche

Oltre a Base64 il modulo fornisce anche funzioni per lavorare con dati codificati con Base85, Base32 e Base16 (esadecimale).

# base64_base32.py

import base64

original_data = b'Questi sono i dati, in chiaro.'
print('Originale    :', original_data)

encoded_data = base64.b32encode(original_data)
print('Codificato   :', encoded_data)

decoded_data = base64.b32decode(encoded_data)
print('Decodificato :', decoded_data)

L'alfabeto Base32 include le 26 lettere maiuscole dall'insieme ASCII e le cifre da 2 a 7.

$ python3 base64_base32.py

Originale    : b'Questi sono i dati, in chiaro.'
Codificato   : b'KF2WK43UNEQHG33ON4QGSIDEMF2GSLBANFXCAY3INFQXE3ZO'
Decodificato : b'Questi sono i dati, in chiaro.'

Le funzioni Base16 lavorano con l'alfabeto esadecimale.

# base64_base16.py

import base64

original_data = b'Questi sono i dati, in chiaro.'
print('Originali    :', original_data)

encoded_data = base64.b16encode(original_data)
print('Codificati   :', encoded_data)

decoded_data = base64.b16decode(encoded_data)
print('Decodificati :', decoded_data)

Ogni volta che il numero di bit da codificare scende, l'output nel formato codificato occupa più spazio.

$ python3 base64_base16.py

Originali    : b'Questi sono i dati, in chiaro.'
Codificati   : b'51756573746920736F6E6F206920646174692C20696E2063686961726F2E'
Decodificati : b'Questi sono i dati, in chiaro.'

Le funzioni Base85 usano un alfabeto espanso che è più efficiente a livello di spazio di quello Base64.

# base64_base85.py

import base64

original_data = b'TQuesti sono i dati, in chiaro..'
print('Originale    : {} byte {!r}'.format(
    len(original_data), original_data))

b64_data = base64.b64encode(original_data)
print('Codifica b64 : {} byte {!r}'.format(
    len(b64_data), b64_data))

b85_data = base64.b85encode(original_data)
print('Codifica b85 : {} byte {!r}'.format(
    len(b85_data), b85_data))

a85_data = base64.a85encode(original_data)
print('Codifica a85 : {} byte {!r}'.format(
    len(a85_data), a85_data))

Ci sono diverse codifiche Base85 e diverse varianti usate nei formati Mercurial, Git e PDF. Python include due implementazioni, b85encode() implementa la versione usata in Git e Mercurial mentre a85encode() implementa la variante Ascii85 usata dai file PDF.

$ python3 base64_base85.py

Originale    : 31 byte b'TQuesti sono i dati, in chiaro.'
Codifica b64 : 44 byte b'VFF1ZXN0aSBzb25vIGkgZGF0aSwgaW4gY2hpYXJvLg=='
Codifica b85 : 39 byte b'R8e(hb98ASb8l{MAZZ|EVRUIMAZczOV`yn%a&Im'
Codifica a85 : 39 byte b'<)IdLF*)+=F)Pr7+DDs/@<?37+DG^9@q]RbEc3Q'

Vedere anche:

base64
La documentazione della libreria standard per questo modulo
RFC 3548
Le codifiche dati Base16, Base32 e Base 64
RFC 1924
Una rappresentazione compatta degli indirizzi IPv6 (suggerisce una codifica Base85 per gli indirizzi di rete IPv6)
Ascii85
Note di Portabilità per base64