PHP Conference Japan 2024

openssl_private_encrypt

(PHP 4 >= 4.0.6, PHP 5, PHP 7, PHP 8)

openssl_private_encrypt使用私钥加密数据

描述

openssl_private_encrypt(
    #[\SensitiveParameter] 字符串 $data,
    字符串 &$encrypted_data,
    #[\SensitiveParameter] OpenSSLAsymmetricKey|OpenSSLCertificate|数组|字符串 $private_key,
    整数 $padding = OPENSSL_PKCS1_PADDING
): 布尔值

openssl_private_encrypt() 使用私钥 private_key 加密 data,并将结果存储到 encrypted_data 中。加密数据可以通过 openssl_public_decrypt() 解密。

此函数可用于例如签署数据(或其哈希值)以证明它不是由其他人编写的。

参数

data

encrypted_data

private_key

private_key 必须是与用于解密数据的公钥对应的私钥。

padding

padding 可以是 OPENSSL_PKCS1_PADDINGOPENSSL_NO_PADDING 之一。

返回值

成功时返回 true,失败时返回 false

变更日志

版本 描述
8.0.0 private_key 现在接受 OpenSSLAsymmetricKeyOpenSSLCertificate 实例;以前,接受的是类型为 OpenSSL keyOpenSSL X.509资源

参见

添加注释

用户贡献的注释 5 条注释

Hernanibus
8 年前
关于 [P.Peyremorte] 的注释的一点说明。

“- openssl_private_encrypt 每次最多可以加密 117 个字符。”

这取决于 $key 的长度

- 对于 1024 位密钥长度 => 最大可加密字符(字节)数 = 1024/8 - 11(使用填充时)= 117 个字符(字节)。
- 对于 2048 位密钥长度 => 最大可加密字符(字节)数 = 2048/8 - 11(使用填充时)= 245 个字符(字节)。
……等等

顺便说一句,如果 openssl_private_encrypt 由于数据大小而失败,你将不会得到任何东西,只会返回 false,openssl_public_decrypt() 解密时也是如此。

“- 加密输出字符串始终为 129 个字符长度。如果对加密输出使用 base64_encode,它将始终给出 172 个字符,最后一个始终为“=”(填充)”

这再次取决于 $key 的长度

- 对于 1024 位密钥长度 => 加密的原始字节数始终是 RSA 设计的 128 字节(1024 位)块。
- 对于 2048 位密钥长度 => 加密的原始字节数始终是 RSA 设计的 256 字节(2048 位)块。
……等等

关于 base64_encode 输出长度,它取决于你编码的内容(意味着它取决于加密后产生的字节),但一般来说,生成的编码字符串大约会大 33%(对于 128 字节大约 170 字节,对于 256 字节大约 340 字节)。

因此,我会稍微概括一下 [P.Peyremorte] 的注释:
<?php
// 将变量作为常量给出:

// 加密分组密码的分组大小
private $ENCRYPT_BLOCK_SIZE = 200;// 例如,对于 2048 位密钥,留出一些空间

// 解密分组密码的分组大小
private $DECRYPT_BLOCK_SIZE = 256;// 同样,对于 2048 位密钥

// 对于加密,我们将使用:
function encrypt_RSA($plainData, $privatePEMKey)
{
$encrypted = '';
$plainData = str_split($plainData, $this->ENCRYPT_BLOCK_SIZE);
foreach(
$plainData as $chunk)
{
$partialEncrypted = '';

// 例如使用 OPENSSL_PKCS1_PADDING 作为填充
$encryptionOk = openssl_private_encrypt($chunk, $partialEncrypted, $privatePEMKey, OPENSSL_PKCS1_PADDING);

if(
$encryptionOk === false){return false;}// 你也可以返回错误。如果太大,这将为 false
$encrypted .= $partialEncrypted;
}
return
base64_encode($encrypted);// 将整个二进制字符串编码为 MIME base 64
}

