Introduction
In most ways, .NET's encryption model is pretty good. But when migrating secure applications from Java, the lack of this built in password-based encryption (PBE) is a pain. This article introduces a class that will encrypt data in the same way as Java's PBEWithMD5AndDES
algorithm.
The implementation of the algorithm I use is adapted from this article by Michel Gallant.
The Algorithm
As the name suggests, this encryption algorithm uses the DES mechanism with a key generated by MD5 hashing the 'password', combined with a salt string. It allows the input string to be repeatedly MD5-hashed a number of times. The 16-byte MD5 string is then split into two halves, an 8-byte DES key and an 8-byte initial seed for DES.
It obviously makes sense to make use of .NET's DESCryptoServiceProvider
to provide the encryption. So this implementation is simply doing the multiple MD5 hash.
The Code
To use this class, simply use:
PKCSKeyProvider kp = new PKCSKeyProvider();
ICryptoTransform crypt = kp.Generate(
"MyPassword",
new byte[] { 100, 200, 255, 3},
15,
1 );
Complete class listing:
using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
namespace RedCorona.Cryptography {
public class PKCSKeyGenerator {
byte[] key = new byte[8], iv = new byte[8];
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
public byte[] Key { get { return key; } }
public byte[] IV { get { return iv; } }
public ICryptoTransform Encryptor { get { return des.CreateEncryptor(key, iv); } }
public PKCSKeyGenerator(){}
public PKCSKeyGenerator(String keystring, byte[] salt,
int md5iterations, int segments){
Generate(keystring, salt, md5iterations, segments);
}
public ICryptoTransform Generate(String keystring, byte[] salt,
int md5iterations, int segments){
int HASHLENGTH = 16;
byte[] keymaterial =
new byte[HASHLENGTH*segments] ;
byte[] psbytes;
psbytes = Encoding.UTF8.GetBytes(keystring);
byte[] data00 = new byte[psbytes.Length + salt.Length] ;
Array.Copy(psbytes, data00, psbytes.Length);
Array.Copy(salt, 0, data00,
psbytes.Length, salt.Length) ;
MD5 md5 = new MD5CryptoServiceProvider();
byte[] result = null;
byte[] hashtarget = new byte[HASHLENGTH +
data00.Length];
for(int j=0; j<segments; j++) {
if(j == 0) result = data00;
else {
Array.Copy(result, hashtarget, result.Length);
Array.Copy(data00, 0, hashtarget, result.Length, data00.Length) ;
result = hashtarget;
}
for(int i=0; i<md5iterations; i++)
result = md5.ComputeHash(result);
Array.Copy(result, 0, keymaterial, j*HASHLENGTH,
result.Length);
}
Array.Copy(keymaterial, 0, key, 0, 8);
Array.Copy(keymaterial, 8, iv, 0, 8);
return Encryptor;
}
}
}
History
- 19th November, 2006: Initial post