SSCrypto/OpenSSL to C# Crypto - c#

Has anyone been able to use the SSCrypto Framework for Cocoa to encrypt text and then decrypt it in C#/.NET ? Or can someone offer some guidance?
I'm pretty sure my issue has to do with getting the crypto settings correct but I am far from fluent in Cocoa so I can't really tell what settings are being used in the library. However my attempt at deciphering it seems like md5 hashing, CBC mode, padding with zeros and I have no idea if the IV is set or not...
My C# code looks like this:
public static string Decrypt( string toDecrypt, string key, bool useHashing )
{
byte[] keyArray;
byte[] toEncryptArray = Convert.FromBase64String( toDecrypt );
if( useHashing )
{
MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
keyArray = hashmd5.ComputeHash( UTF8Encoding.UTF8.GetBytes( key ) );
hashmd5.Clear();
}
else
keyArray = UTF8Encoding.UTF8.GetBytes( key );
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
tdes.Key = keyArray;
tdes.Mode = CipherMode.CBC;
tdes.Padding = PaddingMode.Zeros;
ICryptoTransform cTransform = tdes.CreateDecryptor();
byte[] resultArray = cTransform.TransformFinalBlock( toEncryptArray, 0, toEncryptArray.Length );
tdes.Clear();
return UTF8Encoding.UTF8.GetString( resultArray );
}
When I run encryption on the Cocoa side I get the encrypted text:
UMldOZh8sBnHAbfN6E/9KfS1VyWAa7RN
but that won't decrypt on the C# side with the same key.
Any help is appreciated, thanks.

You could use OpenSSL directly in C# with the OpenSSL.NET wrapper!

A couple of things to watch out for:
1- Make sure that you're interpreting the key and data strings correctly. For example, is the key encoded in ASCII instead of UTF8? Does it perhaps represented in binhex format instead?
2- You're not initializing the IV (Initialization Vector) before decrypting. It needs to match the IV you're using to encrypt on the Cocoa side.

