Conversão de rotina de criptografia em C para Java

Thingol

Conforme vc me informou, a chave do 3DES é de 21 bytes. Adicionando os bits de paridade fica com 24 bytes.
Implementei o metodo que adiciona os bits de paridade e ainda estou com os resultados diferentes no Java dos obtidos no C.

Será que vc tem mais alguma idéia?

Segue abaixo o código completo em Java e em C.

A senha utilizada é “test password” e os dados criptorafados são “The book is on the table”

Estou passando o IV zerado.

package foo.bar.cripto;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Crypto {

	public static final int HASH_TYPE_MD5 = 1;
	public static final int HASH_TYPE_SHA1 = 2;

	public byte[] calcHash(byte[] data, int algorithmType) throws NoSuchAlgorithmException {

		MessageDigest md = null;
		if (HASH_TYPE_MD5 == algorithmType)
			md = MessageDigest.getInstance("MD5");
		else if (HASH_TYPE_SHA1 == algorithmType)
			md = MessageDigest.getInstance("SHA-1");
		else
			throw new NoSuchAlgorithmException();

		md.update(data);
		byte bufferSaida[] = md.digest();

		return bufferSaida;
	}

	public byte[] crypt(String data, String key, int algorithmType) throws Exception {

		byte[] hashKey = null;
		Cipher cipher = null;
		int keySize = 21; // 3DES key size

		hashKey = calcHash(key.getBytes(), HASH_TYPE_SHA1);
		
		byte[] derivedKey = addParityBit(cryptDeriveKey(hashKey, keySize, HASH_TYPE_SHA1));

		byte[] iv = new byte[8]; 	
		Arrays.fill(iv, (byte)0x00);  
		IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);	
	
		SecretKeySpec secretKeySpec = new SecretKeySpec(derivedKey, "DESede");
		cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
		cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);

		byte[] cryptData = cipher.doFinal(data.getBytes());
		return cryptData;
	}

	private byte[] cryptDeriveKey(byte[] key, int size, int algorithmType) throws Exception {
		byte[] buffer1 = new byte[64];
		byte[] buffer2 = new byte[64];

		// Preenche os buffers
		Arrays.fill(buffer1, (byte) 0x36);
		Arrays.fill(buffer2, (byte) 0x5C);

		// Faz o XOR da chave com os buffers
		for (int i = 0; i < key.length; i++) {
			buffer1[i] ^= key[i];
			buffer2[i] ^= key[i];
		}

		// Calcula o hash utilizando o mesmo algoritmo utilizado na chave passada
		try {
			buffer1 = calcHash(buffer1, algorithmType);
			buffer2 = calcHash(buffer2, algorithmType);
		} catch (NoSuchAlgorithmException e) {
			throw new Exception(e);
		}

		// Transporta os bytes dos buffers para a chave final até o tamanho desejado
		byte[] derivedKey = new byte[size];
		for (int i = 0; i < size; i++) {
			if (i < buffer1.length)
				derivedKey[i] = buffer1[i];
			else
				derivedKey[i] = buffer2[i - buffer1.length];
		}

		return derivedKey;
	}

	private byte[] addParityBit(byte[] key) throws Exception {

		if (key.length % 7 != 0)
			throw new Exception("Invalid data length");

		byte[] keyData = new byte[key.length + key.length / 7];
		byte b, deslocamento = 0, sobra = 0, j = 0;

		for (short i = 0; i < key.length; i++) {
			// Adiciona a sobra anterior no início do próximo byte			
			b = (byte) ((sobra << 8 - deslocamento) | ((key[i] & 0xFF) >> deslocamento));

			// Obtém a nova sobra
			sobra = (byte) (key[i] & (0x0FF >> (7 - deslocamento++)));

			// Adiciona o bit de paridade na saída
			keyData[i + j] = addParityBit(b);

			// Calcula o oitavo byte em cima da sobra do setimo byte
			if ((i + 1) % 7 == 0) {
				keyData[i + ++j] = addParityBit((byte) (sobra << 1));
				sobra = 0;
				deslocamento = 0;
			}
		}
		return keyData;
	}

	private byte addParityBit(byte b) {
		byte paridade = (byte) ((b >> 1) ^ (b >> 2) ^ (b >> 3) ^ (b >> 4) ^ (b >> 5) ^ (b >> 6) ^ (b >> 7));
		return (byte) ((b & 0xFE) | (paridade & 0x01));
	}

	private void showBinary(byte value) {
		StringBuffer sb = new StringBuffer();
		for (int i = 7; i >= 0; i--)
			sb.append((value >> i) & 0x01);
		System.out.println(sb.toString());
	}
}

