O que necessito é poder, no java, descriptografar dados que foram gerados pela rotina em C. Por isso, o processo deve ser identico.
A rotina em C é utilizada pelo legado e as novas aplicações estão sendo feitas em Java / J2EE. Não gostaria de no java ter que utilizar uma dll em C para fazer esse processo já que o Java nos fornece toda a API de criptografia.
Vou passar um código C completo que fiz para testar e o código Java.
O que estou tentando fazer agora é obter a Session Key no Java identica ao C. No C, a session key é gerada pela api CryptDeriveKey.
Na documentação da CryptDeriveKey no MSDN tem uma explicação de como é feita a derivação da chave.
Senha e dados utilizados nos testes:
Password = "test password"
Data = "The book is on the table"
Resultado no programa C:
Hash da senha = 2C EB 02 A8 5F 6D 4D E6 C2 8B 2E 59 FD A8 86 D5 26 DA FB 0D
Hash da session key = 12 93 D7 12 C2 CD D2 79 1D 43 2F CE 58 C4 33 CA 31 41 AA 77
(A session key não pode ser visualizada diretamente, apenas o seu hash)
No programa Java:
Hash da senha = 2C EB 02 A8 5F 6D 4D E6 C2 8B 2E 59 FD A8 86 D5 26 DA FB 0D
Hash da session key = 42 85 CA D4 84 9B 13 34 74 B5 EF 19 04 73 D6 6F 0C EF EA 99
Como podem ver, o hash da senha é identico mas o da session key não.
O código em C utilizado para o teste é:
#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;
HCRYPTHASH hHashSession;
CHAR szPassword[14] = "test password";
CHAR szData[25] = "The book is on the table";
DWORD dwPassLen = strlen(szPassword);
DWORD dwDataLen = strlen(szData);
DWORD dwSessionHashLen;
DWORD dwPassHashLen;
//--------------------------------------------------------------------
// 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!");
}
if(!CryptGetHashParam(hHash, HP_HASHVAL, NULL, &dwPassHashLen, 0)) {
MyHandleError("Error during CryptGetHashParam!");
}
BYTE * szPassHash = new BYTE[dwPassHashLen + 1];
memset(szPassHash, 0, dwPassHashLen + 1);
if(!CryptGetHashParam(hHash, HP_HASHVAL, szPassHash, &dwPassHashLen, 0)) {
MyHandleError("Error during CryptGetHashParam!");
}
//--------------------------------------------------------------------
// Create a session key based on the hash of the password.
if(!CryptDeriveKey(hCryptProv, CALG_3DES, hHash, CRYPT_EXPORTABLE, &hKey)) {
MyHandleError("Error during CryptDeriveKey!");
}
//--------------------------------------------------------------------
// Create an empty hash object for session key
if(!CryptCreateHash(hCryptProv, CALG_SHA1, 0, 0, &hHashSession)) {
MyHandleError("Error during CryptCreateHash for session key!");
}
//--------------------------------------------------------------------
// Create a hash of the session key
if(!CryptHashSessionKey(hHashSession, hKey, 0)) {
MyHandleError("Error during CryptHashSessionKey for session key!");
}
DWORD dwCount = sizeof(DWORD);
if(!CryptGetHashParam(hHashSession, HP_HASHSIZE, (BYTE *)&dwSessionHashLen, &dwCount, 0)) {
MyHandleError("Error during CryptGetHashParam for session key - Getting size!");
}
BYTE * szSessionHash = new BYTE[dwSessionHashLen + 1];
memset(szSessionHash, 0, dwSessionHashLen + 1);
if(!CryptGetHashParam(hHashSession, HP_HASHVAL, szSessionHash, &dwSessionHashLen, 0)) {
MyHandleError("Error during CryptGetHashParam!");
}
// Destroy the session hash object.
if(hHashSession) {
if(!(CryptDestroyHash(hHashSession)))
MyHandleError("Error during session CryptDestroyHash");
}
// 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);
}
O código em Java utilizado é:
import java.io.File;
import java.io.FileWriter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
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 static 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 static String crypt(String data, String key, int algorithmType) throws Exception {
byte[] hashKey = null;
Cipher cipher = null;
int keySize = 24; // 3DES key size
hashKey = calcHash(key.getBytes(), HASH_TYPE_SHA1);
byte[] derivedKey = cryptDeriveKey(hashKey, keySize, HASH_TYPE_SHA1);
byte[] sessionKey = calcHash(derivedKey, HASH_TYPE_SHA1);
/*SecretKeySpec secretKeySpec = new SecretKeySpec(derivedKey, "DESede");
cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);*/
return "";
}
private static 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;
}
}