From C# encryption key derivation to Ruby (PBKDF2) - c#

I'm trying to rewrite the following key generation method written in C# into its Ruby equivalent:
private static byte[] CreateKey(string password, int length)
{
var salt = new byte[] { 0x01, 0x02, 0x23, 0x34, 0x37, 0x48, 0x24, 0x63, 0x99, 0x04 };
const int Iterations = 1000;
using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt, Iterations))
return rfc2898DeriveBytes.GetBytes(length);
}
I'm using PBKDF2 implementation. And here's my Ruby code:
def create_key password, length
salt_a = [0x01, 0x02, 0x23, 0x34, 0x37, 0x48, 0x24, 0x63, 0x99, 0x04]
salt = salt_a.pack('C*') # Think here there is something to change
iterations = 1000
derived_b = PBKDF2.new do |p|
p.password = password
p.salt = salt
p.iterations = iterations
p.key_length = length
p.hash_function = OpenSSL::Digest::SHA1
end
derived_b.bin_string # and here too
end
In order to work those two methods should return the same output. The problem is that I can't figure out how to do this. PBKDF2 implementations takes salt as String, but C# takes a byte array... I think the problem is there.

If you can use a recent version of OpenSSL, then this worked for me:
SALT = [ 0x94, 0x67, 0x16, 0xe6, 0x20, 0xd4, 0x56, 0x46, 0x67, 0x56, 0x46, 0x56, 0x23 ].pack("c*")
PBKDF2_ITERATIONS = 1000
def create_key(password, length)
OpenSSL::PKCS5::pbkdf2_hmac_sha1(password, SALT, PBKDF2_ITERATIONS, length)
end

Related

Replacing multiple bytes in a byte array

I have a byte array like so
var byteArray = new byte[]
{
0x9C, 0x50, 0x53, 0x51, 0x52, 0x41, 0x50, 0x41, 0x51, 0x41, 0x52, 0x41, 0x53,
0x48, 0x83, 0xEC, 0x28,
0x48, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Line 3
0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Line 4
0xFF, 0xD0,
0x48, 0x83, 0xC4, 0x28,
0x41, 0x5B, 0x41, 0x5A, 0x41, 0x59, 0x41, 0x58, 0x5A, 0x59, 0x5B, 0x58,0x9D,
0xC3
};
I want to replace the following bytes on line 3
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
As well as the following bytes on line 4
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
With different bytes (other than 0x00)
Note that the bytes I want to change on line 3 are different to the bytes I want to change on line 4
What is the simplest way to accomplish this?
If you don't want / can't modify your source array, you can easily create another one with same values :
byte[] copyArray;
byteArray.CopyTo(copyArray, 0);
// or
copyArray = byteArray.ToArray() // following #Matthew Watson
Then if you want to change your eigth last values for line 3 :
byte[] ReplaceThirdLineValues(byte[] source, params byte[] newValues)
{
byte[] copyArray;
byteArray.CopyTo(source, 0);
for (int i = 0 ; i < newValues.Length && i < 8 ; i++)
// i < 8 because in your array there are 8 0x00 in a row
if (copyArray[19 + i] == 0x00 && newValues[i] > 0x00)
// newValues[i] > 0x00, so if you do not want to override value,
// just give 0x00 to your parameter
copyArray[19 + i] = newValues[i];
}
For your fourth line, replace copyArray[19 + i] by copyArray[29 + i]
byte[] copyArray = ReplaceThirdLineValues(byteArray, 0x01, 0x02, 0x03, 0x04);