======================================
Segue o código de teste em C

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0400
#endif

#include "stdafx.h"

#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <windows.h>
#include <wincrypt.h>

void crypt();
void MyHandleError(char *s);

int main() {
	crypt();
	return 0;
}

void crypt() {
	HCRYPTPROV hCryptProv;
	HCRYPTKEY hKey;
	HCRYPTHASH hHash;

	CHAR szPassword[14] = "test password";
	CHAR szDataIn[25] = "The book is on the table";

	DWORD dwPassLen = strlen(szPassword);
	DWORD dwDataInLen = strlen(szDataIn);

	//--------------------------------------------------------------------
	// Acquire a cryptographic provider context handle.
	if(!CryptAcquireContext(&hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
		MyHandleError("Error during CryptAcquireContext!");
	}

	//--------------------------------------------------------------------
	// Create an empty hash object.
	if(!CryptCreateHash(hCryptProv, CALG_SHA1, 0, 0, &hHash)) {
		MyHandleError("Error during CryptCreateHash!");
	}

	//--------------------------------------------------------------------
	// Hash the password string.
	if(!CryptHashData(hHash, (BYTE *)szPassword, dwPassLen, 0)) {
		 MyHandleError("Error during CryptHashData!");
	}


	/*DWORD tamHash2 = 0;
	if(!CryptGetHashParam(hHash, HP_HASHVAL, NULL, &tamHash2, 0)) {
		MyHandleError("Bla bla");
	}

	BYTE * pbHash2 = new BYTE[tamHash2 + 1];
	memset(pbHash2, 0, tamHash2 + 1);
	if(!CryptGetHashParam(hHash, HP_HASHVAL, pbHash2, &tamHash2, 0)) {
		MyHandleError("Bla bla");
	}*/

	//--------------------------------------------------------------------
	// Create a session key based on the hash of the password.
	if(!CryptDeriveKey(hCryptProv, CALG_3DES, hHash, CRYPT_EXPORTABLE, &hKey)) {
		MyHandleError("Error during CryptDeriveKey!");
	}


	DWORD dwDataOutLen = dwDataInLen;
	if(!CryptEncrypt(hKey, 0, TRUE, 0, NULL, &dwDataOutLen, dwDataOutLen)) {
		MyHandleError("Error during CryptEncrypt!");
	}

	BYTE * szDataOut = new BYTE[dwDataOutLen + 1];
	memset(szDataOut, 0, dwDataOutLen + 1);
	if(!CryptEncrypt(hKey, 0, TRUE, 0, szDataOut, &dwDataInLen, dwDataOutLen)) {
		MyHandleError("Error during CryptEncrypt!");
	}

	// Destroy the hash object.
	if(hHash) {
	   if(!(CryptDestroyHash(hHash)))
		   MyHandleError("Error during CryptDestroyHash");
	}

	// Destroy the session key.
	if(hKey) {
		if(!(CryptDestroyKey(hKey)))
			MyHandleError("Error during CryptDestroyKey");
	}

	// Release the provider handle.
	if(hCryptProv) {
	   if(!(CryptReleaseContext(hCryptProv, 0)))
		   MyHandleError("Error during CryptReleaseContext");
	}

	printf("The program to derive a key completed without error. \n");
}

void MyHandleError(char *s) {
	printf("An error occurred in running the program.\n");
	printf("%s\n",s);
	printf("Error number %x\n.",GetLastError());
	printf("Program terminating.\n");
	exit(1);
}

Puxa, agora não sei mais. Estou sem tempo para testar eu mesmo; é que se eu mesmo tentasse (começando do zero), talvez evitasse algum erro que você estivesse porventura cometendo. Será que alguém mais conseguiu fazer isso? talvez fosse interessante ver se alguém conseguiu fazer a interoperabilidade entre MS CryptoAPI e OpenSSL ou outro pacote (como a CryptLib), só para ver o que está errado).

