Back to site
Since 2004, our University project has become the Internet's most widespread web hosting directory. Here we like to talk a lot about web development, networking and server security. It is, after all, our expertise. To make things better we've launched this science section with the free access to educational resources and important scientific material translated to different languages.

Punerea în aplicare Digital Signature în Java

Source: https://www.owasp.org/index.php/Digital_Signature_Implementation_in_Java#Performance_Considerations_while_Implementing_Digital_Signature

Conţinut

Stare

Lansat 14/1/2007

ATENŢIONARE

Această pagină wiki a fost recent adus in atentia mea de Jim Manico, care ma rugat să-l revizuiască pentru acurateţe. În acest sens, am observat mai multe erori şi domenii de weakness.Ultimately, am de gând să revizuiască această pagină (sperăm, cu ajutorul unuia dintre proprietari), dar nu am timp pentru a face o revizuire completă în momentul de faţă. Prin urmare, voi rezuma ceea ce vad este problematic cu restul de această pagină şi vă poate decide să folosească sau nu, cu aceste limitări.

  1. În primul rând, această pagină nu nu descrie "semnăturile digitale". Mai degrabă, el descrie un concept cunoscut sub numele de "plicuri digitale", care este un sistem folosit cu lucruri cum ar fi S / MIME. Semnăturile digitale niciodată cripta numai textul mesajului real, ci numai o cripta hash din textul mesajului.
  2. Codificarea UTF-8 ar trebui să fie utilizate pe parcursul a converti între coarde Java şi matrice octet pentru a asigura portabilitatea corectă între diferite sisteme de operare.
  3. Lanţul de certificat trebuie să fie întotdeauna validate. În exemplul de aici, este certificat auto-semnat, deci acest lucru nu este relevant, dar care nu va fi valabil şi în cazul normal. În plus, trebuie notat faptul că auto-semnat certificatele, în timp ce acceptabilă în scopuri demonstrative este considerată o practică dubioasă pentru producţia, deoarece deschide uşa pentru atacuri personificare.
  4. NIST acum recomanda utilizarea de 2048 mărimea cheii biţi pentru chei RSA sau DSA.
  5. Există o utilizare consecventă a algoritmi slab. Iată câteva înlocuiri propuse:

Sper să am ceva timp în curând pentru a curăţa această pagină wiki. În acelaşi timp, mi e-mail dacă aveţi întrebări.

Kevin Wall-

Prezentare

Acest articol oferă o scurtă prezentare generală a conceptelor implicate cu semnături digitale şi să ofere mostre de cod pentru punerea în aplicare a semnăturii digitale în Java folosind Java Arhitectura Criptografie.

Ce este o semnătură digitală?

O semnătură digitală este un construct care ajută la atingerea non-repudiere de origine (de Integritate Originea ex) de date. Prin semnarea digitală a documentului, persoana care se semnează asigura că el este autorul documentului sau un mesaj care a fost semnat.

Aveti nevoie de Digital Signature

În timpul "E" revoluţie, a fost nevoie pentru autentificarea tranzacţiilor critică în special în lumea financiară. În cazul în care Alice a fost de acord să transfere $ x pentru a Bob, atunci a trebuit să fie o cale de Bob pentru a fi sigur că:

  1. Acesta a fost Alice care a efectuat tranzacţia, şi nu altcineva pretinde că Alice (cu autentificare)
  2. Suma convenită de către Alice este de $ x (Integritate)
  3. Alice nu a putut disputa declaraţia ei de tranzactionare $ x pentru a Bob (non-repudiere de origine)

Aceste preocupări au fost abordate cu o soluţie cunoscută sub numele de semnături digitale. Mai multe informaţii generale despre Semnăturile digitale pot fi găsite pe articolul Wikipedia

Semnăturile digitale în Java folosind JCA

Java Criptografia Arhitectura este un cadru pentru accesarea şi în curs de dezvoltare funcţionalitatea criptografică pentru platforma Java. Un furnizor de JCA implementează funcţionalităţile criptografice cum ar fi semnăturile digitale şi mesaje Lichidele de digestie. Furnizorul implicit JCA în JDK 1.4.2 este SUN.

Consideraţii de securitate în timp ce de punere în aplicare Digital Signature

Două considerente principale de securitate ar trebui luate în considerare atunci când punerea în aplicare a semnăturilor digitale.

  1. Înregistraţi mesajul şi apoi criptarea mesajului semnat
  2. Conectaţi-vă Hash a mesajului în loc de întregul mesaj

Consideraţii de performanţă în timp ce de punere în aplicare Digital Signature

Deoarece algoritmi de criptare asimetrică ca RSA, DSA sunt de calcul mai lent decât algoritmi de criptare simetrică ca AES, este o bună practică pentru a cripta mesajul real pe care urmează să fie transmise cu ajutorul unui algoritm simetric cheie şi apoi criptarea cheie utilizate în algoritmul de cheie simetric utilizând o cheie asimetric Algoritmul. De exemplu: dacă cineva doreşte să transmită mesajul "Hello World de semnătura digitală", apoi cripta prima dată acest mesaj folosind o cheie simetrică, spune o cheie de 128 biţi AES ca x7oFaHSPnWxEMiZE/0qYrg şi apoi cripta această cheie folosind un algoritm asimetric cheie cum ar fi RSA.

