Difference between revisions of "Digital Signature Implementation in Java"

From OWASP
Jump to: navigation, search
m
(4 intermediate revisions by 2 users not shown)
Line 1: Line 1:
== Status  ==
+
Aplicación de Firma Digital en Java
  
Released 14/1/2007
+
'''1. Estado'''
  
[http://webhostinggeeks.com/science/digital-signature-ro Translation]
+
'''Fecha de lanzamiento''' 14/1/2007
  
== WARNING  ==
+
'''2. ADVERTENCIA'''
  
This wiki page was recently brought to my attention by Jim Manico, who asked me to review it for accuracy. In doing so, I noticed several errors and areas of weakness.Ultimately, I plan to revise this page (hopefully with the assistance of one of the original owners), but I do not have time to make a complete revision at the present time. Therefore, I will summarize what I see is problematic with the rest of this page and you can decide to use or not, with these caveats.<br>
+
Recientemente  Jim Manico trajo a mi atención esta página wiki, el mismo que me pidió que revisara con exactitud. Al hacerlo, me di cuenta de varios errores y áreas de weakness. Últimamente, tengo la intención de revisar esta página (espero que con la ayuda de uno de los propietarios originales), pero no dispongo de tiempo para hacer una revisión completa en este momento. Por lo tanto, resumiré la problemática que observe de esta página y dejándole  a usted la decisión de utilizar o no, con estas advertencias.
  
#First and foremost, this page does ''not ''describe "digital signatures". Rather, it decribes a concept known as "digital envelopes", which is a scheme used with things like S/MIME. Digital signatures alone never encrypt the actual message text, but only encrypt a ''hash ''of the message text.
+
1. En primer lugar, esta página no describe "firmas digitales". Más bien, describe un concepto conocido como "sobres digitales", que es un esquema utilizado con objetos como S / MIME. Las firmas digitales no sólo cifra el texto real del mensaje, sólo cifra el hash del texto del mensaje.
#UTF-8 encoding should be used throughout to convert between Java Strings and byte arrays to ensure proper portability across different operating systems.
+
#The certificate chain should always be validated. In the example here, the certificate is self-signed, so this is not relevant, but that will not be true in the normal case. Furthermore, it should be noted that self-signed certificates, while acceptable for demonstration purposes is considered a dubious practice for production as it opens the door for impersonation attacks.
+
#NIST now recommends the use of 2048 bit key size for RSA or DSA keys.
+
#There is a consistent use of weak algorithms. Here are some suggested replacements:
+
  
*Use "RSA/ECB/OAEPWithSHA1AndMGF1Padding" instead of "RSA/ECB/PKCS1Padding".  
+
2. Codificación UTF-8, debe utilizarse para realizar conversiones entre cadenas y matrices Java byte para asegurar la portabilidad adecuada a través de diferentes sistemas operativos.
*Use SHA1 (or better SHA256, but at least SHA1) instead of MD5 for the message digest.
+
*Use "SHA1withRSA" for the signature algorithm rather than "MD5withRSA".
+
*When creating a symmetric cipher to encrypt the plaintext message, use "AES/CBC/PKCS5Padding" and choose a random IV for each plaintext message rather than using simply "AES", which ends up using "AES/ECB/PKCS5Padding". ECB&nbsp;mode is extremely weak for regular plaintext. (It is OK for encrypting random bits though, which is why it is OK to use with RSA.) However, using CBC and PKCS5Padding could make you vulnerable to "padding oracle" attacks, so be careful. You can use ESAPI 2.0's Encryptor to avoid it. (Note also, this is part of the "digital envelope" concept. If this page were truly limited to "digital ''signatures''", it would not apply, as it would be irrelevant.)
+
  
I hope to get some time soon to clean up this wiki page. In the meantime, email me if you have questions.  
+
3. La cadena de certificados siempre deben ser validados. En este ejemplo, el certificado es auto firmado, por lo que este no es relevante y no será verdadero en el caso normal. Además, debe tenerse en cuenta que los certificados auto firmados, aunque aceptables para fines de demostración se considera una práctica dudosa para la producción, ya que abre la puerta a ataques de suplantación.
  
-kevin wall
+
4. NIST ahora recomienda el uso de 2048 tamaño de la clave de bits para claves RSA o DSA.
  
== Overview  ==
+
5. Hay un consistente uso de algoritmos débiles. Aquí están algunos reemplazos sugeridos:
  
This article gives a brief overview of the concepts involved with Digital Signatures and provide code samples for implementing Digital Signatures in Java using the Java Cryptography Architecture.  
+
• Utilice "RSA/ECB/OAEPWithSHA1AndMGF1Padding" en lugar de "RSA/ECB/PKCS1Padding".
  
=== What is a Digital Signature&nbsp;?  ===
+
• Utilice SHA1 (o mejor SHA256, SHA1 pero por lo menos) en lugar de MD5 para la síntesis del mensaje.
  
A Digital Signature is a construct which helps achieve non-repudiation of Origin (ie. Origin Integrity) of data. By digitally signing the document, the person who signs it assures that he is the author of the document or the message that was signed.  
+
• Utilice "SHA1withRSA" para el algoritmo de la firma en lugar de "MD5withRSA".
  
=== Need for Digital Signature ===
+
• Al crear un sistema de cifrado simétrico para cifrar el mensaje de texto, utilice "AES/CBC/PKCS5Padding" y elija un IV aleatorio para cada mensaje de texto en lugar de utilizar simplemente "AES", que termina con "AES/ECB/PKCS5Padding". Modo ECB es extremadamente débil para un texto plano regular. (Está bien para el cifrado de bits aleatorios, sin embargo, está bien usar con RSA.) Sin embargo, el uso CBC y PKCS5Padding podría hacer vulnerable a "Padding Oracle" ataques, así que se recomienda tener cuidado. Puede utilizar Encryptor ESAPI 2.0 's para evitarlo. (Tenga en cuenta también, esta parte es del concepto de "sobre digital". Si esta página fuera limitada verdaderamente por "firmas digitales", no se aplicaría, ya que sería irrelevante.)
  
During the "E" revolution, there was a need for authenticating critical transactions especially in the financial World. If Alice has agreed to transfer $x to Bob, then there had to be a way for Bob to be sure that:
+
Espero sacar tiempo lo más pronto, antes de limpiar esta página wiki. Mientras tanto, envíeme por correo electrónico sus preguntas.
 +
-Kevin Wall
  
#It was Alice who performed the transaction and not someone else impersonating Alice (Authentication)
+
'''3. Visión de conjunto'''
#The amount agreed by Alice is $x (Integrity)
+
#Alice could not dispute her statement of transacting $x to Bob (Non-Repudiation of Origin)
+
  
These concerns were addressed with a solution known as Digital Signatures. More background information about Digital Signatures can be found on the [http://en.wikipedia.org/wiki/Digital_signature Wikipedia article]
+
Este artículo presenta un breve resumen de los conceptos involucrados con firmas digitales y proporciona ejemplos de código para la aplicación de firmas digitales en Java utilizando la Arquitectura de Java Cryptography.
  
== Digital Signatures in Java using JCA  ==
+
'''3.1. ¿Qué es una firma digital?'''
  
The Java Cryptography Architecture is a framework for accessing and developing cryptographic functionality for the Java platform. A JCA provider implements the cryptographic functionalities like Digital Signatures and Message Digests. The default JCA provider in JDK 1.4.2 is SUN.  
+
Una firma digital es un concepto que ayuda a obtener el no repudio de origen (es decir, la Integridad del Origen) de datos. Al firmar digitalmente el documento, la persona que firma, asegura que él es el autor del documento o el mensaje firmado.
  
=== Security Considerations while Implementing Digital Signature  ===
+
'''3.2. Necesidad de Firma Digital'''
  
Two main Security considerations should be taken into account when implementing Digital Signatures.  
+
Durante la "E" revolución, fue una necesidad para la autenticación de críticas transacciones sobre todo en el mundo financiero. Si Alice se comprometió a transferir $ x para Bob, entonces tenía que haber una manera para que Bob se asegure de que:
  
#Sign the message and then encrypt the signed message
+
1. Fue Alice quien realizó la transacción y no otra persona suplantando Alice (Autenticación).
#Sign the Hash of the message instead of the entire message
+
  
=== Performance Considerations while Implementing Digital Signature  ===
+
2. El monto acordado por Alice es $x (Integridad).
  
Since Asymmetric encryption algorithms like RSA, DSA are computationally slower than symmetric encryption algorithms like AES, it is good practice to encrypt the actual message to be transmitted using a Symmetric key Algorithm and then encrypt the key used in the Symmetric Key Algorithm using an Asymmetric Key Algorithm. E.g.: if one wants to transmit the message "Hello World of Digital Signatures", then first encrypt this message using a symmetric key ,say an 128 bit AES key like x7oFaHSPnWxEMiZE/0qYrg and then encrypt this key using an asymmetric key algorithm like RSA.  
+
3. Alicia no pudo discutir su declaración de transacción $x a Bob (No repudio de origen).
  
== Algorithm for Implementing Digital signature using RSA Algorithm  ==
+
Estas preocupaciones fueron tratadas con una solución conocida como firmas digitales. Más información de fondo sobre las firmas digitales se puede encontrar en el artículo de Wikipedia.
  
The RSA implementation provider in Java has a limitation in that encryption can be done only on data of length &lt;= 117 bytes. If the data is of length &gt; 117 bytes, it would throw an IllegalBlockSizeException&nbsp;: Data must not be longer than 117 bytes Hence the symmetric has to be encrypted and then signed.
+
'''4. Firmas digitales en Java utilizando JCA'''
  
The RSA algorithm with PKCS#1 padding can only encrypt data of size k - 11 [http://www.rsa.com/rsalabs/node.asp?id=2125 1], where k is the octet length of the RSA modulus and 11 is amount of bytes used by PCKS#1 v1.5 padding. Hence if we use an RSA key of size 1024 bits, we could encrypt only 128 - 11 =&gt; 117 bytes of data. There are 2 options available to encrypt data of larger byte size.  
+
La Arquitectura de Java Cryptography es un marco para el acceso y el desarrollo de la funcionalidad criptográfica para la plataforma Java. Un proveedor de JCA implementa las funcionalidades criptográficas como firmas digitales y compendios de mensajes. El proveedor predeterminado JCA en JDK 1.4.2 es SUN.
  
#We could use an RSA key of length &gt; 1024. For eg, say if we use 2048 bits, then we could encrypt 256 - 11 =&gt; 245 bytes of data. The disadvantage with this approach is that we would not be able to encrypt data of size &gt; x bytes (in the above example x is 245).
+
'''4.1. Consideraciones de seguridad al implementar la firma digital'''
#Break down the input data into chunks of bytes of size &lt; 117 and apply encryption on each chunk. Code sample for this approach can be found [http://www.aviransplace.com/2004/10/12/using-rsa-encryption-with-java/3/ here]
+
  
Both these approaches would have performance implications since RSA key of larger sizes or a "divide and conquer" approach on input bytes are computationally expensive.
+
Dos consideraciones principales de seguridad que se deben tener en cuenta al aplicar firmas digitales son:
  
=== Algorithm  ===
+
1. Firma el mensaje y, a continuación cifrar el mensaje firmado.
  
With the above considerations, the algorithm below can be used for implementing public key cryptography in Java.  
+
2. Firma el hash del mensaje en lugar del mensaje completo.
  
#Encrypt the message using a symmetric key.
 
#Concatenate the symmetric key + Hash of symmetric key + Hash of message.
 
#Encrypt the concatenated string using the receivers public key.
 
#Sign the data to be transmitted (Encrypted symmetric key + Hash of the key + Hash of message).
 
#Validate the Signature.
 
#Decrypt the message using Receiver private key to get the symmetric key.
 
#Validate the integrity of the key using the Hash of the key.
 
#Decrypt the actual message using the symmetric key which has been decrypted and parsed and checked for integrity.
 
#Compute MessageDigest of data.
 
#Validate if the Message Digest of the decrypted text matches the Message Digest of the Original Message.
 
  
=== Commands for generating keys  ===
+
'''4.2. Consideraciones sobre el rendimiento al implementar la firma digital'''
  
prompt# keytool -genkey -alias testsender -keystore testkeystore.ks -keyalg RSA Enter keystore password: testpwd What is your first and last name?
+
Dado que los algoritmos de cifrado asimétricos, como RSA, DSA son computacionalmente más lento que los algoritmos de cifrado simétricos como AES, es una buena práctica, cifrar el mensaje real que se transmite utilizando un algoritmo de clave simétrica y luego cifrar la clave que se utiliza en el algoritmo de clave simétrica utilizando un algoritmo de clave asimétrica. Por ejemplo: si se quiere transmitir el mensaje "Hola Mundo de Firmas Digitales", entonces primero se cifra este mensaje con una clave simétrica, por ejemplo una clave AES de 128 bits como x7oFaHSPnWxEMiZE/0qYrg y luego se cifra esta clave con un algoritmo de clave asimétrica como RSA.
  
[Unknown]:  Alice Sender
+
'''5. Algoritmo para implementar la firma digital utilizando el algoritmo RSA'''
  
What is the name of your organizational unit?
+
El proveedor de la implementación en Java RSA tiene una limitación en que el cifrado se puede realizar sólo en los datos de longitud <= 117 bytes. Si los datos son de longitud > 117 bytes, se lanzaría un IllegalBlockSizeException: Los datos no debe tener más de 117 bytes ahí la simetría tiene que ser cifrados y luego firmados.
  
  [Unknown]:  IT
+
El algoritmo RSA PKCS # 1 con relleno sólo puede cifrar los datos de tamaño k - 11, donde k cuya longitud es de un octeto del módulo RSA y 11 es la cantidad de bytes utilizados por el relleno PCKS # 1 v1.5. Por lo tanto, si usamos una clave RSA de tamaño de 1024 bits, podríamos cifrar sólo 128 - 11 => 117 bytes de datos. Hay dos opciones disponibles para cifrar los datos de tamaño de byte más grande.
  
What is the name of your organization?
+
1. Podríamos usar una clave RSA de longitud> 1024. Por ejemplo, si usamos 2048 bits, entonces podríamos cifrar 256-11 => 245 bytes de datos. La desventaja de este enfoque es que no sería capaz de codificar los datos de tamaño> x bytes (en el ejemplo anterior x es 245).
  
[Unknown]:  ABC Inc
+
2. Analizar los datos de entrada en trozos de bytes de tamaño <117 y aplicar la encriptación en cada trozo. El código de ejemplo de este enfoque se puede encontrar aquí.
  
What is the name of your City or Locality?
+
Ambos enfoques podrían afectar al rendimiento ya que la clave RSA de mayor tamaño o un enfoque de "divide y vencerás" de bytes de entrada son computacionalmente costosa.
  
[Unknown]:  LA
+
'''5.1. Algoritmo'''
 +
Con las anteriores consideraciones, el algoritmo siguiente puede ser usado para la implementación de la criptografía de clave pública en Java.
  
What is the name of your State or Province?
+
1. Cifrar el mensaje utilizando una clave simétrica.
  
[Unknown]:  CA
+
2. Concatenar la clave simétrica + hash de clave simétrica + hash del mensaje.
  
What is the two-letter country code for this unit?
+
3. Cifrar la cadena concatenada con la clave pública de los receptores.
  
[Unknown]:  US
+
4. Firmar los datos a transmitir (clave simétrica cifrada + Hash de la tecla + Hash del mensaje).
  
Is CN=Alice Sender, OU=IT, O=ABC Inc, L=LA, ST=CA, C=US correct?
+
5. Validar la firma.
  
[no]:  y
+
6. Descifrar el mensaje con la clave privada del receptor para obtener la clave simétrica.
  
Enter key password for &lt;testsender&gt;
+
7. Validar la integridad de la clave utilizando el hash de la clave.
  
(RETURN if same as keystore password):  send123
+
8. Descifrar el mensaje real usando la clave simétrica que se ha descifrado, se analiza y se comprueba la integridad.
  
prompt # keytool -genkey -alias testrecv -keystore testkeystore.ks -keyalg RSA Enter keystore password: testpwd What is your first and last name?
+
9. Calcular MessageDigest de datos.
  
[Unknown]:  Bob Receiver
+
10. Validar si la síntesis del mensaje del texto descifrado coincide con el resumen de mensaje del mensaje original.
  
What is the name of your organizational unit?
+
'''5.2. Comandos para la generación de claves'''
  
[Unknown]:  HR
+
prompt # keytool-genkey-alias Testsender-keystore testkeystore.ks-keyalg RSA
  
What is the name of your organization?
+
Introduzca la contraseña del almacén de claves: testpwd
  
[Unknown]:  ABC Inc
+
¿Cuál es su nombre y apellido?
  
What is the name of your City or Locality?
+
[Unknown]: Alice Sender
  
[Unknown]:  SFO
+
¿Cuál es el nombre de la unidad organizativa?
  
What is the name of your State or Province?
+
[Unknown]:  IT
  
[Unknown]:  CA
+
¿Cuál es el nombre de su organización?
  
What is the two-letter country code for this unit?
+
[Unknown]:  ABC Inc
  
[Unknown]:  US
+
¿Cuál es el nombre de su ciudad o localidad?
  
Is CN=Bob Receiver, OU=HR, O=ABC Inc, L=SFO, ST=CA, C=US correct?
+
[Unknown]:  LA
  
[no]:  y
+
¿Cuál es el nombre de su estado o provincia?
  
Enter key password for &lt;testrecv&gt;
+
[Unknown]:  CA
  
(RETURN if same as keystore password):  recv123
+
¿Qué es el código de país de dos letras para esta unidad?
  
=== Code Sample ===
+
[Unknown]: US
  
==== PublicKeyCryptography.java ====
+
Es CN = Alice Sender, OU = IT, O = ABC Inc, L = LA, ST = CA, C = EE.UU. correcto?
<pre>package org.owasp.crypto;
+
 
 +
[NO]: y  
 +
 
 +
Introduzca la contraseña clave para <testsender>
 +
 
 +
(RETURN if same as keystore password): send123
 +
 
 +
prompt # keytool-genkey-alias testrecv-keystore testkeystore.ks-keyalg RSA
 +
 
 +
Introduzca la contraseña del almacén de claves: testpwd
 +
 
 +
¿Cuál es su nombre y apellido?
 +
 
 +
[Unknown]:  Bob Receiver
 +
 
 +
¿Cuál es el nombre de la unidad organizativa?
 +
 
 +
[Unknown]:  HR
 +
 
 +
¿Cuál es el nombre de su organización?
 +
 
 +
[Unknown]:  ABC Inc
 +
 
 +
¿Cuál es el nombre de su ciudad o localidad?
 +
 
 +
[Unknown]:  SFO
 +
 
 +
¿Cuál es el nombre de su estado o provincia?
 +
 
 +
[Unknown]:  CA
 +
 
 +
¿Qué es el código de país de dos letras para esta unidad?
 +
 
 +
[Unknown]:  US
 +
 
 +
Es CN = Bob receptor, OU = HR, O = ABC Inc, L = SFO, ST = CA, C = EE.UU. correcto?
 +
 
 +
[no]: y 
 +
 
 +
Introduzca la contraseña clave para <testrecv>
 +
 
 +
(RETURN if same as keystore password): recv123
 +
 
 +
'''5.3. Code Sample'''
 +
 
 +
'''5.3.1. PublicKeyCryptography.java'''
 +
 
 +
 
 +
package org.owasp.crypto;
  
 
import java.security.*;
 
import java.security.*;
 +
 
import java.security.cert.*;
 
import java.security.cert.*;
 +
 
import javax.crypto.*;
 
import javax.crypto.*;
 +
 
import sun.misc.BASE64Encoder;
 
import sun.misc.BASE64Encoder;
 +
 
import sun.misc.BASE64Decoder;
 
import sun.misc.BASE64Decoder;
 +
  
  
Line 165: Line 199:
 
  * @author Joe Prasanna Kumar
 
  * @author Joe Prasanna Kumar
 
  *  
 
  *  
* 1. Encrypt the data using a Symmetric Key
+
* 1. Cifrar los datos utilizando una clave simétrica
* 2. Encrypt the Symmetric key using the Receivers public key
+
* 2. Cifrar la clave simétrica con la clave pública Receptores
* 3. Create a Message Digest of the data to be transmitted
+
* 3. Crear un resumen del mensaje de los datos a transmitir
* 4. Sign the message to be transmitted
+
* 4. Firma el mensaje a transmitir
* 5. Send the data over to an unsecured channel
+
* 5. Envíe los datos a través de un canal no seguro
* 6. Validate the Signature
+
* 6. Validar la firma
* 7. Decrypt the message using Recv private Key to get the Symmetric Key
+
* 7. Descifrar el mensaje usando la llave privada Pets para obtener la clave simétrica
* 8. Decrypt the data using the Symmetric Key
+
* 8. Descifrar los datos usando la clave simétrica
* 9. Compute MessageDigest of data + Signed message
+
* 9. Calcule MessageDigest de datos + mensaje firmado
* 10.Validate if the Message Digest of the Decrypted Text matches the Message Digest of the Original Message
+
* 10.Valide si la síntesis del mensaje del texto descifrado coincide con el resumen de mensaje del mensaje original *  
*  
+
 
  *  
 
  *  
 
  */
 
  */
Line 190: Line 223:
 
byte[] byteDataToTransmit = strDataToEncrypt.getBytes();
 
byte[] byteDataToTransmit = strDataToEncrypt.getBytes();
  
// Generating a SecretKey for Symmetric Encryption
+
// Generación de un SecretKey para el cifrado simétrico
 
SecretKey senderSecretKey = SymmetricEncrypt.getSecret();
 
SecretKey senderSecretKey = SymmetricEncrypt.getSecret();
 
 
//1. Encrypt the data using a Symmetric Key
+
//1. Cifrar los datos utilizando una clave simétrica
 
byte[] byteCipherText = encryptUtil.encryptData(byteDataToTransmit,senderSecretKey,"AES");
 
byte[] byteCipherText = encryptUtil.encryptData(byteDataToTransmit,senderSecretKey,"AES");
 
String strCipherText = new BASE64Encoder().encode(byteCipherText);
 
String strCipherText = new BASE64Encoder().encode(byteCipherText);
 
 
 
 
//2. Encrypt the Symmetric key using the Receivers public key
+
//2. Cifrar la clave simétrica con la clave pública Receptores
 
try{
 
try{
// 2.1 Specify the Keystore where the Receivers certificate has been imported
+
     
 +
      // 2.1 Especifique el almacén de claves que se haya importado el certificado Receptores
 
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
 
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
 
char [] password = "testpwd".toCharArray();
 
char [] password = "testpwd".toCharArray();
 
java.io.FileInputStream fis = new java.io.FileInputStream("/home/Joebi/workspace/OWASP_Crypto/org/owasp/crypto/testkeystore.ks");
 
java.io.FileInputStream fis = new java.io.FileInputStream("/home/Joebi/workspace/OWASP_Crypto/org/owasp/crypto/testkeystore.ks");
    ks.load(fis, password);
+
        ks.load(fis, password);
    fis.close();
+
        fis.close();
 
      
 
      
// 2.2 Creating an X509 Certificate of the Receiver
+
// 2.2 Creación de un certificado X509 del receptor
    X509Certificate recvcert&nbsp;;
+
        X509Certificate recvcert ;
    MessageDigest md = MessageDigest.getInstance("MD5");
+
        MessageDigest md = MessageDigest.getInstance("MD5");
    recvcert = (X509Certificate)ks.getCertificate("testrecv");
+
        recvcert = (X509Certificate)ks.getCertificate("testrecv");
    // 2.3 Getting the Receivers public Key from the Certificate
+
 
    PublicKey pubKeyReceiver = recvcert.getPublicKey();
+
        // 2.3 Getting the Receivers public Key from the Certificate
 +
     
 +
      PublicKey pubKeyReceiver = recvcert.getPublicKey();
 
      
 
      
    // 2.4 Encrypting the SecretKey with the Receivers public Key
+
        // 2.4 Cifrado de la SecretKey con la clave pública Receptores
    byte[] byteEncryptWithPublicKey = encryptUtil.encryptData(senderSecretKey.getEncoded(),pubKeyReceiver,"RSA/ECB/PKCS1Padding");
+
        byte[] byteEncryptWithPublicKey = encryptUtil.encryptData(senderSecretKey.getEncoded(),pubKeyReceiver,"RSA/ECB/PKCS1Padding");
    String strSenbyteEncryptWithPublicKey = new BASE64Encoder().encode(byteEncryptWithPublicKey);
+
        String strSenbyteEncryptWithPublicKey = new BASE64Encoder().encode(byteEncryptWithPublicKey);
 
          
 
          
    // 3. Create a Message Digest of the Data to be transmitted
+
      // 3. Crear un resumen del mensaje de los datos a transmitir
    md.update(byteDataToTransmit);
+
        md.update(byteDataToTransmit);
 
byte byteMDofDataToTransmit[] = md.digest();
 
byte byteMDofDataToTransmit[] = md.digest();
 
 
 
String strMDofDataToTransmit = new String();
 
String strMDofDataToTransmit = new String();
for (int i = 0; i &lt; byteMDofDataToTransmit.length; i++){
+
for (int i = 0; i < byteMDofDataToTransmit.length; i++){
strMDofDataToTransmit = strMDofDataToTransmit + Integer.toHexString((int)byteMDofDataToTransmit[i] &amp; 0xFF)&nbsp;;
+
strMDofDataToTransmit = strMDofDataToTransmit + Integer.toHexString((int)byteMDofDataToTransmit[i] & 0xFF) ;
 
             }
 
             }
 
 
    // 3.1 Message to be Signed = Encrypted Secret Key + MAC of the data to be transmitted
+
      // 3.1 Mensaje que se Firmado = clave secreta cifrada + MAC de los datos a transmitir
String strMsgToSign = strSenbyteEncryptWithPublicKey + "|" + strMDofDataToTransmit;
+
        String strMsgToSign = strSenbyteEncryptWithPublicKey + "|" + strMDofDataToTransmit;
 
      
 
      
    // 4. Sign the message
+
      // 4. Firma el mensaje
    // 4.1 Get the private key of the Sender from the keystore by providing the password set for the private key while creating the keys using keytool
+
      // 4.1 Obtener la clave privada del remitente desde el almacén de claves, proporcionando la contraseña establecida
 +
      for the private key while creating the keys using keytool
 
char[] keypassword = "send123".toCharArray();
 
char[] keypassword = "send123".toCharArray();
    Key myKey =  ks.getKey("testsender", keypassword);
+
      Key myKey =  ks.getKey("testsender", keypassword);
    PrivateKey myPrivateKey = (PrivateKey)myKey;
+
      PrivateKey myPrivateKey = (PrivateKey)myKey;
 
      
 
      
    // 4.2 Sign the message
+
    / / 4,2 firmar el mensaje
 
     Signature mySign = Signature.getInstance("MD5withRSA");
 
     Signature mySign = Signature.getInstance("MD5withRSA");
 
     mySign.initSign(myPrivateKey);
 
     mySign.initSign(myPrivateKey);
 
     mySign.update(strMsgToSign.getBytes());
 
     mySign.update(strMsgToSign.getBytes());
 
     byte[] byteSignedData = mySign.sign();
 
     byte[] byteSignedData = mySign.sign();
          
+
         / / 5. Los valores byteSignedData (la firma) y strMsgToSign (los datos que se firmó) se pueden enviar a través del receptor
// 5. The Values byteSignedData (the signature) and strMsgToSign (the data which was signed) can be sent across to the receiver
+
 
+
      / / 6.Validate la Firma
// 6.Validate the Signature
+
    / / 6,1 extraer la clave pública de su certificado de remitentes
    // 6.1 Extracting the Senders public Key from his certificate
+
X509Certificate sendercert ;
X509Certificate sendercert&nbsp;;
+
 
sendercert = (X509Certificate)ks.getCertificate("testsender");
 
sendercert = (X509Certificate)ks.getCertificate("testsender");
 
     PublicKey pubKeySender = sendercert.getPublicKey();
 
     PublicKey pubKeySender = sendercert.getPublicKey();
Line 263: Line 299:
 
     System.out.println(" Successfully validated Signature ");
 
     System.out.println(" Successfully validated Signature ");
  
    // 7. Decrypt the message using Recv private Key to get the Symmetric Key
+
  / / 7. Descifrar el mensaje usando la llave privada Pets para obtener la clave simétrica
 
     char[] recvpassword = "recv123".toCharArray();
 
     char[] recvpassword = "recv123".toCharArray();
 
     Key recvKey =  ks.getKey("testrecv", recvpassword);
 
     Key recvKey =  ks.getKey("testrecv", recvpassword);
 
     PrivateKey recvPrivateKey = (PrivateKey)recvKey;
 
     PrivateKey recvPrivateKey = (PrivateKey)recvKey;
 
      
 
      
    // Parsing the MessageDigest and the encrypted value
+
  / / Analizar el MessageDigest y el valor cifrado
 
     String strRecvSignedData = new String (byteSignedData);
 
     String strRecvSignedData = new String (byteSignedData);
 
     String[] strRecvSignedDataArray = new String [10];
 
     String[] strRecvSignedDataArray = new String [10];
Line 276: Line 312:
 
     String strHashOfData = strMsgToSign.substring(intindexofsep+1);
 
     String strHashOfData = strMsgToSign.substring(intindexofsep+1);
  
     // Decrypting to get the symmetric key
+
     / / Descifrado para obtener la clave simétrica
 
     byte[] bytestrEncryptWithPublicKey = new BASE64Decoder().decodeBuffer(strEncryptWithPublicKey);
 
     byte[] bytestrEncryptWithPublicKey = new BASE64Decoder().decodeBuffer(strEncryptWithPublicKey);
 
     byte[] byteDecryptWithPrivateKey = encryptUtil.decryptData(byteEncryptWithPublicKey,recvPrivateKey,"RSA/ECB/PKCS1Padding");
 
     byte[] byteDecryptWithPrivateKey = encryptUtil.decryptData(byteEncryptWithPublicKey,recvPrivateKey,"RSA/ECB/PKCS1Padding");
 
      
 
      
     // 8. Decrypt the data using the Symmetric Key
+
     / / 8. Descifrar los datos usando la clave simétrica
 
     javax.crypto.spec.SecretKeySpec secretKeySpecDecrypted = new javax.crypto.spec.SecretKeySpec(byteDecryptWithPrivateKey,"AES");
 
     javax.crypto.spec.SecretKeySpec secretKeySpecDecrypted = new javax.crypto.spec.SecretKeySpec(byteDecryptWithPrivateKey,"AES");
 
     byte[] byteDecryptText = encryptUtil.decryptData(byteCipherText,secretKeySpecDecrypted,"AES");
 
     byte[] byteDecryptText = encryptUtil.decryptData(byteCipherText,secretKeySpecDecrypted,"AES");
Line 286: Line 322:
 
     System.out.println(" Decrypted data is " +strDecryptedText);
 
     System.out.println(" Decrypted data is " +strDecryptedText);
 
      
 
      
     // 9. Compute MessageDigest of data + Signed message
+
     / / 9. Calcule MessageDigest de datos + mensaje firmado
 
     MessageDigest recvmd = MessageDigest.getInstance("MD5");
 
     MessageDigest recvmd = MessageDigest.getInstance("MD5");
 
     recvmd.update(byteDecryptText);
 
     recvmd.update(byteDecryptText);
Line 293: Line 329:
 
String strHashOfRecvSignedData = new String();
 
String strHashOfRecvSignedData = new String();
 
 
for (int i = 0; i &lt; byteHashOfRecvSignedData.length; i++){
+
for (int i = 0; i < byteHashOfRecvSignedData.length; i++){
strHashOfRecvSignedData = strHashOfRecvSignedData + Integer.toHexString((int)byteHashOfRecvSignedData[i] &amp; 0xFF)&nbsp;;
+
strHashOfRecvSignedData = strHashOfRecvSignedData + Integer.toHexString((int)byteHashOfRecvSignedData[i] & 0xFF) ;
 
             }
 
             }
// 10. Validate if the Message Digest of the Decrypted Text matches the Message Digest of the Original Message
+
    / / 10. Validar si la síntesis del mensaje del texto coincide con el mensaje descifrado
 +
    Digest of the Original Message
 
if (!strHashOfRecvSignedData.equals(strHashOfData))
 
if (!strHashOfRecvSignedData.equals(strHashOfData))
 
{
 
{
Line 309: Line 346:
 
exp.printStackTrace();
 
exp.printStackTrace();
 
}
 
}
 
 
}
 
 
 
}
 
}
</pre>
+
}
==== SymmetricEncrypt.java ====
+
 
