3

I've got a script that runs in PhantomJS on my server that I need to pass a password to as a command line argument to the script. For those of you unfamiliar with PhantomJS, it's a headless webkit browser that runs server side with no interaction from the client.

In order to load the password into the context of a virtual webpage object in PhantomJS, it needs to be stored in a javascript file that is included on a particular web page object. The password file will only persist long enough to login and will be deleted. I have a fail safe mechanism that deletes the file after the script no longer needs it. I do not want this password to be stored in plain text so I want to use AES encryption.

Passwords will be stored in my database using AES encryption. I know that storing passwords this way isn't the most secure method, but the script needs to know the password so hashing is not an option.

I will be invoking this PhantomJS script from C# in my ASP.NET MVC3 web application and I'd like to pass an encrypted password from C# using either AesManaged or RijndaelManaged. I've got a basic understanding of cryptography and have managed to get some code working in C# and javascript. However, when I encrypt text from C#, I am unable to decrypt it with javascript. Here is some of my C# code for an example:

class Encrypter
{
    public RijndaelManaged rijndael { get; set; }
    public byte[] bytesToDecrypt { get; set; }
    public byte[] bytesToEncrypt { get; set; }

    public Encrypter(string base64key, string base64IV)
    {
        // CBC/128/PKCS7
        rijndael = new RijndaelManaged();
        rijndael.BlockSize = 128;
        rijndael.KeySize = 192;
        rijndael.IV = Convert.FromBase64String(base64IV);
        rijndael.Padding = PaddingMode.PKCS7;
        rijndael.Mode = CipherMode.CBC;
        rijndael.Key = Convert.FromBase64String(base64key);
    }
    public string Encrypt(string strInptData)
    {
        bytesToEncrypt = UTF8Encoding.UTF8.GetBytes(strInptData);

        ICryptoTransform encryptor = rijndael.CreateEncryptor(rijndael.Key, rijndael.IV);
        bytesToDecrypt = encryptor.TransformFinalBlock(bytesToEncrypt, 0, bytesToEncrypt.Length);

        return Convert.ToBase64String(bytesToDecrypt);
    }

    public string Decrypt()
    {
        ICryptoTransform decryptor = rijndael.CreateDecryptor(rijndael.Key, rijndael.IV);

        byte[] decryptBytes = decryptor.TransformFinalBlock(bytesToDecrypt, 0, bytesToDecrypt.Length);
        // return original string
        return UTF8Encoding.UTF8.GetString(decryptBytes);
    }


    public string ByteArrayToString(byte[] array)
    {
        StringBuilder sb = new StringBuilder();

        sb.Append('[');
        foreach (byte b in array)
        {
            sb.Append(b);
            sb.Append(", ");
        }
        sb.Append("] length : " + array.Length);
        return sb.ToString();
    }
}

The javascript code uses the slowAES implementation:

$('#encrypt').click(function () {
                var plainText = $('#plaintext').val();
                var $encrypted = $('#encryptedtext');
                var bytesToEncrypt = cryptoHelpers.convertStringToByteArray(plainText);
                var key = cryptoHelpers.base64.decode("bALREf3IwJhzO8JdUwZ55coMydj2YD8R");
                var iv = cryptoHelpers.base64.decode("v/VCTAlV5+kexBFN16WY5A==");

                var result = slowAES.encrypt(bytesToEncrypt, 
                                           slowAES.modeOfOperation.CBC,
                                           key,
                                           slowAES.aes.keySize.SIZE_192,
                                           iv);
                var base64Result = cryptoHelpers.base64.encode(result);
                var hexKey = cryptoHelpers.toHex(key);
                $encrypted.val(base64Result);
                $('div#results').append('<p>bytes to enrypt: ' + bytesToEncrypt + ' length ' + bytesToEncrypt.length + '</p>');
                $('div#results').append('<p>Key: ' + key + '</p>');
                $('div#results').append('<p>IV: ' + iv + '</p>');
                $('div#results').append('<p>Result: ' + result['cipher'] + '</p>');
                $('div#results').append('<p>Hex Key: ' + hexKey + '</p>');
            });
            $('#decrypt').click(function () {
                var $plain = $('#plaintext');
                var encrypted = $('#encryptedtext').val();
                var bytesToDecrypt = cryptoHelpers.base64.decode(encrypted);
                var key = cryptoHelpers.base64.decode("bALREf3IwJhzO8JdUwZ55coMydj2YD8R");
                var iv = cryptoHelpers.base64.decode("v/VCTAlV5+kexBFN16WY5A==");

                var result = slowAES.decrypt(bytesToDecrypt, 
                                           slowAES.modeOfOperation.CBC,
                                           key,
                                           slowAES.aes.keySize.SIZE_192,
                                           iv);
                var plainresult = cryptoHelpers.convertByteArrayToString(result);
                $plain.val(plainresult);
            });

