views:

32

answers:

2

I need to encrypt text (a password specifically) correctly so a 3rd party can decrypt it. They have provided me with the key they use to decrypt and told me that they will be decrypting it on their end with OpenSSL.

I have been trying to get the AESManaged, and the RijndaelManaged classes in System.Security.Cryptography to create something that is decryptable but not being very successful.

The OpenSSL example for encryption they provided was:

echo "password" | enc -base64 -aes-256-cbc -k "providedKey" -p -md sha1

So I need to create a CryptoStream that is an AES encryptor with a keysize of 256, and a cipher mode of CBC. And then base64 encode the resulting byte array. That part is pretty straight forward.

What I'm not sure how to create is the key and I'm looking for a point in the right direction.

I noticed that all the base64 decode's of text created by these parameters in OpenSSL started with the same 8 characters "Salted__". So I'm guessing that is the Salt used to generate the key.

Anyone know how to create an SHA1 key with the private key and "Salted__"?

I'm aware of the OpenSSL.Net project to wrap the OpenSSL dll's but I would like to avoid going that route if it is possible.

A: 

I poked around a bit in the relevant openssl source (apps/enc.c), and I think openssl uses a somewhat proprietary approach. I admit I was too lazy to figure out all the details, but they are there. It appears the ASCII string "Salted__" followed by the 8 random salt bytes are prepended to the file. An AES key and an AES iv are derived from the password and salt using an openssl method named EVP_BytesToKey(). The algorithm is described in that man page. In the enc.c source code from version 1.0.0a this part starts at line 510.

Hopefully this can get you started.

GregS
A: 

Thanks to GregS's Point in the right direction, It was pretty easy to replicate the Key and Initial Vector generation that was happening in EVP_BytesToKey().

Basically they are creating a 48 byte array (32 byte array for the key and a 16 byte array for the IV) and using SHA1 to hash the private key and the 8 salt bytes into that buffer. The first hash is just the private key and salt and the successive hashes are the last hash generated concatenated with the key and salt bytes until the 48 byte array is filled.

The key is then just the first 32 bytes and the IV is the last 16 bytes.

Using the AESManaged class from System.Security.Crytpography and the key and IV derived from this method, I was able to encrypt my passwords in a way that the 3rd party using OpenSSL libraries could decrypt.

Here is the algorithm I used to derive the key and initial vector:

    /// <summary>
    /// Derives the key and IV.
    /// </summary>
    /// <param name="saltBytes">The salt bytes.</param>
    /// <param name="privateKeyBytes">The private key bytes.</param>
    /// <param name="iv">The iv.</param>
    /// <returns>The Key</returns>
    private static byte[] DeriveKeyAndIV(byte[] saltBytes, byte[] privateKeyBytes, out byte[] iv)
    {
        // we are creating a 16 byte initial vector and a 32 byte key 
        const int ivLength   = 16;
        iv = new byte[ivLength];
        const int keyLength  = 32;
        var key = new byte[keyLength];

        //SHA1 creates a 20 byte hash
        const int hashLength = 20;

        // container to store the hashed values
        var keyContainer = new byte[keyLength + ivLength];

        // munge together the privateKey and salt
        var privateKeyAndSalt = new byte[privateKeyBytes.Length + saltBytes.Length];
        Array.Copy(privateKeyBytes, privateKeyAndSalt, privateKeyBytes.Length);
        Array.Copy(saltBytes, 0, privateKeyAndSalt, privateKeyBytes.Length, saltBytes.Length);

        // use SHA1 crypto to match the -md SHA1 command line.
        var sha1 = new SHA1CryptoServiceProvider();

        // hashtarget holds the successive hash's source bytes.
        var hashtarget = new byte[hashLength + privateKeyAndSalt.Length];

        byte[] currentHash = null;
        var bytesCopied = 0;

        // do the hashing until we fill the container
        while (bytesCopied < (ivLength + keyLength))
        {
            // Hash(0) is an empty set so just concatenate private key and salt.
            if (currentHash == null)
            {
                currentHash = sha1.ComputeHash(privateKeyAndSalt);
            }
            else
            {
                // successive hashes are done on Hash(prev) + private key + salt.
                Array.Copy(currentHash, hashtarget, currentHash.Length);
                Array.Copy(privateKeyAndSalt, 0, hashtarget, currentHash.Length, privateKeyAndSalt.Length);
                currentHash = hashtarget;
                currentHash = sha1.ComputeHash(currentHash);
            }
            var copyAmount = Math.Min(currentHash.Length, keyContainer.Length - bytesCopied);
            Array.Copy(currentHash, 0, keyContainer, bytesCopied, copyAmount);
            bytesCopied += copyAmount;
        }

        // split out bytes in the container. first 32 are key, last 16 are iv.
        Array.Copy(keyContainer, 0, key, 0, key.Length);
        Array.Copy(keyContainer, key.Length, iv, 0, iv.Length);

        return key;
    }  
Jeff Alexander