<pre>package org.owasp.crypto;
+
'''5.3.2. SymmetricEncrypt.java'''
 +
 
 +
package org.owasp.crypto;
  
 
import javax.crypto.KeyGenerator;
 
import javax.crypto.KeyGenerator;
Line 334: Line 369:
 
/**
 
/**
 
  * @author Joe Prasanna Kumar
 
  * @author Joe Prasanna Kumar
  * This program provides the following cryptographic functionalities
+
  * Este programa ofrece las funcionalidades criptográficas siguientes
* 1. Encryption using AES
+
  * 1. Cifrado con AES
* 2. Decryption using AES
+
  * 2. El descifrado con AES
*  
+
  *
* High Level Algorithm&nbsp;:
+
  * Algoritmo de alto nivel:
* 1. Generate a DES key (specify the Key size during this phase)  
+
  * 1. Generar una clave DES (especificar el tamaño de la clave durante esta fase)
* 2. Create the Cipher  
+
  * 2. Cree el Cipher
* 3. To Encrypt&nbsp;: Initialize the Cipher for Encryption
+
  * 3. Para cifrar: Inicializar el cifrado para el cifrado
* 4. To Decrypt&nbsp;: Initialize the Cipher for Decryption
+
  * 4. Para descifrar: Inicializar el cifrado para descifrar
*
+
  *  
*  
+
 
  */
 
  */
  
