在 PHP 中使用 RSA 加密和解密文本
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4484246/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Encrypt and Decrypt text with RSA in PHP
提问by mic-kul
Is there any class for PHP 5.3 that provides RSA encryption/decryption without padding?
是否有任何 PHP 5.3 类可以提供无需填充的 RSA 加密/解密?
I've got private and public key, p,q, and modulus.
我有私钥和公钥、p、q 和模数。
回答by
You can use phpseclib, a pure PHP RSA implementation:
您可以使用phpseclib,一个纯 PHP RSA 实现:
<?php
include('Crypt/RSA.php');
$privatekey = file_get_contents('private.key');
$rsa = new Crypt_RSA();
$rsa->loadKey($privatekey);
$plaintext = new Math_BigInteger('aaaaaa');
echo $rsa->_exponentiate($plaintext)->toBytes();
?>
回答by Pratik
Security warning:This code snippet is vulnerable to Bleichenbacher's 1998 padding oracle attack. See this answerfor better security.
安全警告:此代码片段容易受到Bleichenbacher 1998 年的 padding oracle 攻击。请参阅此答案以获得更好的安全性。
class MyEncryption
{
public $pubkey = '...public key here...';
public $privkey = '...private key here...';
public function encrypt($data)
{
if (openssl_public_encrypt($data, $encrypted, $this->pubkey))
$data = base64_encode($encrypted);
else
throw new Exception('Unable to encrypt data. Perhaps it is bigger than the key size?');
return $data;
}
public function decrypt($data)
{
if (openssl_private_decrypt(base64_decode($data), $decrypted, $this->privkey))
$data = $decrypted;
else
$data = '';
return $data;
}
}
回答by Scott Arciszewski
No application written in 2017 (or thereafter) that intends to incorporate serious cryptography should use RSA any more. There are better options for PHP public-key cryptography.
2017 年(或之后)编写的任何打算合并严格密码学的应用程序都不应再使用 RSA。PHP public-key cryptography有更好的选择。
There are two big mistakes that people make when they decide to encrypt with RSA:
人们在决定使用 RSA 加密时会犯两个大错误:
- Developers choose the wrong padding mode.
- Since RSA cannot, by itself, encrypt very long strings, developers will often break a string into small chunks and encrypt each chunk independently. Sort of like ECB mode.
The Best Alternative: sodium_crypto_box_seal()
(libsodium)
最佳选择:sodium_crypto_box_seal()
(libsodium)
$keypair = sodium_crypto_box_keypair();
$publicKey = sodium_crypto_box_publickey($keypair);
// ...
$encrypted = sodium_crypto_box_seal(
$plaintextMessage,
$publicKey
);
// ...
$decrypted = sodium_crypto_box_seal_open(
$encrypted,
$keypair
);
Simple and secure. Libsodium will be available in PHP 7.2, or through PECL for earlier versions of PHP. If you need a pure-PHP polyfill, get paragonie/sodium_compat.
简单安全。Libsodium 将在 PHP 7.2 中可用,或通过 PECL 用于早期版本的 PHP。如果您需要纯 PHP polyfill,请获取paragonie/sodium_compat。
Begrudgingly: Using RSA Properly
不情愿地:正确使用 RSA
The only reason to use RSA in 2017 is, "I'm forbidden to install PECL extensions and therefore cannot use libsodium, and for some reason cannot use paragonie/sodium_compateither."
在 2017 年使用 RSA 的唯一原因是,“我被禁止安装 PECL 扩展,因此不能使用 libsodium,并且由于某种原因也不能使用paragonie/sodium_compat。”
Your protocol should look something like this:
您的协议应如下所示:
- Generate a random AES key.
- Encrypt your plaintext message with the AES key, using an AEAD encryption mode or, failing that, CBC then HMAC-SHA256.
- Encrypt your AES key (step 1) with your RSA public key, using RSAES-OAEP + MGF1-SHA256
- Concatenate your RSA-encrypted AES key (step 3) and AES-encrypted message (step 2).
- 生成随机 AES 密钥。
- 使用 AES 密钥加密您的明文消息,使用 AEAD 加密模式,或者,如果失败,则使用 CBC 和 HMAC-SHA256。
- 使用RSAES-OAEP + MGF1-SHA256使用您的 RSA 公钥加密您的 AES 密钥(步骤 1)
- 连接 RSA 加密的 AES 密钥(步骤 3)和 AES 加密的消息(步骤 2)。
Instead of implementing this yourself, check out EasyRSA.
Further reading: Doing RSA in PHP correctly.
进一步阅读:在 PHP 中正确执行 RSA。
回答by M_R_K
If you are using PHP >= 7.2 consider using inbuilt sodium core extension for encrption.
如果您使用 PHP >= 7.2,请考虑使用内置的sodium 核心扩展进行加密。
It is modern and more secure. You can find more information here - http://php.net/manual/en/intro.sodium.php. and here - https://paragonie.com/book/pecl-libsodium/read/00-intro.md
它是现代的,更安全。您可以在此处找到更多信息 - http://php.net/manual/en/intro.sodium.php。在这里 - https://paragonie.com/book/pecl-libsodium/read/00-intro.md
Example PHP 7.2 sodium encryption class -
示例 PHP 7.2 钠加密类 -
<?php
/**
* Simple sodium crypto class for PHP >= 7.2
* @author MRK
*/
class crypto {
/**
*
* @return type
*/
static public function create_encryption_key() {
return base64_encode(sodium_crypto_secretbox_keygen());
}
/**
* Encrypt a message
*
* @param string $message - message to encrypt
* @param string $key - encryption key created using create_encryption_key()
* @return string
*/
static function encrypt($message, $key) {
$key_decoded = base64_decode($key);
$nonce = random_bytes(
SODIUM_CRYPTO_SECRETBOX_NONCEBYTES
);
$cipher = base64_encode(
$nonce .
sodium_crypto_secretbox(
$message, $nonce, $key_decoded
)
);
sodium_memzero($message);
sodium_memzero($key_decoded);
return $cipher;
}
/**
* Decrypt a message
* @param string $encrypted - message encrypted with safeEncrypt()
* @param string $key - key used for encryption
* @return string
*/
static function decrypt($encrypted, $key) {
$decoded = base64_decode($encrypted);
$key_decoded = base64_decode($key);
if ($decoded === false) {
throw new Exception('Decryption error : the encoding failed');
}
if (mb_strlen($decoded, '8bit') < (SODIUM_CRYPTO_SECRETBOX_NONCEBYTES + SODIUM_CRYPTO_SECRETBOX_MACBYTES)) {
throw new Exception('Decryption error : the message was truncated');
}
$nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
$ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
$plain = sodium_crypto_secretbox_open(
$ciphertext, $nonce, $key_decoded
);
if ($plain === false) {
throw new Exception('Decryption error : the message was tampered with in transit');
}
sodium_memzero($ciphertext);
sodium_memzero($key_decoded);
return $plain;
}
}
Sample Usage -
示例用法 -
<?php
$key = crypto::create_encryption_key();
$string = 'Sri Lanka is a beautiful country !';
echo $enc = crypto::encrypt($string, $key);
echo crypto::decrypt($enc, $key);
回答by Jerry Wickey
Yes. Look at http://jerrywickey.com/test/testJerrysLibrary.php
是的。看看http://jerrywickey.com/test/testJerrysLibrary.php
It gives sample code examples for RSA encryption and decryption in PHP as well as RSA encryption in javascript.
它给出了 PHP 中的 RSA 加密和解密以及 javascript 中的 RSA 加密的示例代码示例。
If you want to encrypt text instead of just base 10 numbers, you'll also need a base to base conversion. That is convert text to a very large number. Text is really just writing in base 63. 26 lowercase letters plus 26 uppercase + 10 numerals + space character. The code for that is below also.
如果您想加密文本而不仅仅是基数为 10 的数字,您还需要一个基数到基数的转换。那就是将文本转换为一个非常大的数字。文本实际上只是以 63 进制书写。26 个小写字母加上 26 个大写字母 + 10 个数字 + 空格字符。代码也在下面。
The $GETn parameter is a file name that holds keys for the cryption functions. If you don't figure it out, ask. I'll help.
$GETn 参数是一个文件名,用于保存加密函数的密钥。如果你不明白,问。我会帮忙的。
I actually posted this whole encryption library yesterday, but Brad Larson a mod, killed it and said this kind of stuff isn't really what Stack Overflow is about. But you can still find all the code examples and the whole function library to carry out client/server encryption decryption for AJAX at the link above.
我昨天实际上发布了整个加密库,但是 Brad Larson 一个 mod,杀死了它并说这种东西并不是 Stack Overflow 的真正含义。但是您仍然可以在上面的链接中找到所有代码示例和整个函数库,用于对 AJAX 进行客户端/服务器加密解密。
function RSAencrypt( $num, $GETn){
if ( file_exists( 'temp/bigprimes'.hash( 'sha256', $GETn).'.php')){
$t= explode( '>,', file_get_contents('temp/bigprimes'.hash( 'sha256', $GETn).'.php'));
return JL_powmod( $num, $t[4], $t[10]);
}else{
return false;
}
}
function RSAdecrypt( $num, $GETn){
if ( file_exists( 'temp/bigprimes'.hash( 'sha256', $GETn).'.php')){
$t= explode( '>,', file_get_contents('temp/bigprimes'.hash( 'sha256', $GETn).'.php'));
return JL_powmod( $num, $t[8], $t[10]);
}else{
return false;
}
}
function JL_powmod( $num, $pow, $mod) {
if ( function_exists('bcpowmod')) {
return bcpowmod( $num, $pow, $mod);
}
$result= '1';
do {
if ( !bccomp( bcmod( $pow, '2'), '1')) {
$result = bcmod( bcmul( $result, $num), $mod);
}
$num = bcmod( bcpow( $num, '2'), $mod);
$pow = bcdiv( $pow, '2');
} while ( bccomp( $pow, '0'));
return $result;
}
function baseToBase ($message, $fromBase, $toBase){
$from= strlen( $fromBase);
$b[$from]= $fromBase;
$to= strlen( $toBase);
$b[$to]= $toBase;
$result= substr( $b[$to], 0, 1);
$f= substr( $b[$to], 1, 1);
$tf= digit( $from, $b[$to]);
for ($i=strlen($message)-1; $i>=0; $i--){
$result= badd( $result, bmul( digit( strpos( $b[$from], substr( $message, $i, 1)), $b[$to]), $f, $b[$to]), $b[$to]);
$f= bmul($f, $tf, $b[$to]);
}
return $result;
}
function digit( $from, $bto){
$to= strlen( $bto);
$b[$to]= $bto;
$t[0]= intval( $from);
$i= 0;
while ( $t[$i] >= intval( $to)){
if ( !isset( $t[$i+1])){
$t[$i+1]= 0;
}
while ( $t[$i] >= intval( $to)){
$t[$i]= $t[$i] - intval( $to);
$t[$i+1]++;
}
$i++;
}
$res= '';
for ( $i=count( $t)-1; $i>=0; $i--){
$res.= substr( $b[$to], $t[$i], 1);
}
return $res;
}
function badd( $n1, $n2, $nbase){
$base= strlen( $nbase);
$b[$base]= $nbase;
while ( strlen( $n1) < strlen( $n2)){
$n1= substr( $b[$base], 0, 1) . $n1;
}
while ( strlen( $n1) > strlen( $n2)){
$n2= substr( $b[$base], 0, 1) . $n2;
}
$n1= substr( $b[$base], 0, 1) . $n1;
$n2= substr( $b[$base], 0, 1) . $n2;
$m1= array();
for ( $i=0; $i<strlen( $n1); $i++){
$m1[$i]= strpos( $b[$base], substr( $n1, (strlen( $n1)-$i-1), 1));
}
$res= array();
$m2= array();
for ($i=0; $i<strlen( $n1); $i++){
$m2[$i]= strpos( $b[$base], substr( $n2, (strlen( $n1)-$i-1), 1));
$res[$i]= 0;
}
for ($i=0; $i<strlen( $n1) ; $i++){
$res[$i]= $m1[$i] + $m2[$i] + $res[$i];
if ($res[$i] >= $base){
$res[$i]= $res[$i] - $base;
$res[$i+1]++;
}
}
$o= '';
for ($i=0; $i<strlen( $n1); $i++){
$o= substr( $b[$base], $res[$i], 1).$o;
}
$t= false;
$o= '';
for ($i=strlen( $n1)-1; $i>=0; $i--){
if ($res[$i] > 0 || $t){
$o.= substr( $b[$base], $res[$i], 1);
$t= true;
}
}
return $o;
}
function bmul( $n1, $n2, $nbase){
$base= strlen( $nbase);
$b[$base]= $nbase;
$m1= array();
for ($i=0; $i<strlen( $n1); $i++){
$m1[$i]= strpos( $b[$base], substr($n1, (strlen( $n1)-$i-1), 1));
}
$m2= array();
for ($i=0; $i<strlen( $n2); $i++){
$m2[$i]= strpos( $b[$base], substr($n2, (strlen( $n2)-$i-1), 1));
}
$res= array();
for ($i=0; $i<strlen( $n1)+strlen( $n2)+2; $i++){
$res[$i]= 0;
}
for ($i=0; $i<strlen( $n1) ; $i++){
for ($j=0; $j<strlen( $n2) ; $j++){
$res[$i+$j]= ($m1[$i] * $m2[$j]) + $res[$i+$j];
while ( $res[$i+$j] >= $base){
$res[$i+$j]= $res[$i+$j] - $base;
$res[$i+$j+1]++;
}
}
}
$t= false;
$o= '';
for ($i=count( $res)-1; $i>=0; $i--){
if ($res[$i]>0 || $t){
$o.= substr( $b[$base], $res[$i], 1);
$t= true;
}
}
return $o;
}
回答by Coderji
I have difficulty in decrypting a long string that is encrypted in python. Here is the python encryption function:
我很难解密在 python 中加密的长字符串。这是python加密函数:
def RSA_encrypt(public_key, msg, chunk_size=214):
"""
Encrypt the message by the provided RSA public key.
:param public_key: RSA public key in PEM format.
:type public_key: binary
:param msg: message that to be encrypted
:type msg: string
:param chunk_size: the chunk size used for PKCS1_OAEP decryption, it is determined by \
the private key length used in bytes - 42 bytes.
:type chunk_size: int
:return: Base 64 encryption of the encrypted message
:rtype: binray
"""
rsa_key = RSA.importKey(public_key)
rsa_key = PKCS1_OAEP.new(rsa_key)
encrypted = b''
offset = 0
end_loop = False
while not end_loop:
chunk = msg[offset:offset + chunk_size]
if len(chunk) % chunk_size != 0:
chunk += " " * (chunk_size - len(chunk))
end_loop = True
encrypted += rsa_key.encrypt(chunk.encode())
offset += chunk_size
return base64.b64encode(encrypted)
The decryption in PHP:
PHP中的解密:
/**
* @param base64_encoded string holds the encrypted message.
* @param Resource your private key loaded using openssl_pkey_get_private
* @param integer Chunking by bytes to feed to the decryptor algorithm.
* @return String decrypted message.
*/
public function RSADecyrpt($encrypted_msg, $ppk, $chunk_size=256){
if(is_null($ppk))
throw new Exception("Returned message is encrypted while you did not provide private key!");
$encrypted_msg = base64_decode($encrypted_msg);
$offset = 0;
$chunk_size = 256;
$decrypted = "";
while($offset < strlen($encrypted_msg)){
$decrypted_chunk = "";
$chunk = substr($encrypted_msg, $offset, $chunk_size);
if(openssl_private_decrypt($chunk, $decrypted_chunk, $ppk, OPENSSL_PKCS1_OAEP_PADDING))
$decrypted .= $decrypted_chunk;
else
throw new exception("Problem decrypting the message");
$offset += $chunk_size;
}
return $decrypted;
}