I am doing RSA encryption and I have to split my long string into small byte[] and encrypt them. I then combine the arrays and convert to string and write to a secure file.
Then encryption creates byte[128]
I use this the following to combine:
public static byte[] Combine(params byte[][] arrays)
{
byte[] ret = new byte[arrays.Sum(x => x.Length)];
int offset = 0;
foreach (byte[] data in arrays)
{
Buffer.BlockCopy(data, 0, ret, offset, data.Length);
offset += data.Length;
}
return ret;
}
When I decrypt I take the string, convert it to a byte[] array and now need to split it to decode the chunks and then convert to string.
Any ideas?
Thanks
EDIT:
I think I have the split working now however the decryption fails. Is this because of RSA keys etc? At TimePointA it encrypts it, then at TimePointB it tries to decrypt and it fails. The public keys are different so not sure if that is the issue.
When you decrypt, you can create one array for your decrypt buffer and reuse it:
Also, normally RSA gets used to encrypt a symmetric key for something like AES, and the symmetric algorithm is used to encrypt the actual data. This is enormously faster for anything longer than 1 cipher block. To decrypt the data, you decrypt the symmetric key with RSA, followed by decrypting the data with that key.
byte[] buffer = new byte[BlockLength];
// ASSUMES SOURCE IS padded to BlockLength
for (int i = 0; i < source.Length; i += BlockLength)
{
Buffer.BlockCopy(source, i, buffer, 0, BlockLength);
// ... decode buffer and copy the result somewhere else
}
Edit 2: If you are storing the data as strings and not as raw bytes, use Convert.ToBase64String() and Convert.FromBase64String() as the safest conversion solution.
Edit 3: From his edit:
private static List<byte[]> splitByteArray(string longString)
{
byte[] source = Convert.FromBase64String(longString);
List<byte[]> result = new List<byte[]>();
for (int i = 0; i < source.Length; i += 128)
{
byte[] buffer = new byte[128];
Buffer.BlockCopy(source, i, buffer, 0, 128);
result.Add(buffer);
}
return result;
}
I'd say something like this would do it:
byte[] text = Encoding.UTF8.GetBytes(longString);
int len = 128;
for (int i = 0; i < text.Length; )
{
int j = 0;
byte[] chunk = new byte[len];
while (++j < chunk.Length && i < text.Length)
{
chunk[j] = text[i++];
}
Convert(chunk); //do something with the chunk
}
Why do you need to break the string into variable length chunks? Fixed-length chunks, or no chunks at all, would simplify this a lot.
why not use a framework instead of doing the byte-stuff yourself?
http://www.codinghorror.com/blog/archives/001275.html
"the public keys are different"?
You encrypt with a private key, and decrypt with the public key that corresponds to the private key.
Anything else will give you gibberish.
Related
This might be a simple one, but I can't seem to find an easy way to do it. I need to save an array of 84 uint's into an SQL database's BINARY field. So I'm using the following lines in my C# ASP.NET project:
//This is what I have
uint[] uintArray;
//I need to convert from uint[] to byte[]
byte[] byteArray = ???
cmd.Parameters.Add("#myBindaryData", SqlDbType.Binary).Value = byteArray;
So how do you convert from uint[] to byte[]?
How about:
byte[] byteArray = uintArray.SelectMany(BitConverter.GetBytes).ToArray();
This'll do what you want, in little-endian format...
You can use System.Buffer.BlockCopy to do this:
byte[] byteArray = new byte[uintArray.Length * 4];
Buffer.BlockCopy(uintArray, 0, byteArray, 0, uintArray.Length * 4];
http://msdn.microsoft.com/en-us/library/system.buffer.blockcopy.aspx
This will be much more efficient than using a for loop or some similar construct. It directly copies the bytes from the first array to the second.
To convert back just do the same thing in reverse.
There is no built-in conversion function to do this. Because of the way arrays work, a whole new array will need to be allocated and its values filled-in. You will probably just have to write that yourself. You can use the System.BitConverter.GetBytes(uint) function to do some of the work, and then copy the resulting values into the final byte[].
Here's a function that will do the conversion in little-endian format:
private static byte[] ConvertUInt32ArrayToByteArray(uint[] value)
{
const int bytesPerUInt32 = 4;
byte[] result = new byte[value.Length * bytesPerUInt32];
for (int index = 0; index < value.Length; index++)
{
byte[] partialResult = System.BitConverter.GetBytes(value[index]);
for (int indexTwo = 0; indexTwo < partialResult.Length; indexTwo++)
result[index * bytesPerUInt32 + indexTwo] = partialResult[indexTwo];
}
return result;
}
byte[] byteArray = Array.ConvertAll<uint, byte>(
uintArray,
new Converter<uint, byte>(
delegate(uint u) { return (byte)u; }
));
Heed advice from #liho1eye, make sure your uints really fit into bytes, otherwise you're losing data.
If you need all the bits from each uint, you're gonna to have to make an appropriately sized byte[] and copy each uint into the four bytes it represents.
Something like this ought to work:
uint[] uintArray;
//I need to convert from uint[] to byte[]
byte[] byteArray = new byte[uintArray.Length * sizeof(uint)];
for (int i = 0; i < uintArray.Length; i++)
{
byte[] barray = System.BitConverter.GetBytes(uintArray[i]);
for (int j = 0; j < barray.Length; j++)
{
byteArray[i * sizeof(uint) + j] = barray[j];
}
}
cmd.Parameters.Add("#myBindaryData", SqlDbType.Binary).Value = byteArray;
I have a small problem in checking MD5 checksum of files in C# and PHP. The hash calculated by PHP script vary from hash calculated by C#.
libcurl.dll C# = c3506360ce8f42f10dc844e3ff6ed999
libcurl.dll PHP = f02b47e41e9fa77909031bdef07532af
In PHP I use md5_file function, and my C# code is:
protected string GetFileMD5(string fileName)
{
FileStream file = new FileStream(fileName, FileMode.Open);
MD5 md5 = new MD5CryptoServiceProvider();
byte[] retVal = md5.ComputeHash(file);
file.Close();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < retVal.Length; i++)
{
sb.Append(retVal[i].ToString("x2"));
}
return sb.ToString();
}
Any ideas how to calculate the same hash? I think that it may be something about encoding.
Thanks in advance!
My C# is rusty, but will:
byte[] retVal = md5.ComputeHash(file);
actually read in the entire file? I think it is just hashing the stream object. I believe you need to read the stream, then hash on the entire file contents?
int length = (int)file.Length; // get file length
buffer = new byte[length]; // create buffer
int count; // actual number of bytes read
int sum = 0; // total number of bytes read
// read until Read method returns 0 (end of the stream has been reached)
while ((count = file.Read(buffer, sum, length - sum)) > 0)
sum += count; // sum is a buffer offset for next reading
byte[] retVal = md5.ComputeHash(buffer);
I'm not sure if that actually runs as is, but I think something along those lines will be needed.
I use this:
I havent had yet any issues with comparison of php md5 with c# md5
System.Text.UTF8Encoding text = new System.Text.UTF8Encoding();
System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
Convert2.ToBase16(md5.ComputeHash(text.GetBytes(encPassString + sess)));
class Convert2
{
public static string ToBase16(byte[] input)
{
return string.Concat((from x in input select x.ToString("x2")).ToArray());
}
}
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.
I am encrypting my data by using RSACryptoServiceProvider() class in c#. I want to decrypt the data in ubuntu, that was encrypted in c#. Can you suggest me which mechanism I need to follow in order to decrypt. Following function is for encryption:
public static void Encrypt(String PublicKey, String plainText, out String cipherText)
{
try
{
int dwKeySize = 1024;
// TODO: Add Proper Exception Handlers
RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider(dwKeySize);
rsaCryptoServiceProvider.FromXmlString(PublicKey);
int keySize = dwKeySize / 8;
byte[] bytes = Encoding.UTF32.GetBytes(plainText);
// The hash function in use by the .NET RSACryptoServiceProvider here is SHA1
// int maxLength = ( keySize ) - 2 - ( 2 * SHA1.Create().ComputeHash( rawBytes ).Length );
int maxLength = keySize - 42;
int dataLength = bytes.Length;
int iterations = dataLength / maxLength;
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i <= iterations; i++)
{
byte[] tempBytes = new byte[(dataLength - maxLength * i > maxLength) ? maxLength : dataLength - maxLength * i];
Buffer.BlockCopy(bytes, maxLength * i, tempBytes, 0, tempBytes.Length);
byte[] encryptedBytes = rsaCryptoServiceProvider.Encrypt(tempBytes, true);
// Be aware the RSACryptoServiceProvider reverses the order
// of encrypted bytes after encryption and before decryption.
// If you do not require compatibility with Microsoft Cryptographic API
// (CAPI) and/or other vendors.
// Comment out the next line and the corresponding one in the
// DecryptString function.
Array.Reverse(encryptedBytes);
// Why convert to base 64?
// Because it is the largest power-of-two base printable using only ASCII characters
stringBuilder.Append(Convert.ToBase64String(encryptedBytes));
}
cipherText = stringBuilder.ToString();
}
catch (Exception e)
{
cipherText = "ERROR_STRING";
Console.WriteLine("Exception in RSA Encrypt: " + e.Message);
//throw new Exception("Exception occured while RSA Encryption" + e.Message);
}
}
Don't use RSA like that. It's not meant to be used that way and it's way too slow.
The right way is to use a symmetric algorithm, e.g. AES, and encrypt the key you used with RSA. See my old blog entry for C# code doing just that.
You need the same mechanisms, but in reverse. Try first, ask later.
I've been trying to encrypt a password with a public RSA key that is sent to me by the server.
var csp = new CspParameters(1, "Microsoft Strong Cryptographic Provider");
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(1280, csp);
byte[] key = ByteUtils.HexToBytes(client.RSAKey);
RSA.ImportCspBlob(key);
byte[] encrypted = RSA.Encrypt(Encoding.ASCII.GetBytes(password), true);
The hex key is provided in such format:
string key = "30819D300D06092A864886F70D010101050003818B0030818702818100C7BD672D8C634D443840AD809790852770D3A2E99F456D6516329E0205D0645C23FD001D4D070CEE368A20526FEB2402358C915D7E86102B1659AA8651C449C344599F72BE904B8E338E7002E9978453C5BBCCA51AC165AA265069E0EAB1411D11A2FFDD35E5A8296A6A2AF238945874E8206979B0A16E2E4260A161CAB5C905020111";
As the string is 320-bytes long in hex format, I assume the key is 160 bytes (RSA 1280)
Using this method, the provider keeps saying "Bad Version of provider.\r\n".
I've tried several methods, convert it to Base64, simply import it as ASCII / Unicode. Nothing worked so far.
EDIT: My HexToBytes function (which works afaik, it returns me correct 160-b array):
public static byte[] HexToBytes(string pValue)
{
// FIRST. Use StringBuilder.
StringBuilder builder = new StringBuilder();
// SECOND... USE STRINGBUILDER!... and LINQ.
foreach (char c in pValue.Where(IsHexDigit).Select(Char.ToUpper))
{
builder.Append(c);
}
// THIRD. If you have an odd number of characters, something is very wrong.
string hexString = builder.ToString();
if (hexString.Length % 2 == 1)
{
//throw new InvalidOperationException("There is an odd number of hexadecimal digits in this string.");
// I will just add a zero to the end, who cares (0 padding)
Log.WriteLine(LogLevel.Debug, "Hexstring had an odd number of hexadecimal digits.");
hexString += '0';
}
byte[] bytes = new byte[hexString.Length / 2];
// FOURTH. Use the for-loop like a pro :D
for (int i = 0, j = 0; i < bytes.Length; i++, j += 2)
{
string byteString = String.Concat(hexString[j], hexString[j + 1]);
bytes[i] = HexToByte(byteString);
}
return bytes;
}
Your public key is not in the correct format. It is not a CSP blob. It is a DER encoded SubjectPublicKeyInfo structure. You can find source code to parse it or you can write your own. Here is one example of such code.