How to replicate AES Encryption used by this online tool - c#

http://aes.online-domain-tools.com/
I am trying to replicate this encryption with c# using CBC but not getting the same results no matter what I try.
So far my code:
private byte[] hex2bytes(string s)
{
return Enumerable.Range(0, s.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(s.Substring(x, 2), 16))
.ToArray();
}
private AesCryptoServiceProvider GetProvider(byte[] key)
{
AesCryptoServiceProvider result = new AesCryptoServiceProvider();
result.BlockSize = 128;
result.KeySize = 128;
result.Mode = CipherMode.CBC;
result.Padding = PaddingMode.PKCS7;
result.GenerateIV();
//result.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
result.IV = hex2bytes("ad77d666311839f5665aeb2e42f64542");
byte[] RealKey = GetKey(key, result);
_key = Encoding.ASCII.GetString(RealKey);
result.Key = RealKey;
// result.IV = RealKey;
return result;
}
private byte[] GetKey(byte[] suggestedKey, SymmetricAlgorithm p)
{
byte[] kRaw = suggestedKey;
List<byte> kList = new List<byte>();
for (int i = 0; i < p.LegalKeySizes[0].MinSize; i += 8)
{
kList.Add(kRaw[(i / 8) % kRaw.Length]);
}
byte[] k = kList.ToArray();
return k;
}
/// <summary>
/// Encrpyts the sourceString, returns this result as an Aes encrpyted, BASE64 encoded string
/// </summary>
/// <param name="plainSourceStringToEncrypt">a plain, Framework string (ASCII, null terminated)</param>
/// <param name="passPhrase">The pass phrase.</param>
/// <returns>
/// returns an Aes encrypted, BASE64 encoded string
/// </returns>
public string EncryptString(string plainSourceStringToEncrypt, string passPhrase)
{
//Set up the encryption objects
using (AesCryptoServiceProvider acsp = GetProvider(Encoding.UTF8.GetBytes(passPhrase)))
{
byte[] sourceBytes = Encoding.UTF8.GetBytes(plainSourceStringToEncrypt);
ICryptoTransform ictE = acsp.CreateEncryptor();
//Set up stream to contain the encryption
MemoryStream msS = new MemoryStream();
//Perform the encrpytion, storing output into the stream
CryptoStream csS = new CryptoStream(msS, ictE, CryptoStreamMode.Write);
csS.Write(sourceBytes, 0, sourceBytes.Length);
csS.FlushFinalBlock();
//sourceBytes are now encrypted as an array of secure bytes
byte[] encryptedBytes = msS.ToArray(); //.ToArray() is important, don't mess with the buffer
var x = BitConverter.ToString(encryptedBytes);
//return the encrypted bytes as a BASE64 encoded string
return Convert.ToBase64String(encryptedBytes);
}
}
/// <summary>
/// Decrypts a BASE64 encoded string of encrypted data, returns a plain string
/// </summary>
/// <param name="base64StringToDecrypt">an Aes encrypted AND base64 encoded string</param>
/// <param name="passphrase">The passphrase.</param>
/// <returns>returns a plain string</returns>
public string DecryptString(string base64StringToDecrypt, string passphrase)
{
//Set up the encryption objects
using (AesCryptoServiceProvider acsp = GetProvider(Encoding.UTF8.GetBytes(passphrase)))
{
byte[] RawBytes = Convert.FromBase64String(base64StringToDecrypt);
ICryptoTransform ictD = acsp.CreateDecryptor();
//RawBytes now contains original byte array, still in Encrypted state
//Decrypt into stream
MemoryStream msD = new MemoryStream(RawBytes, 0, RawBytes.Length);
CryptoStream csD = new CryptoStream(msD, ictD, CryptoStreamMode.Read);
//csD now contains original byte array, fully decrypted
//return the content of msD as a regular string
return (new StreamReader(csD)).ReadToEnd();
}
}

OK, so according to the description the key is padded with zero bytes. You are actually repeating key bytes. Both methods are of course completely insecure, a key should just consist of random bytes.
The IV seems to be calculated over the key bytes before it is padded, using SHA-1. I don't see anything about that in your code. Note that the IV should change each time the same key is used, and using the SHA-1 over the key is therefore insecure.
The padding is zero padding, up to the size of the block. This means that if your plaintext ends with a 00 byte that you will loose data. Padding is not mentioned, but I tested this by encrypting something and then decrypting it with the same key. It seems the padding bytes are still there. This is not insecure, but as it may lead to invalid plaintext, it is definitely wrong.
There is no authentication tag (e.g. HMAC) added, meaning that anybody can just change the ciphertext and get away with it. In the best case this will lead just to garbage on the other system. In the worst case (and this one is most likely) you will completely lose confidentiality as well. This is probably what you were trying to achieve in the first place.
I hope I have given you enough pointers to create an implementation - for learning purposes or to migrate away from the given code. If you are using random crap - no other word for this - from the Internet, you will however end up with zero security. I won't provide code, as I don't want this to proliferate.

