RSA és un algorisme de xifratge asimètric de clau pública.
S'utilitza àmpliament per protegir dades sensibles, com ara números de targeta de crèdit,
números d'identificació personal (PIN) i contrasenyes.
L'algorisme va ser inventat l'any 1977 per Ron Rivest, Adi Shamir i Leonard Adleman,
i rep el nom de les inicials dels inventors.
L'algorisme RSA implica la generació de dos nombres primers grans i una clau pública i una privada.
La clau pública s'utilitza per xifrar les dades i la clau privada per desxifrar-les.
Les dades xifrades amb la clau pública només es poden desxifrar amb la clau privada, i viceversa.
El procés de xifratge i desxifrat mitjançant RSA és el següent:
El remitent genera una clau pública i una clau privada.
El remitent xifra les dades mitjançant la clau pública del destinatari.
Les dades xifrades s'envien al destinatari.
El destinatari utilitza la seva clau privada per desxifrar les dades.
L'objectiu principal de RSA és proporcionar transmissió de dades segura entre dues parts.
Com que el procés de xifratge i desxifrat requereix tant les claus públiques com les privades,
és extremadament difícil per a un tercer interceptar i llegir les dades transmeses.
Per generar el parell de claus KeyPair a Java podem utilitzar
l'objecte KeyPairGenerator, indicant l'algorisme que volem
utilitzar.
publicstaticKeyPairgenerateKeyPair(intlen)throwsException{if(!(len==1024||len==2048||len==4096))thrownewException("La mida de la clau no és vàlida");KeyPairkeys=null;try{KeyPairGeneratorkeyGen=KeyPairGenerator.getInstance("RSA");keyGen.initialize(len);keys=keyGen.genKeyPair();}catch(Exceptionex){System.err.println("Generador no disponible.");}returnkeys;}
L'algorisme RSA serveix per xifrar i desxifrar dades.
Aquestes operacions es poden dur a terme a Java mitjançant la classe Cipher.
En el següent mètode, s'utilitza la classe Chiper per portar a terme
l'acció d'encriptar o desencriptar, indicada mitjançant el paràmetre int opmode.
Aquest paràmetre pot rebre els valors Cipher.ENCRYPT_MODE o Cipher.DECRYPT_MODE.
Si volem encriptar dades de tipus String, primer caldrà convertir-les
a byte[].
Podem crear un altre mètode wrapper per fer-ho:
publicstaticStringencrypt(PublicKeykey,Stringstr){try{// Decode the UTF-8 String into byte[] and encrypt itbyte[]data=encrypt(key,str.getBytes(StandardCharsets.UTF_8));// Encode the encrypted data into base64returnBase64.getEncoder().encodeToString(data);}catch(Exceptionex){System.err.println("Error xifrant les dades: "+ex);}returnnull;}
Si volem desencriptar dades de tipus String, primer caldrà convertir-les
a byte[].
Podem crear un altre mètode wrapper per fer-ho:
publicstaticStringdecrypt(PrivateKeykey,Stringstr){try{// Decodifiquem les dades xifrades en base64 a byte[]byte[]data=Base64.getDecoder().decode(str);// Desencriptem les dadesbyte[]decrypted=decrypt(key,data);// Codifiquem les dades desencriptades a StringreturnnewString(decrypted);}catch(Exceptionex){System.err.println("Error desxifrant les dades: "+ex);}returnnull;}
Per guardar una clau privada en un fitxer, cal convertir la clau al format PEM mitjançant la classe PKCS8EncodedKeySpec
i després escriure-la a un fitxer.
El format PEM és un format codificat en base64 per guardar claus criptogràfiques.
En el següent exemple, s'ha afegit un "header" i "footer" a la clau,
perquè la clau es guarde en un format estàndard.
publicstaticvoidsavePrivateKeyToFile(PrivateKeykey,Stringpath)throwsIOException{// Get the encoded private keyPKCS8EncodedKeySpeckeySpec=newPKCS8EncodedKeySpec(key.getEncoded());// Base64 encode the keyStringencodedKey=Base64.getEncoder().encodeToString(keySpec.getEncoded());// Add the PEM headers and footersencodedKey="-----BEGIN PRIVATE KEY-----\n"+encodedKey+"-----END PRIVATE KEY-----";// Write the key to a fileFiles.write(Paths.get(path),encodedKey.getBytes());}
Per guardar una clau pública en un fitxer, cal convertir la clau al format PEM mitjançant la classe X509EncodedKeySpec
i després escriure-la a un fitxer.
El format PEM és un format codificat en base64 per guardar claus criptogràfiques.
En el següent exemple, s'ha afegit un "header" i "footer" a la clau,
perquè la clau es guarde en un format estàndard.
publicstaticvoidsavePublicKeyToFile(PublicKeykey,Stringpath)throwsIOException{// Get the encoded public keyX509EncodedKeySpeckeySpec=newX509EncodedKeySpec(key.getEncoded());// Base64 encode the keyStringencodedKey=Base64.getEncoder().encodeToString(keySpec.getEncoded());// Add the PEM headers and footersencodedKey="-----BEGIN PUBLIC KEY-----\n"+encodedKey+"-----END PUBLIC KEY-----";// Write the key to a fileFiles.write(Paths.get(path),encodedKey.getBytes());}
Per carregar una clau privada d'un fitxer, cal llegir les dades codificades en el format PEM
mitjançant la classe PKCS8EncodedKeySpec i convertir-la al objecte Java corresponent.
i després escriure-la a un fitxer.
En el següent exemple, s'ha eliminat el "header" i "footer" de la clau,
que es pot haver afegit en el moment de guardar-la.
publicstaticPrivateKeyloadPrivateKeyFromFile(Stringpath)throwsInvalidKeySpecException,IOException,NoSuchAlgorithmException{// Read the private key from the PEM fileStringprivateKeyPem=newString(Files.readAllBytes(Paths.get(path)));privateKeyPem=privateKeyPem.replace("-----BEGIN PRIVATE KEY-----\n","");privateKeyPem=privateKeyPem.replace("-----END PRIVATE KEY-----","");byte[]privateKeyBytes=Base64.getDecoder().decode(privateKeyPem);// Create the private keyPKCS8EncodedKeySpeckeySpec=newPKCS8EncodedKeySpec(privateKeyBytes);KeyFactorykeyFactory=KeyFactory.getInstance("RSA");returnkeyFactory.generatePrivate(keySpec);}
Per carregar una clau pública d'un fitxer, cal llegir les dades codificades en el format PEM
mitjançant la classe X509EncodedKeySpec i convertir-la al objecte Java corresponent.
i després escriure-la a un fitxer.
En el següent exemple, s'ha eliminat el "header" i "footer" de la clau,
que es pot haver afegit en el moment de guardar-la.
publicstaticPublicKeyloadPublicKeyFromFile(Stringpath)throwsInvalidKeySpecException,IOException,NoSuchAlgorithmException{// Read the public key from the PEM fileStringpublicKeyPem=newString(Files.readAllBytes(Paths.get(path)));publicKeyPem=publicKeyPem.replace("-----BEGIN PUBLIC KEY-----\n","");publicKeyPem=publicKeyPem.replace("-----END PUBLIC KEY-----","");byte[]publicKeyBytes=Base64.getDecoder().decode(publicKeyPem);// Create the public keyX509EncodedKeySpeckeySpec=newX509EncodedKeySpec(publicKeyBytes);KeyFactorykeyFactory=KeyFactory.getInstance("RSA");returnkeyFactory.generatePublic(keySpec);}
Tot el codi font anteriorment proporcionat s'ha compilat
en la següent llibreria:
RSA.java
packageud4.examples;importjavax.crypto.Cipher;importjava.io.IOException;importjava.nio.charset.StandardCharsets;importjava.nio.file.Files;importjava.nio.file.Paths;importjava.security.*;importjava.security.spec.InvalidKeySpecException;importjava.security.spec.PKCS8EncodedKeySpec;importjava.security.spec.X509EncodedKeySpec;importjava.util.Base64;publicclassRSA{publicstaticKeyPairgenerateKeyPair(intlen)throwsException{if(!(len==1024||len==2048||len==4096))thrownewException("La mida de la clau no és vàlida");KeyPairkeys=null;try{KeyPairGeneratorkeyGen=KeyPairGenerator.getInstance("RSA");keyGen.initialize(len);keys=keyGen.genKeyPair();}catch(Exceptionex){System.err.println("Generador no disponible.");}returnkeys;}publicstaticStringencrypt(PublicKeykey,Stringstr){try{// Decode the UTF-8 String into byte[] and encrypt itbyte[]data=encrypt(key,str.getBytes(StandardCharsets.UTF_8));// Encode the encrypted data into base64returnBase64.getEncoder().encodeToString(data);}catch(Exceptionex){System.err.println("Error xifrant les dades: "+ex);}returnnull;}publicstaticStringdecrypt(PrivateKeykey,Stringstr){try{// Decodifiquem les dades xifrades en base64 a byte[]byte[]data=Base64.getDecoder().decode(str);// Desencriptem les dadesbyte[]decrypted=decrypt(key,data);// Codifiquem les dades desencriptades a StringreturnnewString(decrypted);}catch(Exceptionex){System.err.println("Error desxifrant les dades: "+ex);}returnnull;}publicstaticbyte[]encrypt(PublicKeykey,byte[]data)throwsException{returnrsa(key,data,Cipher.ENCRYPT_MODE);}publicstaticbyte[]decrypt(PrivateKeykey,byte[]data)throwsException{returnrsa(key,data,Cipher.DECRYPT_MODE);}privatestaticbyte[]rsa(Keykey,byte[]data,intopmode)throwsException{Ciphercipher=Cipher.getInstance("RSA");cipher.init(opmode,key);returncipher.doFinal(data);}publicstaticPrivateKeyloadPrivateKeyFromFile(Stringpath)throwsInvalidKeySpecException,IOException,NoSuchAlgorithmException{// Read the private key from the PEM fileStringprivateKeyPem=newString(Files.readAllBytes(Paths.get(path)));privateKeyPem=privateKeyPem.replace("-----BEGIN PRIVATE KEY-----\n","");privateKeyPem=privateKeyPem.replace("-----END PRIVATE KEY-----","");byte[]privateKeyBytes=Base64.getDecoder().decode(privateKeyPem);// Create the private keyPKCS8EncodedKeySpeckeySpec=newPKCS8EncodedKeySpec(privateKeyBytes);KeyFactorykeyFactory=KeyFactory.getInstance("RSA");returnkeyFactory.generatePrivate(keySpec);}publicstaticvoidsavePrivateKeyToFile(PrivateKeykey,Stringpath)throwsIOException{// Get the encoded private keyPKCS8EncodedKeySpeckeySpec=newPKCS8EncodedKeySpec(key.getEncoded());// Base64 encode the keyStringencodedKey=Base64.getEncoder().encodeToString(keySpec.getEncoded());// Add the PEM headers and footersencodedKey="-----BEGIN PRIVATE KEY-----\n"+encodedKey+"-----END PRIVATE KEY-----";// Write the key to a fileFiles.write(Paths.get(path),encodedKey.getBytes());}publicstaticPublicKeyloadPublicKeyFromFile(Stringpath)throwsInvalidKeySpecException,IOException,NoSuchAlgorithmException{// Read the public key from the PEM fileStringpublicKeyPem=newString(Files.readAllBytes(Paths.get(path)));publicKeyPem=publicKeyPem.replace("-----BEGIN PUBLIC KEY-----\n","");publicKeyPem=publicKeyPem.replace("-----END PUBLIC KEY-----","");byte[]publicKeyBytes=Base64.getDecoder().decode(publicKeyPem);// Create the public keyX509EncodedKeySpeckeySpec=newX509EncodedKeySpec(publicKeyBytes);KeyFactorykeyFactory=KeyFactory.getInstance("RSA");returnkeyFactory.generatePublic(keySpec);}publicstaticvoidsavePublicKeyToFile(PublicKeykey,Stringpath)throwsIOException{// Get the encoded public keyX509EncodedKeySpeckeySpec=newX509EncodedKeySpec(key.getEncoded());// Base64 encode the keyStringencodedKey=Base64.getEncoder().encodeToString(keySpec.getEncoded());// Add the PEM headers and footersencodedKey="-----BEGIN PUBLIC KEY-----\n"+encodedKey+"-----END PUBLIC KEY-----";// Write the key to a fileFiles.write(Paths.get(path),encodedKey.getBytes());}publicstaticvoidmain(String[]args){Stringmessage="Aquest és un missatge super secret";KeyPairkeys=null;try{keys=generateKeyPair(1024);System.out.printf("Public key: %s\n",Base64.getEncoder().encodeToString(keys.getPublic().getEncoded()));System.out.printf("Private key: %s\n",Base64.getEncoder().encodeToString(keys.getPrivate().getEncoded()));System.out.printf("Message: %s\n",message);Stringencrypted=encrypt(keys.getPublic(),message);System.out.printf("Encrypted message: %s\n",encrypted);Stringdecrypted=decrypt(keys.getPrivate(),encrypted);System.out.printf("Decrypted message: %s\n",decrypted);System.out.println();}catch(Exceptione){System.err.println("Error creant el parell de claus: "+e);}}}