Line 356: Line 390:
  
 
public static SecretKey getSecret(){
 
public static SecretKey getSecret(){
/**
+
/ **
* Step 1. Generate an AES key using KeyGenerator
+
* Paso 1. Generación de una clave AES mediante keygenerator
* Initialize the keysize to 128  
+
* Inicializa el tamaño de clave de 128
*  
+
*
*/
+
* /
+
 
try{
 
try{
 
keyGen = KeyGenerator.getInstance("AES");
 
keyGen = KeyGenerator.getInstance("AES");
Line 377: Line 410:
 
}
 
}
 
 
/**
+
/ **
* Step2. Create a Cipher by specifying the following parameters
+
* Paso 2. Crear un sistema de cifrado mediante la especificación de los siguientes parámetros
* a. Algorithm name - here it is AES
+
*a. Nombre del algoritmo - aquí es AES
*/
+
* /
+
 
 
 
public byte[] encryptData(byte[] byteDataToEncrypt, Key secretKey, String Algorithm) {
 
public byte[] encryptData(byte[] byteDataToEncrypt, Key secretKey, String Algorithm) {
Line 389: Line 421:
 
Cipher aesCipher = Cipher.getInstance(Algorithm);
 
Cipher aesCipher = Cipher.getInstance(Algorithm);
 
 
/**
+
/ **
* Step 3. Initialize the Cipher for Encryption
+
* Paso 3. Inicialice el cifrado para el cifrado
*/
+
* /
 
if(Algorithm.equals("AES")){
 
if(Algorithm.equals("AES")){
 
aesCipher.init(Cipher.ENCRYPT_MODE,secretKey,aesCipher.getParameters());
 
aesCipher.init(Cipher.ENCRYPT_MODE,secretKey,aesCipher.getParameters());
Line 399: Line 431:
 
}  
 
}  
 
 
/**
+
/ **
* Step 4. Encrypt the Data
+
* Paso 4. Cifrar los datos
* 1. Declare / Initialize the Data. Here the data is of type String
+
* 1. Declarar / inicializar los datos. Aquí los datos son de tipo String
* 2. Convert the Input Text to Bytes
+
* 2. Convertir el texto de entrada a Bytes
* 3. Encrypt the bytes using doFinal method
+
* 3. Cifrado de los bytes, utilizando el método doFinal
*/
+
* /
 
byteCipherText = aesCipher.doFinal(byteDataToEncrypt);  
 
byteCipherText = aesCipher.doFinal(byteDataToEncrypt);  
 
strCipherText = new BASE64Encoder().encode(byteCipherText);
 
strCipherText = new BASE64Encoder().encode(byteCipherText);
Line 442: Line 474:
 
return byteCipherText;
 
return byteCipherText;
 
}
 
}
/**
+
/ **
* Step 5. Decrypt the Data
+
* Paso 5. Descifrar los datos
* 1. Initialize the Cipher for Decryption
+
* 1. Inicialice el cifrado para descifrar
* 2. Decrypt the cipher bytes using doFinal method
+
* 2. Descifrar los bytes cifrados utilizando el método doFinal
*/
+
* /
 
 
 
public byte[] decryptData(byte[] byteCipherText, Key secretKey, String Algorithm) {
 
public byte[] decryptData(byte[] byteCipherText, Key secretKey, String Algorithm) {
Line 508: Line 540:
 
int nibble = -1;
 
int nibble = -1;
  
for (int i = 0; i &lt; strInput.length(); ++i) {
+
for (int i = 0; i < strInput.length(); ++i) {
 
interimVal = strHexVal.indexOf(strInput.charAt(i));
 
interimVal = strHexVal.indexOf(strInput.charAt(i));
if (interimVal &gt;= 0) {
+
if (interimVal >= 0) {
if (nibble &lt; 0) {
+
if (nibble < 0) {
 
nibble = interimVal;
 
nibble = interimVal;
 
} else {
 
} else {
byteConverted[j++] = (byte) ((nibble &lt;&lt; 4) + interimVal);
+
byteConverted[j++] = (byte) ((nibble << 4) + interimVal);
 
nibble = -1;
 
nibble = -1;
 
}
 
}
Line 520: Line 552:
 
}
 
}
  
if (nibble &gt;= 0) {
+
if (nibble >= 0) {
byteConverted[j++] = (byte) (nibble &lt;&lt; 4);
+
byteConverted[j++] = (byte) (nibble << 4);
 
}
 
}
  
if (j &lt; byteConverted.length) {
+
if (j < byteConverted.length) {
 
byte[] byteTemp = new byte[j];
 
byte[] byteTemp = new byte[j];
 
System.arraycopy(byteConverted, 0, byteTemp, 0, j);
 
System.arraycopy(byteConverted, 0, byteTemp, 0, j);
Line 536: Line 568:
 
StringBuffer buf = new StringBuffer();
 
StringBuffer buf = new StringBuffer();
  
for (int i = 0; i &lt; block.length; ++i) {
+
for (int i = 0; i < block.length; ++i) {
buf.append(strHexVal.charAt((block[i] &gt;&gt;&gt; 4) &amp; 0xf));
+
buf.append(strHexVal.charAt((block[i] >>> 4) & 0xf));
buf.append(strHexVal.charAt(block[i] &amp; 0xf));
+
buf.append(strHexVal.charAt(block[i] & 0xf));
 
}
 
}
  
Line 544: Line 576:
 
}
 
}
 
}
 
}
</pre>
 
== References  ==
 
  
#Computer Security Arts and Science – Matt Bishop
 
#Core Security Patterns – Christopher Steele, Ray Lai and Ramesh Nagappan
 
  
[[Category:OWASP_Java_Project]]
+
'''6. Referencias'''
 +
 
 +
1. Computer Security Arts and Science – Matt Bishop
 +
 
 +
2. Core Security Patterns – Christopher Steele, Ray Lai and Ramesh Nagappan
 +
 
 +
'''Aplicaciones en Ambientes Libres'''
 +
'''Tutor: ''' Ing. Tito Armas
 +
'''Responsables: ''' Bravo Maria Jose y Portilla Maria Fernanda
 +
'''2012'''

Revision as of 20:01, 23 November 2012

Aplicación de Firma Digital en Java

1. Estado

Fecha de lanzamiento 14/1/2007

2. ADVERTENCIA

Recientemente Jim Manico trajo a mi atención esta página wiki, el mismo que me pidió que revisara con exactitud. Al hacerlo, me di cuenta de varios errores y áreas de weakness. Últimamente, tengo la intención de revisar esta página (espero que con la ayuda de uno de los propietarios originales), pero no dispongo de tiempo para hacer una revisión completa en este momento. Por lo tanto, resumiré la problemática que observe de esta página y dejándole a usted la decisión de utilizar o no, con estas advertencias.

1. En primer lugar, esta página no describe "firmas digitales". Más bien, describe un concepto conocido como "sobres digitales", que es un esquema utilizado con objetos como S / MIME. Las firmas digitales no sólo cifra el texto real del mensaje, sólo cifra el hash del texto del mensaje.

2. Codificación UTF-8, debe utilizarse para realizar conversiones entre cadenas y matrices Java byte para asegurar la portabilidad adecuada a través de diferentes sistemas operativos.

3. La cadena de certificados siempre deben ser validados. En este ejemplo, el certificado es auto firmado, por lo que este no es relevante y no será verdadero en el caso normal. Además, debe tenerse en cuenta que los certificados auto firmados, aunque aceptables para fines de demostración se considera una práctica dudosa para la producción, ya que abre la puerta a ataques de suplantación.

4. NIST ahora recomienda el uso de 2048 tamaño de la clave de bits para claves RSA o DSA.

5. Hay un consistente uso de algoritmos débiles. Aquí están algunos reemplazos sugeridos:

• Utilice "RSA/ECB/OAEPWithSHA1AndMGF1Padding" en lugar de "RSA/ECB/PKCS1Padding".

• Utilice SHA1 (o mejor SHA256, SHA1 pero por lo menos) en lugar de MD5 para la síntesis del mensaje.

• Utilice "SHA1withRSA" para el algoritmo de la firma en lugar de "MD5withRSA".

• Al crear un sistema de cifrado simétrico para cifrar el mensaje de texto, utilice "AES/CBC/PKCS5Padding" y elija un IV aleatorio para cada mensaje de texto en lugar de utilizar simplemente "AES", que termina con "AES/ECB/PKCS5Padding". Modo ECB es extremadamente débil para un texto plano regular. (Está bien para el cifrado de bits aleatorios, sin embargo, está bien usar con RSA.) Sin embargo, el uso CBC y PKCS5Padding podría hacer vulnerable a "Padding Oracle" ataques, así que se recomienda tener cuidado. Puede utilizar Encryptor ESAPI 2.0 's para evitarlo. (Tenga en cuenta también, esta parte es del concepto de "sobre digital". Si esta página fuera limitada verdaderamente por "firmas digitales", no se aplicaría, ya que sería irrelevante.)

Espero sacar tiempo lo más pronto, antes de limpiar esta página wiki. Mientras tanto, envíeme por correo electrónico sus preguntas. -Kevin Wall

3. Visión de conjunto

Este artículo presenta un breve resumen de los conceptos involucrados con firmas digitales y proporciona ejemplos de código para la aplicación de firmas digitales en Java utilizando la Arquitectura de Java Cryptography.

3.1. ¿Qué es una firma digital?

Una firma digital es un concepto que ayuda a obtener el no repudio de origen (es decir, la Integridad del Origen) de datos. Al firmar digitalmente el documento, la persona que firma, asegura que él es el autor del documento o el mensaje firmado.

3.2. Necesidad de Firma Digital

Durante la "E" revolución, fue una necesidad para la autenticación de críticas transacciones sobre todo en el mundo financiero. Si Alice se comprometió a transferir $ x para Bob, entonces tenía que haber una manera para que Bob se asegure de que:

1. Fue Alice quien realizó la transacción y no otra persona suplantando Alice (Autenticación).

2. El monto acordado por Alice es $x (Integridad).

3. Alicia no pudo discutir su declaración de transacción $x a Bob (No repudio de origen).

Estas preocupaciones fueron tratadas con una solución conocida como firmas digitales. Más información de fondo sobre las firmas digitales se puede encontrar en el artículo de Wikipedia.

4. Firmas digitales en Java utilizando JCA

La Arquitectura de Java Cryptography es un marco para el acceso y el desarrollo de la funcionalidad criptográfica para la plataforma Java. Un proveedor de JCA implementa las funcionalidades criptográficas como firmas digitales y compendios de mensajes. El proveedor predeterminado JCA en JDK 1.4.2 es SUN.

4.1. Consideraciones de seguridad al implementar la firma digital

Dos consideraciones principales de seguridad que se deben tener en cuenta al aplicar firmas digitales son:

1. Firma el mensaje y, a continuación cifrar el mensaje firmado.

2. Firma el hash del mensaje en lugar del mensaje completo.


4.2. Consideraciones sobre el rendimiento al implementar la firma digital

Dado que los algoritmos de cifrado asimétricos, como RSA, DSA son computacionalmente más lento que los algoritmos de cifrado simétricos como AES, es una buena práctica, cifrar el mensaje real que se transmite utilizando un algoritmo de clave simétrica y luego cifrar la clave que se utiliza en el algoritmo de clave simétrica utilizando un algoritmo de clave asimétrica. Por ejemplo: si se quiere transmitir el mensaje "Hola Mundo de Firmas Digitales", entonces primero se cifra este mensaje con una clave simétrica, por ejemplo una clave AES de 128 bits como x7oFaHSPnWxEMiZE/0qYrg y luego se cifra esta clave con un algoritmo de clave asimétrica como RSA.

5. Algoritmo para implementar la firma digital utilizando el algoritmo RSA

El proveedor de la implementación en Java RSA tiene una limitación en que el cifrado se puede realizar sólo en los datos de longitud <= 117 bytes. Si los datos son de longitud > 117 bytes, se lanzaría un IllegalBlockSizeException: Los datos no debe tener más de 117 bytes ahí la simetría tiene que ser cifrados y luego firmados.

El algoritmo RSA PKCS # 1 con relleno sólo puede cifrar los datos de tamaño k - 11, donde k cuya longitud es de un octeto del módulo RSA y 11 es la cantidad de bytes utilizados por el relleno PCKS # 1 v1.5. Por lo tanto, si usamos una clave RSA de tamaño de 1024 bits, podríamos cifrar sólo 128 - 11 => 117 bytes de datos. Hay dos opciones disponibles para cifrar los datos de tamaño de byte más grande.

1. Podríamos usar una clave RSA de longitud> 1024. Por ejemplo, si usamos 2048 bits, entonces podríamos cifrar 256-11 => 245 bytes de datos. La desventaja de este enfoque es que no sería capaz de codificar los datos de tamaño> x bytes (en el ejemplo anterior x es 245).

2. Analizar los datos de entrada en trozos de bytes de tamaño <117 y aplicar la encriptación en cada trozo. El código de ejemplo de este enfoque se puede encontrar aquí.

Ambos enfoques podrían afectar al rendimiento ya que la clave RSA de mayor tamaño o un enfoque de "divide y vencerás" de bytes de entrada son computacionalmente costosa.

5.1. Algoritmo Con las anteriores consideraciones, el algoritmo siguiente puede ser usado para la implementación de la criptografía de clave pública en Java.

1. Cifrar el mensaje utilizando una clave simétrica.

2. Concatenar la clave simétrica + hash de clave simétrica + hash del mensaje.

3. Cifrar la cadena concatenada con la clave pública de los receptores.

4. Firmar los datos a transmitir (clave simétrica cifrada + Hash de la tecla + Hash del mensaje).

5. Validar la firma.

6. Descifrar el mensaje con la clave privada del receptor para obtener la clave simétrica.

7. Validar la integridad de la clave utilizando el hash de la clave.

8. Descifrar el mensaje real usando la clave simétrica que se ha descifrado, se analiza y se comprueba la integridad.

9. Calcular MessageDigest de datos.

10. Validar si la síntesis del mensaje del texto descifrado coincide con el resumen de mensaje del mensaje original.

5.2. Comandos para la generación de claves

prompt # keytool-genkey-alias Testsender-keystore testkeystore.ks-keyalg RSA

Introduzca la contraseña del almacén de claves: testpwd

¿Cuál es su nombre y apellido?

[Unknown]: Alice Sender

¿Cuál es el nombre de la unidad organizativa?

[Unknown]: IT

¿Cuál es el nombre de su organización?

[Unknown]: ABC Inc

¿Cuál es el nombre de su ciudad o localidad?

[Unknown]: LA

¿Cuál es el nombre de su estado o provincia?

[Unknown]: CA

¿Qué es el código de país de dos letras para esta unidad?

[Unknown]: US

Es CN = Alice Sender, OU = IT, O = ABC Inc, L = LA, ST = CA, C = EE.UU. correcto?

[NO]: y

Introduzca la contraseña clave para <testsender>

(RETURN if same as keystore password): send123

prompt # keytool-genkey-alias testrecv-keystore testkeystore.ks-keyalg RSA

Introduzca la contraseña del almacén de claves: testpwd

¿Cuál es su nombre y apellido?

[Unknown]: Bob Receiver

¿Cuál es el nombre de la unidad organizativa?

[Unknown]: HR

¿Cuál es el nombre de su organización?

[Unknown]: ABC Inc

¿Cuál es el nombre de su ciudad o localidad?

[Unknown]: SFO

¿Cuál es el nombre de su estado o provincia?

[Unknown]: CA

¿Qué es el código de país de dos letras para esta unidad?

[Unknown]: US

Es CN = Bob receptor, OU = HR, O = ABC Inc, L = SFO, ST = CA, C = EE.UU. correcto?

[no]: y

Introduzca la contraseña clave para <testrecv>

(RETURN if same as keystore password): recv123

5.3. Code Sample

5.3.1. PublicKeyCryptography.java


package org.owasp.crypto;

import java.security.*;

import java.security.cert.*;

import javax.crypto.*;

import sun.misc.BASE64Encoder;

import sun.misc.BASE64Decoder;


/**

* 
* @author Joe Prasanna Kumar
* 
  • 1. Cifrar los datos utilizando una clave simétrica
  • 2. Cifrar la clave simétrica con la clave pública Receptores
  • 3. Crear un resumen del mensaje de los datos a transmitir
  • 4. Firma el mensaje a transmitir
  • 5. Envíe los datos a través de un canal no seguro
  • 6. Validar la firma
  • 7. Descifrar el mensaje usando la llave privada Pets para obtener la clave simétrica
  • 8. Descifrar los datos usando la clave simétrica
  • 9. Calcule MessageDigest de datos + mensaje firmado
  • 10.Valide si la síntesis del mensaje del texto descifrado coincide con el resumen de mensaje del mensaje original *
* 
*/

public class PublicKeyCryptography {

/** * @param args */ public static void main(String[] args) {

SymmetricEncrypt encryptUtil = new SymmetricEncrypt(); String strDataToEncrypt = "Hello World"; byte[] byteDataToTransmit = strDataToEncrypt.getBytes();

// Generación de un SecretKey para el cifrado simétrico SecretKey senderSecretKey = SymmetricEncrypt.getSecret();

//1. Cifrar los datos utilizando una clave simétrica byte[] byteCipherText = encryptUtil.encryptData(byteDataToTransmit,senderSecretKey,"AES"); String strCipherText = new BASE64Encoder().encode(byteCipherText);


//2. Cifrar la clave simétrica con la clave pública Receptores try{

      // 2.1 Especifique el almacén de claves que se haya importado el certificado Receptores

KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); char [] password = "testpwd".toCharArray(); java.io.FileInputStream fis = new java.io.FileInputStream("/home/Joebi/workspace/OWASP_Crypto/org/owasp/crypto/testkeystore.ks");

       ks.load(fis, password);
       fis.close();
   

// 2.2 Creación de un certificado X509 del receptor

       X509Certificate recvcert ;
       MessageDigest md = MessageDigest.getInstance("MD5");
       recvcert = (X509Certificate)ks.getCertificate("testrecv");
       // 2.3 Getting the Receivers public Key from the Certificate
      
      PublicKey pubKeyReceiver = recvcert.getPublicKey();
   
       // 2.4 Cifrado de la SecretKey con la clave pública Receptores
       byte[] byteEncryptWithPublicKey = encryptUtil.encryptData(senderSecretKey.getEncoded(),pubKeyReceiver,"RSA/ECB/PKCS1Padding");
       String strSenbyteEncryptWithPublicKey = new BASE64Encoder().encode(byteEncryptWithPublicKey);
       
      // 3. Crear un resumen del mensaje de los datos a transmitir
        md.update(byteDataToTransmit);

byte byteMDofDataToTransmit[] = md.digest();

String strMDofDataToTransmit = new String(); for (int i = 0; i < byteMDofDataToTransmit.length; i++){ strMDofDataToTransmit = strMDofDataToTransmit + Integer.toHexString((int)byteMDofDataToTransmit[i] & 0xFF) ;

            }
      // 3.1 Mensaje que se Firmado = clave secreta cifrada + MAC de los datos a transmitir	
       String strMsgToSign = strSenbyteEncryptWithPublicKey + "|" + strMDofDataToTransmit;
   
      // 4. Firma el mensaje
      // 4.1 Obtener la clave privada del remitente desde el almacén de claves, proporcionando la contraseña establecida 
      for the private key while creating the keys using keytool

char[] keypassword = "send123".toCharArray();

      Key myKey =  ks.getKey("testsender", keypassword);
      PrivateKey myPrivateKey = (PrivateKey)myKey;
   
    / / 4,2 firmar el mensaje
   Signature mySign = Signature.getInstance("MD5withRSA");
   mySign.initSign(myPrivateKey);
   mySign.update(strMsgToSign.getBytes());
   byte[] byteSignedData = mySign.sign();
       / / 5. Los valores byteSignedData (la firma) y strMsgToSign (los datos que se firmó) se pueden enviar a través del receptor
     / / 6.Validate la Firma
    / / 6,1 extraer la clave pública de su certificado de remitentes

X509Certificate sendercert ; sendercert = (X509Certificate)ks.getCertificate("testsender");

   PublicKey pubKeySender = sendercert.getPublicKey();
   // 6.2 Verifying the Signature
   Signature myVerifySign = Signature.getInstance("MD5withRSA");
   myVerifySign.initVerify(pubKeySender);
   myVerifySign.update(strMsgToSign.getBytes());
   
   boolean verifySign = myVerifySign.verify(byteSignedData);
   if (verifySign == false)
   {
   	System.out.println(" Error in validating Signature ");
   }
   
   else
   	System.out.println(" Successfully validated Signature ");
  / / 7. Descifrar el mensaje usando la llave privada Pets para obtener la clave simétrica
   char[] recvpassword = "recv123".toCharArray();
   Key recvKey =  ks.getKey("testrecv", recvpassword);
   PrivateKey recvPrivateKey = (PrivateKey)recvKey;
   
 / / Analizar el MessageDigest y el valor cifrado
   String strRecvSignedData = new String (byteSignedData);
   String[] strRecvSignedDataArray = new String [10];
   strRecvSignedDataArray = strMsgToSign.split("|");
   int intindexofsep = strMsgToSign.indexOf("|");
   String strEncryptWithPublicKey = strMsgToSign.substring(0,intindexofsep);
   String strHashOfData = strMsgToSign.substring(intindexofsep+1);
   / / Descifrado para obtener la clave simétrica
   byte[] bytestrEncryptWithPublicKey = new BASE64Decoder().decodeBuffer(strEncryptWithPublicKey);
   byte[] byteDecryptWithPrivateKey = encryptUtil.decryptData(byteEncryptWithPublicKey,recvPrivateKey,"RSA/ECB/PKCS1Padding");
   
   / / 8. Descifrar los datos usando la clave simétrica
   javax.crypto.spec.SecretKeySpec secretKeySpecDecrypted = new javax.crypto.spec.SecretKeySpec(byteDecryptWithPrivateKey,"AES");
   byte[] byteDecryptText = encryptUtil.decryptData(byteCipherText,secretKeySpecDecrypted,"AES");
   String strDecryptedText = new String(byteDecryptText);
   System.out.println(" Decrypted data is " +strDecryptedText);
   
   / / 9. Calcule MessageDigest de datos + mensaje firmado
   MessageDigest recvmd = MessageDigest.getInstance("MD5");
   recvmd.update(byteDecryptText);

byte byteHashOfRecvSignedData[] = recvmd.digest();

String strHashOfRecvSignedData = new String();

for (int i = 0; i < byteHashOfRecvSignedData.length; i++){ strHashOfRecvSignedData = strHashOfRecvSignedData + Integer.toHexString((int)byteHashOfRecvSignedData[i] & 0xFF) ;

            }
   / / 10. Validar si la síntesis del mensaje del texto coincide con el mensaje descifrado 
   Digest of the Original Message

if (!strHashOfRecvSignedData.equals(strHashOfData)) { System.out.println(" Message has been tampered "); }

}

catch(Exception exp) { System.out.println(" Exception caught " + exp); exp.printStackTrace(); } } }

5.3.2. SymmetricEncrypt.java

package org.owasp.crypto;

import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.Cipher; import java.security.Key;

import java.security.NoSuchAlgorithmException; import java.security.InvalidKeyException; import java.security.InvalidAlgorithmParameterException; import javax.crypto.NoSuchPaddingException; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException;

import sun.misc.BASE64Encoder;

/**

* @author Joe Prasanna Kumar
* Este programa ofrece las funcionalidades criptográficas siguientes
 * 1. Cifrado con AES
 * 2. El descifrado con AES
 *
 * Algoritmo de alto nivel:
 * 1. Generar una clave DES (especificar el tamaño de la clave durante esta fase)
 * 2. Cree el Cipher
 * 3. Para cifrar: Inicializar el cifrado para el cifrado
 * 4. Para descifrar: Inicializar el cifrado para descifrar
 * 
*/

public class SymmetricEncrypt {

String strDataToEncrypt = new String(); String strCipherText = new String(); String strDecryptedText = new String(); static KeyGenerator keyGen; private static String strHexVal = "0123456789abcdef";

public static SecretKey getSecret(){ / **

  • Paso 1. Generación de una clave AES mediante keygenerator
  • Inicializa el tamaño de clave de 128
  • /

try{ keyGen = KeyGenerator.getInstance("AES"); keyGen.init(128);

}

catch(Exception exp) { System.out.println(" Exception inside constructor " +exp); }

SecretKey secretKey = keyGen.generateKey(); return secretKey; }

/ **

  • Paso 2. Crear un sistema de cifrado mediante la especificación de los siguientes parámetros
  • a. Nombre del algoritmo - aquí es AES
  • /

public byte[] encryptData(byte[] byteDataToEncrypt, Key secretKey, String Algorithm) { byte[] byteCipherText = new byte[200];

try { Cipher aesCipher = Cipher.getInstance(Algorithm);

/ **

  • Paso 3. Inicialice el cifrado para el cifrado
  • /

if(Algorithm.equals("AES")){ aesCipher.init(Cipher.ENCRYPT_MODE,secretKey,aesCipher.getParameters()); } else if(Algorithm.equals("RSA/ECB/PKCS1Padding")){ aesCipher.init(Cipher.ENCRYPT_MODE,secretKey); }

/ **

  • Paso 4. Cifrar los datos
  • 1. Declarar / inicializar los datos. Aquí los datos son de tipo String
  • 2. Convertir el texto de entrada a Bytes
  • 3. Cifrado de los bytes, utilizando el método doFinal
  • /

byteCipherText = aesCipher.doFinal(byteDataToEncrypt); strCipherText = new BASE64Encoder().encode(byteCipherText);

}

catch (NoSuchAlgorithmException noSuchAlgo) { System.out.println(" No Such Algorithm exists " + noSuchAlgo); }

catch (NoSuchPaddingException noSuchPad) { System.out.println(" No Such Padding exists " + noSuchPad); }

catch (InvalidKeyException invalidKey) { System.out.println(" Invalid Key " + invalidKey); }

catch (BadPaddingException badPadding) { System.out.println(" Bad Padding " + badPadding); }

catch (IllegalBlockSizeException illegalBlockSize) { System.out.println(" Illegal Block Size " + illegalBlockSize); illegalBlockSize.printStackTrace(); } catch (Exception exp) { exp.printStackTrace(); }

return byteCipherText; } / **

  • Paso 5. Descifrar los datos
  • 1. Inicialice el cifrado para descifrar
  • 2. Descifrar los bytes cifrados utilizando el método doFinal
  • /

public byte[] decryptData(byte[] byteCipherText, Key secretKey, String Algorithm) { byte[] byteDecryptedText = new byte[200];

try{ Cipher aesCipher = Cipher.getInstance(Algorithm); if(Algorithm.equals("AES")){ aesCipher.init(Cipher.DECRYPT_MODE,secretKey,aesCipher.getParameters()); } else if(Algorithm.equals("RSA/ECB/PKCS1Padding")){ aesCipher.init(Cipher.DECRYPT_MODE,secretKey); }

byteDecryptedText = aesCipher.doFinal(byteCipherText); strDecryptedText = new String(byteDecryptedText); }

catch (NoSuchAlgorithmException noSuchAlgo) { System.out.println(" No Such Algorithm exists " + noSuchAlgo); }

catch (NoSuchPaddingException noSuchPad) { System.out.println(" No Such Padding exists " + noSuchPad); }

catch (InvalidKeyException invalidKey) { System.out.println(" Invalid Key " + invalidKey); invalidKey.printStackTrace(); }

catch (BadPaddingException badPadding) { System.out.println(" Bad Padding " + badPadding); badPadding.printStackTrace(); }

catch (IllegalBlockSizeException illegalBlockSize) { System.out.println(" Illegal Block Size " + illegalBlockSize); illegalBlockSize.printStackTrace(); }

catch (InvalidAlgorithmParameterException invalidParam) { System.out.println(" Invalid Parameter " + invalidParam); }

return byteDecryptedText; }


public static byte[] convertStringToByteArray(String strInput) { strInput = strInput.toLowerCase(); byte[] byteConverted = new byte[(strInput.length() + 1) / 2]; int j = 0; int interimVal; int nibble = -1;

for (int i = 0; i < strInput.length(); ++i) { interimVal = strHexVal.indexOf(strInput.charAt(i)); if (interimVal >= 0) { if (nibble < 0) { nibble = interimVal; } else { byteConverted[j++] = (byte) ((nibble << 4) + interimVal); nibble = -1; } } }

if (nibble >= 0) { byteConverted[j++] = (byte) (nibble << 4); }

if (j < byteConverted.length) { byte[] byteTemp = new byte[j]; System.arraycopy(byteConverted, 0, byteTemp, 0, j); byteConverted = byteTemp; }

return byteConverted; }

public static String convertByteArrayToString(byte[] block) { StringBuffer buf = new StringBuffer();

for (int i = 0; i < block.length; ++i) { buf.append(strHexVal.charAt((block[i] >>> 4) & 0xf)); buf.append(strHexVal.charAt(block[i] & 0xf)); }

return buf.toString(); } }


6. Referencias

1. Computer Security Arts and Science – Matt Bishop

2. Core Security Patterns – Christopher Steele, Ray Lai and Ramesh Nagappan

Aplicaciones en Ambientes Libres Tutor: Ing. Tito Armas Responsables: Bravo Maria Jose y Portilla Maria Fernanda 2012