Related

Correctness of ECIES implementation with XChaCha20-Poly1305 encryption in C#

Recently I was looking for an ECIES implementation for C# and found this: https://gist.github.com/cocowalla/b39091c79b22130f111b6a6d4071820d. However, I am not sure if this implementation is correct and safe and additionally I decided to use XChaCha20-Poly1305 instead of AES. So please evaluate the correctness of the version modified by me. I used the NuGet Sodium.Core package for the XChaCha20-Poly1305 encryption.
The EciesResult class hasn't been modified.
Ecies.cs:
/*
* Modified gist from: https://gist.github.com/cocowalla/b39091c79b22130f111b6a6d4071820d
*/
using Sodium;
using System.Security.Cryptography;
namespace ECIES;
public static class Ecies
{
private static readonly byte[] KdfCounter1 = { 0, 0, 0, 1 };
private static readonly byte[] KdfCounter2 = { 0, 0, 0, 2 };
/// <summary>
/// Based on http://www.secg.org/sec1-v2.pdf, section 5.1.3
/// Encrypt data using ECIES (Elliptic Curve Integrated Encryption Scheme)
/// </summary>
/// <param name="recipientPubKey">Public key of the recipient</param>
/// <param name="data">𝑀, the message to be encrypted</param>
/// <param name="hashAlgorithm">Hash algorithm to use to generate an HMAC of the encrypted data</param>
/// <returns>A result containing the elliptic curve parameters, encrypted message and HMAC (RΜ„, 𝐸𝑀, 𝐷̄)</returns>
public static EciesResult Encrypt(byte[] data, ECDiffieHellmanPublicKey recipientPublicKey, HashAlgorithmName hashAlgorithm, byte[] nonce)
{
if (recipientPublicKey == null)
throw new ArgumentNullException(nameof(recipientPublicKey));
if (data == null)
throw new ArgumentNullException(nameof(data));
if (string.IsNullOrEmpty(hashAlgorithm.Name))
throw new ArgumentException("Hash algorithm name must have a value", nameof(hashAlgorithm));
ECCurve curve = recipientPublicKey.ExportParameters().Curve;
// Generate an ephemeral keypair on the correct curve
using (ECDiffieHellman ephemeral = ECDiffieHellman.Create(curve))
{
// encodedEphemeralPoint (R)Μ„ contains the parameters to be used for encryption/decryption operations, encoded using X9.62
ECParameters ephemeralPublicParams = ephemeral.ExportParameters(false);
int pointLen = ephemeralPublicParams.Q.X.Length;
byte[] encodedEphemeralPoint = new byte[pointLen * 2 + 1];
encodedEphemeralPoint[0] = 0x04;
Buffer.BlockCopy(ephemeralPublicParams.Q.X, 0, encodedEphemeralPoint, 1, pointLen);
Buffer.BlockCopy(ephemeralPublicParams.Q.Y, 0, encodedEphemeralPoint, 1 + pointLen, pointLen);
// Use ANSI-X9.63-KDF to derive the encryption key, 𝐸𝐾
byte[] encryptionKey = ephemeral.DeriveKeyFromHash(recipientPublicKey, HashAlgorithmName.SHA256, null, KdfCounter1);
// Use ANSI-X9.63-KDF to derive the HMAC key, 𝑀𝐾
byte[] hmacKey = ephemeral.DeriveKeyFromHash(recipientPublicKey, HashAlgorithmName.SHA256, null, KdfCounter2);
// The ciphertext, 𝐸𝑀
// Encrypt message with XChaCha20-Poly1305
byte[] ciphertext = SecretAeadXChaCha20Poly1305.Encrypt(data, nonce, encryptionKey);
// Use the requested HMAC algorithm to compute 𝐷, HMAC of the ciphertext
byte[] tag = ComputeHMAC(hashAlgorithm, hmacKey, ciphertext);
return new EciesResult(encodedEphemeralPoint, ciphertext, tag);
}
}
/// <summary>
/// Based on http://www.secg.org/sec1-v2.pdf, section 5.1.4
/// Decrypt data using ECIES (Elliptic Curve Integrated Encryption Scheme)
/// </summary>
/// <param name="recipient">Recipient of the message</param>
/// <param name="encryptionResult">The result of an ECIES encryption operation</param>
/// <param name="hashAlgorithm">Hash algorithm to use to verify the HMAC of the encrypted data</param>
/// <returns>The decrypted message</returns>
public static byte[] Decrypt(ECDiffieHellman recipient, EciesResult encryptionResult, HashAlgorithmName hashAlgorithm, byte[] nonce)
{
if (recipient == null)
throw new ArgumentNullException(nameof(recipient));
if (encryptionResult == null)
throw new ArgumentNullException(nameof(encryptionResult));
if (encryptionResult.EncodedEphemeralPoint.Length == 0 || encryptionResult.EncodedEphemeralPoint[0] != 0x04)
throw new ArgumentOutOfRangeException(nameof(encryptionResult), "Encoded ephemeral point not in correct formtat - expected first byte to be 0x04 (uncompressed point)");
if (encryptionResult.Ciphertext == null)
throw new ArgumentException("Ciphertext must has a value", nameof(encryptionResult.Ciphertext));
if (encryptionResult.Tag == null)
throw new ArgumentException("Tag (HMAC) must have a value", nameof(encryptionResult.Tag));
ECParameters recipientParams = recipient.ExportParameters(false);
int pointLen = recipientParams.Q.X.Length;
int expectedRLen = 1 + pointLen * 2;
if (encryptionResult.EncodedEphemeralPoint.Length < expectedRLen)
throw new ArgumentOutOfRangeException(nameof(encryptionResult.EncodedEphemeralPoint), $"Incorrect length for curve parameters - expected {expectedRLen} bytes");
// Extract the ephemeral elliptic curve point R=(xR, yR) from RΜ„
var ecParameters = new ECParameters
{
Curve = recipientParams.Curve,
Q =
{
X = new byte[pointLen],
Y = new byte[pointLen]
}
};
Buffer.BlockCopy(encryptionResult.EncodedEphemeralPoint, 1, ecParameters.Q.X, 0, pointLen);
Buffer.BlockCopy(encryptionResult.EncodedEphemeralPoint, pointLen + 1, ecParameters.Q.Y, 0, pointLen);
ecParameters.Validate();
using (ECDiffieHellman senderEcdh = ECDiffieHellman.Create(ecParameters))
using (ECDiffieHellmanPublicKey senderPublicKey = senderEcdh.PublicKey)
{
// Use ANSI-X9.63-KDF to derive the encryption key, 𝐸𝐾
byte[] encryptionKey = recipient.DeriveKeyFromHash(senderPublicKey, HashAlgorithmName.SHA256, null, new byte[] { 0, 0, 0, 1 });
// Use ANSI-X9.63-KDF to derive the HMAC key, 𝑀𝐾
byte[] hmacKey = recipient.DeriveKeyFromHash(senderPublicKey, HashAlgorithmName.SHA256, null, new byte[] { 0, 0, 0, 2 });
// Use the requested HMAC algorithm to verify that the HMAC matches 𝐷
Span<byte> currentTag = stackalloc byte[HashLength(hashAlgorithm)];
ComputeHMAC(hashAlgorithm, hmacKey, encryptionResult.Ciphertext, currentTag);
if (!CryptographicOperations.FixedTimeEquals(encryptionResult.Tag, currentTag))
throw new CryptographicException($"Invalid Tag (HMAC-{hashAlgorithm.Name})");
if (!CryptographicOperations.FixedTimeEquals(encryptionResult.Tag, currentTag))
throw new CryptographicException($"Invalid Tag (HMAC-{hashAlgorithm.Name})");
// 𝑀, the plaintext
// Decrypt message with XChaCha20-Poly1305
byte[] plaintext = SecretAeadXChaCha20Poly1305.Decrypt(encryptionResult.Ciphertext, nonce, encryptionKey);
return plaintext;
}
}
private static byte[] ComputeHMAC(HashAlgorithmName hashAlgorithmName, byte[] key, byte[] data)
{
using (IncrementalHash hmac = IncrementalHash.CreateHMAC(hashAlgorithmName, key))
{
hmac.AppendData(data);
return hmac.GetHashAndReset();
}
}
private static int ComputeHMAC(HashAlgorithmName hashAlgorithmName, byte[] key, byte[] data, Span<byte> output)
{
var hashLen = HashLength(hashAlgorithmName);
if (output.Length < hashLen)
throw new ArgumentException($"Output buffer is not large enough for the hash result ({hashLen} bytes are required)", nameof(output));
using (IncrementalHash hmac = IncrementalHash.CreateHMAC(hashAlgorithmName, key))
{
hmac.AppendData(data);
if (!hmac.TryGetHashAndReset(output, out int bytesWritten))
{
throw new CryptographicException("HMAC operation failed unexpectedly");
}
return bytesWritten;
}
}
private static int HashLength(HashAlgorithmName hashAlgorithmName)
{
if (hashAlgorithmName == HashAlgorithmName.SHA1)
{
return 160 / 8;
}
else if (hashAlgorithmName == HashAlgorithmName.SHA256)
{
return 256 / 8;
}
else if (hashAlgorithmName == HashAlgorithmName.SHA384)
{
return 384 / 8;
}
else if (hashAlgorithmName == HashAlgorithmName.SHA512)
{
return 512 / 8;
}
else if (hashAlgorithmName == HashAlgorithmName.MD5)
{
return 128 / 8;
}
else
{
throw new ArgumentOutOfRangeException(nameof(hashAlgorithmName));
}
}
}