// 对于解密,我们将使用:
protected function decrypt_RSA($publicPEMKey, $data)
{
$decrypted = '';

// 必须在拆分之前进行解码才能获得二进制字符串
$data = str_split(base64_decode($data), $this->DECRYPT_BLOCK_SIZE);

foreach(
$data as $chunk)
{
$partial = '';

// 确保匹配填充
$decryptionOK = openssl_public_decrypt($chunk, $partial, $publicPEMKey, OPENSSL_PKCS1_PADDING);

if(
$decryptionOK === false){return false;}// 在此处处理解密中的错误。如果太大,这将为 false
$decrypted .= $partial;
}
return
$decrypted;
}
?>
wfredkNOSPAM at L5DevelopmentNOSPAM dot com
22 年前
使用私钥加密,使用公钥解密。

将其用于发布签名消息:任何拥有访问权限的人
你的公钥可以阅读它,但他们无法创建一个带有
你的签名。

<?php
echo "来源: $source";
$fp=fopen("/path/to/private.key","r");
$priv_key=fread($fp,8192);
fclose($fp);
// 如果您的密钥已编码(建议),则需要 $passphrase
$res = openssl_get_privatekey($priv_key,$passphrase);
/*
* 注意:此处使用返回的资源值
*/
openssl_private_encrypt($source,$crypttext,$res);
echo
"加密后的字符串: $crypttext";

$fp=fopen ("/path/to/certificate.crt","r");
$pub_key=fread($fp,8192);
fclose($fp);
openssl_get_publickey($pub_key);
/*
* 注意:此处使用 $pub_key 值(已转换,我猜)
*/
openssl_public_decrypt($crypttext,$newsource,$pub_key);
echo
"解密后的字符串: $newsource";
?>
[email protected]
12年前
这是一个关于如何开始使用加密功能的简化版本

$res = openssl_pkey_new();

// 获取私钥
openssl_pkey_export($res, $privkey);

// 获取公钥
$pubkey = openssl_pkey_get_details($res);
$pubkey = $pubkey["key"];
var_dump($privkey);
var_dump($pubkey);

// 从命令行获取要处理的文本
$tocrypt = trim(fgets(STDIN));

// 一些要使用的变量
$encryptedviaprivatekey = ""; // 保存用私钥加密的文本
$decryptedviapublickey = ""; // 保存用公钥解密(私钥加密后)的文本,应该与 $tocrypt 相同
$encryptedviapublickey = ""; // 保存用公钥加密的文本
$decryptedviaprivatekey = ""; // 保存用私钥解密(公钥加密后)的文本,应该与 $tocrypt 相同

openssl_private_encrypt($tocrypt, $encryptedviaprivatekey, $privkey);
echo $tocrypt . "->" . $encryptedviaprivatekey;
echo "\n\n";
openssl_public_decrypt($encryptedviaprivatekey, $decryptedviapublickey, $pubkey);
echo $encryptedviaprivatekey . "->" . $decryptedviapublickey;
echo "\n\n";

openssl_public_encrypt($tocrypt,$encryptedviapublickey, $pubkey);
echo $tocrypt . "->" . $encryptedviapublickey;
echo "\n\n";
openssl_private_decrypt($encryptedviapublickey, $decryptedviaprivatekey, $privkey);
echo $encryptedviapublickey . "->" . $decryptedviaprivatekey;

输出