This code for the javascript was merely setup so I could do quick tests of encryption and decryption. I will be processing the password as a command line argument from PhantomJS after I've got the encryption and decryption working between C# and javascript.

The key and IV are:

string base64key = "bALREf3IwJhzO8JdUwZ55coMydj2YD8R";
string base64iv = "v/VCTAlV5+kexBFN16WY5A==";

After running the encryption of the text 'test', Here's the output for the C# code:

BytesToEncrypt: [116, 101, 115, 116, ] length : 4
Base64 Encrypted String: RxNZztdUgbx3KPdvVvMmBg== 
Key: [108, 2, 209, 17, 253, 200, 192, 152, 115, 59, 194, 93, 83, 6, 121, 229, 202, 12, 201, 216, 246, 96, 63, 17 ] length : 24 
IV: [191, 245, 66, 76, 9, 85, 231, 233, 30, 196, 17, 77, 215, 165, 152, 228 ] length : 16 
Result: [71, 19, 89, 206, 215, 84, 129, 188, 119, 40, 247, 111, 86, 243, 38, 6 ] length : 16 
Decrypted: test

Here's the output from the javascript:

Bytes to Encrypt: 116,101,115,116,12,12,12,12,12,12,12,12,12,12,12,12 length 16

Base64 Encrypted String: VRPeZsEJpP7OURwg3FdI8g==

Key: 108,2,209,17,253,200,192,152,115,59,194,93,83,6,121,229,202,12,201,216,246,96,63,17 length 24

IV: 191,245,66,76,9,85,231,233,30,196,17,77,215,165,152,228 length 16

Result: 85,19,222,102,193,9,164,254,206,81,28,32,220,87,72,242 length 16

I've applied PKCS7 padding to the C# code because slowAES uses PKCS7 padding. I've even tried using my own implementation of PKCS7 padding and still haven't gotten the same result byte array. Please let me know if I'm missing something. Thanks much.

Cameron Tinker
  • 9,119
  • 10
  • 42
  • 80
  • 1
    isn't encryption pointless because you are sending the keys unencrypted through the air? – Thom Wiggers Nov 16 '11 at 19:38
  • Why are you sending the password to the client? That sounds like a REALLY, REALLY BAD IDEA... Plus how can you garentee the javascript file containing the password will be deleted? What if a user copies the file? – Stuart Blackler Nov 16 '11 at 19:41
  • Like I said, the above code is simply for my testing. The actual keys will be stored in the sender, the C# code, and the receiver, the javscript code. The key will only be known the sender and receiver. I have no intention of sending keys unencrypted over the air. That indeed would be pointless. My question more pertains to the fact that I can't get the same result vector from both implementations. – Cameron Tinker Nov 16 '11 at 19:44
  • Also, PhantomJS runs server side with no client interaction. The client will never see the password. I just want to encrypt the password stored on my server that PhantomJS will pickup in my script so that the passwords that are stored temporarily for the script are not plaintext. – Cameron Tinker Nov 16 '11 at 19:48
  • Are you sure they are using the same mode of encryption? Eg. both CBC. Cannot stress how much of a bad idea this is – Stuart Blackler Nov 16 '11 at 19:49
  • They are using the same mode of encryption. Both are CBC. On the server, there is only one administrator account so there is no concern about anyone else having access to the files on the server. Also, I have a fail safe deletion routine that deletes the password file after the script no longer needs it. – Cameron Tinker Nov 16 '11 at 19:55

1 Answers1

2

Don't store passwords in the database! Store a salt and hash, and use the salt to derive the hash.

Here on SO: Password hashing, salt and storage of hashed values

Community
  • 1
  • 1
Jason
  • 15,640
  • 3
  • 46
  • 70
  • I agree that password hashing and salt is the most secure way, but my script that runs on the server must know the password so hashing cannot work in this case. – Cameron Tinker Nov 16 '11 at 19:51
  • 1
    "my script that runs on the server must know the password" screams entirely wrong. – Chris Marisic Nov 16 '11 at 20:18
  • totally agree, if you control both script & server you can probably design around whatever limitation you think you are facing. – Jason Nov 16 '11 at 21:06
  • Thank you for your suggestions. I will look at changing my design such that it will be simpler and more secure. I apologize for any confusion or security concerns caused by my question. – Cameron Tinker Nov 16 '11 at 22:03