AES / RSA implementation from Python to C#

I want to migrate following python code into c#.
The entry point is the method encrypted_request
I have no real clue about aes/rsa in python or in c#.
Maybe someone could explain the different code sections and if possible give me a hint how to implement that in c#.
Especially the magic numbers used here and there I do not understand.
modulus = ('00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7'
'b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280'
'104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932'
'575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b'
'3ece0462db0a22b8e7')
nonce = '0CoJUm6Qyw8W8jud'
pubKey = '010001'
def encrypted_request(text):
text = json.dumps(text)
secKey = createSecretKey(16)
encText = aesEncrypt(aesEncrypt(text, nonce), secKey)
encSecKey = rsaEncrypt(secKey, pubKey, modulus)
data = {'params': encText, 'encSecKey': encSecKey}
return data
def aesEncrypt(text, secKey):
pad = 16 - len(text) % 16
text = text + chr(pad) * pad
encryptor = AES.new(secKey, 2, '0102030405060708')
ciphertext = encryptor.encrypt(text)
ciphertext = base64.b64encode(ciphertext).decode('u8')
return ciphertext
def rsaEncrypt(text, pubKey, modulus):
text = text[::-1]
rs = pow(int(binascii.hexlify(text), 16), int(pubKey, 16)) % int(modulus, 16)
return format(rs, 'x').zfill(256)
def createSecretKey(size):
return binascii.hexlify(os.urandom(size))[:16]
Source: https://github.com/darknessomi/musicbox/blob/master/NEMbox/api.py
My current state in c#:
private byte[] hex2Binary(string hex) {
byte[] binaryVal = new byte[hex.Length];
for (int i = 0; i < hex.Length; i++) {
string byteString = hex.Substring(i, 1);
byte b = Convert.ToByte(byteString, 16);
binaryVal[i] = b;
}
return binaryVal;
}
private string aesEncryptBase64(String plainText, string key) {
return aesEncryptBase64(plainText, hex2Binary(key));
}
private string aesEncryptBase64(String plainText, byte[] key) {
//pad = 16 - len(text) % 16
//text = text + chr(pad) * pad
int pad = 16 - plainText.Length % 16;
for (int i=0; i<pad; i++) {
plainText = plainText + ((char)pad);
}
byte[] plainBytes = null;
RijndaelManaged aes = new RijndaelManaged();
//aes.KeySize = 16;
aes.Mode = CipherMode.CBC;
aes.Key = key;
aes.IV = hex2Binary(client.neteaseFix.encryptInfo.iv);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(plainBytes, 0, plainBytes.Length);
cs.Close();
byte[] encryptedBytes = ms.ToArray();
return Convert.ToBase64String(encryptedBytes); //decode("u8")
}
Here are a couple of things I see right off the bat, but the question is a bit too open-ended:
In aesEncryptBase64 you are manually applying padding. The AES implementation in .NET does that for you. If you prefer to do it yourself you need to set aes.Padding = PaddingMode.None
In aesEncryptBase64 you create a RijndaelManaged object. Don't do that. You want AES, just use AES.Create(), which returns an AES object (not a Rijndael object).
.NET had support for the larger Rijndael algorithm before AES; and Rijndael with a block size of 128 bits is what got selected as AES, but Rijndael supports modes that AES does not, and you shouldn't really use them interchangeably (though many samples do).
In aesEncryptBase64 your aes, ms, and cs objects are all IDisposable, so you should have them in using statements.
The rsaEncrypt method in Python is doing raw RSA, which isn't supported in .NET (nor generally considered a good idea). Unless it's only called by routines which do the padding (and then it's just a pit of side-channel vulnerabilities).
If your rsaEncrypt (in Python) is only being called from routines which do the signature or encryption (or PSS or OAEP) padding then your .NET equivalent would be (using your method naming casing, instead of the normal ones in .NET)
private static rsaEncrypt(string text, string pubKey, string modulus)
{
RSAParameters rsaParams = new RSAParameters
{
Exponent = hex2Binary(pubKey),
Modulus = hex2Binary(modulus),
};
using (RSA rsa = RSA.Create())
{
rsa.ImportParameters(rsaParams);
return rsa.Encrypt(Encoding.ASCII.GetBytes(text), YOUNEEDTOPICKTHEPADDINGMODE);
}
}
It would be worlds better to improve all of the code around this, though, so that it doesn't have to do so much string re-parsing.

Secure File transmission in C# using AES

I'm trying to read a file on the server (in blocks of 5KB), encrypt the block using AES and send it to the client. On the client, i decrypt the received block, and append to a file to get back the original file.
However, my decrypted block size received on the client differs from the plaintext block which is encrypted on the server.
e.g.
I have a 15.5 KB exe file, so i have 15.5*1024/5*1024 = 4 blocks (round figure) to encrypt and send to client (The first 3 blocks are of 5120 bytes and last block is 512 bytes in length). On the client however, the blocks decrypted are of size 5057, 4970, 5016 and 512 bytes which equals a file size of 15.1 KB (less than what was actually sent by the server).
Here is my code snippet:
Server (sends the file to client):
FileStream fs = new FileStream("lcd.exe", FileMode.Open, FileAccess.Read);
//block size = 5KB
int blockSize = 5 * 1024;
//calculate number of blocks in data
long numberOfBlocks = fs.Length / blockSize;
if (fs.Length % blockSize != 0) numberOfBlocks++;
byte[] numberOfBlocksBytes = BitConverter.GetBytes(numberOfBlocks);
//send number of blocks to client
SendMessage(sw, numberOfBlocksBytes);
int count = 0, offset = 0, numberOfBytesToRead=0;
Aes objAes = new Aes();
while (count < numberOfBlocks)
{
byte[] buffer;
numberOfBytesToRead = blockSize;
if (fs.Length < offset + blockSize)
{
numberOfBytesToRead = (int)(fs.Length - offset);
}
buffer = new byte[numberOfBytesToRead];
fs.Read(buffer, 0, numberOfBytesToRead);
//encrypt before sending
byte[] encryptedBuffer = objAes.Encrypt(buffer, Encoding.Default.GetBytes(sessionKey), initVector);
SendMessage(sw, encryptedBuffer);
offset += numberOfBytesToRead;
count++;
}
fs.Close();
Client side code which receives the file:
byte[] numberOfBlocksBytes = ReadMessage(sr);
long numberOfBlocks = BitConverter.ToInt64(numberOfBlocksBytes, 0);
FileStream fs = new FileStream("lcd.exe", FileMode.Append, FileAccess.Write);
//block size = 5KB
int blockSize = 5 * 1024;
Aes objAes = new Aes();
int count = 0, offset = 0;
while (count < numberOfBlocks)
{
byte[] encryptedBuffer = ReadMessage(sr);
byte[] buffer = objAes.Decrypt(encryptedBuffer, sessionKey, initVector);
fs.Write(buffer, 0, buffer.Length);
offset += buffer.Length;
count++;
}
fs.Close();
My AES code for encryption:
private const int StandardKeyLength = 16;
public byte[] Encrypt(byte[] plainText, byte[] key, byte[] initVector)
{
if (key.Length != StandardKeyLength | initVector.Length != StandardKeyLength)
{
throw new ArgumentException("Key Length and Init Vector should be 16 bytes (128 bits) in size");
}
var bPlainBytes = plainText;
var objRm = new RijndaelManaged();
objRm.Key = key;
objRm.IV = initVector;
objRm.Padding = PaddingMode.PKCS7;
objRm.BlockSize = 128;
var ict = objRm.CreateEncryptor(objRm.Key, objRm.IV);
var objMs = new MemoryStream();
var objCs = new CryptoStream(objMs, ict, CryptoStreamMode.Write);
objCs.Write(bPlainBytes, 0, bPlainBytes.Length);
objCs.FlushFinalBlock();
var bEncrypted = objMs.ToArray();
return bEncrypted;
}
My AES code for decryption:
public byte[] Decrypt(byte[] cipherText, byte[] key, byte[] initVector)
{
if (key.Length != StandardKeyLength | initVector.Length != StandardKeyLength)
{
throw new ArgumentException("Key Length and Init Vector should be 16 bytes (128 bits) in size");
}
var bCipherBytes = cipherText;
var objRm = new RijndaelManaged();
objRm.Key = key;
objRm.IV = initVector;
objRm.Padding = PaddingMode.PKCS7;
objRm.BlockSize = 128;
var ict = objRm.CreateDecryptor(objRm.Key, objRm.IV);
var objMs = new MemoryStream(bCipherBytes);
var objCs = new CryptoStream(objMs, ict, CryptoStreamMode.Read);
var streamobj = new StreamReader(objCs);
var strDecrypted = streamobj.ReadToEnd();
return (Encoding.Default.GetBytes(strDecrypted));
}
These are the results i got while debugging the while loop which sends file blocks on the server:
Actual File Size sent: 15.5 KB = 15872 bytes
Buffer size(plaintext) Encrypted Buffer Size(Sent) Offset Count
5120 5136 5120 0
5120 5136 10240 1
5120 5136 15360 2
512 528 15872 3
These are the results i got while debugging the while loop which receives file blocks on the client:
Actual File Size received: 15.1 KB = 15555 bytes
Received Buffersize Decrypted Buffer Size Offset Count
5136 5057 5057 0
5136 4970 10027 1
5136 5016 15043 2
528 512 15555 3
It is evident that the sending and receiving code is working fine (since encrypted buffer size which is sent = received buffer size). However, the decrypted buffer size does not match the buffer size (plaintext) at all except for the last block which is of length 512 bytes.
What can be possibly wrong with decryption because of which i'm not receiving the file completely on the client side?
You're being tripped up because in your Decrypt statement you are treating your ciphertext as if it is a string. Specifically, these lines:
var streamobj = new StreamReader(objCs);
var strDecrypted = streamobj.ReadToEnd();
return (Encoding.Default.GetBytes(strDecrypted));
Instead you want to be calling Read on your CryptoStream to read a raw byte array into a buffer. You can then return that buffer without attempting to coerce it into a string (which is what is happening by using the stream reader).
You should use something more like:
public byte[] Decrypt(byte[] cipherText, byte[] key, byte[] initVector)
{
if (key.Length != StandardKeyLength | initVector.Length != StandardKeyLength)
{
throw new ArgumentException("Key Length and Init Vector should be 16 bytes (128 bits) in size");
}
var bCipherBytes = cipherText;
var objRm = new RijndaelManaged();
objRm.Key = key;
objRm.IV = initVector;
objRm.Padding = PaddingMode.PKCS7;
objRm.BlockSize = 128;
var ict = objRm.CreateDecryptor(objRm.Key, objRm.IV);
var objMs = new MemoryStream(bCipherBytes);
var objCs = new CryptoStream(objMs, ict, CryptoStreamMode.Read);
var buffer = new byte[cipherText.Length];
int readBytes = objCs.Read(buffer, 0, cipherText.Length);
var trimmedData = new byte[readBytes];
Array.Copy(buffer, trimmedData, readBytes);
return trimmedData;
}
I would also suggest you take a look at the encryption utilities I maintain on Snipt. Specifically the Symmetric Encrypt and Decrypt methods. Your code as it stands has a lot of using blocks missing and a number of potential resource leaks.
var streamobj = new StreamReader(objCs);
That's pretty unlikely to work well. The StreamReader will assume that the decrypted data is utf-8 encoded text. There is no hint whatsoever that this is actually the case from the code that encrypts the data, it takes a byte[].
Use a FileStream instead so no conversion is made at all. Also helps you avoid the Encoding.Default.GetBytes() data randomizer.
Quick observation, which may just be my ignorance: Encrypt() method uses default encoding to get the session key bytes. On the receiving end the Decrypt() method uses the sessionKey itself as second parameter, i.e., without getting bytes?

Decrypt a larger text using RSA private key

I have to encrypt and decrypt a large string using RSA public key and private key. I have managed to encrypt a larger text using the following sample code
public static string Encrypt(string publicKey, string data, RsaKeyLengths length)
{
// full array of bytes to encrypt
byte[] bytesToEncrypt;
// worker byte array
byte[] block;
// encrypted bytes
byte[] encryptedBytes;
// length of bytesToEncrypt
var dataLength = 0;
// number of bytes in key
var keySize = 0;
// maximum block length to encrypt
var maxLength = 0;
// how many blocks must we encrypt to encrypt entire message?
var iterations = 0;
// the encrypted data
var encryptedData = new StringBuilder();
// instantiate the crypto provider with the correct key length
var rsaCryptoServiceProvider = new RSACryptoServiceProvider((int)length);
// initialize the RSA object from the given public key
rsaCryptoServiceProvider.FromXmlString(publicKey);
// convert data to byte array
bytesToEncrypt = Encoding.Unicode.GetBytes(data);
// get length of byte array
dataLength = bytesToEncrypt.Length;
// convert length of key from bits to bytes
keySize = (int)length / 8;
// .NET RSACryptoServiceProvider uses SHA1 Hash function
// use this to work out the maximum length to encrypt per block
maxLength = ((keySize - 2) - (2 * SHA1.Create().ComputeHash(bytesToEncrypt).Length));
// how many blocks do we need to encrypt?
iterations = dataLength / maxLength;
// encrypt block by block
for (int index = 0; index <= iterations; index++)
{
// is there more than one full block of data left to encrypt?
if ((dataLength - maxLength * index) > maxLength)
{
block = new byte[maxLength];
}
else
{
block = new byte[dataLength - maxLength * index];
}
// copy the required number of bytes from the array of bytes to encrypt to our worker array
Buffer.BlockCopy(bytesToEncrypt, maxLength * index, block, 0, block.Length);
// encrypt the current worker array block of bytes
encryptedBytes = rsaCryptoServiceProvider.Encrypt(block, true);
// RSACryptoServiceProvider reverses the order of encrypted bytesToEncrypt after encryption and before decryption.
// Undo this reversal for compatibility with other implementations
Array.Reverse(encryptedBytes);
// convert to base 64 string
encryptedData.Append(Convert.ToBase64String(encryptedBytes));
}
return encryptedData.ToString();
}
Then I tried to decrypt the larger text using the following code
/// <summary>
/// Encrypt an arbitrary string of data under the supplied public key
/// </summary>
/// <param name="publicKey">The public key to encrypt under</param>
/// <param name="data">The data to encrypt</param>
/// <param name="length">The bit length or strength of the public key: 1024, 2048 or 4096 bits. This must match the
/// value actually used to create the publicKey</param>
/// <returns></returns>
public static string Decrypt(string privateKey, string data, RsaKeyLengths length)
{
// full array of bytes to encrypt
byte[] bytesToDecrypt;
// worker byte array
byte[] block;
// encrypted bytes
byte[] decryptedBytes;
// length of bytesToEncrypt
var dataLength = 0;
// number of bytes in key
var keySize = 0;
// maximum block length to encrypt
var maxLength = 0;
// how many blocks must we encrypt to encrypt entire message?
var iterations = 0;
// the encrypted data
var decryptedData = new StringBuilder();
// instantiate the crypto provider with the correct key length
var rsaCryptoServiceProvider = new RSACryptoServiceProvider((int)length);
// initialize the RSA object from the given public key
rsaCryptoServiceProvider.FromXmlString(privateKey);
// convert data to byte array
bytesToDecrypt = Encoding.Unicode.GetBytes(data);
// get length of byte array
dataLength = bytesToDecrypt.Length;
// convert length of key from bits to bytes
keySize = (int)length / 8;
// .NET RSACryptoServiceProvider uses SHA1 Hash function
// use this to work out the maximum length to encrypt per block
//maxLength = ((keySize - 2) - (2 * SHA1.Create().ComputeHash(bytesToDecrypt).Length));
maxLength = ((keySize / 8) % 3 != 0) ?
(((keySize / 8) / 3) * 4) + 4 : ((keySize / 8) / 3) * 4; ;
// how many blocks do we need to encrypt?
iterations = dataLength / maxLength;
// encrypt block by block
for (int index = 0; index <= iterations; index++)
{
// is there more than one full block of data left to encrypt?
if ((dataLength - maxLength * index) > maxLength)
{
block = new byte[maxLength];
}
else
{
block = new byte[dataLength - maxLength * index];
}
// copy the required number of bytes from the array of bytes to encrypt to our worker array
Buffer.BlockCopy(bytesToDecrypt, maxLength * index, block, 0, block.Length);
// encrypt the current worker array block of bytes
decryptedBytes = rsaCryptoServiceProvider.Decrypt(block, true);
// RSACryptoServiceProvider reverses the order of encrypted bytesToEncrypt after encryption and before decryption.
// Undo this reversal for compatibility with other implementations
Array.Reverse(decryptedBytes);
// convert to base 64 string
decryptedData.Append(Convert.ToBase64String(decryptedBytes));
}
return decryptedData.ToString();
}
Actually the encryption is going on smoothly. No problem with it. But when I am trying to decrypt it. I am getting the following exception
Unhandled Exception:
System.Security.Cryptography.CryptographicException: Error occurred
while decoding OAEP padding.
Can anybody help me out?
Use stream cipher instead and only encrypt that key for this cipher with RSA. This may help if you need RSA because of its public-private key logic and want to use the different keys for encryption and decryption. With stream cipher you would be able encrypt and decrypt gigabytes of data no problem.
RSA is normally not used for really big amounts of data.
Maybe a bit late, but I found http://tekaris.com/blog/2013/02/08/encrypting-large-data-with-asymetric-rsacryptoserviceprovider/ helpful. The trick here obviously is to split the data, encrypt and re-join the data.

How to use 3DES encryption results to calculate the MAC value

I have a project to do for work to do in C#. I have the requirements for a project and this part is just a small piece in the entire project. I was given test data and the result. I need to code it so that I get the correct results. And at the moment I'm not getting the final result.
Please don't question or criticise the requirements, it's what I have and need to sort out and code it.
I was told to take input string "abc" and compute the SHA-1 hash for this. I got this part to work, here is the code:
private string CalculateSHA1Hash(string text, Encoding characterEncoding)
{
byte[] buffer = characterEncoding.GetBytes(text);
SHA1CryptoServiceProvider sha1CryptoServiceProvider = new SHA1CryptoServiceProvider();
byte[] s = sha1CryptoServiceProvider.ComputeHash(buffer);
string hash = BitConverter.ToString(sha1CryptoServiceProvider.ComputeHash(buffer)).Replace("-", "");
return hash;
}
I used UTF8Encoding because none was specified in the requirements doc. The result that I got from this is A9993E364706816ABA3E25717850C26C9CD0D89D.
I was then told to break up this string into 3 string blocks, of 16 characters each, and use just the 1st block. This is what I got:
block1: A9993E364706816A
I was also given 2 keys:
K1: 0123456789ABCDEF
K2: FEDCBA9876543210
Block1 is to be used as input string to a 3DES encryption using the 2 keys.
The result of the cipher text must be 6E5271A3F3F5C418, I am not getting this.
Below is my calculations. Can someone please see if I am doing this correctly and where I am doing this wrong. Chris (on SO) gave me some articles to read but I still can't get the results that I need to get. Is there something that cater for this already, I'm I just totally confused, or what?
public string Encrypt(string message)
{
string result = string.Empty;
// Calculate the SHA1 hash
UTF8Encoding characterEncoding = new UTF8Encoding();
string sha1HashResult = CalculateSHA1Hash(message, characterEncoding);
block1 = sha1HashResult.Substring(0, 16);
byte[] block1ByteArray = characterEncoding.GetBytes(block1);
string key = "0x" + accessKey1 + accessKey2 + accessKey1;
byte[] keyByteArray = StringToByteArray(key).ToArray();
byte[] enc = ComputeTripleDesEncryption(block1ByteArray, keyByteArray);
result = ByteArrayToString(enc);
return result;
}
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;
}
private 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;
}
private string ByteArrayToString(byte[] ba)
{
string hex = BitConverter.ToString(ba);
return hex.Replace("-", "");
}
I really do not know what to do further.
There's a few things wrong with what you have right now:
You're specifying that you need to use an IV, but you're using ECB (which doesn't use an IV)
You're generating the IV randomly, using GenerateIV(). This will cause the result to be different every time, if you're not using ECB.
You're only performing the transform on the final block, instead of the whole data.
See the following code sample for a decent idea of how to use 3DES in C#:
http://msdn.microsoft.com/en-us/library/system.security.cryptography.tripledescryptoserviceprovider.aspx
I'd guess that since you're specifying an IV, you're actually meant to be using CBC instead of ECB. Try it out and see what you get.

Categories

Resources