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".
Related
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);
I tried to connect and authenticate app with Mi Band 2, but on the last step (sending encryption key and receive successful authentication response) i receive error response.
First, Second and Third steps are successfully, no exceptions.
Here is all code of Authentication.
Main Auth Method with authentication level 1 (notification to band with needed touch response)
Checking new updates of authCharacteristic and waiting new response from band. There is handle 2, 3 and 4 levels of Authentication.
Encryption to AES/ECB/NoPadding encryption (i also tried AesCbc, but i have same result).
public async Task<bool> Authenticate()
{
var authCharacteristic = await Gatt.GetCharacteristicByServiceUuid(new Guid("0000FEE1-0000-1000-8000-00805F9B34FB"), new Guid("00000009-0000-3512-2118-0009af100700"));
// Subscribe to notifications
await authCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
// Level 1
Debug.WriteLine("Level 1 started");
byte[] secretKey = new byte[] { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45 };
List<byte> sendKey = new List<byte>();
sendKey.Add(1);
sendKey.Add(8);
sendKey.AddRange(secretKey);
if (await authCharacteristic.WriteValueAsync(sendKey.ToArray().AsBuffer()) == GattCommunicationStatus.Success)
{
Debug.WriteLine("Level 1 success");
authCharacteristic.ValueChanged += authCharacteristic_ValueChanged;
}
return isAuthed;
}
private async void authCharacteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
{
var authCharacteristic = await Gatt.GetCharacteristicByServiceUuid(new Guid("0000FEE1-0000-1000-8000-00805F9B34FB"), new Guid("00000009-0000-3512-2118-0009af100700"));
if (sender.Uuid.ToString() == "00000009-0000-3512-2118-0009af100700")
{
Debug.WriteLine("Received characteristic value: " + args.CharacteristicValue.ToArray().ToList()[0]);
Debug.WriteLine("Received SendKey: " + args.CharacteristicValue.ToArray().ToList()[1]);
Debug.WriteLine("Received Status: " + args.CharacteristicValue.ToArray().ToList()[2]);
var request = args.CharacteristicValue.ToArray().ToList();
byte authResponse = 0x10;
byte authSendKey = 0x01;
byte authRequestRandomAuthNumber = 0x02;
byte authRequestEncryptedKey = 0x03;
byte authSuccess = 0x01;
byte authFail = 0x04;
if (request[0] == authResponse && request[1] == authSendKey && request[2] == authSuccess)
{
Debug.WriteLine("Level 2 started");
List<byte> authNumber = new List<byte>();
authNumber.Add(0x02);
authNumber.Add(0x08);
if (await authCharacteristic.WriteValueAsync(authNumber.ToArray().AsBuffer()) == GattCommunicationStatus.Success)
Debug.WriteLine("Level 2 success");
}
else if (request[0] == authResponse && request[1] == authRequestRandomAuthNumber && request[2] == authSuccess)
{
Debug.WriteLine("Level 3 started");
List<byte> randomKey = new List<byte>();
List<byte> relevantResponsePart = new List<byte>();
var responseValue = args.CharacteristicValue.ToArray();
for (int i = 0; i < responseValue.Count(); i++)
{
if (i >= 3)
relevantResponsePart.Add(responseValue[i]);
}
randomKey.Add(0x03);
randomKey.Add(0x08);
randomKey.AddRange(Encrypt(relevantResponsePart.ToArray()));
if (await authCharacteristic.WriteValueAsync(randomKey.ToArray().AsBuffer()) == GattCommunicationStatus.Success)
Debug.WriteLine("Level 3 success");
}
else if (request[0] == authResponse && request[1] == authRequestEncryptedKey && request[2] == authSuccess)
{
// Can't reach this code. Last byte is 4 (error).
Debug.WriteLine("Auth completed");
isAuthed = true;
}
}
}
public byte[] Encrypt(byte[] data)
{
byte[] secretKey = new byte[] { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45 };
string aesKey = Convert.ToBase64String(secretKey);
IBuffer key = Convert.FromBase64String(aesKey).AsBuffer();
SymmetricKeyAlgorithmProvider algorithmProvider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesEcb);
CryptographicKey ckey = algorithmProvider.CreateSymmetricKey(key);
IBuffer buffEncrypt = CryptographicEngine.Encrypt(ckey, data.AsBuffer(), null);
return buffEncrypt.ToArray();
}
All debug messages in console here:
Connected to MI Band 2
Level 1 started
Level 1 success
Received characteristic value: 16
Received SendKey: 1
Received Status: 1
Level 2 started
Level 2 success
Received characteristic value: 16
Received SendKey: 2
Received Status: 1
Level 3 started
Level 3 success
Received characteristic value: 16
Received SendKey: 3
Received Status: 4
Problem was solved, when i change strings in Encryption()
byte[] secretKey = new byte[] { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45 };
IBuffer key = secretKey.AsBuffer();
I think my problem was in wrong Encryption code.
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.
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
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...