Decrypting data (in C#) encrypted by pgcrypto

I'm trying to decrypt data which was encrypted using pgcrypto. I didn't use an IV as it's only a test, but I can't decrypt the data in C#.
Encrypting in PostGres:
enc_key := '\\xAACE38F289EC3EA209B48D';
-- Time insertions
ts_start := clock_timestamp();
FOR i IN 1..num_loops LOOP
-- The text to insert and its key
plaintext := 'Number: ' || i;
plaintext_pk := gen_random_uuid();
plaintext_pk_as_text := plaintext_pk::text;
-- The ref entries
user_pk := gen_random_uuid();
user_ref_pk := encrypt(plaintext_pk_as_text::bytea, enc_key, 'aes');
-- Add the enries
INSERT INTO "Text" VALUES(plaintext_pk, plaintext);
INSERT INTO "User" VALUES(user_ref_pk, user_pk);
END LOOP;
ts_end := clock_timestamp();
elapsed_raw := cast(extract(epoch from (ts_end - ts_start)) as numeric(18,3));
Decrypting in C#:
// The decryption key
byte[] enc_key = new byte[] { 0xAA, 0xCE, 0x38, 0xF2, 0x89, 0xEC, 0x3E, 0xA2, 0x09, 0xB4, 0x8D,
0x00, 0x00, 0x00, 0x00, 0x00 };
public static string AESDecryptByteArray(byte [] encoded_data, byte [] key)
{
string result = "";
byte [] result_ba = new byte[64];
using (Aes myAes = Aes.Create())
{
if (myAes == null)
{
throw new Exception("Failed to create AES object.");
}
myAes.Key = key;
myAes.Mode = CipherMode.CBC;
myAes.Padding = PaddingMode.PKCS7;
MemoryStream streamMem = new MemoryStream(encoded_data);
byte[] IV = new byte[16];
// streamMem.Read(IV, 0, 16);
for (int i = 0; i < 16; ++i )
{
IV[i] = 0;
}
myAes.IV = IV;
int iNumBytes = 0;
var decryptor = myAes.CreateDecryptor();
using (CryptoStream streamCrypt = new CryptoStream(streamMem, decryptor, CryptoStreamMode.Read))
{
iNumBytes = streamCrypt.Read(result_ba, 0, 48);
}
result = System.Text.Encoding.ASCII.GetString(result_ba);
}
return result;
} // AESDecryptByteArray
I copied the resulting encrypted data from one of the rows, and the binary key, but the C# code keeps blowing with a CryptographicException ("Padding is invalid and cannot be removed") exception. My understanding is that pgcrypto's encrypt() defaults to cbc \ pkcs. Obviously, I'm missing something.
Any help gratefully received.
Adam.
Tried Michael's suggestion and was not getting the right results, of course. Found the issue. PG's string to bytea conversion is not for the unwary. The vital clue came from
DO $$
declare enc_data bytea;
enc_key bytea;
dec_bytea bytea;
dec_text text;
begin
enc_data := '\305\347fyau\030 \223\014E\307\346\267|\365R\3236l\322f\344\312z\220\271\207C\003\255\210+\316\330&\205l>\342\203\350\214$W\253\370D';
enc_key := '\\xAACE38F289EC3EA209B48D';
dec_bytea := decrypt(enc_data, enc_key, 'aes');
dec_text := dec_bytea::text;
raise info 'Decoded text -> %', dec_text;
DROP TABLE IF EXISTS tmpTable;
CREATE TEMPORARY TABLE tmpTable AS
select dec_text as "Decoded text",
char_length(dec_text) as "Decoded length",
length(enc_data) as "Encoded length",
enc_key as "Enc Key",
length(enc_key) as "Enc Key Len",
encode(enc_key, 'hex') as "Hex key",
encode(enc_key, 'escape') as "Esc key";
END $$;
select * from tmpTable;
This showed the binary key in PG was 24 bytes long - not 11 as I expected.
It was down to a misunderstanding on my part of how PG's string to bytea conversion works.
I thought "\\xAACE38F289EC3EA209B48D" would translate into an 11 byte array (https://www.postgresql.org/docs/9.6/static/datatype-binary.html, section 8.4.1) but the doubled backslash is not needed.
So my string translates into '\', 'x', 'A' ... 'D' - a 24 byte array.
//
// In C# this is the key needed
//
byte[] enc_key_aaaahhhh =
new byte[] { 0x5c, 0x78, 0x41, 0x41, 0x43, 0x45, 0x33, 0x38,
0x46, 0x32, 0x38, 0x39, 0x45, 0x43, 0x33, 0x45,
0x41, 0x32, 0x30, 0x39, 0x42, 0x34, 0x38, 0x44 };
//
// This is wrong.
// For this key you'd need to enter '\xAACE38F289EC3EA209B48D' in PG - only one backslash
//
byte[] enc_key = new byte[] { 0xAA, 0xCE, 0x38, 0xF2, 0x89, 0xEC, 0x3E, 0xA2, 0x09, 0xB4, 0x8D,
0x00, 0x00, 0x00, 0x00, 0x00 };
(Didn't help that I copied the wrong GUID into my C# code to compare against - the real GUID was "d6edd775-47c5-4779-a761-7f8297130073".)
Hope this maybe helps someone one day.
Adam.

C# Changing parts of byte array based on user input

I am sending an API frame using the following code:
byte[] bytesToSend5 = new byte[]
{
0x7E, 0x00, 0x10, 0x01, 0x00, 0x13,
0xA2, 0x00, 0x40, 0xA6, 0x5E, 0x23,
0xFF, 0xFE, 0x02, 0x44, 0x37, 0x04, 0x4D
};
serialPort1.Write(bytesToSend5, 0, bytesToSend5.Length);
It's split up like this:
byte[] bytesToSend5 = new byte[]
{
5Startbits(won't change),
8IDbits(changes when a part of the device is swapped),
6determinationbits(tells the device what to do),
1checksumbit(calculated based on previous bits)
};
The first code example works as is desired with the current product. If, for whatever reason, a part of the device needs to be changed, it will not work because the ID bits won't fit. The ID number is printed on the device, 16 digits with numbers and letters, such as "0013A20043A25E86".
What I want to do is make a textbox where the user can input the new ID number and it will be replaced with the appropriate bits in the aforementioned byte array.
Here is my attempt using the Array.Copy function, trying to display the result in a textbox - but no change is detected. I've tried typing "1" "1,2,3" etc as well as the actual ID's "0013A20043A25E86":
string xbee_serienr = prop1_serienr.Text;
byte[] front = { 0x7E, 0x00, 0x10, 0x17, 0x01 };
byte[] back = { 0xFF, 0xFE, 0x02, 0x44, 0x37, 0x04 };
string[] xbee = { xbee_serienr };
byte[] combined = new byte[front.Length + xbee.Length + back.Length];
Array.Copy(front, combined, front.Length);
Array.Copy(back, 0, combined, 5, back.Length);
var result = string.Join(",", combined.Select(x => x.ToString()).ToArray());
OutputWindow.Text = result;
It would have to be possible to change the 8ID bits based on user input, and calculate the last checksum bit based on the rest of the bits.
I have searched the internet and tried Array.copy, Concat etc. but I haven't made any progress with it. Any guidance or input on this would be highly appreciated, even if it means guiding me in a direction of taking a different approach.
EDIT:
I now have the desired information in the byte array "result" using roughly the same example as below (taking user input for the var "xbee_serienr"). I now want to pass this to a method that looks like this:
private void button_D07_Lav_Click(object sender, EventArgs e)
{
byte[] bytesToSend5 = new byte[] { 0x7E, 0x00, 0x10, 0x01, 0x00, 0x13, 0xA2, 0x00, 0x40, 0xA6, 0x5E, 0x23, 0xFF, 0xFE, 0x02, 0x44, 0x37, 0x04, 0x4D };
serialPort1.Write(bytesToSend5, 0, bytesToSend5.Length);
And make the "bytesToSend5" use the array "result" from the other method.
I've tried using this example, like so:
byte result { get; set; } //above and outside of the two methods
var result = string.Join(string.Empty, combined.Select(x => x.ToString("X2")).ToArray()); //this is the end of the first method
private void button_D07_Lav_Click(object sender, EventArgs e)
{
byte[] bytesToSend5 = new byte[] { 0x7E, 0x00, 0x10, 0x01, 0x00, 0x13, 0xA2, 0x00, 0x40, 0xA6, 0x5E, 0x23, 0xFF, 0xFE, 0x02, 0x44, 0x37, 0x04, 0x4D };
bytesToSend5 = result; //using the array stored in result instead of the one currently in bytesToSend5.
serialPort1.Write(bytesToSend5, 0, bytesToSend5.Length);
}
I realize the obvious problem here, that it's not on the same form. This is why I wanted to split the array and add 0x in front of every item in the array, and separate them with commas.
I'm also going to use this for several different devices once I figure it out properly, which makes me fear there will be a lot of duplicated code, but I suspect once I'm understanding how to pass and use the array in a different method, I can always "duplicate" the code for every device, since the ID will indeed need to be different for the different devices.
Well, you never add the parsed string.
var xbee_serienr = "0013A20043A25E86";
byte[] front = { 0x7E, 0x00, 0x10, 0x17, 0x01 };
byte[] back = { 0xFF, 0xFE, 0x02, 0x44, 0x37, 0x04 };
var xbee = new byte[xbee_serienr.Length / 2];
for (var i = 0; i < xbee.Length; i++)
{
xbee[i] = byte.Parse(xbee_serienr.Substring(i * 2, 2), NumberStyles.HexNumber);
}
byte[] combined;
using (var ms = new MemoryStream(front.Length + xbee.Length + back.Length))
{
ms.Write(front, 0, front.Length);
ms.Write(xbee, 0, xbee.Length);
ms.Write(back, 0, back.Length);
combined = ms.ToArray();
}
var result = string.Join(string.Empty, combined.Select(x => x.ToString("X2")).ToArray());
Since you're adding multiple arrays one after another, I just used a MemoryStream. If you already have the byte[] ready (and mutable), you can write directly to that byte array and avoid allocating (and collecting) the extra array, but it doesn't make much of a difference when the limiting factor is the UI anyway.

BouncyCastel Cast5 setkey

using System;
using System.Linq;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Utilities.Encoders;
namespace Common.Encryption {
public class Cast5Cryptographer {
private bool forEncryption;
private BufferedBlockCipher cipher;
public Cast5Cryptographer(bool forEncryption) {
this.forEncryption = forEncryption;
cipher = new BufferedBlockCipher(new CfbBlockCipher(new Cast5Engine(), 64));
cipher.Init(forEncryption, new ParametersWithIV(new KeyParameter(Encoding.ASCII.GetBytes("BC234xs45nme7HU9")), new byte[8]));
}
public void ReInit(byte[] IV, BigInteger pubkey) {
cipher.Init(forEncryption, new ParametersWithIV(new KeyParameter(pubkey.ToByteArrayUnsigned()), IV));
}
public int BlockSize {
get {
return cipher.GetBlockSize();
}
}
public byte[] DoFinal() {
return cipher.DoFinal();
}
public byte[] DoFinal(byte[] buffer) {
return cipher.DoFinal(buffer);
}
public byte[] DoFinal(byte[] buffer, int startIndex, int len) {
return cipher.DoFinal(buffer, startIndex, len);
}
public byte[] ProcessBytes(byte[] buffer) {
return cipher.ProcessBytes(buffer);
}
public byte[] ProcessBytes(byte[] buffer, int startIndex, int len) {
return cipher.ProcessBytes(buffer, startIndex, len);
}
}
}
it's working fine with the key above which it's 16 length , but when am trying to ReInit() it with this key
byte[] newkey = new byte[] { 0x39, 0x65, 0x38, 0x63, 0x64, 0x32, 0x36, 0x63, 0x37, 0x37, 0x34, 0x31, 0x33, 0x65, 0x61, 0x36, 0x65, 0x35, 0x35, 0x39, 0x61, 0x32, 0x35, 0x32, 0x66, 0x30, 0x31, 0x35, 0x32, 0x38, 0x66, 0x39, 0x34, 0x38, 0x66, 0x33, 0x33, 0x34, 0x32, 0x62, 0x31, 0x38, 0x37, 0x36, 0x34, 0x61, 0x66, 0x35, 0x36, 0x38, 0x62, 0x39, 0x63, 0x39, 0x30, 0x33, 0x63, 0x35, 0x38, 0x38, 0x35, 0x34, 0x65, 0x63 };
it throw this exception Index was outside the bounds of the array.
for (int i = 0; i < key.Length; i++) {
x[i] = (int)(key[i] & 0xff);
}
inside the SetKey method in Cast5Engine.cs , so I updated this method so instead of having fixed length to x which is 16 , I made it
int[] x = new int[key.Length];
for (int i = 0; i < 16; i++) x[i] = 0;
/* copy the key into x */
for (int i = 0; i < key.Length; i++) {
x[i] = (int)(key[i] & 0xff);
}`
but now by comparing the result which am getting from Cast5 of BouncyCastel to the Cast5 of OpenSSL , it seems like Bouncycastel Cast5 is not being updated with the right key, so it produce wrong Encryption/Decryption.
is there any suggestion to fix the Setkey method?
From looking at the source code for OpenSSL's CAST_setKey method...
void CAST_set_key(CAST_KEY *key, int len, const unsigned char *data)
#ifdef OPENSSL_FIPS
{
fips_cipher_abort(CAST);
private_CAST_set_key(key, len, data);
}
void private_CAST_set_key(CAST_KEY *key, int len, const unsigned char *data)
#endif
{
CAST_LONG x[16];
CAST_LONG z[16];
CAST_LONG k[32];
CAST_LONG X[4],Z[4];
CAST_LONG l,*K;
int i;
for (i=0; i<16; i++) x[i]=0;
if (len > 16) len=16;
See the line "if (len > 16) len = 16;", they only keep the first 16 bytes of the key. This wouldn't happen to be for Conquer Online 2.0 would it?, I recognise "BC234xs45nme7HU9".

can I get ProcessBytes to return an exact amount?

am using BouncyCastel to make a CfbBlockCipher so here is the codes.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
namespace Common.Encryption
{
public class BlowfishCryptographer
{
private bool forEncryption;
private IBufferedCipher cipher;
public BlowfishCryptographer(bool forEncryption)
{
this.forEncryption = forEncryption;
cipher = new BufferedBlockCipher(new CfbBlockCipher(new BlowfishEngine(), 64));
cipher.Init(forEncryption, new ParametersWithIV(new KeyParameter(Encoding.ASCII.GetBytes("DR654dt34trg4UI6")), new byte[8]));
}
public void ReInit(byte[] IV,BigInteger pubkey)
{
cipher.Init(forEncryption, new ParametersWithIV(new KeyParameter(pubkey.ToByteArrayUnsigned()),IV));
}
public byte[] DoFinal()
{
return cipher.DoFinal();
}
public byte[] DoFinal(byte[] buffer)
{
return cipher.DoFinal(buffer);
}
public byte[] DoFinal(byte[] buffer, int startIndex, int len)
{
return cipher.DoFinal(buffer, startIndex, len);
}
public byte[] ProcessBytes(byte[] buffer)
{
return cipher.ProcessBytes(buffer);
}
public byte[] ProcessBytes(byte[] buffer, int startIndex, int len)
{
return cipher.ProcessBytes(buffer, startIndex, len);
}
public void Reset()
{
cipher.Reset();
}
}
}
so...
byte[] buf = new byte[] { 0x83, 0x00, 0xEE, 0x03, 0x26, 0x6D, 0x14, 0x00, 0xF1, 0x65, 0x27, 0x00, 0x19, 0x02, 0xD8, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, 0xD7, 0x0F, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
if i said ProcessBytes(buf, 0, 17) it will only return 16, i also tried DoFinal() but it's not doing it's job!!!
is that up to IBufferedCipher should i use IStreamCipher or something else to get the exact amount of what am dec/enc-ing? And i believe CfbBlockCipher is broken somehow or am doing something worng here.
I don't know what you consider not doing the job here. You need to call ProcessBytes multiple times and finish with DoFinal(). It's normal that ProcessBytes() only returns 16 bytes because that is x times the block size. The cipher does not know if you've finished feeding it bytes, so it cannot calculate another block until you call DoFinal(). Of course, you need to append the output of the ProcessBytes() and DoFinal() calls to get the end result...

Categories

Resources