Un certificat és un document digital que s'utilitza per demostrar la identitat d'una entitat,
com ara un lloc web o una persona.
Conté informació sobre la identitat de l'entitat,
així com la clau pública de l'entitat.
Els certificats els emet un tercer de confiança anomenat autoritat de certificació (CA),
que verifica la identitat de l'entitat abans d'emetre el certificat.
Encara que normalment els certificats els emet una CA, qualsevol persona pot generar
certificats i allotjar una CA.
L'eina KeyTool ve incorporada amb el JDK de Java. Per poder utilitzar-la, comproveu que la carpeta <JDK Installation Folder>/bin està
afegida a la variable PATH del sistema.
Consell
Com configurar les variables d'entorn del sistema a Windows 11:
Aquesta comanda s'utilitza per generar un parell de claus RSA de 2048 bits i guardar-les en un magatzem de claus Java KeyStore amb el nom
keystore.jks i identificat amb el àlies example que serà vàlid durant 360 dies.
Les opcions utilitzades són:
keytool es la comanda de la utilitat Java KeyTool.
-genkey es l'opció per indicar que es genere un nou parell de claus.
-keyalg RSA especifica que les claus han d'utilitzar l'algorisme RSA.
-alias especifica l'àlies que identifica el certificat en el magatzem de claus KeyStore.
-keypass especifica la contrasenya del certificat.
-keystore especifica el nom del magatzem de claus on es guardarà el certificat.
-storepass especifica la contrasenya que protegeix el magatzem de claus.
-validity 360 especifica el nombre de dies que el certificat serà vàlid.
-keysize 2048 especifica la mida en bits de el parell de claus.
-dname permet especificar la informació del certificat.
CN o Common Name: Identificació del certificat.
O o Organization: Organització a qui pertany el certificat.
OU o Organizational Unit: Unitat dins de la organització.
Aquesta comanda exporta el certificat identificat per l'àlies example de el magatzem
de claus ubicat a example_keystore.jks al fitxer example_certificate.crt.
Les opcions utilitzades són:
keytool es la comanda de la utilitat Java KeyTool.
-export es l'opció per indicar que s'exporte un certificat.
-alias especifica l'àlies que identifica el certificat
en el magatzem de claus KeyStore que es desitja exportar.
-keystore especifica el nom del magatzem de claus des d'on s'exportarà el certificat.
-file especifica el fitxer on el certificat serà exportat.
Aquesta comanda importa el certificat del fitxer example_certificate.crt i el emmagatzemarà en el magatzem
de claus ubicat a example_truststore.jks i l'identificarà per l'àlies example.
Les opcions utilitzades són:
keytool es la comanda de la utilitat Java KeyTool.
-import es l'opció per indicar que s'importe un certificat.
-alias especifica l'àlies que identifica el certificat
importat en el magatzem de claus KeyStore
-keystore especifica el nom del magatzem de claus on s'importarà el certificat.
-file especifica el fitxer on el certificat importat.
Entre els mètodes més significatius, podem trobar:
int size(): consulta el nombre d’entrades al magatzem.
Key getKey(String alias, char[] password): obté la clau (simètrica o privada,
el que corresponga per l’entrada) associada a un àlies, si existeix.
Si la seva entrada està protegida amb contrasenya, cal proporcionar-la al segon paràmetre.
Una clau simètrica és de tipus SecretKey, mentre que una de privada és de tipus PrivateKey.
Certificate getCertificate(String alias): permet extreure un certificat guardat a un àlies concret.
Els certificats inclouen una clau pública PublicKey i una clau privada PrivateKey,
que poden ser utilitzades per encriptar i desencriptar missatges mitjançant l'algorisme RSA.
La clau pública i privada d'un certificat també serveixen per signar un missatge i verificar que la signatura és correcta.
El procés és:
L'emissor signa un missatge amb la seua clau privada.
La signatura és generada a partir de la clau privada i el contingut del missatge.
L'emissor envia el missatge i la signatura al receptor.
El receptor verifica la signatura a partir de la clau pública de l'emissor i el missatge rebut.
Si la signatura és vàlida, significa que el missatge rebut és el mateix que el missatge enviat per l'emissor.
Si la signatura és invàlida, significa que el missatge rebut no és el mateix missatge enviat per l'emissor i ha pogut ser modificat
per tercers.
Els següents mètodes permeten signar i comprovar la signatura d'un String fàcilment.
No obstant això, qualsevol byte[] pot ser signat i verificat.
En aquest exemple s'ha utilitzat un fitxer de configuració .properties per especificar
les dades sensibles o que poden canviar ens els diferents entorns (desenvolupament, test, producció).
Utilitzar fitxers de configuració es considera una bona pràctica, ja que permet
configurar diferents dades per diferents entorns i evita que es puguen extraure
dades sensibles del codi font o dels executables (utilitzant mètodes com la descompilació).
En el nostre entorn, hem creat el fitxer application.properties, on s'ha guardat
la informació:
ud4.examples.keystore.passwd=123456
Després, hem definit la classe Config que permet llegir la configuració d'un fitxer.
Aquesta classe recopila els mètodes anteriors per poder ser utilitzats.
A més, inclou un exemple d'utilització.
CertificateUtils.java
packageud4.examples;importjava.io.File;importjava.io.FileInputStream;importjava.io.IOException;importjava.security.*;importjava.security.cert.Certificate;importjava.security.cert.CertificateException;importjava.security.cert.X509Certificate;importjava.util.Base64;importjava.util.Collections;importjava.util.List;importjava.util.Properties;publicclassCertificateUtils{publicstaticKeyStoreloadKeyStore(StringksFile,StringksPwd)throwsKeyStoreException,IOException,CertificateException,NoSuchAlgorithmException{KeyStoreks=KeyStore.getInstance("JKS");Filef=newFile(ksFile);if(f.isFile()){FileInputStreamin=newFileInputStream(f);ks.load(in,ksPwd.toCharArray());}returnks;}publicstaticStringgetCertificateInfo(Certificatecertificate){X509Certificatecert=(X509Certificate)certificate;returncert.getSubjectX500Principal().getName();}publicstaticStringsignText(PrivateKeyprivateKey,Stringtext)throwsNoSuchAlgorithmException,InvalidKeyException,SignatureException{Signaturesignature=Signature.getInstance("SHA256withRSA");signature.initSign(privateKey);signature.update(text.getBytes());byte[]signatureBytes=signature.sign();returnBase64.getEncoder().encodeToString(signatureBytes);}publicstaticbooleanverifySignature(PublicKeypublicKey,Stringtext,Stringsigned)throwsNoSuchAlgorithmException,InvalidKeyException,SignatureException{Signaturesignature=Signature.getInstance("SHA256withRSA");signature.initVerify(publicKey);signature.update(text.getBytes());byte[]signatureBytes=Base64.getDecoder().decode(signed);returnsignature.verify(signatureBytes);}publicstaticvoidmain(String[]args){try{/* * Guardar contrasenyes en el codi NO ÉS UNA BONA PRÀCTICA, * cal utilitzar variables d'entorn o un fitxer de configuració. */Propertiesconfig=Config.getConfig("application.properties");StringkeyStorePassword=config.getProperty("ud4.examples.keystore.passwd");KeyStorekeyStore=loadKeyStore("files/ud4/example_keystore.jks",keyStorePassword);List<String>aliases=Collections.list(keyStore.aliases());System.out.println("Certificats en el magatzem de claus.");System.out.printf("Total: %d\n",keyStore.size());for(Stringalias:aliases)System.out.println("- "+alias);/* El certificat ha segut creat prèviament amb la comanda: ``` keytool -genkey -keyalg RSA \ -alias example \ -keystore files/ud4/example_keystore.jks \ -validiry 360 \ -storepass 123456 -keysize 2048 \ -dname "CN=Example, O=CIPFP Mislata, OU=PSP-DAM2S, L=Mislata, ST=València, C=ES" ``` Per poder llegir informació sobre el subjecte del certificat, necessitem utilitzar l'objecte X509Certificate. */for(Stringalias:aliases){Certificatecertificate=keyStore.getCertificate(alias);System.out.println(getCertificateInfo(certificate));// Clau pública del certificatPublicKeyexamplePublic=certificate.getPublicKey();// Clau privadaPrivateKeyexamplePrivate=(PrivateKey)keyStore.getKey(alias,keyStorePassword.toCharArray());Stringmessage="This is a secret information.";System.out.printf("Message: %s\n",message);Stringencrypted=RSA.encrypt(examplePublic,message);System.out.printf("Encrypted: %s\n",encrypted);Stringdecrypted=RSA.decrypt(examplePrivate,encrypted);System.out.printf("Decrypted: %s\n",decrypted);Stringsignautre=signText(examplePrivate,message);System.out.printf("Signed: %s\n",signautre);System.out.println("Signature verification: "+verifySignature(examplePublic,message,signautre));}}catch(Exceptione){System.err.println(e.getMessage());}}}