Ofrece un API (application programming interface) que permite:
Las implementaciones de los distintos algoritmos de cifrado, generación de claves y demás son ofertadas por paquetes externos denominados providers.
Así, es posible dotar a JCA de nuevas funcionalidades sin necesidad de cambiar el API básica y permitir la distribución de algoritmos criptográficos con limitaciones de exportación.
Para usar las clases y métodos del API JCA las aplicaciones tienen que importar, como mínimo, los siguientes paquetes:
import java.security.*; import java.security.interfaces.*; import java.security.spec.*; import javax.crypto.*; import javax.crypto.interfaces.*; import javax.crypto.spec.*;
En la página web del proyecto (http://www.bouncycastle.org) es posible descargar la versión actual (160) del provider Bouncy Castle para distintas versiones de la máquina virtual de Java.
También incluye información resumida y el javadoc completo de la distribución.
import org.bouncycastle.jce.provider.BouncyCastleProvider;
Dentro del código será necesario indicar la carga del provider BouncyCastle del siguiente modo:
Security.addProvider(new BouncyCastleProvider());
Este código se deberá situar antes de cualquier uso que se realice de este provider. La abreviatura que identifica a este provider es ”BC” y deberá indicarse cuando se solicite alguna de las implementaciones de algoritmos que ofrece el provider BouncyCastle.
Cipher cifrador = Cipher.getInstance("DES/ECB/PKCS1Padding", "BC"); KeyGenerator keyGen = KeyGenerator.getInstance("AES", "BC");
Para compilar y ejecutar estos programas será necesario indicar mediante el parámetro -classpath el fichero .jar con las clases BouncyCastle adecuado a la máquina virtual Java que esté instalada.
$ javac -classpath ".:bcprov-jdk15on-xxx.jar" Fichero.java $ java -classpath ".:bcprov-jdk15on-xxx.jar" Fichero
C:\ javac -classpath ".;bcprov-jdk15on-xxx.jar" Fichero.java C:\ java -classpath ".;bcprov-jdk15on-xxx.jar" Fichero
Otra alternativa que evita especificar el CLASSPATH en cada invocación es copiar el fichero JAR de BouncyCastle en el directorio de librerías externas de la instalación del JDK o JRE, de modo que se incluya en el CLASSPATH por defecto de la máquina virtual Java.
$ cp bcbcprov-jdk15on-xxx.jar /usr/lib/jvm/java-x-yyy/jre/lib/ext/
El acceso al uso de funciones Hash criptográficas se lleva a cabo mediante instancias que hereden de MessageDigest
Se debe indicar el nombre (alias) del algoritmo de HASH y opcionalmente el nombre del provider.
static MessageDigest getInstance(String algorithm) static MessageDigest getInstance(String algorithm, String provider)
void update(byte input) void update(byte[] input) void update(byte[] input, int offset, int len)
byte[] digest() byte[] digest(byte[] input) // Método de conveniencia, crea el resumen de los datos directamente
Ejemplo de uso del algoritmo MD5: EjemploHash
$ javac EjemploHash.java $ java EjemploHash fichero.txt
El uso de cifradores simétricos (y también asimétricos) se hace mediante instancias que heredan de la clase Cypher
Se debe indicar una especificación del esquema de cifrado a utilizar.
static Cipher getInstance(String transformation) static Cipher getInstance(String transformation, String provider)
Las claves se crean empleando un objeto KeyGenerator.
KeyGenerator generadorDES = KeyGenerator.getInstance("DES");
generadorDES.init(56); // clave de 56 bits
SecretKey clave = generadorDES.generateKey();
cifrador.init(Cipher.ENCRYPT_MODE, clave); cifrador.init(Cipher.DECRYPT_MODE, clave);
byte[] update(byte[] input) byte[] update(byte[] input, int offset, int len)Devuelve un array de byte con el resultado ”parcial” del cifrado/descrifrado
byte[] doFinal() byte[] doFinal(byte[] input) // Metodo de conveniencia, combina update() y doFinal() byte[] doFinal(byte[] input, int inputOffset, int inputLen) // Metodo de conveniencia, combina update() y doFinal()Devuelve un array de byte con el resultado del cifrado/descrifrado del último bloque
Ejemplo de uso del algoritmo DES: EjemploDES
$ javac EjemploDES.java $ java EjemploDES fichero.txt
El funcionamiento de los cifradores asimétricos es idéntico al de los simétricos ya que también hacen uso de objetos que hereden de la clase Cypher.
La única diferencia es el modo en que se generan los pares de claves públicas y privadas y el uso que se hace de ellas (en cifrado y/o descrifrado).
Las claves se gestionan mediante objetos que implementan el interfaz PublicKey y PrivateKey (que a su vez hereda del interfaz Key)
Las claves se crean empleando un objeto KeyPairGenerator especifico para cada algoritmo asimétrico.
Se debe indicar el alias del algoritmo de cifrado y el nombre del provider [inicialmente el provider por defecto ”SUN” no incluía RSA por limitaciones a la exportación de algoritmos de cifrado].
Security.addProvider(new BouncyCastleProvider()); // Cargar el provider BC ... KeyPairGenerator keyGenRSA = KeyPairGenerator.getInstance("RSA", "BC"); // Usa BouncyCastle
gkeyGenRSA.initialize(1024); // clave RSA de 1024 bits
KeyPair clavesRSA = keyGenRSA.generateKeyPair(); PrivateKey clavePrivada = clavesRSA.getPrivate(); PublicKey clavePublica = clavesRSA.getPublic();
Nota: Se pueden inspeccionar los parámetros de las claves publica y privada RSA forzando un cast a los interfaces RSAPrivateKey y RSAPublicKey (se necesita hacer un import del paquete java.security.interfaces.*)
RSAPrivateKey clavePrivadaRSA = (RSAPrivateKey) clavePrivada; System.out.println("exponente descrifrado: " + clavePrivadaRSA.getPrivateExponent().toString() ); System.out.println("modulo: " + clavePrivadaRSA.getModulus().toString() ); RSAPublicKey clavePublicaRSA = (RSAPublicKey) clavePublica; System.out.println("exponente cifrado: " + clavePublicaRSA.getPublicExponent().toString() ); System.out.println("modulo: " + clavePublicaRSA.getModulus().toString() );
Ejemplo de uso del algoritmo RSA: EjemploRSA
$ javac -classpath ".:bcprov-jdk15on-xxx.jar" EjemploRSA.java (en Linux) $ java -classpath ".:bcprov-jdk15on-xxx.jar" EjemploRSA (en Linux)
Dado que JCE tiene una arquitectura basada en providers donde las clases de implementación del provider son independientes de los interfaces genéricos definidos en el API y, en principio, no son accesibles directamente, son necesarios ”pasos intermedios” para almacenar y recuperar las representaciones internas de las claves (especialmente en el caso de los pares de claves asimétricas).
byte[]
con una representación independiente e interoperable de la propia clave (la especificación [Spec] de la clave).
Ejemplo de escritura/lectura de claves: AlmacenarClaves
$ javac -classpath ".:bcprov-jdk15on-xxx.jar" AlmacenarClaves.java (en Linux) $ java -classpath ".:bcprov-jdk15on-xxx.jar" AlmacenarClaves (en Linux)
JCA ofrece objetos Signature para simplificar la generación y validación de firmas digitales
Nota: Otra alternativa es combinar ”a mano” el uso de funciones HASH con objetos Cypher
Se debe indicar el nombre (alias) de la combinacion de algormitmo de HASH y del algoritmo asimétrico a utilizar (MD5withRSA, MD5withDSA, SHA1withRSA, SHA1withDSA), etc.
Dependiendo del caso, suele ser necesario indicar también el nombre del provider.
static Signature getInstance(String algorithm) static Signature getInstance(String algorithm, String provider)
void initSign(PrivateKey privateKey) void initSign(PrivateKey privateKey, SecureRandom random)
void update(byte[] data) void update(byte[] data, int off, int len)
byte[] sign() int sign(byte[] outbuf, int offset, int len)
void initVerify(PublicKey publicKey) void initVerify(Certificate certificate)
void update(byte[] data) void update(byte[] data, int off, int len)
Devuelve un valor booleano indicado el resultado de la verificación (coincidencia o no de los HASHES).
boolean verify(byte[] signature) boolean verify(byte[] signature, int offset, int length)