L'autenticació és el procés de confirmar l'autenticitat reclamada per una entitat. Tenim bàsicament dos tipus:
L'autenticació no vol dir que tinguem encriptació.
La criptografia (del grec "kryptos" - amagat, secret - i "graphin" - escriptura. Per tant seria "escriptura oculta") és l'estudi de formes de convertir informació des de la seva forma original cap a un codi incomprensible, de forma que sigui incomprensible pels que no coneguin aquesta tècnica.
En la terminologia de criptografia, trobem els següents aspectes:
Existeixen dos grans grups d'algorismes de xifrat, en funció de si les claus són úniques o van en parella:
La clau simètrica té inconvenients. Primer, necessites una clau per cada parella origen/destí. Segon, necessites una forma segura de compartir-les. La clau asimètrica té l'avantatge que pots compartir la part pública de la clau, ja que sense la part privada no pots fer res, però els algorismes són més lents i tenen limitacions en la mida del missatge a xifrar. Per exemple, RSA té un missatge màxim de floor(n/8)-11 bytes.
El xifrat també pot ser de bloc o de stream:
Si no es diu el contrari, parlem només de xifrats en bloc.
Xifrat amb clau privada: les dues parts comparteixen una clau privada
Xifrat amb clau pública: només el propietari de les claus (el receptor) pot desxifrar les dades
Com ja hem vist, el xifratge simètric té el problema de compartir la clau entre les dues parts. I l'asimètric, no permet xifrar blocs gaire grans.
La solució és utilitzar els dos tipus de xifrat de forma combinada: mitjançant el xifrat asimètric, compartim la clau simètrica. Després, tota la comunicació està xifrada de forma simètrica. En l'esquema:
Aquests són els avantatges:
Un "message digest" o hash és una seqüència de bytes produïda quan un conjunt de dades passen per un motor. No és sempre necessària una clau perquè aquest motor operi. Alguns coneguts: MD5, SHA-256. Les seves propietats són:
Un resum ens permet protegir la integritat d'un missatge.
Veiem com funciona SHA-256 a Linux sobre un petit text "hello world!". Si ho proveu, veureu que el resultat és instantani (2). Com que té 256 bits, genera un resum de 32 bytes (en hex). Mireu com, canviar un caràcter, canvia totalment el hash (4).
$ echo 'hello world!' | openssl sha256(stdin)= ecf701f727d9e2d77c4aa49ac6fbbcc997278aca010bddeeb961c10cf54d435a$ echo 'hello,world!' | openssl sha256(stdin)= 4c4b3456b6fb52e6422fc2d1b4b35da2afbb4f44d737bb5fc98be6db7962073fSi busqueu el resum de "hello world!" o el de "123456" el trobareu a la xarxa. Dues conclusions: per a un algorisme i una entrada, tenim la mateixa sortida (1). No tenim la funció inversa (3), però hi ha taules de resums per a texts que fan la correspondència, i que s'utilitzen per esbrinar credencials.
Si volem protegir la integritat i l'autenticitat, podem utilitzar els MAC (message authentication code). Bàsicament, es tracta de resums segurs xifrats amb una clau privada, que cal compartir entre les dues parts per tal de verificar la comunicació.
La signatura digital és un mecanisme de xifrat per autentificar informació digital. El mecanisme utilitzat és la criptografia de clau pública. Per això aquest tipus de signatura també rep el nom de signatura digital de clau pública. S'utilitzen per a garantir tres aspectes: autenticitat, integritat i no repudi.
Aquest és el procés per a obtenir una signatura digital:
Aquest és el procés per a verificar una signatura digital:
Signatura digital: el propietari de les claus (l'emissor) envia la prova de les dades originals
Si se signa un document digital utilitzant una clau privada, el receptor ha de tenir la clau pública per verificar la signatura. El problema és que una clau no indica a qui pertany. Els certificats resolen aquest problema: una entitat ben coneguda (Certificate Authority: CA) verifica la propietat de la clau pública que se t'ha enviat.
Un certificat conté:
Un certificat podria contenir aquesta informació:
"L'autoritat de certificació 2276172 certifica que la clau pública de John Doe és 217126371812".Problema: l'emissor del certificat té una clau pública en què hem de confiar. I poden estar encadenades. La darrera de la cadena està autosignada per ella mateixa. Llavors, hem d'acceptar certes CA com a confiables, i habitualment Java (igual que els navegadors) tenen una llista de CA confiables.
El certificat de servidor TLS/SSL és el més comú. El client utilitza l'algorisme de validació del camí de certificació:
Com s'estableix la comunicació segura entre el navegador i un servidor HTTPS?
motors criptogràfics de Java proporcionen mecanismes per signatures digitals, resums de missatges, etc. Aquests motors estan implementats per proveïdors de seguretat (java.security.Provider), que es poden visualitzar mitjançant java.security.Security.getProviders(). Cada motor (java.security.Provider.Service) té un tipus i un algorisme.
Podem generar claus de tipus simètric (KeyGenerator) o asimètric (KeyPairGenerator).
KeyGenerator keyGen = KeyGenerator.getInstance(algorithm);keyGen.init(size);SecretKey secretKey = keyGen.generateKey();Algorismes simètrics típics són DES (56 bits) o AES (128, 192, 256 bits).
KeyPairGenerator kpg = KeyPairGenerator.getInstance(algorithm);kpg.initialize(size);KeyPair kp = kpg.generateKeyPair();PublicKey publicKey = kp.getPublic();PrivateKey privateKey = kp.getPrivate();Cal indicar l'algorisme. El més habitual és RSA (1024, 2048 bits).
Tant SecretKey, com PublicKey i PrivateKey, són subclasses de java.security.Key. Totes elles tenen un mètode getEncoded(): la clau en format binari. Aquest format pot convertir-se, per exemple, a un format fàcil de gestionar amb cadenes. El més conegut és java.util.Base64, que permet intercanviar el format d'una clau Key key:
String stringKey = Base64.getEncoder().encode(key.getEncoded());byte[] encoded = Base64.getDecoder().decode(stringKey);Per a poder xifrar, necessitem un objecte javax.crypto.Cipher:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");El format del paràmetre (algorithm) és algorithm/mode/padding. Mode i padding són opcionals: si no s'indiquen, s'utilitza el mode i padding per defecte. Què són?
Després, hem d'inicialitzar l'objecte utilitzant el mode (Cipher.ENCRYPT_MODE o Cipher.DECRYPT_MODE) i la clau de xifrat:
cipher.init(Cipher.ENCRYPT_MODE, key); // xifratcipher.init(Cipher.DECRYPT_MODE, key); // desxifratFinalment, realitzem el xifrat o desxifrat:
byte[] bytesOriginal = textOriginal.getBytes("UTF-8"); // necessito bytes com a entradabyte[] bytesXifrat = cipher.doFinal(bytesOriginal);El desxifrat podria ser:
byte[] bytesOriginal = llegirBytes(); // un mètode qualsevol que obté dadesbyte[] bytesDesxifrat = cipher.doFinal(bytesOriginal);// alternativament, si el contingut a desxifrar és una part de l'array:byte[] bytesDesxifrat = cipher.doFinal(bytesOriginal, inici, longitud);No té sentit xifrar grans quantitats de dades amb mètodes de bloc. Per a aquestes situacions, podem utilitzar el xifrat de streams. Aquest xifrat és sempre simètric.
A Java, tenim les classes CipherInputStream i CipherOutputStream.
Per exemple, si volem obrir un arxiu i xifrar-lo o desxifrar-lo, podem fer-ho així:
FileInputStream in = new FileInputStream(inputFilename);FileOutputStream fileOut = new FileOutputStream(outputFilename);CipherOutputStream out = new CipherOutputStream(fileOut, cipher);Llavors, caldria copiar el stream in a out.
L'objecte cipher ha d'inicialitzar-se amb el mode que calgui, ENCRYPT_MODE o DECRYPT_MODE.
CipherInputStream es pot utilitzar de forma anàloga. En aquest cas, el stream de sortida podria ser un FileOutputStream:
FileInputStream fileIn = new FileInputStream(inputFilename);CipherInputStream in = new CipherInputStream(fileIn, cipher);FileOutputStream fileOut = new FileOutputStream(outputFilename);Els resums s'implementen utilitzant la classe java.security.MessageDigest, que permet generar un resum de dades. El resum es pot fer segur utilitzant javax.crypto.Mac. També es pot realitzar l'operació amb Streams gràcies a java.security.DigestInputStream i java.security.DigestOutputStream.
Alguns algorismes típics de resums: MD2, MD5, SHA-1, SHA-256, SHA-384, SHA-512.
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);byte[] resum = messageDigest.digest(text.getBytes());Un Message Authentication Code (javax.crypto.Mac) és un resum xifrat amb una clau privada. Només es pot verificar si tens aquesta clau. Podem generar-lo així:
Mac mac = Mac.getInstance(algorithm);mac.init(key); // la clau privadabyte[] macBytes = mac.doFinal(text.getBytes());Un algorisme podria ser HmacSHA256 (HMAC: Hash-based MAC).
Una signatura digital equival a fer un resum i xifrar-lo amb una clau privada. El receptor podria desxifrar-lo amb la pública, i comparar-lo amb un resum que faci de les dades (en pla) rebudes.
Els algorismes són variats, per exemple, SHA256withRSA indica que el resum es fa amb SHA256 i l'encriptació amb RSA. Per tant, les claus utilitzades han de ser RSA. Per a signar una entrada (array de bytes):
Signature sign = Signature.getInstance(algorithm);sign.initSign(privateKey); sign.update(input);byte[] signatura = sign.sign();Per verificar-la:
Signature sign = Signature.getInstance(algorithm);sign.initVerify(publicKey);sign.update(input);boolean correcte = sign.verify(signatura);Els certificats (java.security.cert.Certificate) més habituals són de tipus X.509, i indiquen una vinculació d'una identitat a una clau pública, garantida per una altra entitat autoritzada. Inclouen:
Els DN (Distinguished Names) conté una sèrie de camps (CN, OU, O, L, S, C) que identifiquen tant l'emissor com el subjecte.
Habitualment, els trobem dins dels magatzems. Per gestionar-los podem utilitzar l'eina keytool del JRE o bé programàticament amb la classe java.security.KeyStore i els mètodes getKey() i getCertificate().
Són necessaris als portals web amb seguretat habilitada HTTPS.
Per tal que un socket es pugui comunicar amb el protocol segur SSL, cal crear objectes una mica diferents.
Al servidor:
ServerSocket serverSocket = new ServerSocket(PORT);Es converteix en:
SSLServerSocketFactory factory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); SSLServerSocket serverSocket = (SSLServerSocket) factory.createServerSocket(PORT);Opcionalment, si volem autenticar al client (ho veurem més endavant):
serverSocket.setNeedClientAuth(true);Al client:
Socket clientSocket = new Socket(HOST, PORT);Es converteix en:
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();SSLSocket clientSocket = (SSLSocket) factory.createSocket(HOST, PORT); A més, cal configurar les claus privada i pública correctament. I això depèn de si volem o no autoritzar la clau pública dels clients al servidor.
Tenim dos tipus de magatzems necessaris:
La configuració es realitza mitjançant propietats de sistema a Java, amb aquesta instrucció:
System.setProperty("nomDeLaPropietat", "valor");Cal generar dos magatzems:
keytool -genkey -alias srvAlias -keyalg RSA -keystore serverKeystore.jks -keysize 2048keytool -export -keystore serverKeystore.jks -alias srvAlias -file server.crtkeytool -importcert -file server.crt -keystore clientTruststore.jks -alias srvAliasConfiguració al servidor per a identificar-se:
javax.net.ssl.keyStore=serverKeystore.jksjavax.net.ssl.keyStorePassword=yourpasswordConfiguració al client per a acceptar al servidor:
javax.net.ssl.trustStore=clientTrustore.jksjavax.net.ssl.trustStorePassword=yourpasswordAls les configuracions anteriors, cal afegir:
keytool -genkey -keyalg RSA -alias cltAlias -keystore clientKeystore.jks -keysize 2048keytool -export -keystore clientKeystore.jks -alias cltAlias -file cliente.crtkeytool -importcert -file cliente.crt -keystore serverTruststore.jks -alias cltAliasConfiguració al servidor per a acceptar el client:
javax.net.ssl.trustStore=serverTruststore.jksjavax.net.ssl.trustStorePassword=yourpasswordConfiguració al client per a identificar-se:
javax.net.ssl.keyStore=clientKeystore.jksjavax.net.ssl.keyStorePassword=yourpasswordUn magatzem de claus, o keystore, pot tenir un alias, i pot contenir:
Les keystores poden tenir diferents formats (JKS, JCEKS, PKCS12, PKCS11, DKS). Els més utilitzats són:
Java té una eina anomenada keytool per gestionar magatzems de claus. A continuació es mostren algunes comandes habituals.
Comenda per llistar els continguts d'una keystore:
$ keytool -list -v -keystore keystore.jksComanda per generar un keystore amb una parella de claus:
$ keytool -genkey -alias mydomain -keyalg RSA -keystore keystore.jks -keysize 2048Comanda per exportar un certificat d'una keystore:
$ keytool -export -alias mydomain -file mydomain.crt -keystore keystore.jksComanda per importar un certificat a una keystore:
$ keytool -importcert -file mydomain.crt -keystore keystore.jks -alias mydomainPEM - Privacy Enhanced Mail
PEM és el format més comú en què les autoritats certificadores emeten certificats. Aquests són més utilitzats pels usuaris d’Unix / Linux. Estan codificats en base-64.
La part pública del certificat estarà limitada per "—–BEGIN PUBLIC KEY—–" i "—–END PUBLIC KEY—–", mentre que la part privada del certificat és dins de "—–BEGIN RSA PRIVATE KEY––" i "—–END RSA PRIVATE KEY—–".
El format PEM pot contenir qualsevol certificat client / servidor, certificat intermedi, CA arrel i la clau privada.
DER - Distinguished Encoding Rule
DER és un certificat de format binari. Es poden codificar tota mena de certificats i claus privades.
Aquest format admet l’emmagatzematge d'un únic certificat i no inclou la clau privada per a la CA arrel/intermèdia.
DER s’utilitza normalment en la plataforma Java.
PKCS#7
Aquest format només conté certificat o cadena de certificats, però no emmagatzema la clau privada.
CA s’utilitza habitualment per proporcionar cadenes de certificats als usuaris, i solen tenir extensió .p7b o .p7s.
PFX - Personal Information Exchange
PFX és un format per emmagatzemar un certificat de servidor o qualsevol certificat intermedi juntament amb la clau privada en un fitxer xifrat. PFX segueix l'estàndard de xifratge de clau pública (PKCS). El terme PFX s'utilitza indistintament amb PKCS#12.
Tenen extensió .pfx o .p12.