string(887) "-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCy745x8AqGKlTWBu2Ub80boPaQxo/midZ4LHZ0zbPpiCAfkADN
VYSe8OckPKutdjPX7SNAx66PgQRH1xrz1gysbRrf8K/mA0LQ00MKBFaFottWt5cC
IaUS9zvCgPw7prwng3hkGShnvTSMXiKFyt1E3RTvpXRk0u46D6hKiy+TSQIDAQAB
AoGBAJe1jjNCDtoz19vi4doBdIhhT8vt3iHbafBX2lMr+MceeAXqpRNy10+e9op9
uh0G4+vGDialZnYbMBLs6Ngl+nVnzn+cN1MMJ18brgf3biZKzVzK9wmOW4eycWaR
9eLa7/+ns8Cw5GsLJdG+OHR2gXRXU4hzUFdf90UUbP+kuqK1AkEA2X04XznFDNmT
NuhyCixwinlziazJBp/81jjaBhYj3cG0nTF0Gactc/yD0yudbrMqjLBfts+FbG3Z
yFHKrAB/cwJBANKetll3M3aCGsermEK+9hbB8yMihCju6pAwClUNkrAgrm9zU4LP
WkC81RDzXbz+pfIqpopfn34F3+U2iMiOe1MCQCXpTgpLZ631v1Oy8S4U0QlSYnF9
TQ16lfhBsL+e3GGrgnBkTniqS6IMQm5tC+RgFuqvU//p7LgZ7fydRVb2P0ECQFp9
YADuKskmutTAj6lVnCtI5upYgQmJJHQQf8/tBfHwCKHPnbic17zqpGwk80go7Ckw
U98tmDuv0HMNTBVGygsCQALck7VNBRjL9iFzJMFis+alcP1ZC88wOLPvIxYbevUH
c8rZwRqt1aHwaWOoxcVom+tyzRC6gEYoBarmU1bX4No=
-----END RSA PRIVATE KEY-----
"
string(272) "-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCy745x8AqGKlTWBu2Ub80boPaQ
xo/midZ4LHZ0zbPpiCAfkADNVYSe8OckPKutdjPX7SNAx66PgQRH1xrz1gysbRrf
8K/mA0LQ00MKBFaFottWt5cCIaUS9zvCgPw7prwng3hkGShnvTSMXiKFyt1E3RTv
pXRk0u46D6hKiy+TSQIDAQAB
-----END PUBLIC KEY-----
"
这是一个加密测试
( ¦E@n¥u?7Fëµ¥+-M¼ìk?7¥;t?8[j¼ñrƒ¦ª-TÜ++ßYG?-úö¦}9+k8OJn_?¦x?¦
dó+aév.cå?-ï`,¦?·5u¦p%Z²¤ÜI?û ¼

( ¦E@n¥u?7Fëµ¥+-M¼ìk?7¥;t?8[j¼ñrƒ¦ª-TÜ++ßYG?-úö¦}9+k8OJn_?¦x?¦
dó+aév.cå?-ï`,¦?·5u¦p%Z²¤ÜI?û ¼->这是一个加密测试

这是一个加密测试->hT!¡_
#+£-íßÿo»¢äSs+üSnäÖ-(¦ëIkl[¤¦=?í?Ç+Uy·N,=b=+¦TàmeNo¦A~ùÑtü¦@ÿ½»¦SV5Ѫ*¦?·UÿoPÖFq
-? O{²¦á|,äIN)+_-öF+*¦{|??G-??£/?¬±"PFL

hT!¡_
#+£-íßÿo»¢äSs+üSnäÖ-(¦ëIkl[¤¦=?í?Ç+Uy·N,=b=+¦TàmeNo¦A~ùÑtü¦@ÿ½»¦SV5Ѫ*¦?·UÿoPÖFq
-? O{²¦á|,äIN)+_-öF+*¦{|??G-??£/?¬±"PFL->这是一个加密测试
P.Peyremorte
8 年前
手册中未提及的一个技巧

- openssl_private_encrypt 每次最多只能加密 117 个字符。
- 加密后的输出字符串长度始终为 129 个字符。如果对加密后的输出使用 base64_encode,它将始终提供 172 个字符,最后一个始终为 "="(填充符)。

因此,要编码更长的输入流,您必须使用类似以下内容:
<?php
$RawData
= ... ; // 要编码的输入数据

$PartialData = '';
$EncodedData = '';
$Split = str_split($RawData , 117);
ForEach(
$Split as $Part)
{
openssl_private_encrypt($Part, $PartialData, $PrivateKey);
$EncodedData .= base64_encode($PartialData);
}
?>

然后,解码:

<?php
$OriginalData
= '';
$Split = str_split($EncodedData , 172);
ForEach(
$Split as $Part)
{
openssl_private_decrypt(base64_decode($Part), $PartialData, $PublicKey);
$OriginalData .= $PartialData;
}
?>
[email protected]
22 年前
<?php
$fp
=fopen ("/path/to/key.pem","r");
$priv_key=fread ($fp,8192);
fclose($fp);
openssl_get_privatekey ($priv_key);
openssl_private_encrypt($source,$finaltext,$priv_key);
echo
"加密后的字符串: $finaltext";
?>

你好!
To Top