Pessoal,

boas novas…
Consegui fazer a criptografia em C e descriptografia em Java.

Agradeço a ajuda de todos.

Muito obrigado

:smiley:

Olá

Viva! :lol:

Agora vc não deixar a gente morrer de curiosidade. Em nome do tempo que dedicamos a ele, descreva como fez para resolver o problema.

[]s
Luca

Segue o código completo

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherSpi;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Crypto {

	// Hash data
	public static final int HASH_TYPE_MD5 = 1;
	public static final int HASH_TYPE_SHA1 = 2;

	public static final int HASH_LENGTH_MD5 = 16;
	public static final int HASH_LENGTH_SHA1 = 20;

	private final String HASH_ALGORITHM_NAME_MD5 = "MD5";
	private final String HASH_ALGORITHM_NAME_SHA1 = "SHA-1";

	// Crypt data
	public static final int CRYPT_TYPE_DES = 3;
	public static final int CRYPT_TYPE_3DES = 4;
	public static final int CRYPT_TYPE_RC2 = 5;
	public static final int CRYPT_TYPE_RC4 = 6;

	private final String CRYPT_ALGORITHM_NAME_DES = "DES";
	private final String CRYPT_ALGORITHM_NAME_3DES = "DESede";
	private final String CRYPT_ALGORITHM_NAME_RC2 = "RC2";
	private final String CRYPT_ALGORITHM_NAME_RC4 = "RC4";

	private final int CRYPT_KEY_LENGTH_DES = 8;
	private final int CRYPT_KEY_LENGTH_3DES = 24; 	// 168 bits + parity   
	private final int CRYPT_KEY_LENGTH_RC2RC4 = 16; // 128 bits + parity

	public byte[] calcHash(byte[] data, int algorithmType) throws NoSuchAlgorithmException {

		MessageDigest md = null;
		if (HASH_TYPE_MD5 == algorithmType)
			md = MessageDigest.getInstance(HASH_ALGORITHM_NAME_MD5);
		else if (HASH_TYPE_SHA1 == algorithmType)
			md = MessageDigest.getInstance(HASH_ALGORITHM_NAME_SHA1);
		else
			throw new NoSuchAlgorithmException();

		md.update(data);
		byte bufferSaida[] = md.digest();

		return bufferSaida;
	}

	public byte[] crypt(byte[] data, String key, int cryptAlgorithmType) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalStateException, IllegalBlockSizeException, BadPaddingException {
		// Valor padrão para hash de chave no BSCripto
		return crypt(data, key, cryptAlgorithmType, HASH_TYPE_SHA1);
	}

	public byte[] crypt(byte[] data, String key, int cryptAlgorithmType, int keyHashAlgorithmType) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalStateException, IllegalBlockSizeException, BadPaddingException {
		return cryptDecrypt(data, key, cryptAlgorithmType, keyHashAlgorithmType, Cipher.ENCRYPT_MODE);
	}

	public byte[] decrypt(byte[] data, String key, int cryptAlgorithmType) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalStateException, IllegalBlockSizeException, BadPaddingException {
		// Valor padrão para hash de chave no BSCripto
		return crypt(data, key, cryptAlgorithmType, HASH_TYPE_SHA1);
	}

	public byte[] decrypt(byte[] data, String key, int cryptAlgorithmType, int keyHashAlgorithmType) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalStateException, IllegalBlockSizeException, BadPaddingException {
		return cryptDecrypt(data, key, cryptAlgorithmType, keyHashAlgorithmType, Cipher.DECRYPT_MODE);
	}

	private byte[] cryptDecrypt(byte[] data, String key, int cryptAlgorithmType, int hashAlgorithmType, int encryptMode) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalStateException, IllegalBlockSizeException, BadPaddingException {
		byte[] hashKey = null;
		Cipher cipher = null;
		String algorithmName = getCryptAlgorithmName(cryptAlgorithmType);

		// Obtém o hash da chave
		hashKey = calcHash(key.getBytes(), hashAlgorithmType);

		// Obtém uma chave com o tamanho correto para o algoritmo de criptografia escolhido
		byte[] derivedKey = cryptDeriveKey(hashKey, cryptAlgorithmType, hashAlgorithmType);

		// Gera um vetor de inicialização zerado igual ao utilizado na CryptoAPI
		byte[] iv = new byte[8];
		Arrays.fill(iv, (byte) 0x00);
		IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

		// Gera a chave secreta para o algoritmo de criptografia escolhido
		SecretKeySpec secretKeySpec = new SecretKeySpec(derivedKey, algorithmName);

		// Criptografa os dados		
		cipher = Cipher.getInstance(algorithmName + "/CBC/PKCS5Padding");
		cipher.init(encryptMode, secretKeySpec, ivParameterSpec);
		byte[] cryptData = cipher.doFinal(data);

		return cryptData;
	}

	private byte[] cryptDeriveKey(byte[] key, int cryptAlgorithmType, int hashAlgorithmType) throws NoSuchAlgorithmException {
		byte[] derivedKey;

		switch (cryptAlgorithmType) {
			case CRYPT_TYPE_3DES :
				derivedKey = cryptDeriveKey(key, CRYPT_KEY_LENGTH_3DES, hashAlgorithmType, true);
				break;
			case CRYPT_TYPE_DES :
				derivedKey = cryptDeriveKey(key, CRYPT_KEY_LENGTH_DES, hashAlgorithmType, true);
				break;
			case CRYPT_TYPE_RC2 :
			case CRYPT_TYPE_RC4 :
				derivedKey = cryptDeriveKey(key, CRYPT_KEY_LENGTH_RC2RC4, hashAlgorithmType, true);
				break;
			default :
				throw new NoSuchAlgorithmException();
		}
		return derivedKey;
	}

	private byte[] cryptDeriveKey(byte[] key, int keySize, int hashAlgorithmType, boolean parity) throws NoSuchAlgorithmException {
		byte[] buffer1 = new byte[64];
		byte[] buffer2 = new byte[64];
		byte[] derivedKey = new byte[keySize];

		// Caso não necessite de derivação retorna a chave sem derivação.
		// A derivação é feita apenas quando o tamanho da chave para o algoritmo
		// de criptografia escolhido é maior que a chave recebida
		if (((HASH_TYPE_MD5 == hashAlgorithmType) && (keySize <= HASH_LENGTH_MD5)) || ((HASH_TYPE_SHA1 == hashAlgorithmType) && (keySize <= HASH_LENGTH_SHA1))) {
			for (int i = 0; i < keySize; i++)
				derivedKey[i] = key[i];
			return derivedKey;
		}

		// Preenche os buffers
		Arrays.fill(buffer1, (byte) 0x36);
		Arrays.fill(buffer2, (byte) 0x5C);

		// Faz o XOR da chave com os buffers
		for (int i = 0; i < key.length; i++) {
			buffer1[i] ^= key[i];
			buffer2[i] ^= key[i];
		}

		// Calcula o hash utilizando o mesmo algoritmo utilizado na chave passada
		buffer1 = calcHash(buffer1, hashAlgorithmType);
		buffer2 = calcHash(buffer2, hashAlgorithmType);

		// Transporta os bytes dos buffers para a chave final até o tamanho desejado
		for (int i = 0; i < keySize; i++) {
			if (i < buffer1.length)
				derivedKey[i] = buffer1[i];
			else
				derivedKey[i] = buffer2[i - buffer1.length];
		}

		// Adiciona paridade
		if (true == parity)
			return addParityBit(derivedKey);
		else
			return derivedKey;
	}

	private byte[] addParityBit(byte[] key) {
		byte[] keyData = new byte[key.length];

		for (short i = 0; i < key.length; i++) {
			byte b = key[i];
			byte paridade = (byte) ((b >> 1) ^ (b >> 2) ^ (b >> 3) ^ (b >> 4) ^ (b >> 5) ^ (b >> 6) ^ (b >> 7));
			keyData[i] = (byte) ((b & 0xFE) | (paridade & 0x01));
		}
		return keyData;
	}

	private String getCryptAlgorithmName(int cryptAlgorithmType) throws NoSuchAlgorithmException {
		String algorithmName;

		switch (cryptAlgorithmType) {
			case CRYPT_TYPE_3DES :
				algorithmName = CRYPT_ALGORITHM_NAME_3DES;
				break;
			case CRYPT_TYPE_DES :
				algorithmName = CRYPT_ALGORITHM_NAME_DES;
				break;
			case CRYPT_TYPE_RC2 :
				algorithmName = CRYPT_ALGORITHM_NAME_RC2;
				break;
			case CRYPT_TYPE_RC4 :
				algorithmName = CRYPT_ALGORITHM_NAME_RC4;
				break;
			default :
				throw new NoSuchAlgorithmException();
		}
		return algorithmName;
	}
}

