There is a snippet of C++ DES encryption/decription code which I could use some
help in converting to Java. Unfortunately I'm not familiar enough with DES to
map the encryption/decription functionality to the appropriate Java JCE routines.
I have included a sample C++ program which contains the encryption and
decription methods, and a corresponding Java program with the empty
encryption and decryption methods.
The two C++ methods which I would like to convert to Java are:
std::string encrypt(const std::string& str)
std::string decrypt(const std::string& str)
Here are the Java methods which need to be implemented:
public static String encrypt(String text)
public static String decrypt(String text)
The solution should only rely on classes included with JDK 1.4
(e.g. import java.security.*), and not on any 3rd party libraries. The included
test program contains helper routines for padding the input string and converting
the encoded data to a human-readable string.
The solution should be able to encrypt the string "hello" to
"32:88:DA:F8:D4:8A:91:9C:" and (decrypt it) back. Basically mapping the
C++ des_cbc_encrypt() method call to the appropriate Java JCE methods.
/********************************* C++ Code **********************************/
#include <iostream>
#include "openssl/des.h"
std::string desPad(const std::string& Pad);
std::string asciiToHexString(const char* ascii, const int length);
std::string hexToAsciiString(const std::string& hex);
std::string encrypt(const std::string& str);
std::string decrypt(const std::string& str);
const int HEX_BUFFER_CHAR_SIZE = 4;
using namespace std;
int main() {
string text = "hello";
cout << "text: " << text.c_str() << endl;
string encrypted = encrypt(text);
cout << "encrypted: " << encrypted.c_str() << endl;
string decrypted = decrypt(encrypted);
cout << "decrypted: " << decrypted.c_str() << endl;
}
std::string encrypt(const std::string& str) {
char encrypted_buffer[256];
string tmp;
des_cblock key;
des_cblock initkey;
des_key_schedule ks;
des_string_to_key("string1", &initkey);
des_string_to_key("string2", &key);
key_sched(&key, ks);
memset(encrypted_buffer, 0, sizeof(encrypted_buffer));
tmp.assign(desPad(str));
des_cbc_encrypt(reinterpret_cast<const u_char *>(tmp.c_str()),
reinterpret_cast<u_char *>(encrypted_buffer),
tmp.length(), ks, &initkey, DES_ENCRYPT);
return asciiToHexString(encrypted_buffer, tmp.length());
}
std::string decrypt(const std::string& text) {
des_cblock key = {0};
des_cblock initkey = {0};
des_key_schedule ks = {0};
u_char encrypted_buffer[256] = {0};
u_char decrypted_buffer[256] = {0};
des_string_to_key("string1", &initkey);
des_string_to_key("string2", &key);
key_sched(&key, ks);
string decrypted;
memset(encrypted_buffer, 0, sizeof(encrypted_buffer));
memset(decrypted_buffer, 0, sizeof(decrypted_buffer));
const char* str = hexToAsciiString(text).c_str();
int length = strlen(str);
memcpy(encrypted_buffer, str, length);
des_cbc_encrypt(encrypted_buffer, decrypted_buffer, length,
ks, &initkey, DES_DECRYPT);
decrypted_buffer[length] = 0;
decrypted.assign(reinterpret_cast<char*>(decrypted_buffer));
int pos = decrypted.find_first_of(" ");
if (pos != std::string::npos) {
decrypted.erase(pos);
}
return decrypted;
}
std::string desPad(const std::string& Pad) {
char Spaces[8] = " ";
if (Pad.length() % 8) {
Spaces[8 - (Pad.length() % 8)] = 0;
} else {
Spaces[0] = 0;
}
return Pad + Spaces;
}
std::string hexToAsciiString(const std::string& hex) {
std::string asciiConversion;
int tempHexInt = 0;
int charPos = 0;
for (unsigned int i = 0; i < hex.length(); ++i) {
if (hex[i] == ':') {
asciiConversion += ' ';
asciiConversion[charPos] = tempHexInt;
++charPos;
tempHexInt = 0;
} else {
tempHexInt *= 0x10;
tempHexInt +=
((isalpha(hex[i])) ? hex[i] - 'A' + 10 : hex[i] - '0');
}
}
return asciiConversion;
}
std::string asciiToHexString(const char* ascii, const int length) {
std::string hexConversion;
char tempBuffer[HEX_BUFFER_CHAR_SIZE] = { 0 };
// Iterate through each character and conver it into hex
for(int i = 0; i < length; i++) {
sprintf(tempBuffer,"%02X:",(unsigned char)ascii[i]);
hexConversion += tempBuffer;
}
return hexConversion;
}
/********************************* Java Code **********************************/
public class Test {
public static void main(String[] args) {
String input = "32:88:DA:F8:D4:8A:91:9C:";
String expected = "hello";
String decrypted = decrypt(input);
System.out.println("decrypted: "+text);
if (decrypted.equals(expected)) {
System.out.println("successfully decrypted!");
}
String encrypted = encrypt(decrypted);
System.out.println("encrypted: "+text);
if (encrypted.equals(input)) {
System.out.println("successfully encrypted!");
}
}
public static String encrypt(String text) {
/* Implement this method */
}
public static String decrypt(String text) {
/* Implement this method */
}
public static String pad(String text) {
StringBuffer sb = new StringBuffer();
sb.append(text);
int count = 8 - (text.length() % 8);
for (int i = 0; i < count; i++) {
sb.append(' ');
}
return sb.toString();
}
public static String asciiToHexString(byte[] bytes)
throws UnsupportedEncodingException
{
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toHexString(0xff & bytes[i])).append(":");
}
return sb.toString();
}
public static String hexToAsciiString(String hex) {
if (hex == null || hex.length() == 0) return null;
String tokens[] = hex.split("[:]");
byte[] data = new byte[tokens.length];
for (int i = 0; i < tokens.length; i++) {
data[i] = (byte)Integer.parseInt(tokens[i], 16);
}
return new String(data);
}
}
/******************************************************************************/ |
Clarification of Question by
codingmonkey-ga
on
10 Sep 2004 09:59 PDT
Thank you for taking the time read my question and post a reply. Definitely
the Java Developers Almanac code samples have been very helpful to me. Using
the code samples I have been able to DES encrypt (and decrypt) strings.
Unfortunately however I have not been able to generate the same encrypted
strings as the C++ code. Being able to generate the same encrypted strings
is critical to this project, as we must be able to decrypt (in Java) the previously
encrypted strings (in C++).
The part that I'm missing is how to initialize the Java Cipher and SecretKey
to produce the same encrypted results as the C++ code.
The C++ code uses the following method to encode the string:
int des_cbc_encrypt(des_cblock *in, des_cblock *out, long length,
des_key_schedule schedule, des_cblock ivec, int encrypt);
So, it is using DES encryption with CBC. As the input data is always padded
(with spaces to the next 8 bytes), padding should not be necessary (right?).
Thus I have been initializing the Cipher as follows:
Cipher cipher = Cipher.getInstance("DES/CBC/NoPadding");
Assuming we are initializing the cipher correctly, then all that is left to
initialize is the secret key.
The C++ code passes a schedule and initialization vector to the des_cbc_encrypt
method as follows:
des_cblock key;
des_cblock ivec;
des_key_schedule schedule;
des_string_to_key("string1", &ivec);
des_string_to_key("string2", &key);
key_sched(&key, schedule);
des_cbc_encrypt(in, out, length, schedule, &ivec, DES_ENCRYPT);
How would I create the secret key (in Java) such that it is setup with the
same schedule an initialization vector?
SecretKey key = KeyGenerator.getInstance(???).generateKey();
Again for reference, the two methods of interest in C++ and Java:
/********************************* C++ Code **********************************/
std::string encrypt(const std::string& str) {
char encrypted_buffer[256];
memset(encrypted_buffer, 0, sizeof(encrypted_buffer));
string tmp;
tmp.assign(desPad(str));
des_cblock key;
des_cblock ivec;
des_key_schedule schedule;
des_string_to_key("string1", &ivec);
des_string_to_key("string2", &key);
key_sched(&key, schedule);
des_cbc_encrypt(reinterpret_cast<const u_char *>(tmp.c_str()),
reinterpret_cast<u_char *>(encrypted_buffer),
tmp.length(), schedule, &ivec, DES_ENCRYPT);
return asciiToHexString(encrypted_buffer, tmp.length());
}
/********************************* Java Code **********************************/
public static String encrypt(String text) {
try {
byte[] bytes = pad(text).getBytes("ASCII");
// Create the cipher
Cipher cipher = Cipher.getInstance("DES/CBC/NoPadding");
SecretKey key = KeyGenerator.getInstance("DES").generateKey();
// Initialize the cipher for encryption
cipher.init(Cipher.ENCRYPT_MODE, key);
// Encrypt the cleartext
byte[] ciphertext = cipher.doFinal(bytes);
:
}
/******************************************************************************/
And also for reference the des_cbc_encrypt man page info:
http://web.mit.edu/macdev/Development/MITKerberos/MITKerberosLib/DESLib/Documentation/api.html
int des_cbc_encrypt(des_cblock *in, des_cblock *out, long length,
des_key_schedule schedule, des_cblock ivec, int encrypt);
des_cbc_encrypt() encrypts/decrypts using the cipher-blockchaining mode of DES.
If the encrypt argument is nonzero, the routine cipher-block-chain
encrypts the cleartext data pointed to by the in argument into the
ciphertext pointed to by the out argument, using the key schedule
provided by the schedule argument, and initialization vector provided
by the ivec argument. If the length argument is not an integral
multiple of eight bytes, the last block is copied to a temp and zero
filled (highest addresses). out is ALWAYS an integral multiple of
eight bytes.
|