2012-11-28 132 views
4

我試圖寫一個C#方法,將吐出同樣的加密字符串(Base64編碼),如OpenSSL的二進制文件沒有,但我有一個時間把事情匹配赫克向上。C#和OpenSSL:它支持AES加密不同的輸出

大量的終端輸出和C#的遵循...:P

我們要使用加密字符串「a」,密碼爲「123」的非常精彩的例子。

首先是OpenSSL的時候我提供我的靜態鹽和密碼(這是怎樣的命令將被理想的運行,而我希望我的C#輸出匹配到什麼):

[email protected] ~# echo -n a | openssl enc -aes-128-cbc -S cc77e2a591358a1c -pass pass:123 -a -p 
salt=CC77E2A591358A1C 
key=7B2AD689138A44AD32297BBAAA5B0EEE 
iv =EC4F0416B2E9A9B2FEEF2E66FF982159 
U2FsdGVkX1/Md+KlkTWKHOtnt1ftHSKyWiz6GxkBVck= 

第二個是openssl,當我提供我的靜態鹽,以及從該鹽派生的密鑰和iv(從第一個命令的輸出C + p'd)但沒有密碼(因爲即使文檔說這將是一個不 - 好主意):

[email protected] ~# echo -n a | openssl enc -aes-128-cbc -S cc77e2a591358a1c -K 7b2ad689138a44ad32297bbaaa5b0eee -iv ec4f0416b2e9a9b2feef2e66ff982159 -a -p 
salt=E85778B7FFFFFFFF 
key=7B2AD689138A44AD32297BBAAA5B0EEE 
iv =EC4F0416B2E9A9B2FEEF2E66FF982159 
62e3V+0dIrJaLPobGQFVyQ== 

這讓我覺得很奇怪。將第一個命令中的「調試」輸出(-p參數)中的鍵和iv值添加到相同的鹽中,我以某種方式得到不同的鹽! (CC77E2A591358A1C和E85778B7FFFFFFFF [0xff的4個字節在這裏似乎很有意思])。

第三是我的應用程序的輸出:

[email protected] ~# mono aestest.exe "a" "123" 
==> INPUT  : a 
==> SECRET : 123 
==> SALT  : cc77e2a591358a1c 
==> KEY  : 7b2ad689138a44ad32297bbaaa5b0eee 
==> IV  : ec4f0416b2e9a9b2feef2e66ff982159 
==> ENCRYPTED : 62e3V+0dIrJaLPobGQFVyQ== 

因此,C#匹配openssl命令的輸出,當我手動指定的密鑰和IV自己(即然後以某種方式產生不同的鹽) ,但似乎有點不對。在我看來,C#應用程序的輸出應該與第一組OpenSSL輸出相匹配,不是嗎?

C#代碼:

public static string EncryptString(string plainText, string password) 
{ 
    byte[] salt = Encryption.GetStaticSalt(); 
    byte[] key, iv; 

    Encryption.DeriveKeyAndIV(password, salt, out key, out iv); 

    var amAes = new AesManaged(); 
    amAes.Mode = CipherMode.CBC; 
    amAes.KeySize = 128; 
    amAes.BlockSize = 128; 
    amAes.Key = key; 
    amAes.IV = iv; 

    var icTransformer = amAes.CreateEncryptor(); 
    var msTemp = new MemoryStream(); 

    var csEncrypt = new CryptoStream(msTemp, icTransformer, CryptoStreamMode.Write); 
    var sw = new StreamWriter(csEncrypt); 
    sw.Write(plainText); 
    sw.Close(); 
    sw.Dispose(); 

    csEncrypt.Clear(); 
    csEncrypt.Dispose(); 

    byte[] bResult = msTemp.ToArray(); 
    string sResult = Convert.ToBase64String(bResult); 

    if (System.Diagnostics.Debugger.IsAttached) 
    { 
    string debugDetails = ""; 
    debugDetails += "==> INPUT  : " + plainText + Environment.NewLine; 
    debugDetails += "==> SECRET : " + password + Environment.NewLine; 
    debugDetails += "==> SALT  : " + Encryption.ByteArrayToHexString(salt) + Environment.NewLine; 
    debugDetails += "==> KEY  : " + Encryption.ByteArrayToHexString(amAes.Key) + " (" + amAes.KeySize.ToString() + ")" + Environment.NewLine; 
    debugDetails += "==> IV  : " + Encryption.ByteArrayToHexString(amAes.IV) + Environment.NewLine; 
    debugDetails += "==> ENCRYPTED : " + sResult; 
    Console.WriteLine(debugDetails); 
    } 

    return sResult; 
} 

private static string ByteArrayToHexString(byte[] bytes) 
{ 
    StringBuilder sbHex = new StringBuilder(); 

    foreach (byte b in bytes) 
    sbHex.AppendFormat("{0:x2}", b); 

    return sbHex.ToString(); 
} 

public static byte[] GetStaticSalt() 
{ 
    // Just random bytes. 
    return new byte[] 
    { 
    0xcc, 
    0x77, 
    0xe2, 
    0xa5, 
    0x91, 
    0x35, 
    0x8a, 
    0x1c 
    }; 
} 

// largely hijacked from http://stackoverflow.com/a/8011654/97423 
public static void DeriveKeyAndIV(string password, byte[] bSalt, out byte[] bKey, out byte[] bIV) 
{ 
    int keyLen = 16; 
    int ivLen = 16; 

    byte[] bPassword = Encoding.UTF8.GetBytes(password); 

    using (var md5Gen = MD5.Create()) 
    { 
    List<byte> lstHashes = new List<byte>(keyLen + ivLen); 
    byte[] currHash = new byte[0]; 

    int preHashLength = bPassword.Length + bSalt.Length; 
    byte[] preHash = new byte[preHashLength]; 

    Buffer.BlockCopy(bPassword, 0, preHash, 0, bPassword.Length); 
    Buffer.BlockCopy(bSalt, 0, preHash, bPassword.Length, bSalt.Length); 

    currHash = md5Gen.ComputeHash(preHash); 
    lstHashes.AddRange(currHash); 

    while (lstHashes.Count < (keyLen + ivLen)) 
    { 
     preHashLength = currHash.Length + password.Length + bSalt.Length; 
     preHash = new byte[preHashLength]; 

     Buffer.BlockCopy(currHash, 0, preHash, 0, currHash.Length); 
     Buffer.BlockCopy(bPassword, 0, preHash, currHash.Length, password.Length); 
     Buffer.BlockCopy(bSalt, 0, preHash, currHash.Length + password.Length, bSalt.Length); 

     currHash = md5Gen.ComputeHash(preHash); 
     lstHashes.AddRange(currHash); 
    } 

    bKey = new byte[keyLen]; 
    bIV = new byte[ivLen]; 

    lstHashes.CopyTo(0, bKey, 0, keyLen); 
    lstHashes.CopyTo(keyLen, bIV, 0, ivLen); 
    } 
} 

我缺少的東西真的很明顯這裏,或者是這個東西更加微妙?我環顧四周,已經看到很多關於C#,OpenSSL和AES的內容,但沒有涉及到這個具體問題......所以,halp? ;)

回答

1

如果你直接指定了一個鍵和IV,那麼鹽甚至不會發揮作用。使用密鑰派生函數(在OpenSSL的情況下專有的EVP_BytesToKey),需要salt將密碼短語轉換爲密鑰。因此,你可能會得到鹽的無關緊要的輸出。

現在的OpenSSL在1中的第一輸出)包含一個標題(檢查ASCII值:),所述鹽,然後將密文,這是在底座64的字符串以十六進制:

53616C7465645F5F CC77E2A591358A1C EB67B757ED1D22B25A2CFA1B190155C9 

你的應用3)和openssl命令中的2)都輸出

EB67B757ED1D22B25A2CFA1B190155C9 

所以每一件小事似乎都沒事。

+2

現在讓鮑勃馬利再次離開我的頭:) –