用 PHP 重写 Rijndael 256 C# 加密代码
我有一个用 C# 编写的加密/解密算法 - 我需要能够在 PHP 中生成相同的加密,以便我可以通过 HTTP 发送加密文本以在 C# 端解密。 这是用于加密的 C# 代码。
this.m_plainText = string.Empty;
this.m_passPhrase = "passpharse";
this.m_saltValue = "saltvalue";
this.m_hashAlgorithm = "SHA1";
this.m_passwordIterations = 2;
this.m_initVector = "1a2b3c4d5e6f7g8h";
this.m_keySize = 256;
public string Encrypt()
{
string plainText = this.m_plainText;
string passPhrase = this.m_passPhrase;
string saltValue = this.m_saltValue;
string hashAlgorithm = this.m_hashAlgorithm;
int passwordIterations = this.m_passwordIterations;
string initVector = this.m_initVector;
int keySize = this.m_keySize;
// Convert strings into byte arrays.
// Let us assume that strings only contain ASCII codes.
// If strings include Unicode characters, use Unicode, UTF7, or UTF8
// encoding.
byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
// Convert our plaintext into a byte array.
// Let us assume that plaintext contains UTF8-encoded characters.
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
// First, we must create a password, from which the key will be derived.
// This password will be generated from the specified passphrase and
// salt value. The password will be created using the specified hash
// algorithm. Password creation can be done in several iterations.
PasswordDeriveBytes password = new PasswordDeriveBytes(
passPhrase,
saltValueBytes,
hashAlgorithm,
passwordIterations);
// Use the password to generate pseudo-random bytes for the encryption
// key. Specify the size of the key in bytes (instead of bits).
byte[] keyBytes = password.GetBytes(keySize / 8);
// Create uninitialized Rijndael encryption object.
RijndaelManaged symmetricKey = new RijndaelManaged();
// It is reasonable to set encryption mode to Cipher Block Chaining
// (CBC). Use default options for other symmetric key parameters.
symmetricKey.Mode = CipherMode.CBC;
// Generate encryptor from the existing key bytes and initialization
// vector. Key size will be defined based on the number of the key
// bytes.
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
keyBytes,
initVectorBytes);
// Define memory stream which will be used to hold encrypted data.
MemoryStream memoryStream = new MemoryStream();
// Define cryptographic stream (always use Write mode for encryption).
CryptoStream cryptoStream = new CryptoStream(memoryStream,
encryptor,
CryptoStreamMode.Write);
// Start encrypting.
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
// Finish encrypting.
cryptoStream.FlushFinalBlock();
// Convert our encrypted data from a memory stream into a byte array.
byte[] cipherTextBytes = memoryStream.ToArray();
// Close both streams.
memoryStream.Close();
cryptoStream.Close();
// Convert encrypted data into a base64-encoded string.
string cipherText = Convert.ToBase64String(cipherTextBytes);
// Return encrypted string.
return cipherText;
}
我有一些类似的 PHP 代码可能会有所帮助。它并不完全符合需要,但我认为这可能是一个很好的起点。
<?php
/*
* DEFINE CONSTANTS
*/
$HashPassPhrase = "passpharse";
$HashSalt = "saltvalue";
$HashAlgorithm = "SHA1";
$HashIterations = "2";
$InitVector = "1a2b3c4d5e6f7g8h"; // Must be 16 bytes
$keySize = "256";
class Cipher {
private $securekey, $iv;
function __construct($textkey) {
$this->securekey = hash($HashAlgorithm,$textkey,TRUE);
$this->iv = $InitVector;
}
function encrypt($input) {
return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->securekey, $input, MCRYPT_MODE_CBC, $this->iv));
}
function decrypt($input) {
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $this->securekey, base64_decode($input), MCRYPT_MODE_CBC, $this->iv));
}
}
$cipher = new Cipher($HashPassPhrase);
$encryptedtext = $cipher->encrypt("Text To Encrypt");
echo "->encrypt = $encryptedtext<br />";
$decryptedtext = $cipher->decrypt($encryptedtext);
echo "->decrypt = $decryptedtext<br />";
var_dump($cipher);
?>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您需要按照与 PasswordDeriveBytes。根据 RFC2898 记录,这是为了进行 PBKDF1 密钥派生:
有一些 PHP 库可以实现 PBKDF1,但基于 RFC 从头开始编写一个库非常简单:
已更新
当您发现自己处于这种情况时,您通常会搜索显示每个步骤的值的示例实现。例如 http://www.di-mgt.com.au/ cryptoKDFs.html#examplespbkdf:
现在让我们编写一个 PHP 函数来执行此操作:
现在您可以看到您的方法的错误:您没有指定所有重要的
raw=true
参数sha1。让我们看看我们的函数输出是什么:这个输出正是预期的结果:
接下来,我们可以验证 C# 函数是否执行相同的操作:
这会产生完全相同的输出:
QED
额外说明
请不要这样做如果您确切不知道自己在做什么,请使用加密货币。即使您正确地实现了 PHP,您发布的 C# 代码也存在一些严重的问题。您将字节数组与代表十六进制转储的stirng混合在一起,您使用硬编码的IV而不是从密码和盐中派生它,这总体上是完全错误的。请使用现成的加密方案,例如 SSL 或 S-MIME,并且不要重新发明自己的方案。你会得到它错误。
You need to derive the key from the pass phrase the same way as the C# code does in the PasswordDeriveBytes. This is documented to do PBKDF1 key derivation, as per RFC2898:
there are PHP libraries that implement PBKDF1 out there, but is really simple to write one from scratch based ont he RFC:
Updated
When you find youself in this situation, you usually search for an example implementaiton that shows the values at every step. for instance the one at http://www.di-mgt.com.au/cryptoKDFs.html#examplespbkdf:
So now lets write a PHP function that does this:
Now you can see the errs of your ways: you did not specify the all important
raw=true
parameter tosha1
. Lets see what is our function output:this output exactly the expected result:
Next, we can validate that the C# function does the same:
this produces the very same output:
QED
bonus explanation
Please don't do crypto if you don't know exactly what you're doing. Even after you get the PHP implementaiton correct, your posted C# code has some serious problems. You are mixing byte arrays with stirng representing hex dumps, you use a hard coded IV instead of deriving it from the passphrase and salt, is just overall plain wrong. Please use an off-the shelf encryption scheme, like SSL or S-MIME and do not re-invent your own. You will get it wrong.
看起来您的主要问题是您使用 PHP 的
hash()
代替 C# 端的PasswordDeriveBytes()
步骤。这两种方法并不等同。后者实现了 PBKDF1 密码推导算法,而hash ()
只是一个哈希值。看起来PEAR 可能有 PBKDF1 实现,但否则你可能必须自己写。您还需要确保双方的文本编码一致(如果还没有的话)。
最后,您应该考虑不要做您正在做的事情,因为密码学比看起来更难。由于您使用的是 HTTP,因此您可以使用 SSL 协议来代替编写自己的协议。这将为您带来更好的安全性,并减少底层细节的麻烦,例如保持增量 IV 同步等。
It looks like your main problem is that you're using PHP's
hash()
in place of thePasswordDeriveBytes()
step on the C# side. Those two methods are not equivalent. The latter implements the PBKDF1 password derivation algorithm, whilehash()
is just a hash. It looks like PEAR might have a PBKDF1 implementation, but otherwise you might have to write it yourself.You also need to make sure your text encoding is consistent on both sides, if you haven't already.
Finally, you should consider not doing what you're doing because cryptography is harder than it looks. Since you're using HTTP, you can make use of the SSL protocol in lieu of writing your own. This will net you far better security and less hassle on low-level details like keeping incremental IVs in sync and whatnot.
有充分的理由为什么你不能只使用 http:// php.net/manual/en/function.mcrypt-module-open.php 并使用 rijndael-256 作为算法???
Is there a good reason why you can't just use http://php.net/manual/en/function.mcrypt-module-open.php and use rijndael-256 as the algorithm????
检查 PHP 中的 OpenSSL 例程,它们应该能够处理您需要执行的操作。
Check OpenSSL routines in PHP, they should be able to handle what you need to do.