I imported this java code:
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
/**
* RFC 2898 password derivation compatible with .NET Rfc2898DeriveBytes class.
*/
public class Rfc2898DeriveBytes {
private Mac _hmacSha1;
private byte[] _salt;
private int _iterationCount;
private byte[] _buffer = new byte[20];
private int _bufferStartIndex = 0;
private int _bufferEndIndex = 0;
private int _block = 1;
/**
* Creates new instance.
* @param password The password used to derive the key.
* @param salt The key salt used to derive the key.
* @param iterations The number of iterations for the operation.
* @throws NoSuchAlgorithmException HmacSHA1 algorithm cannot be found.
* @throws InvalidKeyException Salt must be 8 bytes or more. -or- Password cannot be null.
*/
public Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations) throws NoSuchAlgorithmException, InvalidKeyException {
if ((salt == null) || (salt.length < 8)) { throw new InvalidKeyException("Salt must be 8 bytes or more."); }
if (password == null) { throw new InvalidKeyException("Password cannot be null."); }
this._salt = salt;
this._iterationCount = iterations;
this._hmacSha1 = Mac.getInstance("HmacSHA1");
this._hmacSha1.init(new SecretKeySpec(password, "HmacSHA1"));
}
/**
* Creates new instance.
* @param password The password used to derive the key.
* @param salt The key salt used to derive the key.
* @param iterations The number of iterations for the operation.
* @throws NoSuchAlgorithmException HmacSHA1 algorithm cannot be found.
* @throws InvalidKeyException Salt must be 8 bytes or more. -or- Password cannot be null.
* @throws UnsupportedEncodingException UTF-8 encoding is not supported.
*/
public Rfc2898DeriveBytes(String password, byte[] salt, int iterations) throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException {
this(password.getBytes("UTF8"), salt, iterations);
}
/**
* Creates new instance.
* @param password The password used to derive the key.
* @param salt The key salt used to derive the key.
* @throws NoSuchAlgorithmException HmacSHA1 algorithm cannot be found.
* @throws InvalidKeyException Salt must be 8 bytes or more. -or- Password cannot be null.
* @throws UnsupportedEncodingException UTF-8 encoding is not supported.
*/
public Rfc2898DeriveBytes(String password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
this(password, salt, 0x3e8);
}
/**
* Returns a pseudo-random key from a password, salt and iteration count.
* @param count Number of bytes to return.
* @return Byte array.
*/
public byte[] getBytes(int count) {
byte[] result = new byte[count];
int resultOffset = 0;
int bufferCount = this._bufferEndIndex - this._bufferStartIndex;
if (bufferCount > 0) { //if there is some data in buffer
if (count < bufferCount) { //if there is enough data in buffer
System.arraycopy(this._buffer, this._bufferStartIndex, result, 0, count);
this._bufferStartIndex += count;
return result;
}
System.arraycopy(this._buffer, this._bufferStartIndex, result, 0, bufferCount);
this._bufferStartIndex = this._bufferEndIndex = 0;
resultOffset += bufferCount;
}
while (resultOffset < count) {
int needCount = count - resultOffset;
this._buffer = this.func();
if (needCount > 20) { //we one (or more) additional passes
System.arraycopy(this._buffer, 0, result, resultOffset, 20);
resultOffset += 20;
} else {
System.arraycopy(this._buffer, 0, result, resultOffset, needCount);
this._bufferStartIndex = needCount;
this._bufferEndIndex = 20;
return result;
}
}
return result;
}
private byte[] func() {
this._hmacSha1.update(this._salt, 0, this._salt.length);
byte[] tempHash = this._hmacSha1.doFinal(getBytesFromInt(this._block));
this._hmacSha1.reset();
byte[] finalHash = tempHash;
for (int i = 2; i <= this._iterationCount; i++) {
tempHash = this._hmacSha1.doFinal(tempHash);
for (int j = 0; j < 20; j++) {
finalHash[j] = (byte)(finalHash[j] ^ tempHash[j]);
}
}
if (this._block == 2147483647) {
this._block = -2147483648;
} else {
this._block += 1;
}
return finalHash;
}
private static byte[] getBytesFromInt(int i) {
return new byte[] { (byte)(i >>> 24), (byte)(i >>> 16), (byte)(i >>> 8), (byte)i };
}
}
And I got this oxygene:
namespace SandW;
interface
uses
java.io,
java.security,
javax.crypto,
javax.crypto.spec;
type
// {*
// * RFC 2898 password derivation compatible with .NET Rfc2898DeriveBytes class.
// }
Rfc2898DeriveBytes = public class
private
var _hmacSha1: Mac;
var _salt: array of Byte;
var _iterationCount: Integer;
var _buffer: array of Byte := new Byte[20];
var _bufferStartIndex: Integer := 0;
var _bufferEndIndex: Integer := 0;
var _block: Integer := 1;
public
constructor(password: array of Byte; salt: array of Byte; iterations: Integer);
constructor(password: String; salt: array of Byte; iterations: Integer);
constructor(password: String; salt: array of Byte);
method getBytes(count: Integer): array of Byte;
private
method func(): array of Byte;
class method getBytesFromInt(i: Integer): array of Byte;
end;
implementation
constructor Rfc2898DeriveBytes(password: array of Byte; salt: array of Byte; iterations: Integer);
begin
if ((salt = nil) or (salt.length < 8)) then begin
raise new InvalidKeyException('Salt must be 8 bytes or more.')
end;
if (password = nil) then begin
raise new InvalidKeyException('Password cannot be null.')
end;
self._salt := salt;
self._iterationCount := iterations;
self._hmacSha1 := Mac.getInstance('HmacSHA1');
self._hmacSha1.init(new SecretKeySpec(password, 'HmacSHA1'))
end;
constructor Rfc2898DeriveBytes(password: String; salt: array of Byte; iterations: Integer);
begin
self(password.getBytes('UTF8'), salt, iterations)
end;
constructor Rfc2898DeriveBytes(password: String; salt: array of Byte);
begin
self(password, salt, $3e8)
end;
method Rfc2898DeriveBytes.getBytes(count: Integer): array of Byte;
begin
var &result: array of Byte := new Byte[count];
var resultOffset: Integer := 0;
var bufferCount: Integer := (self._bufferEndIndex - self._bufferStartIndex);
if (bufferCount > 0) then begin
// if there is some data in buffer
if (count < bufferCount) then begin
// if there is enough data in buffer
System.arraycopy(self._buffer, self._bufferStartIndex, &result, 0, count);
self._bufferStartIndex := self._bufferStartIndex + count;
exit &result
end;
System.arraycopy(self._buffer, self._bufferStartIndex, &result, 0, bufferCount);
self._bufferStartIndex := 0;
self._bufferEndIndex := 0;;
resultOffset := resultOffset + bufferCount
end;
while (resultOffset < count) do begin
var needCount: Integer := (count - resultOffset);
self._buffer := self.func();
if (needCount > 20) then begin
// we one (or more) additional passes
System.arraycopy(self._buffer, 0, &result, resultOffset, 20);
resultOffset := resultOffset + 20
end
else
begin
System.arraycopy(self._buffer, 0, &result, resultOffset, needCount);
self._bufferStartIndex := needCount;
self._bufferEndIndex := 20;
exit &result
end
end;
exit &result
end;
method Rfc2898DeriveBytes.func(): array of Byte;
begin
self._hmacSha1.update(self._salt, 0, self._salt.length);
var tempHash: array of Byte := self._hmacSha1.doFinal(getBytesFromInt(self._block));
self._hmacSha1.reset();
var finalHash: array of Byte := tempHash;
// TODO: Check continue in unwrapped java for loop
begin
var i: Integer := 2;
while (i <= self._iterationCount) do begin
tempHash := self._hmacSha1.doFinal(tempHash);
// TODO: Check continue in unwrapped java for loop
begin
var j: Integer := 0;
while (j < 20) do begin
finalHash[j] := Byte((finalHash[j] xor tempHash[j]));
{postfix}inc(j)
end
end;
{postfix}inc(i)
end
end;
if (self._block = 2147483647) then begin
self._block := - 2147483648
end
else
begin
self._block := self._block + 1
end;
exit finalHash
end;
class method Rfc2898DeriveBytes.getBytesFromInt(i: Integer): array of Byte;
begin
exit array of Byte([Byte((i shr 24)), Byte((i shr 16)), Byte((i shr 8)), Byte(i)])
end;
end.
But that code gets these compiler errors:
Error 1 (E407) No overloaded constructor with these parameters for type "Rfc2898DeriveBytes", best matching overload is "constructor (password: array of nullable Byte; salt: array of nullable Byte; iterations: Integer)" C:\Users\Mark\Documents\Visual Studio 2013\Projects\Encryption\consoleapplication7\ClassLibrary1\Rfc2898.pas 65 23 SandWEncryptDecrypt
Warning 2 (H3) parameter 1 is "String" should be "array of nullable Byte" C:\Users\Mark\Documents\Visual Studio 2013\Projects\Encryption\consoleapplication7\ClassLibrary1\Rfc2898.pas 65 43 SandWEncryptDecrypt
Warning 3 (H3) parameter 2 is "array of SByte" should be "array of nullable Byte" C:\Users\Mark\Documents\Visual Studio 2013\Projects\Encryption\consoleapplication7\ClassLibrary1\Rfc2898.pas 65 53 SandWEncryptDecrypt
Error 4 (E62) Type mismatch, cannot assign "array of nullable Byte" to "array of SByte" C:\Users\Mark\Documents\Visual Studio 2013\Projects\Encryption\consoleapplication7\ClassLibrary1\Rfc2898.pas 68 19 SandWEncryptDecrypt
Error 5 (E407) No overloaded constructor with these parameters for type "SecretKeySpec", best matching overload is "constructor (arg1: array of SByte; arg2: String)" C:\Users\Mark\Documents\Visual Studio 2013\Projects\Encryption\consoleapplication7\ClassLibrary1\Rfc2898DeriveBytes1.pas 45 27 SandWEncryptDecrypt
Warning 6 (H3) parameter 1 is "array of nullable Byte" should be "array of SByte" C:\Users\Mark\Documents\Visual Studio 2013\Projects\Encryption\consoleapplication7\ClassLibrary1\Rfc2898DeriveBytes1.pas 45 41 SandWEncryptDecrypt
Error 7 (E105) Member "self" on type "Rfc2898DeriveBytes" is a variable but is used as a method C:\Users\Mark\Documents\Visual Studio 2013\Projects\Encryption\consoleapplication7\ClassLibrary1\Rfc2898DeriveBytes1.pas 50 3 SandWEncryptDecrypt
Warning 8 (N4) Type "Rfc2898DeriveBytes" was declared here C:\Users\Mark\Documents\Visual Studio 2013\Projects\Encryption\consoleapplication7\ClassLibrary1\Rfc2898DeriveBytes1.pas 13 1 SandWEncryptDecrypt
Error 9 (E105) Member "self" on type "Rfc2898DeriveBytes" is a variable but is used as a method C:\Users\Mark\Documents\Visual Studio 2013\Projects\Encryption\consoleapplication7\ClassLibrary1\Rfc2898DeriveBytes1.pas 55 3 SandWEncryptDecrypt
Warning 10 (N4) Type "Rfc2898DeriveBytes" was declared here C:\Users\Mark\Documents\Visual Studio 2013\Projects\Encryption\consoleapplication7\ClassLibrary1\Rfc2898DeriveBytes1.pas 13 1 SandWEncryptDecrypt
Error 11 (E406) No overloaded method "update" with these parameters on type "Mac", best matching overload is "method update(arg1: array of SByte; arg2: Integer; arg3: Integer)" C:\Users\Mark\Documents\Visual Studio 2013\Projects\Encryption\consoleapplication7\ClassLibrary1\Rfc2898DeriveBytes1.pas 97 18 SandWEncryptDecrypt
Warning 12 (H3) parameter 1 is "array of nullable Byte" should be "array of SByte" C:\Users\Mark\Documents\Visual Studio 2013\Projects\Encryption\consoleapplication7\ClassLibrary1\Rfc2898DeriveBytes1.pas 97 25 SandWEncryptDecrypt
Error 13 (E406) No overloaded method "doFinal" with these parameters on type "Mac", best matching overload is "method doFinal(arg1: array of SByte): array of SByte" C:\Users\Mark\Documents\Visual Studio 2013\Projects\Encryption\consoleapplication7\ClassLibrary1\Rfc2898DeriveBytes1.pas 98 49 SandWEncryptDecrypt
Warning 14 (H3) parameter 1 is "array of nullable Byte" should be "array of SByte" C:\Users\Mark\Documents\Visual Studio 2013\Projects\Encryption\consoleapplication7\ClassLibrary1\Rfc2898DeriveBytes1.pas 98 57 SandWEncryptDecrypt
Error 15 (E406) No overloaded method "doFinal" with these parameters on type "Mac", best matching overload is "method doFinal(arg1: array of SByte): array of SByte" C:\Users\Mark\Documents\Visual Studio 2013\Projects\Encryption\consoleapplication7\ClassLibrary1\Rfc2898DeriveBytes1.pas 105 34 SandWEncryptDecrypt
Warning 16 (H3) parameter 1 is "array of nullable Byte" should be "array of SByte" C:\Users\Mark\Documents\Visual Studio 2013\Projects\Encryption\consoleapplication7\ClassLibrary1\Rfc2898DeriveBytes1.pas 105 42 SandWEncryptDecrypt
I’m not sure how to fix the code without affecting what it does. The actual .net version seems to always return positive integers and anything I’ve done in changing array of byte to array of sbyte has given results with positive and negatives values in the getBytes method.
This was the best I could do at cleaning it up and getting past compiler errors (but of course, it doesn’t work right)
namespace SandW;
interface
uses
java.io,
java.security,
javax.crypto,
javax.crypto.spec;
type
//
// RFC 2898 password derivation compatible with .NET Rfc2898DeriveBytes class.
//
Rfc2898DeriveBytes = public class
private
_hmacSha1 : Mac;
_salt : array of SByte;
_iterationCount : Integer;
_buffer : array of SByte := new SByte[20];
_bufferStartIndex : Integer := 0;
_bufferEndIndex : Integer := 0;
_block : Integer := 1;
public
constructor( password: array of SByte; salt: array of SByte; iterations: Integer );
constructor( password: String; salt: array of SByte; iterations: Integer );
constructor( password: String; salt: array of SByte );
method getBytes(count: Integer): array of SByte;
private
method func(): array of SByte;
class method getBytesFromInt(i: Integer): array of SByte;
end;
implementation
constructor Rfc2898DeriveBytes(password: array of SByte; salt: array of SByte; iterations: Integer);
begin
if ( salt = nil ) or
( salt.length < 8) then
raise new InvalidKeyException('Salt must be 8 bytes or more.');
if password = nil then
raise new InvalidKeyException('Password cannot be null.');
_salt := salt;
_iterationCount := iterations;
_hmacSha1 := Mac.getInstance('HmacSHA1');
_hmacSha1.init( new SecretKeySpec( password, 'HmacSHA1' ) )
end;
constructor Rfc2898DeriveBytes(password: String; salt: array of SByte; iterations: Integer);
begin
constructor( password.getBytes('UTF8'), salt, iterations );
end;
constructor Rfc2898DeriveBytes(password: String; salt: array of SByte);
begin
constructor( password, salt, $3e8 )
end;
method Rfc2898DeriveBytes.getBytes( count: Integer ) : array of SByte;
begin
var &result: array of SByte := new SByte[count];
var resultOffset: Integer := 0;
var bufferCount: Integer := _bufferEndIndex - _bufferStartIndex;
if bufferCount > 0 then begin
// if there is some data in buffer
if count < bufferCount then begin
// if there is enough data in buffer
System.arraycopy( _buffer, _bufferStartIndex, &result, 0, count );
_bufferStartIndex := _bufferStartIndex + count;
exit &result;
end;
System.arraycopy( _buffer, _bufferStartIndex, &result, 0, bufferCount );
_bufferStartIndex := 0;
_bufferEndIndex := 0;
resultOffset := resultOffset + bufferCount;
end;
while resultOffset < count do begin
var needCount: Integer := count - resultOffset;
_buffer := func();
if needCount > 20 then begin
// we need one (or more) additional passes
System.arraycopy( _buffer, 0, &result, resultOffset, 20 );
resultOffset := resultOffset + 20
end
else begin
System.arraycopy( _buffer, 0, &result, resultOffset, needCount );
_bufferStartIndex := needCount;
_bufferEndIndex := 20;
exit &result
end
end;
end;
method Rfc2898DeriveBytes.func(): array of SByte;
begin
_hmacSha1.update( _salt, 0, _salt.length );
var tempHash: array of SByte := _hmacSha1.doFinal( getBytesFromInt( _block ) );
_hmacSha1.reset();
var finalHash: array of SByte := tempHash;
// TODO: Check continue in unwrapped java for loop
var i: Integer := 2;
while i <= _iterationCount do begin
tempHash := _hmacSha1.doFinal( tempHash );
// TODO: Check continue in unwrapped java for loop
var j: Integer := 0;
while j < 20 do begin
finalHash[j] := SByte((finalHash[j] xor tempHash[j]));
{postfix}inc(j)
end;
{postfix}inc(i)
end;
if _block = 2147483647
then _block := - 2147483648
else _block := _block + 1;
exit finalHash
end;
class method Rfc2898DeriveBytes.getBytesFromInt( i: Integer ): array of SByte;
begin
exit array of SByte( [Byte(i shr 24), Byte(i shr 16), Byte(i shr 8), Byte(i)] )
end;
end.