C#&;OpenSSL:AES加密的不同输出

C#&;OpenSSL:AES加密的不同输出,c#,openssl,aes,C#,Openssl,Aes,我正在尝试编写一个C#方法,该方法将输出与openssl二进制文件相同的加密字符串(Base64),但我要花很长时间来匹配 大量的终端输出和C#要遵循…:P 我们将使用一个非常激动人心的例子,用密码“123”加密字符串“a” 当我提供静态salt和密码时,首先是openssl(这是命令理想的运行方式,也是我希望C#输出匹配的内容): 第二个是openssl,当我提供我的静态salt时,以及从该salt派生的密钥和iv(从第一个命令的输出得到C+p),但没有密码(因为即使是文档也说这不是一个好主意

我正在尝试编写一个C#方法,该方法将输出与openssl二进制文件相同的加密字符串(Base64),但我要花很长时间来匹配

大量的终端输出和C#要遵循…:P

我们将使用一个非常激动人心的例子,用密码“123”加密字符串“a”

当我提供静态salt和密码时,首先是openssl(这是命令理想的运行方式,也是我希望C#输出匹配的内容):

第二个是openssl,当我提供我的静态salt时,以及从该salt派生的密钥和iv(从第一个命令的输出得到C+p),但没有密码(因为即使是文档也说这不是一个好主意):

我觉得这很奇怪。将第一个命令中的“debug”输出(-p param)中的key和iv值添加到同一个salt中,我不知何故得到了一个不同的salt!(CC77E2A591358A1C与E85778B7FFFFFFFF[这里0xff的4个字节似乎很有趣])

第三个是我的应用程序的输出:

dev@magoo ~# mono aestest.exe "a" "123"
==> INPUT     : a
==> SECRET    : 123
==> SALT      : cc77e2a591358a1c
==> KEY       : 7b2ad689138a44ad32297bbaaa5b0eee
==> IV        : ec4f0416b2e9a9b2feef2e66ff982159
==> ENCRYPTED : 62e3V+0dIrJaLPobGQFVyQ==
因此,当我自己手动指定密钥和IV时,C#与openssl命令的输出相匹配(然后以某种方式生成了一个不同的salt),但这似乎是错误的。在我看来,C#应用程序的输出应该与OpenSSL的第一组输出匹配,不是吗

C#代码:

公共静态字符串加密字符串(字符串明文、字符串密码)
{
字节[]salt=Encryption.GetStaticSalt();
字节[]键,iv;
加密.DeriveKeyAndIV(密码、salt、out-key、out-iv);
var amAes=新的AES管理();
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=新加密流(msTemp、icTransformer、CryptoStreamMode.Write);
var sw=新的StreamWriter(csEncrypt);
sw.Write(纯文本);
sw.Close();
sw.Dispose();
csEncrypt.Clear();
csEncrypt.Dispose();
字节[]bResult=msTemp.ToArray();
string sResult=Convert.ToBase64String(bResult);
if(System.Diagnostics.Debugger.IsAttached)
{
字符串debugDetails=“”;
debugDetails+=“==>输入:”+纯文本+环境.NewLine;
debugDetails+=“==>SECRET:”+密码+环境.NewLine;
debugDetails+=”=>SALT:“+Encryption.ByteArrayToHexString(SALT)+Environment.NewLine;
debugDetails++“==>密钥:“+Encryption.ByteArrayToHexString(amAes.KEY)+”(“+amAes.KeySize.ToString()+”)+Environment.NewLine;
debugDetails+==>IV:“+Encryption.ByteArrayToHexString(amAes.IV)+Environment.NewLine;
debugDetails+=“==>加密:”+sResult;
Console.WriteLine(调试详细信息);
}
返回sResult;
}
私有静态字符串ByteArrayToHexString(字节[]字节)
{
StringBuilder sbHex=新的StringBuilder();
foreach(字节中的字节b)
sbHex.AppendFormat(“{0:x2}”,b);
返回sbHex.ToString();
}
公共静态字节[]GetStaticSalt()
{
//只是随机字节。
返回新字节[]
{
0xcc,
0x77,
0xe2,
0xa5,
0x91,
0x35,
0x8a,
0x1c
};
}
//主要是从http://stackoverflow.com/a/8011654/97423
public static void DeriveKeyAndIV(字符串密码,字节[]bSalt,输出字节[]bKey,输出字节[]bIV)
{
int-keyLen=16;
int-ivLen=16;
byte[]bPassword=Encoding.UTF8.GetBytes(密码);
使用(var md5Gen=MD5.Create())
{
列表lstHashes=新列表(keyLen+ivLen);
字节[]currHash=新字节[0];
int preHashLength=bPassword.Length+bSalt.Length;
byte[]preHash=新字节[preHashLength];
块复制(bPassword,0,preHash,0,bPassword.Length);
Buffer.BlockCopy(bSalt,0,preHash,bPassword.Length,bSalt.Length);
currHash=md5Gen.ComputeHash(preHash);
lstHashes.AddRange(currHash);
而(lstHashes.Count<(keyLen+ivLen))
{
preHashLength=currHash.Length+password.Length+bSalt.Length;
preHash=新字节[preHashLength];
块复制(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=新字节[keyLen];
bIV=新字节[ivLen];
复制到(0,bKey,0,keyLen);
复制到(keyLen,bIV,0,ivLen);
}
}

我是错过了一些非常明显的东西,还是更微妙的东西?我环顾四周,看到了很多关于C#、OpenSSL和AES的内容,但没有看到关于这个特定问题的内容。。。那么,哈尔普?;)

如果您直接指定一个键和IV,那么盐甚至不会起作用。salt需要使用密钥派生函数将密码短语转换为密钥(在OpenSSL的情况下是专有的
EVP_BytesToKey
)。因此,您可能会得到无关紧要的盐输出

现在,1)中OpenSSL的第一个输出包含一个标头(检查ASCII值:)、salt和密码文本,这是以十六进制表示的基64字符串:

53616C7465645F5F CC77E2A591358A1C EB67B757ED1D22B25A2CFA1B190155C9
应用程序3)和2)中的openssl命令都是输出

EB67B757ED1D22B25A2CFA1B190155C9

所以,每一件小事似乎都很好。

现在,让鲍勃·马利再次从我的脑海中消失:)
53616C7465645F5F CC77E2A591358A1C EB67B757ED1D22B25A2CFA1B190155C9
EB67B757ED1D22B25A2CFA1B190155C9