Só uma observação.

A criptografia com RC2 e RC4 ainda não estão funcionando.

Estou corrigindo agora.

Olá

Mas o que realmente estava errado? O código ou o conceito?

[]s
Luca

Conforme o thingol havia passado, tive que acertar o Mode (CBC - Cipher Block Chaining Mode) e o Padding (PKCS5Padding).

Também acertei o cálculo de paridade. Eu estava inserindo os bits de paridade na chave de 21 bytes (3DES) gerando uma de 24 bytes.
Agora gero uma de 24 bytes e altero o último bit de cada byte para o bit da paridade.

Outra coisa. Utilizei um vetor de inicialização (IV) zerado.

Parabéns!

Pessoal,

Sei que estou ressuscitando o tópico, mas preciso desta função em java.
Dei uma olhada, tentei usar o exemplo citado acima mas não consigo executar.
Alguém consegue me dar uma ajuda aí?

#define HASH_ALGORITHM	CALG_MD5
#define ENCRYPT_ALGORITHM	CALG_RC2 
#define ENCRYPT_ALGORITHM2	CALG_RC4 
#define ENCRYPT_KEY	(0x00280000 | CRYPT_EXPORTABLE)

BOOL CCrypto::Encrypt(TCHAR* szPassword, _bstr_t & sRetorno)
{
	BOOL bResult = TRUE;

	HCRYPTPROV hProv = NULL;
	HCRYPTKEY hKey = NULL;
	HCRYPTKEY hXchgKey = NULL;
	HCRYPTHASH hHash = NULL;
	DWORD dwLength;

	TCHAR szLocalPassword[] = _T("Mz6@a0i*");

	if (CryptAcquireContext(&hProv, NULL, CSP, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
	{
		if (CryptCreateHash(hProv, HASH_ALGORITHM, 0, 0, &hHash))
		{
			dwLength = sizeof(TCHAR)*_tcslen(szLocalPassword);
			if (CryptHashData(hHash, (BYTE *)szLocalPassword, dwLength, 0))
			{
				if (CryptDeriveKey(hProv, ENCRYPT_ALGORITHM2, hHash, ENCRYPT_KEY, &hKey))
				{
					dwLength = sizeof(TCHAR)*_tcslen(szPassword);
					
					BYTE *pbBuffer = (BYTE *)malloc(dwLength);
					if (pbBuffer != NULL)
					{
						memcpy(pbBuffer, szPassword, dwLength);

						if (CryptEncrypt(hKey, 0, TRUE, 0, (BYTE *)pbBuffer, &dwLength, dwLength))
						{

							BinBuffer bin((BYTE *)pbBuffer, dwLength);
							sRetorno = BinHex(bin);
							bResult = TRUE;
						}
						else
						{
							bResult = FALSE;
						}

					  free(pbBuffer);
					}
					else
					{
						bResult = FALSE;
					}
					CryptDestroyKey(hKey);
				}
				else
				{

					bResult = FALSE;
				}
			}
			else
			{

				bResult = FALSE;
			}
			CryptDestroyHash(hHash);

		}
		else
		{
			bResult = FALSE;
		}
		CryptReleaseContext(hProv, 0);
	}
	return bResult;

Valeu!!!