Algoritmul de punere în aplicare Semnătura digitală folosind algoritmul RSA

Furnizorul de punere în aplicare RSA în Java are o limitare în care criptarea se poate face numai pe datele de lungime <= 117 bytes. În cazul în care datele este de lungime > 117 octeţi, ar arunca o IllegalBlockSizeException: Datele nu trebuie să fie mai puţin de 117 bytes Prin urmare, simetric trebuie să fie criptate şi apoi semnat.

Algoritmul RSA cu # 1 umplutură PKCS poate cripta date doar de dimensiunea k - 11 1, unde k este lungimea octet de modulul RSA si 11 este suma de bytes utilizate de către PCKS # 1 v1.5 umplutură. Astfel, dacă vom folosi o cheie RSA 1024 biţi de mărime, am putea cripta doar 128 - 11 => 117 octeţi de date. Există 2 opţiuni disponibile pentru a cripta datele de marime mai mare.

  1. Am putea folosi o cheie RSA cu o lungime > 1024. Pentru exemplu, să spunem dacă vom folosi 2048 biţi, atunci am putea cripta 256 - 11 => 245 octeţi de date. Dezavantajul acestei abordări este că nu vom putea pentru a cripta datele de dimensiune> x octeţi (în exemplul de mai sus este x 245).
  2. Defalcarea datelor de intrare în bucăţi de octeţi de dimensiuni < 117 şi se aplică pe fiecare bucată de criptare. Mostră de cod pentru această abordare poate fi găsit aici

Ambele aceste abordări ar avea implicaţii de performanţă de la chei RSA de dimensiuni mai mari sau a unui "dezbină şi stăpîneşte", abordare pe octeţi de intrare sunt scumpe computational.

Algoritmul

Cu consideraţiile de mai sus, algoritmul de mai jos poate fi folosit pentru punerea în aplicare criptografia cu chei publice în Java.

  1. Cripta mesajul utilizând o cheie simetrică.
  2. Înlănţui tasta simetric de+ Hash cheie simetrică+ Hash a mesajului.
  3. Cripta şir concatenarea folosind cheia publică receptoare.
  4. Conectaţi-vă datele care urmează să fie transmise (cheie criptată simetrice+ Hash din cheie+ Hash de mesaj).
  5. Validarea Semnătura.
  6. Decripta mesajul folosind cheia privată pentru a obţine receptor cheie simetric.
  7. Validarea integritatea cheie folosind Hash a cheii.
  8. Decripta mesajul real folosind tasta simetrice care a fost decriptat şi analizat şi verificat pentru integritate.
  9. Calculaţi MessageDigest de date.
  10. Validaţi în cazul în care Digest Mesajul a textului decriptate meciuri Digest Mesajul a mesajului original.

Comenzi pentru generarea de chei

promptă # keytool-genkey-alias testsender-keystore testkeystore.ks-keyalg RSA Introduceţi parola keystore: testpwd Care este numele şi prenumele dumneavoastră?

[Unknown]:  Alice Sender

Care este numele de unitatea ta de organizare?

[Unknown]:  IT

Care este numele organizaţiei dvs.?

[Unknown]:  ABC Inc

Care este numele oraşului sau Localitate?

[Unknown]:  LA

Care este numele statului sau provinciei?

[Unknown]:  CA

Care este codul de două litere al ţării pentru această unitate?

[Unknown]:  US

Este NC = Alice Sender, OU = IT, O = ABC Inc, L = LA, ST = CA, C = US corect?

[no]:  y

Introduceţi parola cheie pentru <testsender>

(RETURN if same as keystore password):  send123

promptă # keytool-genkey-alias testrecv-keystore testkeystore.ks-keyalg RSA Introduceţi parola keystore: testpwd Care este numele şi prenumele dumneavoastră?

[Unknown]:  Bob Receiver

Care este numele de unitatea ta de organizare?

[Unknown]:  HR

Care este numele organizaţiei dvs.?

[Unknown]:  ABC Inc

Care este numele oraşului sau Localitate?

[Unknown]:  SFO

Care este numele statului sau provinciei?

[Unknown]:  CA

Care este codul de două litere al ţării pentru această unitate?

[Unknown]:  US

Este NC = Bob Receiver, OU = HR, O = ABC Inc, L = SFO, ST = CA, C = US corect?

[no]:  y

Introduceţi parola cheie pentru <testrecv>

(RETURN if same as keystore password):  recv123

Codul de propoziţii

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. Encrypt the data using a Symmetric Key
 * 2. Encrypt the Symmetric key using the Receivers public key
 * 3. Create a Message Digest of the data to be transmitted
 * 4. Sign the message to be transmitted
 * 5. Send the data over to an unsecured channel
 * 6. Validate the Signature
 * 7. Decrypt the message using Recv private Key to get the Symmetric Key
 * 8. Decrypt the data using the Symmetric Key
 * 9. Compute MessageDigest of data+ Signed message
 * 10.Validate if the Message Digest of the Decrypted Text matches the Message Digest of the Original Message
 * 
 * 
 */

