Fin dai tempi di Giulio Cesare la crittografia ha giocato un ruolo importantissimo nella vita dell’uomo, decidendo le grandi guerre e muovendo grandi somme di denaro.
Figuriamoci se Oracle si lasciava scappare l’opportunità di implementare i più importanti algoritmi di cifratura!
Già in Oracle8i era presente il package DBMS_OBFUSCATION_TOOLKIT che implementava il DES, principale algoritmo di cifratura dell’epoca e tuttora utilizzatissimo, anche nella variante più complessa da violare: il Triple DES (o 3-DES).
In quest’ultimo decennio, però, ha ricevuto sempre maggiori consensi l’algoritmo AES, adottato come standard dal governo degli Stati Uniti.
Era quindi necessario implementare anche questo nuovo algoritmo in Oracle e con l’occasione è stato deciso di passare ad un nuovo package di funzionalità, DBMS_CRYPTO presentato per la prima volta nella versione 10R1 del DB.
In quest’articolo mi propongo di illustrare velocemente le principali funzionalità del package e mostrare un esempio funzionante di codice che lo utilizza.
Innanzi voglio dettagliare le funzionalità implementate in DBMS_CRYPTO:
Algoritmi di Crittografia:
Oltre ai già citati DES, Triple DES (in questa versione anche a due ed a tre chiavi) ed AES c’è anche l’RC4.
Funzioni di Hashing:
MD4, MD5, SHA-1.
Funzioni per la generazione di MAC code:
MD5 e SHA-1.
Funzioni per la generazione di valori pseudo-casuali:
E’ possibile generare un valore pseudo-casuale di tipo BINARY_INTEGER, NUMBER o RAW.
Prima di passare ad un esempio di procedura per la cifratura/decifratura voglio specificare che il package DBMS_CRYPTO contiene due procedure (ENCRYPT e DECRYPT) che gestiscono cifratura e decifratura di campi di tipo LOB, sia BLOB che CLOB, e due funzioni (sempre ENCRYPT e DECRYPT) che gestiscono cifratura e decifratura di campi RAW. I campi VARCHAR2 devono essere convertiti in RAW e nel set di caratteri AL32UTF8 prima di essere cifrati.
Cominciamo con una funzione per la cifratura di una stringa varchar2 che ritorna la stringa cifrata, ovviamente in un campo RAW, e riceve in input la stringa da cifrare, la chiave di cifratura ed un flag che indica l’algoritmo da utilizzare: A=AES con chiave a 32 byte, D=3DES con chiave a 24 byte.
CREATE OR REPLACE function CIFRA(in_str in VARCHAR2, chiave in varchar2, Algoritmo in char) return RAW is raw_da_cifrare RAW(2048) := UTL_RAW.CAST_TO_RAW(CONVERT(in_str,'AL32UTF8','WE8MSWIN1252')); raw_chiave RAW(128) := UTL_RAW.CAST_TO_RAW(CONVERT(chiave,'AL32UTF8','WE8MSWIN1252')); alg PLS_INTEGER; BEGIN if Algoritmo = 'D' then alg := DBMS_CRYPTO.DES3_CBC_PKCS5; elsif Algoritmo = 'A' then alg := DBMS_CRYPTO.ENCRYPT_AES256 + DBMS_CRYPTO.CHAIN_CBC + DBMS_CRYPTO.PAD_PKCS5; else RAISE_APPLICATION_ERROR(-20001, 'Algoritmo non previsto, scegliere D o A!'); end if; return dbms_crypto.Encrypt(raw_da_cifrare, alg, raw_chiave); END; /
La funzione ENCRYPT prende in input i seguenti parametri:
Una stringa di massimo 2048 byte da cifrare, prima di essere cifrata viene convertita in RAW e nel set di caratteri AL32UTF8.
In realtà non bisogna solo specificare un algoritmo, ma una terna formata da algoritmo, modalità di chaining (ECB, CBC, CFB, OFB) e criterio di riempimento dei blocchi per raggiungere la lunghezza standard (PKCS5, nessun riempimento, riempimento a zeri).
Ogni algoritmo di cifratura è rappresentato da una costante e lo stesso vale per ogni modalità di chaining e per ogni criterio di padding.
Il valore del parametro da passare alla funzione di cifratura, dunque, è la somma di tre valori scelti tra i suddetti.
Nell’esempio per l’AES questo è chiaro, ho scelto ENCRYPT_AES256 come algoritmo di cifratura, CHAIN_CBC come modalità di chaining e PAD_PKCS5 come criterio di padding.
Per il DES la somma delle tre componenti scelte (ENCRYPT_3DES + CHAIN_CBC + PAD_PKCS5) è sintetizzata con l’unica costante DES3_CBC_PKCS5 che è già predefinita nel package.
Anche la chiave di cifratura deve essere convertita in RAW e nel set di caratteri AL32UTF8. Bisogna fare attenzione alla lunghezza della chiave.
La lunghezza minima dipende dall’algoritmo utilizzato, nel nostro caso è 24 byte per il 3DES e 32 per l’AES.
Noi non lo passiamo.
Ed ecco come cifriamo un testo utilizzando la funzione appena definita:
SQL> select cifra('Questo è un testo molto lungo da 2 cifrare, contiene anche ritorni a capo etc...', 3 'MIACHIAVE012345678901234','D') cifrato 4 from dual; CIFRATO ---------------------------------------- 812DAC49DBEBD0619CEC10E4341FDA9DCC435007 1EB29B3B0F62AA8D95B1AA5996BA75ABB1E4AB91 FEA914480892D248A5D92E5642559135CD15DD50 6D878D5E6B17355800729310C0675B33B7546C12
passiamo alla decifratura:
CREATE OR REPLACE function DECIFRA(in_raw in RAW, chiave in varchar2, Algoritmo in char) return VARCHAR2 is raw_chiave RAW(128) := UTL_RAW.CAST_TO_RAW(CONVERT(chiave,'AL32UTF8','WE8MSWIN1252')); raw_decifrata RAW(2048); alg PLS_INTEGER; BEGIN if Algoritmo = 'D' then alg := DBMS_CRYPTO.DES3_CBC_PKCS5; elsif Algoritmo = 'A' then alg := DBMS_CRYPTO.ENCRYPT_AES256 + DBMS_CRYPTO.CHAIN_CBC + DBMS_CRYPTO.PAD_PKCS5; else RAISE_APPLICATION_ERROR(-20001, 'Algoritmo non previsto, scegliere D o A!'); end if; raw_decifrata := dbms_crypto.Decrypt(in_raw, alg, raw_chiave); return CONVERT(UTL_RAW.CAST_TO_VARCHAR2(raw_decifrata),'WE8MSWIN1252','AL32UTF8'); END; /
Dove banalmente rispetto a prima abbiamo chiamato la funzione per decifrare e poi abbiamo riconvertito la RAW ottenuta in VARCHAR2 nel nostro set di caratteri WE8MSWIN1252.
Facciamo la prova del nove, proviamo a decifrare la stringa che prima avevamo cifrato:
SQL> r 1 select decifra(hextoraw('DC84F4C1CA98FC27208C3A501 3F0A88720DCCE5E5C1E250ADD38352404F3CB73E6C4A97DE64 E999997B43F5531EAE824D6A2EA7AC5EE78BF5DBC3CCB86CC1 31B89CBADD7295D71DBE57B7CDC1DFE3787'), 'MIACHIAVE012345678901234','D') decifrato 2* from dual DECIFRATO -------------------------------------------------- Questo è un testo molto lungo da cifrare, contiene anche ritorni a capo etc...
Attenzione: ho aggiunto alcuni ritorni a capo nell’esadecimale da decodificare per una questione di leggibilità. Quando ho eseguito l’istruzione sul DB i ritorni a capo non c’erano!
Faccio notare che nel chiamare la funzione ho dovuto utilizzare la funzione di conversione HEXTORAW per trasformare la stringa esadecimale in una stringa binaria.
Nell’esempio della cifratura, la conversione opposta RAWTOHEX era stata eseguita automaticamente da SQL*Plus all’atto di visualizzare i risultati.
Gli esempi precedenti utilizzavano il 3DES, facciamo anche una prova con l’AES:
SQL> select cifra('Testo da cifrare con AES', 2 'CHIAVEDA32BYTE00CHIAVEDA32BYTE00','A') cifrato 3 from dual; CIFRATO ---------------------------------------- 84FB4135CD3FAF07AF7B729ED2A0FD7BF306A5AA 6923B39E81D27CA693D5E343 SQL> select decifra(HEXTORAW('84FB4135CD3FAF07AF7B729ED2A0FD 7BF306A5AA6923B39E81D27CA693D5E343'), 2 'CHIAVEDA32BYTE00CHIAVEDA32BYTE00','A') decifrato 3 from dual; DECIFRATO -------------------------------------------------- Testo da cifrare con AES
Come cambiano le cose se devo lavorare su campi BLOB e CLOB? Come sempre quando si maneggiano i LOB si lavora su riferimenti ad oggetti del DB.
Quindi bisogna prima di tutto disporre di una tabella che contenga i BLOB da cifrare e cifrati. Io ho una tabella MYFILES che contiene una canzone degli U2 ed un episodio di FlashForward:
SQL> select * from myfiles ID NAME CONTENT ---------- -------------------- -------------------- 1 WithOrWithoutYou.mp3 49443303000000000F76 47454F42000006A60000 0062696E617279000052 65616C4A756B65626F78 3A4D6574616461746100 524A4D44000000010000 06800000000000000000 0000001D000000220000 2 FlashForward.avi 5249464632B9DF154156 49204C4953547E220000 6864726C617669683800 0000409C000000000000 000000001001000072ED 00000000000002000000 00000000700200006001 00000000000000000000
Mi propongo di creare una funzione che, ricevendo in input l’ID del file da cifrare, crei un nuovo record con il file cifrato e restituisca il nuovo ID.
create or replace function cifra_file(in_id in number, chiave in varchar2, Algoritmo in char) return number is blob_da_cifrare BLOB; blob_cifrato BLOB; raw_chiave RAW(128) := UTL_RAW.CAST_TO_RAW(CONVERT(chiave,'AL32UTF8','WE8MSWIN1252')); alg PLS_INTEGER; out_id number; begin if Algoritmo = 'D' then alg := DBMS_CRYPTO.DES3_CBC_PKCS5; elsif Algoritmo = 'A' then alg := DBMS_CRYPTO.ENCRYPT_AES256 + DBMS_CRYPTO.CHAIN_CBC + DBMS_CRYPTO.PAD_PKCS5; else RAISE_APPLICATION_ERROR(-20001, 'Algoritmo non previsto, scegliere D o A!'); end if; out_id := fileid.nextval; -- inserisco un record che conterrà il blob cifrato: insert into myfiles (id, name, content) select out_id, name||' - cifrato', empty_blob() from myfiles where id = in_id; select content into blob_da_cifrare from myfiles where id = in_id; select content into blob_cifrato from myfiles where id = out_id; DBMS_CRYPTO.Encrypt(blob_cifrato,blob_da_cifrare,alg,raw_chiave); return out_id; end; /
Rispetto a prima c’è pochissimo di nuovo, giusto la gestione dei puntatori a BLOB ed il fatto che chiamiamo la procedura ENCRYPT anziché la funzione.
Ed ecco un esempio di utilizzo, ovviamente non posso chiamare la funzione da SQL perché inserisce un record nel DB, quindi la chiamo con una EXEC e stampo a video l’ID che ritorna.
SQL> exec dbms_output.put_line(cifra_file(1, 'MIACHIAVE012345678901234','D')) 3 Procedura PL/SQL completata correttamente. SQL> select * from myfiles; ID NAME CONTENT ---------- -------------------- -------------------- 1 WithOrWithoutYou.mp3 49443303000000000F76 47454F42000006A60000 0062696E617279000052 65616C4A756B65626F78 3A4D6574616461746100 524A4D44000000010000 06800000000000000000 0000001D000000220000 2 FlashForward.avi 5249464632B9DF154156 49204C4953547E220000 6864726C617669683800 0000409C000000000000 000000001001000072ED 00000000000002000000 00000000700200006001 00000000000000000000 3 WithOrWithoutYou.mp3 9243E49958296FFC341B - cifrato 1120021A70A6006A285A CE89D6F62EA04187E59B 6523DC6336B9FDDDC820 37DDCF8FDCA6FF1E0681 507A44F117F94F2DE517 8298B6F39FE6FA710003 8BE3C3F80F43AABF0200
Ed ora la funzione per decifrare un blob già presente in tabella.
E’ quasi identica alla precedente…
create or replace function decifra_file(in_id in number, chiave in varchar2, Algoritmo in char) return number is blob_da_decifrare BLOB; blob_decifrato BLOB; raw_chiave RAW(128) := UTL_RAW.CAST_TO_RAW(CONVERT(chiave,'AL32UTF8','WE8MSWIN1252')); alg PLS_INTEGER; out_id number; begin if Algoritmo = 'D' then alg := DBMS_CRYPTO.DES3_CBC_PKCS5; elsif Algoritmo = 'A' then alg := DBMS_CRYPTO.ENCRYPT_AES256 + DBMS_CRYPTO.CHAIN_CBC + DBMS_CRYPTO.PAD_PKCS5; else RAISE_APPLICATION_ERROR(-20001, 'Algoritmo non previsto, scegliere D o A!'); end if; out_id := fileid.nextval; -- inserisco un record che conterrà il blob decifrato: insert into myfiles (id, name, content) select out_id, name||' - decifrato', empty_blob() from myfiles where id = in_id; select content into blob_da_decifrare from myfiles where id = in_id; select content into blob_decifrato from myfiles where id = out_id; DBMS_CRYPTO.Decrypt(blob_decifrato,blob_da_decifrare,alg,raw_chiave); return out_id; end; /
Ed ecco un test di decifratura:
SQL> exec dbms_output.put_line(decifra_file(3, 'MIACHIAVE012345678901234','D')) 4 Procedura PL/SQL completata correttamente. SQL> select * from myfiles; ID NAME CONTENT ---------- -------------------- -------------------- 1 WithOrWithoutYou.mp3 49443303000000000F76 47454F42000006A60000 0062696E617279000052 65616C4A756B65626F78 3A4D6574616461746100 524A4D44000000010000 06800000000000000000 0000001D000000220000 2 FlashForward.avi 5249464632B9DF154156 49204C4953547E220000 6864726C617669683800 0000409C000000000000 000000001001000072ED 00000000000002000000 00000000700200006001 00000000000000000000 3 WithOrWithoutYou.mp3 9243E49958296FFC341B - cifrato 1120021A70A6006A285A CE89D6F62EA04187E59B 6523DC6336B9FDDDC820 37DDCF8FDCA6FF1E0681 507A44F117F94F2DE517 8298B6F39FE6FA710003 8BE3C3F80F43AABF0200 4 WithOrWithoutYou.mp3 49443303000000000F76 - cifrato - decifra 47454F42000006A60000 to 0062696E617279000052 65616C4A756B65626F78 3A4D6574616461746100 524A4D44000000010000 06800000000000000000 0000001D000000220000
Il record numero 4 è identico all’originale numero 1 ed è stato ottenuto prima cifrando e poi decifrando l’originale.
Alla prossima,
Massimo
Tag: crittografia, DBMS_CRYPTO, oracle, PL/SQL
Lascia un commento