Compare AES encryption OpenSSL versus .NET(RijndaelManaged) - c#

I need to encrypt a URL. According the specs of the URL it should be (Rijndael) encoded using CBC encryption mode, using an IV of 0., PKCS7 padding and a key length of 128-bit.
The decryption of the URL is done in a .NET environment using the RijndaelManaged class. I encrypt using OpenSSL 1.0.2a (C++ unmanaged) and use the following code (from the internet):
// ctx holds the state of the encryption algorithm so that it doesn't
// reset back to its initial state while encrypting more than 1 block.
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
unsigned char key[] = {0x41, 0x41, 0x45, 0x43, 0x41, 0x77, 0x51, 0x46,
0x43, 0x67, 0x63, 0x49, 0x43, 0x5A, 0x6F, 0x4C };
unsigned char iv[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
assert(sizeof(key) == 16); // AES128 key size
assert(sizeof(iv) == 16); // IV is always the AES block size
// If data isn't a multiple of 16, the default behavior is to pad with
// n bytes of value n, where n is the number of padding bytes required
// to make data a multiple of the block size. This is PKCS7 padding.
// The output then will be a multiple of the block size.
std::string plain("someId=007&accountNo=119955244351&user=admin&");
std::vector<unsigned char> encrypted;
size_t max_output_len = plain.length() + 16 - (plain.length() % 16);
encrypted.resize(max_output_len);
// Enc is 1 to encrypt, 0 to decrypt, or -1 (see documentation).
EVP_CipherInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv, 1);
// EVP_CipherUpdate can encrypt all your data at once, or you can do
// small chunks at a time.
int actual_size = 0;
if( !EVP_CipherUpdate(&ctx,
&encrypted[0], &actual_size,
reinterpret_cast<unsigned char *>(&plain[0]), plain.size()))
{
EVP_CIPHER_CTX_cleanup(&ctx);
}
// EVP_CipherFinal_ex is what applies the padding. If your data is
// a multiple of the block size, you'll get an extra AES block filled
// with nothing but padding.
int final_size;
EVP_CipherFinal_ex(&ctx, &encrypted[actual_size], &final_size);
actual_size += final_size;
encrypted.resize(actual_size);
for( size_t index = 0; index < encrypted.size(); ++index )
{
std::cout << std::hex << std::setw(2) << std::setfill('0') <<
static_cast<unsigned int>(encrypted[index]);
}
std::cout << "\n";
EVP_CIPHER_CTX_cleanup(&ctx);
AESWrapper aesWrapper;
std::string encryptedbase64(aesWrapper.base64encode( &encrypted[0], encrypted.size()));
I've checked (obviously) the key, the iv and the algorithm aes-cbc-128 and afaik OpenSSL uses PKCS7 padding by default, but the result doesn't match!
Striking is that the padding doesn't seem to happen in EVP_CipherFinal_ex.The padding should consist of the required number of bytes to get the correct blocksize, filling each byte with that number. But it appears to be filled with random data (or maybe more encryption?)
Is there an issue with this code, that can explain why the encryption is not 'correct'?
Should I focus on the incorrect padding and how to debug in that case?
Any pointers?

Nevermind. I got it working. The C# application that decrypts the URL uses a SHA1 hash on the key. After I hashed my key before encrypting the URL it started working.

Related

AES Encryption with EasyCrypto C# and Decryption in C++ using Crypto++