public class PublicKeyCryptography {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
	SymmetricEncrypt encryptUtil = new SymmetricEncrypt();
	String strDataToEncrypt = "Hello World";
	byte[] byteDataToTransmit = strDataToEncrypt.getBytes();

	// Generating a SecretKey for Symmetric Encryption
	SecretKey senderSecretKey = SymmetricEncrypt.getSecret();
	
	//1. Encrypt the data using a Symmetric Key
	byte[] byteCipherText = encryptUtil.encryptData(byteDataToTransmit,senderSecretKey,"AES");
	String strCipherText = new BASE64Encoder().encode(byteCipherText);
	
	
	//2. Encrypt the Symmetric key using the Receivers public key
	try{
		// 2.1 Specify the Keystore where the Receivers certificate has been imported
	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 Creating an X509 Certificate of the Receiver
    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 Encrypting the SecretKey with the Receivers public Key
    byte[] byteEncryptWithPublicKey = encryptUtil.encryptData(senderSecretKey.getEncoded(),pubKeyReceiver,"RSA/ECB/PKCS1Padding");
    String strSenbyteEncryptWithPublicKey = new BASE64Encoder().encode(byteEncryptWithPublicKey);
        
    // 3. Create a Message Digest of the Data to be transmitted
    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 Message to be Signed = Encrypted Secret Key+ MAC of the data to be transmitted
	String strMsgToSign = strSenbyteEncryptWithPublicKey+ "|"+ strMDofDataToTransmit;
    
    // 4. Sign the message
    // 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
	char[] keypassword = "send123".toCharArray();
    Key myKey =  ks.getKey("testsender", keypassword);
    PrivateKey myPrivateKey = (PrivateKey)myKey;
    
    // 4.2 Sign the message
    Signature mySign = Signature.getInstance("MD5withRSA");
    mySign.initSign(myPrivateKey);
    mySign.update(strMsgToSign.getBytes());
    byte[] byteSignedData = mySign.sign();
        
	// 5. The Values byteSignedData (the signature) and strMsgToSign (the data which was signed) can be sent across to the receiver
	
	// 6.Validate the Signature
    // 6.1 Extracting the Senders public Key from his certificate
	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. Decrypt the message using Recv private Key to get the Symmetric Key
    char[] recvpassword = "recv123".toCharArray();
    Key recvKey =  ks.getKey("testrecv", recvpassword);
    PrivateKey recvPrivateKey = (PrivateKey)recvKey;
    
    // Parsing the MessageDigest and the encrypted value
    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);

    // Decrypting to get the symmetric key
    byte[] bytestrEncryptWithPublicKey = new BASE64Decoder().decodeBuffer(strEncryptWithPublicKey);
    byte[] byteDecryptWithPrivateKey = encryptUtil.decryptData(byteEncryptWithPublicKey,recvPrivateKey,"RSA/ECB/PKCS1Padding");
    
    // 8. Decrypt the data using the Symmetric Key
    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. Compute MessageDigest of data+ Signed message
    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. Validate if the Message Digest of the Decrypted Text matches the Message 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();
	}
	
	
	}

}

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
 * This program provides the following cryptographic functionalities
 * 1. Encryption using AES
 * 2. Decryption using AES
 * 
 * High Level Algorithm :
 * 1. Generate a DES key (specify the Key size during this phase) 
 * 2. Create the Cipher 
 * 3. To Encrypt : Initialize the Cipher for Encryption
 * 4. To Decrypt : Initialize the Cipher for Decryption
 * 
 * 
 */

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(){
		/**
		 *  Step 1. Generate an AES key using KeyGenerator
		 *  		Initialize the keysize to 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;
		}
		
		/**
		 *  Step2. Create a Cipher by specifying the following parameters
		 * 			a. Algorithm name - here it is AES
		 */
		
		
		public byte[] encryptData(byte[] byteDataToEncrypt, Key secretKey, String Algorithm) {
			byte[] byteCipherText = new byte[200];
			
			try {
			Cipher aesCipher = Cipher.getInstance(Algorithm);
		
		/**
		 *  Step 3. Initialize the Cipher for Encryption 
		 */
			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);
				} 
				
		/**
		 *  Step 4. Encrypt the Data
		 *  		1. Declare / Initialize the Data. Here the data is of type String
		 *  		2. Convert the Input Text to Bytes
		 *  		3. Encrypt the bytes using doFinal method 
		 */
		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;
		}
		/**
		 *  Step 5. Decrypt the Data
		 *  		1. Initialize the Cipher for Decryption 
		 *  		2. Decrypt the cipher bytes using doFinal method 
		 */
		
		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();
		}
}

Referinte

  1. Computer de securitate Arte şi Ştiinţă - Matt Bishop
  2. Core Modele de securitate - Christopher Steele, Ray Lai şi Ramesh Nagappan
Published (Last edited): 04-11-2011