IIRC, OpenSSL uses what MS calls PKCS7 padding (though OpenSSL refers to it as PKCS5, and I'm not enough of a standards wonk to care why).

One of the classic issues in moving data back and forth from Mac to PC is byte ordering. You didn't say what the execution platform is for the Cocoa code, but that's something to look out for, especially if it's a PowerPC Mac.

There could be something to do with endianness,
Try to call Array.Reverse before decryption.
var reversedArr = Array.Reverse(toEncrytArray)
byte[] resultArray = cTransform.TransformFinalBlock( reversedArr, 0, reversedArr.Length );

You should really post the Cocoa code, too, to give us a chance to find your problem.
But there are some hints hidden in what you have posted:
Decrypting PyPqLI/d18Q= (base64) with the key and iv gives "97737D09E48B0202" (hex). This looks like the plaintext "97737D09E48B" with PKCS7-padding. So I would start by changing the .NET code to use PaddingMode.PKCS7 and look closely at where you pass the plaintext to the Cocoa code.

Related

AES 256 bit encryption with Rfc2898DeriveBytes

I've run into a confusing use of AES with Rfc2898DeriveBytes. Here's the code that I've found....
public static string Decrypt(string encryptionKey, string cipherValue)
{
byte[] cipherBytes = Convert.FromBase64String(cipherValue);
using (var encryptor = Aes.Create())
{
var pdb = new Rfc2898DeriveBytes(encryptionKey, new byte[] { (13 element byte array) });
if (encryptor != null)
{
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherBytes, 0, cipherBytes.Length);
cs.Close();
}
cipherValue = Encoding.Unicode.GetString(ms.ToArray());
}
}
}
return cipherValue;
}
So, "cipherValue" is an encrypted string...as well as "encryptionKey". The other examples of how to use AES and Rfc2898Derive bytes don't seem to fit this code. The other examples I've seen have something very plain-text in place of the "encryptionKey" parameter up above, but those examples are usually demonstrating encryption rather than decryption.
This code is being used to decrypt a password in the config file of my application. The encryption has already been done and I have no resources available to me to tell me how it was accomplished. I'm assuming that the password was encrypted using the indicated "encryptionKey" and the salt value, along with the default 1000 iterations and the max size Key and IV.
I'm curious mostly about how the "encryptionKey" parameter figures into things. The "cipherValue" is what's being decrypted and is giving me the right output. What methodology was at work here, and what advantages, if any, does this have over the other examples I've seen?
Encryption and security aren't my strong suits yet...let me know if I've left out anything important that might shed more light on this. Thanks in advance!
RFC2898DeriveBytes is a poorly-named implementation of PBKDF2, which is defined in RFC 2898. (Part of why it's poorly named is RFC 2898 also describes the PBKDF1 algorithm, which is what PasswordDeriveBytes uses)
You can read the full algorithm in the linked RFC section, but what it does is use the password as an HMAC key, then take the HMAC of the salt and some state data, then take the HMAC of that, and of that, up to iterations HMACs.
The purpose is to take an input password (low entropy) and predictably turn it into a cryptographic key (with high entropy) in a manner that makes it hard to figure out what the original password is.
As long as all the inputs are the same, it produces the same answer. But changing any input just a little makes a wildly different answer.
If the other approaches you've seen turn the password into a key by just using Encoding.UTF8.GetBytes() (or similar), then yes: this is a better approach (it's harder to break, and it doesn't care how long your password is).

Cannot figure out why PHP to C# .NET will not symmetrically encrypt/decrypt

I've been working on this for a while and I don't know much about either PHP or C#. We are building an application that is using AES 128 CBC mode encryption to store things in the database. One part is PHP and JS, the other is C# .NET WPF.
The person who wrote the PHP used the Mcrypt library to crypt/decrypt. I'm using the Chilkat library to encrypt/decrypt. Chilkat had a default C# example that is supposed to mimic the PHP Mcrypt.
Currently I can symmetrically encrypt/decrypt things on .Net, and .Net can decrypt anything from PHP. However the PHP side cannot decrypt anything that I encrypt to the database from the .Net side.
I've narrowed at least part of down to encoding issues, but I'm not sure how to fix it. The decryption scheme on PHP side usually decrypts to ASCII, but for things that I send it decrypts to UTF-8. I've tried to decrypt it then encode from UTF-8 to ASCII to no avail.
I'll show you the in/outputs and the functions. The IV is being set to 16 ASCII 0s to help my debugging along even though it shouldn't really matter.
input from .Net to mcrypt_encrypt func: string "1220"
output: 3tRIG7qUxUsU7WoXDybRRcdQRobOfeFGtQ438V7XRD8=
Parameter input into database = 'same as above'
input of PHP side decrypt func = 3tRIG7qUxUsU7WoXDybRRcdQRobOfeFGtQ438V7XRD8= 'same as above'
output of the mcrypt_decrypt function = ��J���{$�Z'?�u 'iconv says utf-8 encoding'
Ask for anything else and I'll get it if it would help. I'm sure this is some stupid easy problem I can't see.
PHP side - if it matters the PHP charset is set to UTF-8
function encrypt($input)
{
$this->iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
/*No longer using random iv :(
$this->iv = mcrypt_create_iv($this->iv_size, MCRYPT_RAND);*/
//use 16 zeros
$this->iv = '0000000000000000';
$encrypted = $this->iv .mcrypt_encrypt(MCRYPT_RIJNDAEL_128, KEY, $input, MODE, $this->iv);
//Finally encode this as base 64, see http://php.net/manual/en/function.base64-encode.php
$encrypted = base64_encode($encrypted);
return $encrypted;
}
function decrypt($input)
{
/*Get our message back!!!
First decode the base 64 string. Note, de/encoding bas 64 != encryption*/
$ciphertext_dec = base64_decode($input);
//Get the iv back out for decryption
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
//$iv_dec = substr($ciphertext_dec, 0, $iv_size);*/
$iv_dec = '0000000000000000';
//Now get the text of encrypted message (all but the iv in front)
$ciphertext_dec = substr($ciphertext_dec, $iv_size);
//Now decrypt the message
$plaintext_dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, KEY, $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);
//Test
//test
//$plaintext_dec = iconv("UTF-8", "ASCII", $plaintext_dec);
//echo mb_detect_encoding($plaintext_dec, "auto");
//echo $plaintext_dec;
/*However, we might now have blank space # end of output b/c
remember we de/encrypt via block, so a 10 char long message
could be padded to 16 char long with blank spaces. Get rid of those.*/
$plaintext_dec = trim($plaintext_dec);
//return so we can compare to, i.e., original input
return $plaintext_dec;
}
.NET C#
public string mcrypt_encrypt(string plainText)
{
plainText = Encoding.ASCII.GetString(crypt.IV) + plainText;
byte[] myText = Encoding.ASCII.GetBytes(plainText);
// Do 128-bit AES encryption:
byte[] cipherText = crypt.EncryptBytes(myText);
return Convert.ToBase64String(cipherText);
}
public string mcrypt_decrypt(string cipher_text)
{
byte[] cipher_dec = Convert.FromBase64String(cipher_text);
byte[] plainBytes = crypt.DecryptBytes(cipher_dec);
string decrypted = Encoding.ASCII.GetString(plainBytes);
string plain_text = decrypted.Substring(16, decrypted.Length - 16);
return plain_text.TrimEnd('\0');
}
C# Chilkat init:
// AES is also known as Rijndael.
crypt.CryptAlgorithm = "aes";
// CipherMode may be "ecb" or "cbc"
crypt.CipherMode = "cbc";
// KeyLength may be 128, 192, 256
crypt.KeyLength = 128;
// Pad with NULL bytes (PHP pads with NULL bytes)
crypt.PaddingScheme = 3;
// EncodingMode specifies the encoding of the output for
// encryption, and the input for decryption.
// It may be "hex", "url", "base64", or "quoted-printable".
crypt.EncodingMode = "hex";
// The secret key must equal the size of the key. For
// 256-bit encryption, the binary secret key is 32 bytes.
// For 128-bit encryption, the binary secret key is 16 bytes.
string keyAscii = #"&=*FS6wksG#Zs3qG";
crypt.SecretKey = Encoding.UTF8.GetBytes(keyAscii);
crypt.Charset = "ASCII";
crypt.SetEncodedIV("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", "ascii");
You have a variety of different problems here. The issue causing the problem you seeing in not being able to decrypt in PHP data that was encrypted in .NET is that in the PHP version you're performing a substr on the ciphertext prior to decryption. The comments in the code indicate that you're removing the IV, except that that seems to have only been relevant in a prior version of your code when you were (correctly) using a random IV each time - now you're just discarding the first 16 bytes of ciphertext which due to the mode of operation corrupts the subsequent data block.
The other problem (though masked by the fact that you're discarding the first 16 bytes of the plaintext data when decrypting in .NET) is that the IV you're using in .NET (16 bytes of 0x00) is not the same as the IV you're using in PHP (16 '0' characters = 16 bytes of 0x30).
I would suggest reverting to using a random IV for every encryption and prepending the IV to the ciphertext after encryption When decrypting, read the IV from the first bytes of the ciphertext then decrypt the remainder. This is much more secure than having a static IV, especially when the data being encrypted is likely to often be the same.
It looks like it's a simple character encoding issue. Your C# code is getting the ASCII representation of your string and encrypting that, but your PHP code is decrypting it and expecting it to be UTF-8.
Try swapping your Encoding.ASCII calls for Encoding.UTF8 and make sure your crypt.Charset and crypt.SetEncodedIV are UTF8 as well

AES Decryption - Porting code from C# to Java

I am trying to port the following code from C# into Java. I have made multiple attempts to try and decrypt my encrypted data and I get gibberish every time. The code below uses the org.bouncycastle library and unfortunately there doesn't seem to be a 1-1 mapping between the C# code and the Java code.
I basically know three things:
byte[] file - This contains my encrypted file. Usually a pretty large array of bytes.
byte[] padding - It is 32*bytes* every time and it seems that the first 16 bytes of this are used as the IV.
byte[] aesKey - It is 32*bytes* every time and I do not know how exactly the C# code is using this array.
Original C# Code
private byte[] decryptmessage(byte[] cmessage, byte[] iVector, byte[] m_Key)
{
{
//// randomly generated number acts as inetialization vector
m_IV = new byte[16];
Array.Copy(iVector, 0, m_IV, 0, 16);
// GenerateAESKey();
KeyParameter aesKeyParam = ParameterUtilities.CreateKeyParameter("AES", m_Key);
ParametersWithIV aesIVKeyParam = new ParametersWithIV(aesKeyParam, m_IV);
IBufferedCipher cipher = CipherUtilities.GetCipher("AES/CFB/NoPadding");
cipher.Init(false, aesIVKeyParam);
return cipher.DoFinal(cmessage);
}
}
My attempt in Java
private static byte[] decryptMessage(byte[] file, byte[] iVector, byte[] aesKey) throws Exception {
IvParameterSpec spec = new IvParameterSpec(Arrays.copyOfRange(iVector, 0, 16));
SecretKeySpec key = new SecretKeySpec(Arrays.copyOfRange(aesKey, 0, 16), "AES");
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, key, spec);
return cipher.doFinal(file);
}
P.S: This is the final step of decryption. Before all this I had to take out some initial set of bytes from my encrypted file and decrypt them using an RSA private key to get this AES key.
If someone has a link / document I can read that properly explains the whole process of using AES to encrypt a file, then using RSA on the key and iv to the begining of the encrypted file, I will be extremely happy. I have just been staring at the C# code, I'd like to see something with pictures.
EDIT: Bytes not bits.
EDIT2: Renamed padding to iVector for consistency and correctness.
In the C# code, you initialize the key with 256 bits (32 bytes) and thus get AES-256. In the Java code, you only use 128 bit (16 bytes) and get AES-128.
So the fix is probably:
SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
You might then find that Java doesn't want to use 256 bit keys (for legal reason). You then have to intall the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 6.

Converting DES encryption to RSA encryption

I have an Encyption Part in my C# program that is encrypted with DES encryption. It just encrypts "005". I want this part but with RSA encryption.I don't know how to use C# codes.Can anyone help me
/*********************Message Encryption******************************/
string smsg = "005";
string venc;
DESCryptoServiceProvider iDESCryptoServiceProvider = new DESCryptoServiceProvider();
System.Text.Encoding iEncoding = new System.Text.UTF8Encoding();
byte[] vkey = iEncoding.GetBytes("12345678");
byte[] viv = { 1, 2, 3, 4, 5, 6, 7, 8 };
ICryptoTransform iICryptoTransform = iDESCryptoServiceProvider.CreateEncryptor(vkey, viv);
byte[] vmsg = iEncoding.GetBytes(smsg);
byte[] benc = iICryptoTransform.TransformFinalBlock(vmsg, 0, vmsg.Length);
venc = System.Convert.ToBase64String(benc);
/****************Message Encryption******************************/
This is more of a cryptography task.
Anyway, what you need to do is to swap DESCryptoServiceProvider for RSACryptoServiceProvider. Additionally, as RSA is a PKI cipher while DES is a symmetric block cipher, you will need to create appropriate parameters and so on. So the actual encryption will be also done using different keys, if that was one of your goals to achieve.
Finally, all the necessary code can be found at
http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.aspx
EDIT
Ok, so from what I gather from your responses, You don't actually want to use RSA. Just any other cipher, right?
In that case, what you can choose from, assuming you want to keep .NET' System.Security.Crytography library as your source, you can choose from 4 symmetric encryption (read the same type of) algorithms. These are:
AES
DES
RC2
TripleDES
They have all very similar interface, so just choose one of them (my advice is to pick AES) and rework your code. They have identical interfaces (if I didn't miss something from the quick look), so all you would really need to do is to replace all occurrences of DES with AES. As simple as that.
Hope this helps.

How to use 'System.Security.Cryptography.AesManaged' to encrypt a byte[]?

Basically i want to use System.Security.Cryptography.AesManaged (or a better class, if you think there is one?) to take one byte array and create another encrypted byte array, using a given symmetric key (i assume i'll need one?).
I also will need the way to reverse this procedure.
The point of this is so i can encrypt stored passwords. I assume there's a simple way to do this?
Thanks
EDIT: You really should generate a random IV each time you encrypt, unlike my ancient code below:
Here's what i did in the end, inspired by (an older version of) michael's answer:
private string Encrypt(string input)
{
return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(input)));
}
private byte[] Encrypt(byte[] input)
{
PasswordDeriveBytes pdb = new PasswordDeriveBytes("hjiweykaksd", new byte[] { 0x43, 0x87, 0x23, 0x72, 0x45, 0x56, 0x68, 0x14, 0x62, 0x84 });
MemoryStream ms = new MemoryStream();
Aes aes = new AesManaged();
aes.Key = pdb.GetBytes(aes.KeySize / 8);
aes.IV = pdb.GetBytes(aes.BlockSize / 8);
CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(input, 0, input.Length);
cs.Close();
return ms.ToArray();
}
private string Decrypt(string input)
{
return Encoding.UTF8.GetString(Decrypt(Convert.FromBase64String(input)));
}
private byte[] Decrypt(byte[] input)
{
PasswordDeriveBytes pdb = new PasswordDeriveBytes("hjiweykaksd", new byte[] { 0x43, 0x87, 0x23, 0x72, 0x45, 0x56, 0x68, 0x14, 0x62, 0x84 });
MemoryStream ms = new MemoryStream();
Aes aes = new AesManaged();
aes.Key = pdb.GetBytes(aes.KeySize / 8);
aes.IV = pdb.GetBytes(aes.BlockSize / 8);
CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(input, 0, input.Length);
cs.Close();
return ms.ToArray();
}
EDIT: Noticed eed3si9n's edit... I agree, symmetric encryption is a bad choice for passwords. Use hashes (and not MD5) instead. Here's a very complete example.
A simple example:
byte[] clear = GetCleartext();
HashAlgorithm sha2 = SHA256CryptoServiceProvider.Create();
byte[] hashed = sha2.ComputeHash(clear);
To validate a correct password, you would run the same computation over the provided password, and compare the result to the hash you have in your database.
It's good practice to add salt (random data) to the cleartext to avoid rainbow table attacks. Basically, append a known randomly-generated value, unique to that user, to the cleartext before hashing.
Simple encrypting and decrypting data in C#.
Edit: For passwords, I would recommend using BCrypt instead of doing a two-way encryption, unless you really need to recover the original password. Normally you just need the fact that someone knew the password, not the password itself.
There is a pretty nice C# implementation of symmetric key encryption at https://web.archive.org/web/20120326090435/http://www.superstarcoders.com/blogs/posts/symmetric-encryption-in-c-sharp.aspx . It supports AES, Triple DES, and Rijndael. It has easy to follow functions in the form:
string Encrypt(string plaintext, string password, string salt)
The OP states they have to pass the credentials to another service, which is a completely different issue than password storage and verification.
Depending on how much control you have over the partner service, or what they expose, the best solutions involve a vendor provided or industry standard approaches such as Kerberos, SAML or other stable, secure bearer token means to flow trust. This is a deep topic.
But let's assume you need to pass credentials via Basic Auth SSL/TLS. So now you need to store them securely in a reversible manner. To solve this problem, I have had success with the secret key being conveyed using a certificate private key. This affords some protection of your secret by the operating system and allows for OPS folks to manage the keys, which is desirable. The account used to run your process must be granted rights to see the private key, which then terminates the trust chain at the OS.
You still might have to think about key rotation, which will require you to store a version number with the cipher text.
Also, SecureString might be of interest, but until all .NET API's allow SecureString to be passed as part of a credential, often times you end up with a string on the managed heap you cannot destroy.
Anyway, this isn't a spoon fed answer with code, but from experience, I have found that managing the chain of secrets is always a problem, and if you cannot terminate at a hardened infrastructure such as Active Directory, certificates are the next best thing.

Categories

Resources