Informaia schimbat prin reea este expus la o serie de riscuri, putnd fi interceptat, alterat sau impersonificat. Criptarea informaiei utiliznd chei simetrice sau asimetrice, calcularea digest-urilor mesajelor i semnarea lor, ofer soluii pentru schimbul privat al informaiei (codificarea mesajelor astfel nct s devin computational imposibil descifrarea acestora) i asigurarea integritii informaiei (detectarea mesajelor corupte sau modificate). n cadrul acestui capitol sunt prezentate soluiile de securizare a informaiei distribuite n cadrul reelelor de calculatoare folosind tehnologii Java. 1. Suportul arhitectural al securitii oferit de Java Modelul de securitate oferit de Java reprezint unul dintre punctele sale forte, fiind ideal pentru dezvoltarea aplicaiilor ce ruleaz n medii distribuite. Modelul de securitate Java permite protejarea utilizatorilor n faa unor situaii critice precum rularea local a programelor descrcate din diverse surse. De exemplu, dac ne gndim c applet-urile Java ruleaz local pe maina utilizatorului n condiiile n care sursa de provenien a acestora ofer adesea prea puin ncredere, modelul de securitate trebuie s fie capabil s protejeze utilizatorul n faa posibilitii descrcrii i rulrii accidentale a unor virui. Modelul de securitate oferit de Java se bazeaz pe conceptul de sandbox, o definiie programabil a limitelor de securitate disponibile unui program. Altfel spus, un program Java poate rula numai n interiorul propriului sandbox. El poate face orice att timp ct se ncadreaz n limitele de securitate stabilite. De exemplu, limitele appleturilor Java includ implicit activiti precum:
Citirea sau scrierea pe discul local; Stabilirea unei conexiuni pe reea cu orice staie, cu excepia staiei de provenien a respectivului applet; Crearea de noi procese; ncrcarea de noi biblioteci dinamice i apelarea direct de metode native.
Prin limitarea anumitor aciuni modelul de securitate Java protejeaz utilizatorul n faa ostilitii codului rulat. Astfel, un applet ce se execut local i 2 care poate fi descrcat din diverse surse de nencredere ar putea transporta inclusiv cod de tip virus, ns modelul de securitate face imposibil acest lucru.
Figura 1. Modelul de securitate oferit de Java.
Componentele arhitecturii modelului de securitate Java ce sunt rspunztoare de limitarea aciunilor aplicaiilor sunt:
Elementele de securitate construite direct la nivel de main virtual (i de limbaj); Arhitectura de ncrcare a claselor; Verificatorul de ncrcare a claselor; Managerul de securitate i API-ul Java.
Dou dintre componentele arhitecturale Java deosebit de importante pentru modelul de securitate oferit sunt class loader i security manager. Accesul unei aplicaii Java la resursele sistemului cum ar fi ecranul, sistemul de fiiere, procese, fire de execuie, reea sunt controlate de ctre managerul de securitate. Limitele de securitate ale unei aplicaii Java pot fi controlate prin specificarea de ctre utilizator a unor noi clase bazate pe managerul implicit java.lang.SecurityManager. Definirea unui nou manager de securitate implic suprascrierea de metode declarate n clasa superioar ce au rolul de a decide permiterea efecturii unor operaii specifice (precum scrierea pe disc). n cadrul mediului de execuie al aplicaiei un singur obiect de tip SecurityManager poate fi specificat la un moment dat, accesul la toate resursele sistemului pentru respectiva aplicaie fiind controlat de metodele respectivului obiect. Managerul de securitate implicit se poate schimba folosind metoda static a clasei System, setSecurityManager(). Se poate chiar s specificm c nu dorim s utilizm deloc un manager prin apelul System.setSecurityManager(null) sau chiar SECURITATE N JAVA
3 3 s utilizm propriul manager de securitate, apelnd System.setSecurityManager(new MySecurityManager()), unde MySecurityManag er reprezint o subclas a clasei SecurityManager. Un exemplu de clas ce implementeaz un manager de securitate propriu care nu permite citirea fiierelor cu extensia .java este urmtorul:
class MySecurityManager extends SecurityManager {
public void checkRead(String s) {
if (s.endsWith(".java")) throw new SecurityException("Access to "+s+" file not allowed"); } }
public class MyApplication{
public static void typeFile(String filename) throws Exception {
int b; FileInputStream fin = new FileInputStream(filename); while( (b = fin.read()) != -1 ) System.out.print( (char) b ); fin.close(); }
O alt component important pentru modelul de securitate Java este class loaderul. Mecanismul de ncrcare a claselor Java are i rolul de a verifica dac clasele ncrcate ndeplinesc constrngeri de siguran la nivelul limbajului. O verificare mai adecvat a securitii claselor ncrcate poate fi obinut prin nlocuirea class loaderului oferit de sistem. De altfel acest mecanism este folosit de exemplu n cazul ncrcrii appleturilor. Class loaderul pentru appleturi n Java tie s ncarce clase de pe alte staii i tie s autentifice pachete jar semnate. Acest class loader utilizeaz spaii de nume diferite pentru clasele locale i pentru clase provenite de pe reea, pentru a evita conflictul acestora. Java permite utilizarea unui class-loader definit de utilizator, ce ar putea chiar fora i mai mult caracteristicile de securitate oferite implicit. Orice class- loader definit de utilizator trebuie s extind clasa abstract java.lang.ClassLoader i s redefineasc metoda abtract loadClass(). n general se recomand ca metoda aceasta s implementeze cel puin urmtoarele aciuni:
4 1. Verific dac clasa indicat prin className este ncrcat. Pentru a putea face acest lucru trebuie s se in evidena claselor anterior ncrcate. 2. Dac clasa nu este nc ncrcat, verific dac este clas sistem. n caz c nu este, va ncrca codul octei (din sistemul local de fiiere de exemplu). 3. Apelezeaz metoda defineClass()pentru a prezenta codul octei mainii virtuale.
Exemplul urmtor ilustreaz modalitatea de definire a unui nou class loader de ctre utilizator:
public class MyClassLoader extends ClassLoader { private Hashtable classes = new Hashtable();
Orice apel ctre o referin a unui obiect Java impune declanarea unor mecanisme de verificare implicite. De exemplu, o referin la un tip de date diferit rezult ntr-o serie de verificri implicite din partea Java asupra corectitudinii cast- ului. Accesarea unui array implic verificarea implicit dac indicele cerut se ncadreaz n limitele respectivului array. Orice astfel de ncercare greit rezult n aruncarea unor excepii la nivelul mainii virtuale.
Figura 2. Maparea Clase -> Domenii -> Permisiuni.
Un alt concept al sistemului de securitate este reprezentat de domeniul de protecie. Domeniu de protecie este un mecanism pentru gruparea i izolarea unui set de clase ale cror instane au impuse anumite permisiuni. Un domeniu de protecie poate fi limitat de ctre setul de obiecte ce sunt direct accesate de ctre un principal, o entitate din program creia i se acord anumite drepturi. Astfel este posibil chiar separarea domeniilor de protecie, a interaciunilor ntre ele, astfel nct orice interaciune cu anumite rutine aparinnd de diverse domenii de protecie s poat fi facut numai prin intermediul unor rutine sigure ale sistemului. 6 Domeniile de protecie se mpart n dou categorii: de sistem i de aplicaie. Orice resurs extern protejat, precum sistemul de fiiere, interfeele de reea, tastatura sau monitorul, poate fi accesat numai prin domenii de protecie ale sistemului. Domeniile de protecie sunt determinate de ctre politica de securitate folosit. Java menine ntotdeauna intern o mapare ntre cod (clase i instane) i domeniile de protecie i permisiunile curent acordate unor entiti ale aplicaiei executate. Un fir de execuie poate executa aciuni doar n interiorul propriului domeniu de protecie sau n interiorul domeniului de protecie al sistemului (de exemplu o aplicaie poate afia un mesaj, aciune ce necesit accesarea domeniului de protecie al sistemului). Ca regul, permisiunile sunt acordate astfel:
Setul de permisiuni acordate unui fir de execuie sunt considerate a fi intersecia tuturor permisiunilor corespunztoare tuturor domeniilor de protecie traversate de ctre execuia sa. Atunci cnd este apelat metoda doPrivileged a clasei AccessController setul de permisiuni acordate firului de execuie curent va include i o permisiune acordat de domeniul de protecie al codului.
Metoda doPrivileged acord unei aplicaii drepturi temporare la mai multe resurse dect sunt disponibile implicit acesteia. Acest aspect este necesar n cteva situaii excepionale. De exemplu, o aplicaie poate s nu aib acces direct la fiierele sistemului coninnd setul de fonturi folosite, dar ea trebuie s permit utilizatorului s aleag dintre fonturile pe care acesta le poate folosi n scrierea unui document. n acest moment poate fi folosit metoda doPrivileged a domeniului de sistem. n timpul execuiei unei aplicaii Java, dac este apelat o operaie critic asupra unei resurse a sistemului (operaie asupra unui fiier de exemplu), transparent pentru utilizator este apelat n fundal i o metod a clasei AccessControler ce evalueaz dreptul de execuie al respectivei operaii. n afara acestui comportament implicit, orice aplicaie poate defini mecanisme suplimentare de protecie a resurselor interne acesteia. De exemplu, o aplicaie bancar poate implementa mecanisme interne de protecie a unor resurse precum conturi bancare, depozite sau retrageri. Domeniul de protecie pentru astfel de aplicaii este specificat de ctre dezvoltatorii acestora, prin intermediul unor resurse ajuttoare puse la dispoziie de Java (ntr-o seciune ulterioar vom prezenta clasa SignedObject de exemplu). n Java accesul la resurse ale sistemului este specificat prin intermediul unor clase speciale ce implementeaz java.security.Permission. Un exemplu de folosire a clasei java.io.FilePermission n scopul acordrii dreptului de a citi fiierul de pe disc /tmp/abc este urmtorul:
perm = new java.io.FilePermission("/tmp/abc", "read");
SECURITATE N JAVA
7 7 Dezvoltatorii de aplicaii pot specifica noi permisiuni prin extinderea clasei abstracte de baz java.security.Permission sau extensii ale acesteia, precum java.security.BasicPermission. O metod important ce trebuie implementat de ctre noile extensii de clase este implies. Aceast metod este apelat pentru verificri de tipul a implic b, adic dac a are o anumit permisiune acordat atunci i b are acordat dreptul respectiv. Pentru crearea unei noi permisiuni este recomandat parcugerea ctorva pai. S presupunem c dorim crearea unei noi permisiuni n cadrul dezvoltrii unei aplicaii Java pentru vizualizare TV. Primul pas va consta n crearea unei noi clase com.abc.TVPermission, ce extinde clasa abstract java.security.Permission (sau o subclas a acesteia).
public class TVPermission extends java.security.Permission
Clasa nou creat trebuie inclus n pachetul furnizat mpreun cu aplicaia dezvoltat. Ulterior, fiecare utilizator ce dorete s adauge aceast permisiune pentru anumite operaii va trebui s adauge o intrare corespunztoare n fiierul de politici. Un exemplu de astfel de intrare ce acord dreptul codului provenind de la http://java.sun.com de a vizualiza canalul 5 este urmtorul:
grant codeBase "http://java.sun.com/" { permission com.abc.TVPermission "channel-5", "watch"; }
n cadrul aplicaiei, atunci cnd dorim s verificm valabilitatea unei aplicaii de a executa un anumit cod folosim metoda checkPermission a clasei AccessController furniznd ca parametru obiectul de tip com.abc.TVPermission, precum n exemplul urmtor:
com.abc.TVPermission tvperm = new com.abc.TVPermission("channel-5", "watch"); AccessController.checkPermission(tvperm);
Politica de securitate a sistemului n cadrul unei aplicaii Java, cuprinznd permisiunile disponibile codului provenind din diverse surse externe, este reprezentat de ctre o instan a clasei java.security.Policy. Mai exact, o subclas a acestei clase abstracte. n cadrul unei aplicaii mai multe astfel de obiecte pot fi definite, dar o singur instan poate fi n funcie n orice moment al ciclului de via al aplicaiei. De altfel putem afla care este obiectul Policy curent activ prin apelarea metodei getPolicy i putem oricnd s modificm politica prin apelarea metodei setPolicy. Locaia informaiei folosit de obiectul Policy este lsat la latitudinea aplicaiei Java (pot fi folosite fiiere de configuraie a politicii de securitate de tip text, fiiere binare serializate sau chiar baze de date). Implementarea de referin a politicii (cea folosit implicit dac nu specificm nimic la rularea unei aplicaii 8 Java) folosete informaia definit n fiiere statice de configurare. Un astfel de fiier de configurare a permisiunilor acordate entitilor aplicaiei este codat n format UTF-8. Un fiier de configurare conine n esen o list de intrri. El poate conine o intrare de tip keystore i zero sau mai multe intrri de tip grant. Un keystore reprezint o baz de date de chei private i de certificate digitale asociate acestora, precum lanuri de certificate X.509 ce sunt folosite pentru autentificarea unor chei publice corespondente (detalii despre certificate sunt prezentate n subcapitolul urmtor). Keystore-ul specificat n fiierul de configurare este folosit pentru a verifica cheile publice ale semnatarilor specificai n intrrile grant. O intrare keystore trebuie specificat numai atunci cnd n intrrile grant sunt specificai semnatari. Sintaxa unei astfel de intrri este urmtoarea:
keystore "some_keystore_url", "keystore_type";
n cadrul exemplului, some_keystore_url desemneaz locaia URL a fiierului keystore, iar keystore_type specific tipul de keystore folosit (ultimul argument fiind opional implicit JKS). Fiecare intrare de tip grant are urmtorul format:
grant [SignedBy "signer_names"] [, CodeBase "URL"] [, Principal [principal_class_name] "principal_name"] [, Principal [principal_class_name] "principal_name"] ... { permission permission_class_name [ "target_name" ] [, "action"] [, SignedBy "signer_names"]; permission ... };
De exemplu, urmtoarea politic acord permisiunea a.b.Foo codului semnat de George:
grant signedBy "George" { permission a.b.Foo; };
Urmtorul exemplu specific acordarea permisiunii de citire fiierelor .tmp ntregului cod (indiferent de semnatarul aplicaiei sau de sursa de provenien a acesteia):
grant { permission java.io.FilePermission ".tmp", "read"; };
Urmtorul exemplu specific acordarea a dou permisiuni codului ce este semnat de Michel i care provine de la http://java.sun.com:
grant codeBase "http://java.sun.com/*", signedBy "Michel" { SECURITATE N JAVA
Java mai ofer cteva mecanisme suplimentare de protecie intern. S presupunem de exemplu un scenariu n care un furnizor de obiecte ruleaz ntr-un alt fir de execuie dect consumatorul respectivelor obiecte. Pentru astfel de scenarii poate fi util folosirea unor obiecte de tip GuardedObject ce asigur protecia accesului la resurse. Furnizorul de resurse creaz obiectul ce trebuie transmis i creaz i un obiect GuardedObject ce ncapsuleaz obiectul iniial mpreun cu un obiect Guard i care este de fapt trimis consumatorului. Consumatorul nu poate obine ulterior obiectul inclus n ceea ce primete dect dac anumite verificri de securitate sunt satisfcute. Guard reprezint o interfa i practic orice obiect o poate implementa. Singura metod a acestei interfee este checkGuard. Metoda primete un obiect i execut anumite verificri de securitate. Clasa java.security.Permission implementeaz aceast interfa. De exemplu, s presupunem c dorim s putem cere unui fir de execuie s deschid un fiier a/b/c.txt pentru citire, dar nu putem avea ncredere n cine execut respectiva cerere sau n ce mprejurri (de exemplu, n cazul aplicaiilor distribuite, putem pune la dispoziie o serie de operaii unor utilizatori...). ntr-un astfel de context putem folosi un obiect GuardedObject pentru a fora verificri de control al accesului, dup cum urmeaz:
FileInputStream f = new FileInputStream("/a/b/c.txt"); FilePermission p = new FilePermission("/a/b/c.txt", "read"); GuardedObject g = new GuardedObject(f, p);
n acest moment firul de execuie poate transmite g ctre firul de execuie consumator, pentru obinerea accesului la f acesta fiind obligat s apeleze:
Metoda aceasta invoc metoda checkGuard a obiectului p, i, doarece p este de tip Permission, apelul respectiv ajunge s execute:
SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission(this);
Aceasta asigur efectuarea unui control al permisiunilor corespunztor. n acest fel pot fi implementate diverse politici de control ce pot verifica informaii de context, precum dreptul execuiei unor operaii n funcie de ora curent sau identitatea apelantului.
10 2. Suportul pentru securitatea comunicaiilor n Java 2.1. Certificate Funcia principal a unui certificat este aceea de a asocia unei identiti o cheie public. Certificatele sunt publice i conin informaie referitoare la subiectul certificatului (Entitatea care deine certificatul - numele, cu ce organizaie e asociat acest nume, locaia i ara), cheia public a certificatului, entitatea care a emis acest certificat i semnatura acesteia. Asocierea cheie-identitate (certificatul) este recunoscut i garantat de un ter, entitatea care semneaz certificatul i care este numit Autoritate de Certificare. Un certificat poate fi obinut printr-o cerere fcut unei Autoriti de Certificare. Cel care dorete s dein un astfel de certificat va genera local o pereche de chei, public i privat i va arta cheia public Autoritii de Certificare care i va creea i semna un certificat coninnd acea cheie public. n principiu se creaz local o cerere de certificat, de fapt un certificat nesemnat, care conine identitatea entitii i cheia public, certificat care este trimis Autoritii de Certificare spre semnare. Autoritatea de Certificare este responsabil s verifice dac identitatea din certificatul primit aparine celui care a trimis spre semnare certificatul. De obicei acest proces se face offline, find implicate i chestiuni legislative. Autoritatea de Certificare asigur integritatea datelor din certificat prin calcularea unui hash peste ntregul certificat i semnarea hash-ului cu cheia privat a Autoritii de Certificare (care deine i ea un certificat i o cheie privat asociat). Dup cum se observ Autoritatea de Certificare trebuie s fie considerat de ncredere pentru ca asocierea cheie publica - identitate prezent n certificat s fie viabil. Este uor de imaginat c la nivel global nu se poate stabili o singur Autoritate de Certificare, att din motive de ncredere ct i de scalabilitate. Astfel exist un numar de Autoriti de Certificare considerate rdcin. Aceste autoriti sunt cunoscute i considerate de ncredere, iar certificatele lor (deci i cheia public) sunt de obicei ncorporate n aplicaiile care fac uz de autentificare prin certificate. Un certificat semnat de o astfel de Autentificare de Certificare este considerat (hardcodat) de ncredere, ns de cele mai multe ori Autoritile de Certificare rdcin nu emit certificate direct utilizatorilor, ci altor Autoriti de Certificare care pot fi responsabile cu anumite regiuni i care, la rndul lor, ar putea emite certificate pentru alte Autoriti de Certificare, construindu-se astfel cteva nivele pn la Autoritile care emit certificate utilizatorilor (a se vedea Figura 5.3). Un certificat al unui client este verificat pe lanul de semnturi pn ce se ajunge la o Autoritate de Certificare de ncredere sau se stabilete c un astfel de lan nu exist, caz n care autentificarea euaz. ntregul sistem cu entiti care dein/cer certificate i Autoriti de Certificare poart numele de Infrastructur cu Chei Publice (Public Key Infrastructure - PKI).
SECURITATE N JAVA
11 11
Figura 3. Autoriti de Certificare.
Autentificarea poate fi cerut de ambele entiti care doresc s comunice sau poate fi pretins numai de una dintre acestea. Principiul este urmtorul: entitatea care trebuie s se autentifice (A) prezint certificatul deinut (de cele mai multe ori va prezenta un lan de certificate pn la o Autoritate de Certificare posibil cunoscut de cealalt parte), cealalt parte (B) verific dac certificatul este de ncredere i, dac acesta este cazul, genereaz aleator un set de date i cere ca acestea s fie semnate cu cheia privat pereche a cheii publice din certificatul prezentat. Dup ce primete datele, B le decripteaz cu cheia public (dezvaluit n certificatul artat anterior) i dac obine aceeai valoare entitatea A (care a prezentat certificatul) este considerat deintoarea de drept a acestuia, deci avnd identitatea din certificat.
Certificatele n formatul X.509 sunt un standard ITU-T pentru Infrastructura cu Chei Publice. Un certificat X.509 conine cel puin urmtoarele date:
Versiunea. Exist trei versiuni de certificate X.509 v1, v2 i v3. Numr Serial. Identific certificatul i trebuie s fie unic ntre certificatele semnate de Autoritatea de Certificare care emite i certificatul curent (numrul serial i numele emitentului identific unic un certificat). Algoritm de Semnare. Emitent. Numele emitentului n format X.500 (n general o Autoritate de Certificare). 12 Perioada de Valabilitate. Data cnd certificatul devine valabil i data cnd acesta expir. Subiectul. Numele celui care deine certificatul (poate fi n format X.500 sau ntr-un alt format - ex. URL). Cheia Publica. Semnatura Emitentului.
Numele n format X.500 conin cteva cmpuri care ofer informaii suplimentare despre entitatea respectiv. n format X.500 pot aprea urmtoarele atribute:
Common Name CN Numele deintorului. Organization O Organizaia cu care este asociat numele. Organization Unit OU Unitatea din cadrul organizaiei. Country C ara.
Certificatele X.509 v1 au aprut n 1988 i au utilizat formatul X.500 pentru reprezentarea emitentului i subiectului unui certificat. X.509 v2 a introdus conceptul de identificatori unici pentru subiect i emitent pentru a se putea reutiliza aceste nume, ns aceast propunere a fost controversat i cele mai multe recomandri referitoare la certificate sunt mpotriva refolosirii numelor. Certificatele X.509 v2 au continuat s utilizeze formatul X.500 de reprezentare a numelor. Certificatele X.509 v3 sunt cele mai recente, fcndu-i apariia n anul 1996. Noutatea adus este prezena extensiilor, care pot fi definite i incluse n certificate (semnatura certificatului este calculat i peste aceste extensii). Extensiile permit ca un certificat v3 s cuprind i alte informaii pe lng identitatea deintorului (ex. atribute ale deintorului). O serie de extensii sunt deja definite i acceptate ca atare (Utilizare Cheii KeyUsage specific scopul n care poate fi utilizat cheia din certificat, Nume Alternative AlternativeNames pentru a asocia i alte nume cu cheia public din certificat etc.) altele pot fi definite de utilizatori. Aceste extensii pot fi marcate ca fiind critice sau necritice. O extensie critic ar trebui verificat i utilizat/interpretat de aplicaia care primete certificatul respectiv, ns aceasta depinde de conveniile i modul de funcionare al aplicaiei. n plus certificatele n format v3 nu oblig reprezentarea n format X.500 a subiectului sau a emitentului putndu-se utiliza nume generice.
n Java mecanismele de securitate sunt legate de noiunea de keystore, fiiere coninnd chei i certificate. Un keystore poate conine dou tipuri de intrri: certificate de ncredere i mapri cheie/certificat, fiecare astfel de intrare coninnd o cheie privat i certificatul corespunztor semnat cu cheia public. Fiecare intrare din keystore este identificat printr-un un alias. Certificatele care sunt folosite n cadrul setrii unor sesiuni de comunicaie sigure sunt citite din keystore-ul furnizat la rularea aplicaiei, iar la recepie SECURITATE N JAVA
13 13 certificatele sunt verificate contra certificatelor coninute n fiiere trustore. Instrumentul pentru crearea de keystore-uri i trustore-uri este keytool. Paii pentru crearea unui keystore i a unui trustore sunt urmtorii: se pornete cu crearea unui keystore, din acest keystore se export un certificat ce este importat ntr-un trustore, keystore-ul rmne folosit local iar trustore-ul este furnizat aplicaiei remote. Crearea unei noi perechi de chei (public i privat) i a unui certificat pentru un subiect Duke, folosind algoritmul DSA se poate face astfel:
> keytool -genkey -alias alias -keystore .keystore Enter keystore password: password What is your first and last name? [Unknown]: Duke What is the name of your organizational unit? [Unknown]: JavaSoft What is the name of your organization? [Unknown]: Sun What is the name of your City or Locality? [Unknown]: Cupertino What is the name of your State or Province? [Unknown]: CA What is the two-letter country code for this unit? [Unknown]: US Is <CN=Duke, OU=JavaSoft, O=Sun, L=Cupertino, ST=CA, C=US> correct? [no]: yes
Dac se dorete folosirea algoritmului RSA pentru creare se poate folosi:
keytool -import -alias student -file keystore.cer -keystore trustore.ks 2.2. SSL De multe ori se pune problema realizrii unor comunicaii sigure, confideniale, astfel nct mesajele schimbate s nu poat fi descifrate. SSL (Secure Socket Layer) este un protocol de securitate care ofer aceste caliti. Prin intermediul su se poate asigura confidenialitate, integritatea mesajelor i autentificarea prilor. SSL acioneaz peste un flux TCP i ofer servicii nivelelor superioare. Protocolul HTTP poate fi utilizat peste SSL i n acest caz este numit HTTPS (Secure Hyper Text Transfer Protocol). HTTPS funcioneaz pe acelai principiu cu HTTP, diferena constnd n criptarea mesajelor schimbate ntre un server web i un browser i n posibilitatea autentificrii att a serverului ct i a clientului. SSL este ns un protocol general care poate oferi servicii oricrui protocol de nivel aplicaie. Protocolul SSL este format din dou etape: handshake i transfer. n timpul handshake-ului clientul i serverul stabilesc un set de algoritmi pentru realizarea criptrii, stabilesc cheile care vor fi utilizate i se autentific. Doar autentificarea serverului este obligatorie, cea a clientului fiind opional. Dup ncheierea handshake-ului, n cea de-a doua faz, transferul, datele sunt sparte n blocuri de dimensiuni mai mici (opional pot fi i compresate) protejate pentru garantarea integritii i criptate dup care are loc transmisia propriu-zis pe reea.
EXEMPLUL 1. Pentru exemplificare, prezentm modul de construcie a unei aplicaii client-server ce folosesc SSL pentru schimbul de informaii.
Un server SSL necesit specificarea unor certificate ce sunt prezentate clienilor n procesul de autentificare. n Java certificatele sunt citite din fiiere de tip keystore ale cror locaii trebuie s fie explicit specificate (nu exist locaie implicit). Crearea unui server SSL este ilustrat n exemplul urmtor. Dup cum se poate observa, Java uureaz sarcina dezvoltatorului prin furnizarea clasei SSLServerSocketFactory. Crearea unui ServerSocket pregtit pentru comunicaia folosind SSL se realizeaz apelnd metoda createServerSocket a acestei clase ajuttoare. Modul de folosire a unui astfel de server este cel obinuit, prin folosirea metodelor accept, getInputStream sau getOutputStream. Practic, n afar de modul diferit de construcie a instanei, nimic nu trdeaz c vorbim de o comunicaie mai special.
16 try { int port = 443; ServerSocketFactory ssocketFactory = SSLServerSocketFactory.getDefault(); ServerSocket ssocket = ssocketFactory.createServerSocket(port);
// acceptam noi conexiuni Socket socket = ssocket.accept();
// crearea stream-urilor de date InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream();
La rularea aplicaiei server trebuie specificat i numele fiierului keystore folosit. Specificarea fiierului keystore coninnd certificatele de folosit se poate specifica folosind proprietatea javax.net.ssl.keyStore, rularea aplicaiei server fiind efectuat astfel:
Codul corespunztor aplicaiei client ce folosete SSL este prezentat n urmtorul exemplu. Din nou, folosim o clas ajuttoare, SSLSocketFactory, ce ascunde detaliile de implementare ale unui socket ce vorbete protocolul SSL. De aceast dat ns vedem o serie de metode noi. Dup stabilirea conexiunii clientul trebuie s apeleze metoda startHandshake pentru a iniia primul pas n stabilirea conectivitii. Fr apelarea aceastei metode o operaie de transfer ar arunca exceptie. Operaia se termin odat cu finalizarea handshake-ului (dac autentificarea nu reuete se arunc excepie). Ulterior se poate continua cu transferul datelor n form sigur, precum este prezentat n exemplu.
try { int port = 443; String hostname = "hostname"; SocketFactory socketFactory = SSLSocketFactory.getDefault(); Socket socket = socketFactory.createSocket(hostname, port);
// conectarea la server declansarea handshakeului socket.startHandshake();
// regasirea lantului de certificate prezentate de server java.security.cert.Certificate[] serverCerts = socket.getSession().getPeerCertificates();
// crearea streamurilor de comunicatie InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); SECURITATE N JAVA
n cadrul exemplui se poate observa i modalitatea prin care clientul poate obine informaii privind identitatea prezentat n certificatul folosit de server n procesul de autentificare. Atunci cnd un client SSL se conecteaz la un server SSL primete un certificat de autentificare din partea serverului. Clientul trebuie s valideze certificatul prin compararea acestui cu un set de certificate coninute n propriul trustore. Fiierul trustore implicit este <java-home>/lib/security/cacerts. Dac certificatul prezentat de server nu poate fi validat contra certificatelor din trustore, certificatul serverului trebuie s fie adugat n trustore nainte ca conexiunea s poat fi stabilit. Un alt fiier trustore poate fi specificat folosit proprietatea javax.net.ssl.trustStore , rularea aplicaiei anterior prezentate fcndu- se astfel:
n cadrul listingului example1 este prezentat un exemplu mai amplu de aplicaie client/server ce folosete de aceast dat TLS ca protocol de autentificare. n cadrul exemplului se demonstreaz i modalitatea de construcie a unui manager de certificate propriu utilizatorului, manager ce este folosit n pasul de stabilire a identitii celor dou entiti participante (server i client). Codul corespunztor serverului responsabil de setarea corespunztoare a canalelor de comunicaie este urmtorul:
Exemplul demonstreaz o modalitate de ncrcare dinamic a certificatelor ce sunt folosite n etapa de stabilire a identitilor participanilor la comunicaie. Acest lucru se realizeaz folosind clasele KeyManagerFactory i KeyStore. Crearea contextului de securitate se realizeaz n cadrul exemplului folosind clasa SSLContext (ce poate fi instaniat, precum n cadrul exemplului, cu specificarea folosirii protocolului TLS). Dup cum s-a prezentat i n exemplul anterior, un SSLServerSocket se poate obine plecnd de la o instan SSLServerSocketFactory, care poate fi cea implicit (SSLServerSocketFactory.getDefault) sau poate fi obinut dintr-un context (ssf=ctx.getServerSocketFactory()). Metoda setNeedClientAuth este important, ea specificnd dac n etapa de handshake este necesar ca i clientul s prezinte sau nu propriul certificat.
Codul corespunztor clientului este urmtorul:
public void createSSLConnection (String address, int port) throws Exception{
pw = new PrintWriter(new OutputStreamWriter(s.getOutputStream())); br = new BufferedReader(new InputStreamReader(s.getInputStream())); }
Setarea contextului este similar celei prezentate n cazul serverului. O construcie diferit este prezentat de iniializarea contextului (metoda init a clasei SSLContext). n cadrul exemplului este folosit o clas CustomTrustManager ce implementeaz interfaa X509TrustManager. 3. Criptare n Java SECURITATE N JAVA
19 19 Pachetul Java Cryptography Extension (JCE) este inclus n JDK ncepnd de la versiunea 1.4. Supportul JCE pentru criptare include:
Bouncy Castle (BC) este un toolkit care ofer o implementare lightweight pentru JCE 1.2.1 i este compatibil cu Java Micro Edition (J2ME), astfel nct reprezint soluia cea mai bun pentru un API cryptografic pentru dispozitive mobile care ruleaz Java.
EXEMPLUL 2. Prezentm un exemplu de aplicaie pentru criptarea/decriptarea unui ir de date folosind algoritmul DES (chei simetrice).
Pentru exemplificarea modului de folosire a mecanismelor de criptografie puse la dispoziie de Java prezentm urmtorul exemplu (a se vedea i listingul example2):
Key key; try { ObjectInputStream in = new ObjectInputStream( new FileInputStream("SecretKey.ser")); key = (Key)in.readObject(); in.close(); } catch (FileNotFoundException fnfe) { KeyGenerator generator = KeyGenerator.getInstance("DES"); generator.init(new SecureRandom()); key = generator.generateKey(); ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream("SecretKey.ser")); out.writeObject(key); out.close(); }
// Get a cipher object. Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
// Encrypt or decrypt the input string. if (args[0].indexOf("e") != -1) { cipher.init(Cipher.ENCRYPT_MODE, key); String amalgam = args[1]; for (int i = 2; i < args.length; i++) amalgam += " " + args[i]; byte[] stringBytes = amalgam.getBytes("UTF8"); byte[] raw = cipher.doFinal(stringBytes); 20 BASE64Encoder encoder = new BASE64Encoder(); String base64 = encoder.encode(raw); System.out.println(base64); } else if (args[0].indexOf("d") != -1) { cipher.init(Cipher.DECRYPT_MODE, key); BASE64Decoder decoder = new BASE64Decoder(); byte[] raw = decoder.decodeBuffer(args[1]); byte[] stringBytes = cipher.doFinal(raw); String result = new String(stringBytes, "UTF8"); System.out.println(result); }
Exemplul poate fi rulat folosind ant run sau folosind:
i are ca rezultat criptarea (dac se furnizeaz opiunea e) sau decriptarea (dac se specific opiunea d) unui text de intrare.
n Java orice operaie de criptare/decriptare se folosete de un obiect Cipher. Dup cum se poate observa n exemplul anterior, un astfel de obiect Cipher se folosete n conjuncie cu o cheie. n cadrul exemplului se ncearc deserializarea cheii dintr-un fiier sau, dac operaia eueaz, se ncearc generarea unei noi chei. Un obiect KeyGenerator este folosit pentru generarea cheii. Un KeyGenerator se obine prin folosirea unei metode adecvate, n funcie de tipul de algoritm de criptare pe care dorim s l folosim: KeyGenerator generator = KeyGenerator.getInstance("DES"); (aici se ncearc obinerea unei chei corespunztoare algoritmului DES Data Encryption Standard). Un generator de chei trebuie s fie iniializat n prealabil cu un numr aleator pentru a produce o nou cheie: generator.init(new SecureRandom()). Dup obinerea cheii, se continu cu obinerea unui cifru folosind un algoritm corespunztor: Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"). Apelul acesta specific c dorim s folosim algoritmul DES i civa parametrii suplimentari necesari acestuia. n finalul exemplului se poate observa modul n care se realizeaz criptarea sau decriptarea datelor. n Java toate operaiile criptografice sunt structurate conform diagramei urmtoare:
SECURITATE N JAVA
21 21
Figura 4. Mecanismul criptografic folosit pentru criptare/decriptare.
n centrul arhitecturii st algoritmul criptografic folosit (engine). Algoritmul primete un set de date de intrare i opional o cheie i produce un set de date de ieire. Algoritmii criptografici existeni depind de providerii de securitate instalai n sistem. JCE de exemplu vine cu un provider de securitate suplimentar fa de ce exist instalat n JDK ce include implementrile algoritmilor criptografici suportai. n Java se pot instala mai muli provideri de securitate n afar de cel instalat implicit de catre JCE. Exemplul urmtor instaleaz provider-ul oferit de Bouncy Castle care se numeste BC.
Implicit pentru toi algoritmii criptografici se folosete primul provider care apare n outputul programului de mai sus. Dac se dorete folosirea unui anumit provider se specific explicit numele acestuia, de exemplu BC pentru Bouncy Castle, precum n exemplul urmtor:
n Java conceptul de cheie este modelat de interfaa java.security.Key. O implementare de cheie depinde de tipul de algoritm de criptare folosit. Exist de altfel dou interfee adiionale ce sunt folosite pentru specificarea cheilor n cazul algoritmilor asimetrici: PublicKey i PrivateKey. Poate prea suprinztor, ns n JDK nu exist mai departe nici o clas care s implementeze oricare dintre aceste interfee (din raiuni de securitate). JCE furnizeaz un concept suplimentar fa de JDK, noiunea de cheie secrete. O cheie secret reprezint o cheie ce este partajat ntre dou entiti n timpul unei operaii criptografice. Cheile secrete pot fi fie simetrice, fie asimetrice. Cheile asimetrice se folosesc de cele dou interfee anterior prezentate, n timp ce cheile simetrice implementeaz interfaa javax.crypto.SecretKey. O clas ce este folosit pentru gestiunea cheilor asimetrice este java.security.KeyPair. Clasa reprezint o structur de date simpl ce conine dou informaii importante: o cheie public i o cheie privat. Clasa poate fi folosit att pentru ncapsularea unor chei, ct deopotriv ea este folosit de ctre generatorul de chei Java. Generarea cheilor asimetrice se face folosind clasa java.security.KeyPairGenerator. Clasa, abstract de altfel, este apelabil prin metodele statice getInstance puse la dispoziie, metode ce primesc ca argumente cel puin un algoritm de folosit. Dup iniializare i nainte de generearea perechii de chei este recomandat folosirea metodei initialize pentru iniializarea corespunztoare a generatorului de numere aleatoare. n final generarea cheilor se realizeaz prin metoda generateKeyPair, precum n exemplul urmtor:
Pentru cheile simetrice JCE introduce mecanisme suplimentare de generare a acestora: clasele KeyGenerator i SecretKeyFactory. Clasa KeyGenerator, folosit pentru generarea cheilor secrete simetrice, are o funcionalitate similar celei a clasei KeyPairGenerator. Diferena major const n apelarea metodei generateKey ce rezult n returnarea unui obiect de tip SecretKey. A doua clas, SecretKeyFactory, convertete o specificaie de cheie algoritmic sau codat n obiecte cheie concrete. Clasa aceasta are un omolog pentru chei asimetrice n clasa KeyFactory. Un exemplu de folosire a acestei clase pentru exportarea pe disc a unei chei si, respectiv, importarea de pe disc a unei chei, este prezentat n listingul example3. n plus utilizatorul are posibilitatea de a defini proprii mecanisme de criptografie. De exemplu, utilizatorul poate implementa o cheie secret proprie bazat pe operaia XOR precum n exemplul urmtor:
public class XORKey implements SecretKey {
byte value; SECURITATE N JAVA
23 23
public XORKey(byte b) { value = b; }
public String getAlgorithm() { return "XOR"; }
public String getFormat() { return "XOR Special Format"; }
public byte[] getEncoded() { byte b[] = new byte[1]; b[0] = value; return b; } }
O astfel de clas poate fi folosit n conjuncie cu un generator de chei propriu utilizatorului ce poate fi definit conform exemplului urmtor (pentru exemplul complet se poate consulta listingul example5):
public class XORKeyGenerator extends KeyGeneratorSpi {
SecureRandom sr;
public void engineInit(SecureRandom sr) { this.sr = sr; }
public void engineInit(AlgorithmParameterSpec ap, SecureRandom sr) throws InvalidAlgorithmParameterException { throw new InvalidAlgorithmParameterException( "No parameters supported in this class"); }
public SecretKey engineGenerateKey() { if (sr == null) sr = new SecureRandom(); byte b[] = new byte[1]; sr.nextBytes(b); return new XORKey(b[0]); } }
Algoritmul de criptografie este implementat prin intermediul clasei javax.crypto.Cipher. Clasa furnizeaz o interfa pentru criptarea sau decriptarea att a unor iruri de date locale, ct i pentru obinerea unor streamuri de date de criptare/decriptare. Numele algoritmilor funizai la iniializarea acestei clase difer de cei folosii la generarea cheilor de criptografie n sensul c se specific, pe lng numele algoritmului folosit, i o serie de parametrii specifici funcionrii respectivului algoritm: padding la nivelul acestei clase se lucreaz numai cu valori multipli de 8 octei i modul de operare (pentru mai multe detalii legate de 24 diferitele moduri de operare a se vedea documentaia oficial JCE). Un exemplu de folosire a acestei clase cu iruri de date locale am vzut anterior. De asemenea, n cap5/example4 este prezentat un exemplu de folosire a PBE. Password-Based Encryption (PBE) creaz o cheie de criptare dintr-o parol. Pentru a minimaliza ansele ca un atacator s ghiceasc parola prin for brut, implementrile de PBE folosesc n plus un numar aleator (salt) pentru a crea cheia. Ca exemplu, pe orice sistem Unix care are pachetul openssl instalat se pot realiza scripturi care s realizeze PBE:
Pentru decriptare: >openssl enc -idea-cbc -d -in ciphertext_filename
n exemplul prezentat la nceputul subcapitolului se poate observa modalitatea de folosire a clasei Cipher pentru criptarea unor variabile locale. Acelai aspect este reliefat i n exemplul de criptare folosind parol (algoritmul PBE). O alt modalitate de folosire a clasei Cipher este legat de contruirea unor stream-uri de date ce automatizeaz criptarea/decriptarea datelor pe msur ce acestea sunt scrise n fiier, respectiv citite din fiier. Un exemplu de criptare a datelor folosind clasa CipherOutputStream, clas ajuttoare pentru criptarea datelor ntr-un stream de comunicaie, este prezentat n continuare:
public class Send {
public static void main(String args[]) { try { KeyGenerator kg = KeyGenerator.getInstance("DES"); kg.init(new SecureRandom()); SecretKey key = kg.generateKey();
Cipher c = Cipher.getInstance("DES/CFB8/NoPadding"); c.init(Cipher.ENCRYPT_MODE, key); CipherOutputStream cos = new CipherOutputStream( new FileOutputStream("ciphertext"), c); PrintWriter pw = new PrintWriter( new OutputStreamWriter(cos)); pw.println("Stand and unfold yourself"); pw.close();
SecretKeyFactory skf = SecretKeyFactory.getInstance("DES"); Class spec = Class.forName("javax.crypto.spec.DESKeySpec"); DESKeySpec ks = (DESKeySpec) skf.getKeySpec(key, spec); ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("keyfile")); oos.writeObject(ks.getKey()); oos.writeObject(c.getIV()); oos.close(); } catch (Exception e) { System.out.println(e); } } } SECURITATE N JAVA
25 25
n cadrul exemplului se ncepe prin crearea unei chei secrete, folosind algoritmul DES i clasa KeyGenerator. Ulterior este instaniat clasa Cipher cu specificarea comportrii algoritmului DES i este iniializat cu modul de lucru (criptare n acest caz, specificat prin parametrul Cipher.ENCRYPT_MODE) i cheia de folosit. Clasa CipherOutputStream este apoi iniializat peste un alt stream de ieire i pe baza obiectului Cipher anterior creat. Astfel, intern, orice dat ce va fi trimis a fi scris pe streamul de ieire va fi trecut prin Cipher, deci prin algoritmul de criptare. Ultimul pas, scrierea n fiierul keyfile, demonstreaz o modalitate prin care putem transmite cheia de criptografie destinaturului, persoanei ce va realiza decriptarea, prin intermediul unui fiier. Acest pas este necesar n cazul unor algoritmi, cum este i cazul algoritmului DES, unde nu putem genera o aceeai cheie pentru decriptare n mod automat, decriptarea necesitnd existena informaiei legat de cheia de criptare. n listingul example4 se poate consulta un exemplu n care criptm un text pornind de la o parol text, caz n care nu aveam nevoie de cheie, aceasta putnd fi ntotdeauna unic generat pornind de la textul parol cunoscut. Funcionalitatea invers, decriptarea datelor pe care le-am scris n fiier folosind tot un stream de date, de data aceasta de intrare, se realizeaz precum n exemplul urmtor:
public class Receive {
public static void main(String args[]) { try { ObjectInputStream ois = new ObjectInputStream( new FileInputStream("keyfile")); DESKeySpec ks = new DESKeySpec((byte[]) ois.readObject()); SecretKeyFactory skf = SecretKeyFactory.getInstance("DES"); SecretKey key = skf.generateSecret(ks); Cipher c = Cipher.getInstance("DES/CFB8/NoPadding"); c.init(Cipher.DECRYPT_MODE, key, newIvParameterSpec((byte[]) ois.readObject()); CipherInputStream cis = new CipherInputStream( new FileInputStream("ciphertext"), c); cis.read(new byte[8]); BufferedReader br = new BufferedReader( new InputStreamReader(cis)); System.out.println("Got message: "+br.readLine()); ois.close(); br.close(); } catch (Exception e) { System.out.println(e); } } }
Ca o regul de securizare a aplicaiilor, n general este indicat ca parolele s fie inute n memorie nu ca String, ci ca array de caractere, i trebuie suprascrise cu zero imediat dup folosire pentru a preveni memory sau disk snooping. De 26 exemplu, informaia ar putea s fie citit uor atunci cnd maina este obligat s fac swapping. De asemenea, atunci cnd se serializeaz obiecte este indicat folosirea cuvntului cheie transient pentru ca informaia de pe aceste canale s nu fie trimis n streamul de date. 4. Aplicaie practic V propunem scrierea unei aplicaii client-server n care clientul decripteaz dintr-un fiier un text, realizeaz o conexiune SSL cu serverul i i trimite acestuia irul anterior citit din fiier. Pentru realizarea acestei aplicaii parcurgei paii:
Task1. Porninnd de la listingul example4 i explicaiile prezentate n subcapitolul 3, realizai un program care citete parola de la tastatur i realizeaz decriptarea unui text dintr-un fiier. Pentru crearea fiierului coninnd textul criptat folosii exemplul prezentat n cadrul clasei PBEnc.
Task2. Creai un keystore coninnd un certificat propriu semnat de ctre un CA. Realizai crearea unui trustore pe baza fiierului keystore obinut anterior. Pentru mai multe informaii consultai subcapitolul 2.
Task3. Realizai o aplicaie client-server ce comunic folosind un canal de comunicaie securizat cu protocolul SSL (lsai setNeedClientAuth(false)). Folosii n acest scop fiierele keystore i trustore create n task-ul anterior. Consultai detaliile certificatelor primite n procesul de handshake. Trimitei din partea clientului mesajul decriptat din fiier i afiai-l la recepie pe partea serverului. Pentru detalii de implementare consultai subcapitolul 2.