Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Criptografia a Java (JCA)

Els 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.

Claus

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.

Xifrat

Per a poder xifrar, necessitem un objecte javax.crypto.Cipher:

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

El format del paràmetre (transformation) és algorithm/mode/padding. Mode i padding són opcionals: si no s’indiquen, s’utilitza el mode i padding per defecte. Compte: el mode per defecte habitualment és ECB, que és insegur. Cal especificar sempre el mode explícitament (p. ex. CBC, CTR o GCM). Què són mode i padding?

  • Padding: és una tècnica que consisteix a afegir dades de farciment al començament, mig o fi d’un missatge abans de ser xifrat. Això es fa perquè els algorismes estan dissenyats per tenir dades d’entrada d’una mida concreta.
  • Mode: defineix com es relacionen els blocs d’entrada (en pla) amb els de sortida (xifrats). El més senzill és el mode ECB: cada bloc es xifra de forma independent, cosa que significa que blocs d’entrada idèntics produeixen blocs de sortida idèntics, exposant patrons en les dades. Altres modes, com CBC o CTR, encadenen o combinen els blocs de manera que entrades idèntics generen sortides diferents.

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); // xifrat cipher.init(Cipher.DECRYPT_MODE, key); // desxifrat

Finalment, realitzem el xifrat o desxifrat:

byte[] bytesOriginal = textOriginal.getBytes("UTF-8"); // necessito bytes com a entrada byte[] bytesXifrat = cipher.doFinal(bytesOriginal);

El desxifrat podria ser:

byte[] bytesDesxifrat = cipher.doFinal(bytesXifrat); // alternativament, si el contingut a desxifrar és una part de l'array: byte[] bytesDesxifrat = cipher.doFinal(bytesXifrat, inici, longitud);

Alguns modes de xifrat en bloc utilitzen el que s’anomena vector d’inicialització (IV). Es tracta d’un paràmetre aleatori per a l’algorisme de xifrat que fa més difícil relacionar els blocs en funció de les seves entrades. Normalment no cal que sigui secret, només que no es repeteixi amb la mateixa clau.

Per utilitzar aquests modes (p.ex. CBC), cal afegir un nou paràmetre quan s’inicialitza el Cipher. La mida de l’IV és habitualment la mateixa del bloc. Per AES és de 16 bytes (128 bits).

byte[] iv = new byte[16]; // 16 bytes = 128 bits per AES SecureRandom random = new SecureRandom(); random.nextBytes(iv); IvParameterSpec parameterSpec = new IvParameterSpec(iv); cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec); // xifrat cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec); // desxifrat

Xifrat de streams

Quan cal xifrar grans quantitats de dades, no és pràctic carregar-les totes en memòria per cridar doFinal(). Per a aquestes situacions, podem processar les dades de forma incremental amb streams. Aquest xifrat és sempre simètric.

A Java, tenim les classes CipherInputStream i CipherOutputStream.

CipherInputStream i CipherOutputStream admeten un Cipher simètric de bloc, com per exemple AES/ECB/PKCS5Padding, o bé els modes de tipus feedback CFB8 or OFB8, (8 = blocs de 8 bits), com per exemple AES/CFB8/NoPadding. Els modes feedback necessiten vectors d’inicialització (IV).

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);

Dades binàries en text

Les claus i la informació xifrada està en format binari. Si cal intercanviar-ho utilitzant un canal de text, es poden convertir utilitzant Base64. A Java, tenim java.util.Base64.

byte[] binary1 = ...; String string = Base64.getEncoder().encodeToString(binary1); byte[] binary2 = Base64.getDecoder().decode(string); // binary1 i binary2 són iguals