欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

PHP OpenSSL扩展 - 非对称加密

程序员文章站 2024-03-16 19:41:52
...

PHP 在进入7.x 时代后,默认就不再附带 mcrypt 扩展,mcrypt 将被 openssl_* 一族函数所替代。所以,对于 PHPer 来说,有必要学习一下 PHP 的 OpenSSL 扩展。

上一篇文章《PHP中OpenSSL扩展 - 对称加密》 ,介绍了 OpenSSL 扩展中对称加密的使用方法,本文将介绍非对称加密的使用方法。

PHP 的 OpenSSL 扩展中,非对称加密的相关函数有:

  • openssl_pkey_new
  • openssl_pkey_export
  • openssl_pkey_export_to_file
  • openssl_pkey_get_details
  • openssl_pkey_free
  • openssl_pkey_get_private
  • openssl_pkey_get_public
  • openssl_get_privatekey
  • openssl_get_publickey
  • openssl_private_decrypt
  • openssl_private_encrypt
  • openssl_public_decrypt
  • openssl_public_encrypt

别被这么多函数吓倒,经过本文的讲解,你会发现非对称加密的过程并不繁琐。让我们通过实例来讲解每个函数的作用。

1. 生成**对

首先,想要进行非对称加密 / 解密,你得有一对公钥(Public key)和私钥(Private key)。在Linux环境下,公钥私钥可以用 openssl 命令生成。PHP的 OpenSSL 扩展中,openssl_pkey_new() 函数可以完成同样的事:

<?php
// 生成私钥
$privateKey = openssl_pkey_new();
openssl_pkey_export($privateKey, $out);
echo $out;

上面两行代码生成了一个私钥,并导出到了 $out 变量中。

延伸一下,如果你打印 $out 变量,会看见一个由大小写字母和数字组成的“乱码块”,外层被-----BEGIN PRIVATE KEY----------END PRIVATE KEY-----包裹着。这其实是 PEM 格式的私钥,乱码块是被 Base64 编码的二进制数据。

注意到现在只生成了一个私钥,那么公钥在哪呢?OpenSSL扩展并没有生成公钥的函数,公钥是从私钥当中提取出来的,使用 openssl_pkey_get_details() 函数:

从私钥提取公钥
<?php
$privateKey = openssl_pkey_new();
$detail = openssl_pkey_get_details($privateKey);
$publicKeyString = $detail['key'];
echo $publicKeyString;

openssl_pkey_get_details() 接受一个私钥对象,返回一个 array 包含私钥中附带的相关信息,比如 RSA 的 ne1e2 值。。。不用深究这几个值,他们已经是密码学原理才能解释的东西了。我们只关心分析结果的 key 值,key 值就是提取出来的公钥啦。

我们将公钥私钥分别保存到磁盘上:

<?php
// 如果**已经是PEM格式的了,那就直接写到磁盘上
file_put_contents('public.key', $publicKeyString);

// 否则需要用 openssl_pkey_export()
// 或者openssl_pkey_export_to_file()
// 转换成PEM格式
openssl_pkey_export_to_file($privateKey, 'private.key');

有了一对公钥、私钥之后,就可以进行非对称加密了。注意 公钥 可以分发给别人用的,而 私钥 只能你自己知道,否则非对称加密系统就完全失效了。

2. 非对称 加密 与 解密

在不管是加密还是解密,都要先读取**。上一节我们保存在磁盘上的**是PEM格式的,不能直接用,需要用 openssl_pkey_get_public()openssl_pkey_get_private() 读取:

<?php
$privateKey = openssl_pkey_get_private(file_get_contents('private.key'));
$publicKey = openssl_pkey_get_public(file_get_contents('public.key'));

另外还有两个函数:openssl_get_privatekey()openssl_get_publickey(),只是上述两个函数的别名,完成的功能相同。

介绍了一大堆,终于到了真正的加密解密了。因为是非对称加密,所以公钥和私钥是交错使用的:公钥加密的数据用**解密,同样,私钥加密的数据用公钥解密。

公钥加密私钥解密:
// 加密
$publicKey = openssl_pkey_get_public(file_get_contents('public.key'));
openssl_public_encrypt('PHP是世界上最好的语言!', $encrypted, $publicKey, OPENSSL_PKCS1_OAEP_PADDING);
echo $encrypted . PHP_EOL;

// 解密
$privateKey = openssl_pkey_get_private(file_get_contents('private.key'));
openssl_private_decrypt($encrypted, $decrypted, $privateKey, OPENSSL_PKCS1_OAEP_PADDING);
echo $decrypted . PHP_EOL;

结果:

????qz¬icG߇!?ꕦNׇ.¿0QM]_+B????i=輬©????>¹N`Z㹔̡ד䴹¨Z9qr죡¾<zɥH«????dy????я³T????G¾q»HE????SAxd綧h` ????6䡝פ????£n????Q¹ۉq????????????
PHP是世界上最好的语言!

可以看到经过加密后,明文已经变成完全无法阅读的乱码了。经过解密后又变回了原文。公钥加密私钥解密满足了最常见的数据保密的需求,别人用你的公钥加密的数据,只能用你自己的私钥解开。

私钥加密公钥解密
// 加密
$privateKey = openssl_pkey_get_private(file_get_contents('private.key'));
openssl_private_encrypt('PHP是世界上最好的语言!', $encrypted, $privateKey);
echo $encrypted . PHP_EOL;

// 解密
$publicKey = openssl_pkey_get_public(file_get_contents('public.key'));
openssl_public_decrypt($encrypted, $decrypted, $publicKey);
echo $decrypted . PHP_EOL;

输出的结果与公钥加密类似,就不再赘述了。私钥加密公钥解密一般用于签名,因为用你的私钥加密的内容,大家只能用你的公钥解开,从而保证了加密的信息确实是由你发出的。