I have encrypted a string using EasyCrypto in C# using the following code
Encryption C#:
/*
EasyCrypto encrypted key format from CryptoContainer.cs file from the EasyCrypto source on GitHub.
* Format:
* 04 bytes 00 - MagicNumber
* 02 bytes 04 - DataVersionNumber
* 02 bytes 06 - MinCompatibleDataVersionNumber
* 16 bytes 08 - IV
* 32 bytes 24 - Salt
* 19 bytes 56 - Key check value
* 48 bytes 75 - MAC
* 04 bytes 123 - Additional header data length
* xx bytes 127 - Additional data
* ----- end of header ----- (sum: 127)
* xx bytes - additional header data (0 for version 1)
* xx bytes - data
*/
AesEncryption.EncryptWithPassword("data to encrypt", "password string");
/*
Method Description:
Encrypts string and returns string. Salt and IV will be embedded to encrypted string. Can later be decrypted with
EasyCrypto.AesEncryption.DecryptWithPassword(System.String,System.String,EasyCrypto.ReportAndCancellationToken)
IV and salt are generated by EasyCrypto.CryptoRandom which is using System.Security.Cryptography.Rfc2898DeriveBytes.
IV size is 16 bytes (128 bits) and key size will be 32 bytes (256 bits).
/*
I am trying to decrypt in C++ using Crypto++, using the following code. I am just getting the error "ciphertext length is not a multiple of block size", what is the missing part in the code? any help would be highly appreciable.
Decryption C++:
string Decrypt() {
// getting CryptoPP::byte array from passowrd
string destination;
CryptoPP::StringSource ss(<hex of password string>, true, new CryptoPP::HexDecoder(new CryptoPP::StringSink(destination)));
CryptoPP::byte* keyByteArray = (CryptoPP::byte*)destination.data();
// getting CryptoPP::byte array from encoded data
string pkDst;
CryptoPP::StringSource ss2(<hex of encoded data>, true, new CryptoPP::HexDecoder(new CryptoPP::StringSink(pkDst)));
CryptoPP::byte* pkByteArray = (CryptoPP::byte*)pkDst.data();
// getting initialization vector from encoded data
CryptoPP::byte iv[16];
for (int i = 8; i < 24; i++) {
iv[i] = pkByteArray[i];
}
string result = CBCMode_Decrypt(keyByteArray, 32, iv);
return result;
}
string CBCMode_Decrypt(CryptoPP::byte key[], int keySize, CryptoPP::byte iv[]) {
string recovered = "";
//Decryption
try
{
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption d;
d.SetKeyWithIV(key, keySize, iv);
// The StreamTransformationFilter removes
// padding as required.
CryptoPP::StringSource s("encoded string", true, new CryptoPP::StreamTransformationFilter(d, new CryptoPP::StringSink(recovered))); // StringSource
}
catch (const CryptoPP::Exception& e)
{
cerr << e.what() << endl;
exit(1);
}
return recovered;
}
In the Crypto++ code, the following steps must be performed for decryption:
Base64 decoding of the EasyCrypto data
Separating IV, salt and ciphertext (using the information from the CryptoContainer.cs file)
Deriving the 32 bytes key via PBKDF2 using salt and password (digest: SHA-1, iteration count: 25000)
Decryption with AES-256 in CBC mode and PKCS#7 padding (using key and IV)
A possible Crypto++ implementation is:
#include "aes.h"
#include "modes.h"
#include "pwdbased.h"
#include "sha.h"
#include "base64.h"
using namespace CryptoPP;
using namespace std;
...
// Base64 decode data from EasyCrypto
string encoded = "bqCrDAQABABtXsh2DxqYdpZc6M6+kGALOsKUHzxoMR6WAVg5Qtj3zWbr4MiEBdqt9nPIiIZAynFAZmweHQPa/PhEItR6M8Jg1bHAYeQ8Cm5eUlKNzPXFNfuUw0+qtds29S0L4wAWY0xfuiBJTUeTJuSLWqoirm/rHGOWAAAAAKtBivUDvxta1d0QXE6J9x5VdSpAw2LIlXARKzmz+JRDtJcaj4KmGmXW/1GjZlMiUA==";
string decoded;
StringSource ssB64(
encoded,
true,
new Base64Decoder(
new StringSink(decoded)
)
);
// Separate IV, salt and ciphertext
string ivStr = decoded.substr(8, 16);
string saltStr = decoded.substr(24, 32);
string ciphertextStr = decoded.substr(127);
// Derive 32 bytes key using PBKDF2
char password[] = "my passphrase";
unsigned int iterations = 25000;
byte key[32];
size_t keyLen = sizeof(key);
PKCS5_PBKDF2_HMAC<SHA1> pbkdf;
pbkdf.DeriveKey(key, keyLen, 0, (byte*)password, sizeof(password), (byte*)saltStr.c_str(), saltStr.length(), iterations, 0.0f);
// Decrypt with AES-256, CBC, PKCS#7 padding
string decrypted;
CBC_Mode<AES>::Decryption decryption(key, keyLen, (byte*)ivStr.c_str());
StringSource ssDec(
ciphertextStr,
true,
new StreamTransformationFilter(
decryption,
new StringSink(decrypted),
BlockPaddingSchemeDef::BlockPaddingScheme::PKCS_PADDING
)
);
// Output
cout << "Decrypted: " << decrypted << "\n";
with the output:
Decrypted: The quick brown fox jumps over the lazy dog
The ciphertext was generated with EasyCrypto:
AesEncryption.EncryptWithPassword("The quick brown fox jumps over the lazy dog", "my passphrase");
The previous section focused on decryption. Note, however, that for security reasons, authentication is required before decryption and decryption may only be performed on successfully authenticated data.
For authentication also the MAC must be determined in addition to IV, salt and ciphertext. EasyCrypto applies an HMAC-SHA-384 as MAC. Only the ciphertext is used to determine the MAC, and the key for authentication is the same as the key for encryption.
For authentication, the calculated and the sent MAC must be compared. If both are the same, the authentication is successful (and the decryption can be performed).
A possible Crypto++ implementation for the authentication is:
// Get the sent MAC
string macSentStr = decoded.substr(75, 48);
// Calculate the MAC using ciphertext and encryption key
string macCalcStr;
HMAC<SHA384> hmac(key, keyLen);
StringSource ssMac(
ciphertextStr,
true,
new HashFilter(hmac,
new StringSink(macCalcStr)
)
);
// Compare both MACs
cout << (!macSentStr.compare(macCalcStr) ? "Authentication successful" : "Authentication failed") << endl; // compare returns 0 if both strings match
which successfully authenticates the sample data.

Need PHP 8 version of C#/.NET encryption code

An encryption C# code that has been in use for many years now needs to be converted to PHP 8.
I came close, and there's one remaining issue as described below:
For example, the secret below is longer than 71 characters and it is not encrypted correctly:
secret = "id=jsmith12&timestamp=2022-07-06t11:10:43&expiration=2022-07-06t11:15:43"; //71 chars-long
However, these secrets will be encrypted correctly, since they are less than 71 chars long:
secret = "id=jsmith&timestamp=2022-07-06t11:10:43&expiration=2022-07-06t11:15:43"; // 69 chars-long
secret = "id=jsmith1&timestamp=2022-07-06t11:10:43&expiration=2022-07-06t11:15:43"; // 70 chars-long
There is an online page where you can test if the generated token is correct: https://www.mybudgetpak.com/SSOTest/
You can evaluate the token by providing the generated token, the key, and the encryption method (Rijndael or Triple DES).
If the evaluation (decryption of the token) is successful, the test page will diplay the id, timestamp and expiration values
used in the secret.
C# Code:
The secret, a concatenated query string values, what needs to be encrypted:
string secret = "id=jsmith123&timestamp=2022-07-06t11:10:43&expiration=2022-07-06t11:15:43";
The key:
string key = "C000000000000000"; //16 character-long
ASCII encoded secret and key converted to byte array:
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
byte[] encodedSecret = encoding.GetBytes(secret);
byte[] encodedKey = encoding.GetBytes(key);
Option 1: Rijndael
// Call the generate token method:
string token = GenerateRijndaelSecureToken(encodedSecret, encodedKey);
private string GenerateRijndaelSecureToken(byte[] encodedSecret, byte[] encodedKey)
{
Rijndael rijndael = Rijndael.Create();
// the encodedKey must be a valid length so we pad it until it is (it checks // number of bits)
while (encodedKey.Length * 8 < rijndael.KeySize)
{
byte[] tmp = new byte[encodedKey.Length + 1];
encodedKey.CopyTo(tmp, 0);
tmp[tmp.Length - 1] = (byte)'\0';
encodedKey = tmp;
}
rijndael.Key = encodedKey;
rijndael.Mode = CipherMode.ECB;
rijndael.Padding = PaddingMode.Zeros;
ICryptoTransform ict = rijndael.CreateEncryptor();
byte[] result = ict.TransformFinalBlock(encodedSecret, 0, encodedSecret.Length);
// convert the encodedSecret to a Base64 string to return
return Convert.ToBase64String(result);
}
Option 2: Triple DES
// Call the generate token method:
string token = GenerateSecureTripleDesToken(encodedSecret, encodedKey);
private string generateSecureTripleDesToken(byte[] encodedSecret, byte[] encodedKey)
{
// Generate the secure token (this implementation uses 3DES)
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
// the encodedKey must be a valid length so we pad it until it is (it checks // number of bits)
while (encodedKey.Length * 8 < tdes.KeySize)
{
byte[] tmp = new byte[encodedKey.Length + 1];
encodedKey.CopyTo(tmp, 0);
tmp[tmp.Length - 1] = (byte) '\0';
encodedKey = tmp;
}
tdes.Key = encodedKey;
tdes.Mode = CipherMode.ECB;
tdes.Padding = PaddingMode.Zeros;
ICryptoTransform ict = tdes.CreateEncryptor();
byte[] result = ict.TransformFinalBlock(encodedSecret, 0, encodedSecret.Length);
// convert the encodedSecret to a Base64 string to return
return Convert.ToBase64String(result);
}
PHP 8 code:
public $cipher_method = "AES-256-ECB";
// Will not work:
//$secret = "id=jsmith12&timestamp=2022-07-06t11:10:43&expiration=2022-07-06t11:15:43";
// Will work:
//$secret = "id=jsmith&timestamp=2022-07-06t11:10:43&expiration=2022-07-06t11:15:43";
$key = "C000000000000000";
$token = openssl_encrypt($secret, $cipher_method, $key);
There are two things to be aware of:
The C# code pads the key with 0x00 values to the required length, i.e. 256 bits for AES-256 and 192 bits for 3DES. Since PHP/OpenSSL automatically pads keys that are too short with 0x00 values, this does not need to be implemented explicitly in the PHP code (although it would be more transparent).
The C# code uses Zero padding. PHP/OpenSSL on the other hand applies PKCS#7 padding. Since PHP/OpenSSL does not support Zero padding, the default PKCS#7 padding must be disabled with OPENSSL_ZERO_PADDING (note: this does not enable Zero padding, the name of the flag is poorly chosen) and Zero padding must be explicitly implemented, e.g. with:
function zeropad($data, $bs) {
$length = ($bs - strlen($data) % $bs) % $bs;
return $data . str_repeat("\0", $length);
}
Here $bs is the block size (16 bytes for AES and 8 bytes for DES/3DES).
Further changes are not necessary! A possible implementation is:
$cipher_method = "aes-256-ecb"; // for AES (32 bytes key)
//$cipher_method = "des-ede3"; // for 3DES (24 bytes key)
// Zero pad plaintext (explicitly)
$bs = 16; // for AES
//$bs = 8; // for 3DES
$secret = zeropad($secret, $bs);
// Zero pad key (implicitly)
$key = "C000000000000000";
$token = openssl_encrypt($secret, $cipher_method, $key, OPENSSL_ZERO_PADDING); // disable PKCS#7 default padding, Base64 encode (implicitly)
print($token . PHP_EOL);
The ciphertexts generated in this way can be decrypted using the linked website (regardless of their length).
The wrong padding causes decryption to fail on the web site (at least to not succeed reliably). However, the logic is not correct that decryption fails only if the plaintext is larger than 71 bytes (even if only the range between 65 and 79 bytes is considered). For example, decryption fails also with 66 bytes. The page source provides a bit more information than the GUI:
Could not read \u0027expiration\u0027 as a date: 2022-07-06t11:15:43\u000e\u000e\u000e\u000e\u000e\u000e\u000e\u000e\u000e\u000e\u000e\u000e\u000e\u000e
The problem is (as expected) the PKCS#7 padding bytes at the end: 14 0x0e values for 66 bytes.
Why decryption works for some padding bytes and not for others can only be reliably answered if the decryption logic of the web site were known. In the end, however, the exact reason doesn't matter.
Note that the applied key expansion is insecure. Also, ECB is insecure, 3DES is outdated, and Zero padding is unreliable.

trying to implement Speck32/64 block Cipher

I am trying to implement Speck 32/64 block Cipher in c# I'm stuck at encryption decryption algorithm. i know that i should split the plain text in to 2 word according to algorithm
x,y = plaintext words
--------------------------- key expansion --------------------------
for i = 0..T-2
[i+m-1] ← (k[i] + S−α
[i]) ⊕ i
k[i+1] ← S
β k[i] ⊕ `[i+m-1]
end for
---------------------------- encryption ----------------------------
for i = 0..T-1
x ← (S−α x + y) ⊕ k[i]
y ← S
βy ⊕ x
end for
References
The SIMON and SPECK Families of Lightweight Block Ciphers
https://eprint.iacr.org/2013/404
my question is the plaintext should be string then i convert to binary or what and use it in the above algo?
the algorithm didnot say the type of plaintext and there is example encryption
Key: 1918 1110 0908 0100
Plaintext: 6574 694c
Ciphertext: a868 42f2
SPECK 32/64 cipher expects 4 bytes as the input.
Plaintext: 6574 694c
means
byte[] plaintext = new byte[] {0x65, 0x74, 0x69, 0x4C};
where each byte is specified as hexadecimal value using the 0x prefix.
You will divide the plaintext in the first step:
byte[] x = new byte[] {plaintext[0], plaintext[1]};
byte[] y = new byte[] {plaintext[2], plaintext[3]};
Note: use some more clever array manipulation to speed up your cipher, the example above is for educational purposes only.
Note 2: handling input as a uint might be a good approach, it could be much faster than arrays with a little of bitwise magic:
uint plaintext = 0x6574694C;
ushort x = (ushort) (plaintext >> 16);
ushort y = (ushort) plaintext;

Separating key and IV in AES encryption in objective-c

I'm trying to convert my AES-encryption class from c# to objective-c(I'm quite newbie to objective-c) and a bit confused about one thing. I'm creating my key and initialization vector objects from a single transferred object like this in c#:
Mode = CipherMode.CBC;
Padding = PaddingMode.PKCS7;
KeySize = 256;
Key = Encoding.UTF8.GetBytes(transferredObject.Substring(x1, x2));
IV = Encoding.ASCII.GetBytes(transferredObject.Substring(y1, y2));
Where x & y are int values, that represent certain range and transferredObject is String. Add then I can use Key & IV for encryption/decryption process.
I know, that in objective-c I should use this method:
CCCryptorStatus cryptStatus = CCCrypt( kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
key, kCCKeySizeAES256, iv,
[self bytes], dataLength,
buffer, bufferSize,
&numBytesEncrypted);
for encryption and similar method for decryption, but I can't find a way, how to get my key & iv objects. The examples for objective-c usually show something like:
[key getCString:keyPtr maxLength:sizeof( keyPtr ) encoding:NSUTF8StringEncoding];
So, how can I get my key & IV if I have the same single NSString * transferredObject object in objective-c?
The solution in accepted answer will work.
Another solution is to use NSString substringToIndex method.
Will look like this:
NSString* IV = [key substringWithRange:NSMakeRange(x1, x2)];
NSString* Key = [key substringWithRange:NSMakeRange(y1, y2)];
char ivPtr[kCCKeySizeAES128 + 1];
bzero(ivPtr, sizeof(ivPtr));
[IV getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
bzero( keyPtr, sizeof( keyPtr ) ); // fill with zeroes (for padding)
[Key getCString:keyPtr maxLength:sizeof( keyPtr ) encoding:NSWindowsCP1251StringEncoding];
CCCryptorStatus cryptStatus = CCCrypt( kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
ivPtr,
[self bytes], dataLength,
buffer, bufferSize,
&numBytesDecrypted );
You can use:
- (BOOL)getBytes:(void *)buffer maxLength:(NSUInteger)maxBufferCount usedLength:(NSUInteger *)usedBufferCount encoding:(NSStringEncoding)encoding options:(NSStringEncodingConversionOptions)options range:(NSRange)range remainingRange:(NSRangePointer)leftover
Example:
int keyLength = kCCKeySizeAES256, x1=0, x2=keyLength;
int ivLength = kCCBlockSizeAES128, y1=keyLength, y2=ivLength;
u_int8_t key[keyLength*2];
u_int8_t iv[ivLength*2];
BOOL keySuccess = [transferredObject getBytes:key maxLength:keyLength usedLength:NULL encoding:NSASCIIStringEncoding options:0 range:NSMakeRange(x1, x2) remainingRange:NULL];
BOOL ivSuccess = [transferredObject getBytes:iv maxLength: ivLength usedLength:NULL encoding:NSASCIIStringEncoding options:0 range:NSMakeRange(y1, y2) remainingRange:NULL];
buffer:
A buffer into which to store the bytes from the receiver. The returned bytes are not NULL-terminated.
maxBufferCount:
The maximum number of bytes to write to buffer.
usedBufferCount:
The number of bytes used from buffer. Pass NULL if you do not need this value.
encoding:
The encoding to use for the returned bytes.
options:
A mask to specify options to use for converting the receiver’s contents to encoding (if conversion is necessary). You can pass NULL.
range:
The range of characters in the receiver to get.
leftover:
The remaining range. Pass NULL If you do not need this value.

Two-key triple DES encryption solution needed

Is this possible in C#? How would I accomplish this?
Two-key triple DES is where we encrypt with K1, then decrypt with K2 and finally encrypt again with K1. The keyspace is thus 2 x 56 = 112 bits.
For example, with K1=0x0123456789ABCDEF and K2=0xFEDCBA9876543210 you would set the triple DES key to be 0x0123456789ABCDEFFEDCBA98765432100123456789ABCDEF.
0123456789ABCDEF FEDCBA9876543210 0123456789ABCDEF
|<------K1------>|<------K2------>|<------K3------>|
It accepts A9993E364706816A and the 2 keys that it must use is K1 = 0123456789ABCDEF and K2 = FEDCBA9876543210. The end result must be: 6E5271A3F3F5C418 which I am not getting.
UPDATE:
I am trying to create the concatenated key that I need to use. The 2 keys used above is converted to a byte array and seems to have a length of 16 each. And when the 2 are concatenated then the length is 32. Then my code bombs out. The key has to have a length of 16 or 24. What do I need to do in this case?
UTF8Encoding characterEncoding = new UTF8Encoding();
byte[] accessKey1ByteArray = characterEncoding.GetBytes(accessKey1);
byte[] accessKey2ByteArray = characterEncoding.GetBytes(accessKey2);
byte[] accessKeysArray = accessKey1ByteArray.Concat(accessKey2ByteArray).ToArray();
Here is where I try to set my values:
public byte[] ComputeTripleDesEncryption(byte[] plainText, byte[] key)
{
TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
des.Key = key;
des.GenerateIV();
des.Mode = CipherMode.ECB;
des.Padding = PaddingMode.None;
ICryptoTransform ic = des.CreateEncryptor();
byte[] enc = ic.TransformFinalBlock(plainText, 0, plainText.Length);
return enc;
}
UPDATE 2
Do I need to set the size? The byte array key that I am sending through is K1 + K2 + K1.
The text that I am sending through, do I need convert this to bytes like what you recommended, or can the following also do the trick?
UTF8Encoding characterEncoding = new UTF8Encoding();
byte[] block1ByteArray = characterEncoding.GetBytes(block1);
The value of block1 is: A9993E364706816A.
How I got A9993E364706816A was from my SHA-1 hashed result. The first 16 characters of this hashed result of my string that I want to encode.
This sounds like you just want to set a 128 bit key for the triple des key.
I believe in this case if you provide a 128 bit key it splits it into two 64 bit keys and uses the first as K1 and K3 and the second as K2 which is exactly what you want.
Unfortunately I can't find a source to quote on this but I did a lot of reading on the subject recently when implementing some crypto stuff myself and finding out about key lengths and this is what I discovered.
If you have K1 and K2 as byte arrays already then you should be able to just use a nice little linq extension method and do:
SymmetricAlgorithm cryptoService = new TripleDESCryptoServiceProvider();
byte[] myKey = K1.Concat(K2).ToArray();
cryptoService.Key = mKey;
That will then do as you want.
In response to your updated part of the question the two keys you have are hexdecimal representations of a sequence of bytes. 0x0123456789ABCDEF is 16 characters of hexdecimal but this is equivalent to 8 bytes of information since it is 4 bits in each character - two making up a byte.
To convert that string to a byte array the following function can be used:
public static byte[] StringToByteArray(String hex)
{
if (hex.Substring(0,2)=="0x")
hex = hex.Substring(2);
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
(From How do you convert Byte Array to Hexadecimal String, and vice versa?)
This will then be used like this:
string K1="0x0123456789ABCDEF";
string K2="0xFEDCBA9876543210";
byte[] key = StringToByteArray(K1).Concat(StringToByteArray(K2)).ToArray();
When implementing TDES you will need to agree a key, Block Cipher mode, Padding method and in most Block modes you will need an initialisation Vector. You'll possibly also want to use a Message Authentication Code.
To get an initialisation vector you'll want to do something like:
cryptoService.GenerateIV();
byte[] iv = cryptoService.IV;
I strongly advise reading pages on encryption to understand better what the various things you are doing actually are rather than just writing the code. It will make you more confident in your security and make you sound more confident while dealing with others. I've taken the liberty of including some links, most of which can be found by just googling.
Useful links:
http://en.wikipedia.org/wiki/Initialization_vector - all about initialisation vectors
http://en.wikipedia.org/wiki/Triple_DES - on the TDES algorithm
http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation - How consecutive blocks of data interact with each other.
http://en.wikipedia.org/wiki/Padding_%28cryptography%29 - Not massively important except there are different ways of padding and both sides need to be using the same one (of course).
http://chargen.matasano.com/chargen/2009/7/22/if-youre-typing-the-letters-a-e-s-into-your-code-youre-doing.html - An excellent and amusing commentary on the use of encryption and where there are weaknesses and what encryption can and cannot do.
http://en.wikipedia.org/wiki/Message_authentication_code - How to confirm that your message hasn't been tampered with
To encrypt/decrypt data with the TripleDES algorithm, you can use the TripleDESCryptoServiceProvider Class. The algorithm supports key lengths from 128 bits to 192 bits in increments of 64 bits.
If you have two 64-bit keys
byte[] k1 = new byte[] { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF };
byte[] k2 = new byte[] { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 };
and want to concatenate k1, k2 and k1 again to a 192-bit key, you can do this as follows:
byte[] key = new byte[K1.Length + K2.Length + K1.Length];
Buffer.BlockCopy(k1, 0, result, 0, 8);
Buffer.BlockCopy(k2, 0, result, 8, 8);
Buffer.BlockCopy(k1, 0, result, 16, 8);
Note that, in addition to the key, you also need an initialization vector:
byte[] iv = // ...
Example:
byte[] data = new byte[] { 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A };
using (var csp = new TripleDESCryptoServiceProvider())
using (var enc = csp.CreateEncryptor(key, iv))
using (var stream = new MemoryStream())
using (var crypto = new CryptoStream(stream, enc, CryptoStreamMode.Write))
{
crypto.Write(data, 0, data.Length);
}

Categories