openssl 下的对称加密和非对称加密
对称加密: 在加密和解密过程中使用相同的密钥, 或是两个可以简单地相互推算的密钥的加密算法.
非对称加密: 也称为公开加密, 它需要一个密钥对, 一个是公钥, 一个是私钥, 一个负责加密, 一个负责解密.
对称加密在性能上要优于非对称加密, 但是安全性低于非对称加密.
PHP 7.1 之后的对称加密和非对称加密都需要借助 openssl 扩展实现. mcrypt 库已经被移除.
对称加密函数
openssl_get_cipher_methods() : 返回 openssl 支持的所有加密方式.
openssl_encrypt($data, $method, $key, $options = 0, $iv = '') : 以指定方式 method 和密钥 key 加密 data, 返回 false 或加密后的数据.
- data : 明文
- method : 加密算法
- key : 密钥
- options :
- 0 : 自动对明文进行 padding, 返回的数据经过 base64 编码.
- 1 : OPENSSL_RAW_DATA, 自动对明文进行 padding, 但返回的结果未经过 base64 编码.
- 2 : OPENSSL_ZERO_PADDING, 自动对明文进行 0 填充, 返回的结果经过 base64 编码. 但是, openssl 不推荐 0 填充的方式, 即使选择此项也不会自动进行 padding, 仍需手动 padding.
- iv : 非空的初始化向量, 不使用此项会抛出一个警告. 如果未进行手动填充, 则返回加密失败.
openssl_decrypt($data, $method, $key, $options = 0, $iv = '') : 解密数据.
openssl_cipher_iv_length($method) : 获取 method 要求的初始化向量的长度.
openssl_random_pseudo_bytes($length) : 生成指定长度的伪随机字符串.
hash_mac($method, $data, $key, $raw_out) : 生成带有密钥的哈希值.
- method : 加密算法
- data : 明文
- key : 密钥
- raw_output :
- TRUE : 输出原始二进制数据
- FALSE : 输出长度固定的小写 16 进制字符串
对称加密
参考文章:
1. https://blog.csdn.net/qq_28205153/article/details/55798628
2. https://www.xxling.com/blog/article/3114.aspx
主流的对称加密方式有 DES, AES. 这两种加密方式都属于分组加密, 先将明文分成多个等长的模块 ( block ), 然后进行加密.
DES 加密
DES 加密的密钥长度为 64 bit, 实际应用中有效使用的是 56 位, 剩余 8 位作为奇偶校验位. 如果密钥长度不足 8 个字节, 将会使用 \0 补充到 8 个字节. 如密钥为 "12345", 其加密后的密文与密钥 "12345\0\0\0" 加密后的密文相同. 明文按 64 bit ( UTF-8 下为 8 个字节长度 ) 进行分组, 每 64 位分成一组 ( 最后一组不足 64 位的需要填充数据 ), 分组后的明文组和密钥按位替代或交换的方法形成密文组.
class DES
{
private $method = 'DES-CBC';
private $key; public function __construct($key)
{
// 密钥长度不能超过64bit(UTF-8下为8个字符长度),超过64bit不会影响程序运行,但有效使用的部分只有64bit,多余部分无效,可通过openssl_error_string()查看错误提示
$this->key = $key;
} public function encrypt($plaintext)
{
// 生成加密所需的初始化向量, 加密时缺失iv会抛出一个警告
$ivlen = openssl_cipher_iv_length($this->method);
$iv = openssl_random_pseudo_bytes($ivlen); // 按64bit一组填充明文
//$plaintext = $this->padding($plaintext);
// 加密数据. 如果options参数为0, 则不再需要上述的填充操作. 如果options参数为1, 也不需要上述的填充操作, 但是返回的密文未经过base64编码. 如果options参数为2, 虽然PHP说明是自动0填充, 但实际未进行填充, 必须需要上述的填充操作进行手动填充. 上述手动填充的结果和options为0和1是自动填充的结果相同.
$ciphertext = openssl_encrypt($plaintext, $this->method, $this->key, 1, $iv);
// 生成hash
$hash = hash_hmac('sha256', $ciphertext, $this->key, false); return base64_encode($iv . $hash . $ciphertext); } public function decrypt($ciphertext)
{
$ciphertext = base64_decode($ciphertext);
// 从密文中获取iv
$ivlen = openssl_cipher_iv_length($this->method);
$iv = substr($ciphertext, 0, $ivlen);
// 从密文中获取hash
$hash = substr($ciphertext, $ivlen, 64);
// 获取原始密文
$ciphertext = substr($ciphertext, $ivlen + 64);
// hash校验
if(hash_equals($hash, hash_hmac('sha256', $ciphertext, $this->key, false)))
{
// 解密数据
$plaintext = openssl_decrypt($ciphertext, $this->method, $this->key, 1, $iv) ?? false;
// 去除填充数据. 加密时进行了填充才需要去填充
$plaintext = $plaintext? $this->unpadding($plaintext) : false; return $plaintext;
} return '解密失败';
} // 按64bit一组填充数据
private function padding($plaintext)
{
$padding = 8 - (strlen($plaintext)%8);
$chr = chr($padding); return $plaintext . str_repeat($chr, $padding);
} private function unpadding($ciphertext)
{
$chr = substr($ciphertext, -1);
$padding = ord($chr); if($padding > strlen($ciphertext))
{
return false;
}
if(strspn($ciphertext, $chr, -1 * $padding, $padding) !== $padding)
{
return false;
} return substr($ciphertext, 0, -1 * $padding);
}
}
AES 加密
AES 加密的分组长度是 128 位, 即每个分组为 16 个字节 ( 每个字节 8 位 ). 密钥的长度根据加密方式的不同可以是 128 位, 192 位, 256 位. 与 DES 加密一样. 密钥长度超过指定长度时, 超出部分无效. 密钥长度不足时, 会自动以`\0`补充到指定长度.
AES | 密钥长度 ( 位 ) | 分组长度 ( 位 ) |
AES-128 | 128 | 128 |
AES-192 | 192 | 128 |
AES-256 | 256 |
128 |
class AES
{
private $key;
private $method = 'aes-128-cbc'; public function __construct($key)
{
// 是否启用了openssl扩展
extension_loaded('openssl') or die('未启用 OPENSSL 扩展');
$this->key = $key;
} public function encrypt($plaintext)
{
if(!in_array($this->method, openssl_get_cipher_methods()))
{
die('不支持该加密算法!');
}
// options为1, 不需要手动填充
//$plaintext = $this->padding($plaintext);
// 获取加密算法要求的初始化向量的长度
$ivlen = openssl_cipher_iv_length($this->method);
// 生成对应长度的初始化向量. aes-128模式下iv长度是16个字节, 也可以自由指定.
$iv = openssl_random_pseudo_bytes($ivlen);
// 加密数据
$ciphertext = openssl_encrypt($plaintext, $this->method, $this->key, 1, $iv);
$hmac = hash_hmac('sha256', $ciphertext, $this->key, false); return base64_encode($iv . $hmac . $ciphertext);
} public function decrypt($ciphertext)
{
$ciphertext = base64_decode($ciphertext);
$ivlen = openssl_cipher_iv_length($this->method);
$iv = substr($ciphertext, 0, $ivlen);
$hmac = substr($ciphertext, $ivlen, 64);
$ciphertext = substr($ciphertext, $ivlen + 64);
$verifyHmac = hash_hmac('sha256', $ciphertext, $this->key, false);
if(hash_equals($hmac, $verifyHmac))
{
$plaintext = openssl_decrypt($ciphertext, $this->method, $this->key, 1, $iv)??false;
// 加密时未手动填充, 不需要去填充
//if($plaintext)
//{
// $plaintext = $this->unpadding($plaintext);
// echo $plaintext;
//} return $plaintext;
}else
{
die('数据被修改!');
}
} private function padding(string $data) : string
{
$padding = 16 - (strlen($data) % 16);
$chr = chr($padding);
return $data . str_repeat($chr, $padding);
} private function unpadding($ciphertext)
{
$chr = substr($ciphertext, -1);
$padding = ord($chr); if($padding > strlen($ciphertext))
{
return false;
} if(strspn($ciphertext, $chr, -1 * $padding, $padding) !== $padding)
{
return false;
} return substr($ciphertext, 0, -1 * $padding);
}
}
非对称加密函数
$res = openssl_pkey_new([array $config]) : 生成一个新的私钥和公钥对. 通过配置数组, 可以微调密钥的生成.
- digest_alg : 摘要或签名哈希算法.
- private_key_bits : 指定生成的私钥的长度.
- private_key_type : 指定生成私钥的算法. 默认 OPENSSL_KEYTYPE_RSA, 可指定 OPENSSL_KEYTYPE_DSA, OPENSSL_KEYTYPE_DH, OPENSSL_KEYTYPE_RSA, OPENSSL_KEYTYPE_EC.
- config : 自定义 openssl.conf 文件的路径.
openssl_pkey_free($res) : 释放有 openssl_pkey_new() 创建的私钥.
openssl_get_md_methods() : 获取可用的摘要算法.
openssl_pkey_export_to_file($res, $outfilename) : 将 ASCII 格式 ( PEM 编码 ) 的密钥导出到文件中. 使用相对路径时, 是相对服务器目录, 而非当前所在目录.
openssl_pkey_export($res, &$out) : 提取 PEM 格式私钥字符串.
openssl_pkey_get_details($res) : 返回包含密钥详情的数组.
openssl_get_privatekey($key) : 获取私钥. key 是一个 PEM 格式的文件或一个 PEM 格式的私钥.
openssl_get_publickey($certificate) : 获取公钥. certificate 是一个 X.509 证书资源或一个 PEM 格式的文件或一个 PEM 格式的公钥.
openssl_private_encrypt($data, &$crypted, $privKey [, $padding = OPENSSL_PKCS1_PADDING]) : 使用私钥加密数据, 并保存到 crypted . 其中填充模式为 OPENSSL_PKCS1_PADDING 时, 如果明文长度不够, 加密时会在明文中随机填充数据. 为 OPENSSL_NO_PADDING 时, 如果明文长度不够, 会在明文的头部填充 0 .
openssl_public_decrypt($crypted, &$decrypted, $pubKey [, $padding]) : 使用公钥解密数据, 并保存到 decrypted .
openssl_public_encrypt($data, &$crypted, $pubKey [, $padding]) : 使用公钥加密数据, 并保存到 crypted .
openssl_private_decrypt($crypted, &$decrypted, $privKey [, $padding]) : 使用私钥解密数据, 并保存到 decrypted .
非对称加密
RSA 也是一种分组加密方式, 但明文的分组长度根据选择的填充方式的不同而不同.
class RSA
{
private $private_key; // 私钥
private $public_key; // 公钥
private $private_res; // 私钥资源
private $public_res; // 公钥资源 public function __construct()
{
extension_loaded('openssl') or die('未加载 openssl');
// 生成新的公钥和私钥对资源
$config = [
'digest_alg' => 'sha256',
'private_key_bits' => 1204,
'private_key_type' => OPENSSL_KEYTYPE_RSA
];
$res = openssl_pkey_new($config);
if(!$res)
{
die('生成密钥对失败');
} // 获取公钥, 生成公钥资源
$this->public_key = openssl_pkey_get_details($res)['key'];
$this->public_res = openssl_pkey_get_public($this->public_key); // 获取私钥, 生成私钥资源
openssl_pkey_export($res, $this->private_key);
$this->private_res = openssl_pkey_get_private($this->private_key); openssl_free_key($res);
} // 加密
public function encrypt($plaintext)
{
$ciphertext = null;
openssl_public_encrypt($plaintext, $ciphertext, $this->public_res);
return $ciphertext;
} // 解密
public function decrypt($ciphertext)
{
$plaintext = null;
openssl_private_decrypt($ciphertext, $plaintext, $this->private_res);
return $plaintext;
}
}
在传输重要信息时, 一般会采用对称加密和非对称加密相结合的方式, 而非使用单一加密方式. 一般先通过 AES 加密数据, 然后通过 RSA 加密 AES 密钥, 然后将加密后的密钥和数据一起发送. 接收方接收到数据后, 先解密 AES 密钥, 然后使用解密后的密钥解密数据.
openssl 下的对称加密和非对称加密的更多相关文章
- https原理及其中所包含的对称加密、非对称加密、数字证书、数字签名
声明:本文章已授权公众号Hollis转载,如需转载请标明转载自https://www.cnblogs.com/wutianqi/p/10654245.html(安静的boy) 一.为什么要使用http ...
- 数字签名中公钥和私钥是什么?对称加密与非对称加密,以及RSA的原理
http://baijiahao.baidu.com/s?id=1581684919791448393&wfr=spider&for=pc https://blog.csdn.net/ ...
- 大话https演化过程(对称加密、非对称加密、公钥、私钥、数字签名、数字证书)
大话https演化过程(包括概念:对称加密.非对称加密.公钥.私钥.数字签名.数字证书.https访问全过程) 在网络上发送数据是非常不安全的,非常容易被劫持或是被篡改,所以每次定向发送数据你都可 ...
- 几个例子理解对称加密与非对称加密、公钥与私钥、签名与验签、数字证书、HTTPS加密方式
# 原创,转载请留言联系 为什么会出现这么多加密啊,公钥私钥啊,签名啊这些东西呢?说到底还是保证双方通信的安全性与完整性.例如小明发一封表白邮件给小红,他总不希望给别人看见吧.而各种各样的技术就是为了 ...
- (转)对称加密与非对称加密,以及RSA的原理
一 概述 二对称加密和非对称加密 对称加密 非对称加密 区别 三RSA原理 整数运算 同余运算 当模数为合数n时 当模数为质数p的时候 离散对数问题 RSA原理 一 , 概述 在现代密码学诞生以前,就 ...
- 你知道,HTTPS用的是对称加密还是非对称加密?
1.引言 随着互联网安全意识的普遍提高,对安全要求稍高的应用中,HTTPS的使用是很常见的,甚至在1年前,苹果公司就将使用HTTPS作为APP上架苹果应用市场的先决条件之一(详见<苹果即将强制实 ...
- 探究公钥、私钥、对称加密、非对称加密、hash加密、数字签名、数字证书、CA认证、https它们究竟是什么,它们分别解决了通信过程的哪些问题。
一.准备 1. 角色:小白.美美.小黑. 2. 剧情:小白和美美在谈恋爱:小黑对美美求而不得.心生怨念,所以从中作梗. 3. 需求:小白要与美美需通过网络进行通信,联络感情,所以必须保证通信的安全性. ...
- 非对称加解密 Asymmetric encryption 对称加密和非对称加密的区别
考虑这样一个问题:一切的装备文件都存储在 Git 长途库房,RAR密码破解装备文件中的一些信息又是比较灵敏的.所以,我们需求对这些灵敏信息进行加密处理.首要的加密方法分为两种:一种是同享密钥加 密(对 ...
- Java安全之对称加密、非对称加密、数字签名
原文地址: http://blog.csdn.net/furongkang/article/details/6882039 Java中加密分为两种方式一个是对称加密,另一个是非对称加密.对称加密是因为 ...
随机推荐
- Python爬虫【第3篇】【多线程】
一.多线程 Python标准库提供2个模块,thread是低级模块,threading是高级模块 1.threading模块创建多线程 方式1:把1个函数传入并创建Thread实例,然后调用start ...
- MySQL-PREPARE语句
MySQL-PREPARE语句 功能介绍: MySQL准备语句用法 为了使用MySQL准备语句,您需要使用其他三个MySQL语句如下: PREPARE - 准备执行的声明. EXECUTE - 执行由 ...
- JPA測试实例
依赖架包 实体 import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.G ...
- 【Android基础】App签名与打包
签名的意义 1. 为了保证程序开发人员的合法 2. 防止部分人通过使用同样的Package Name(包名)来混淆替换已安装的程序 3. 保证我们每次公布的版本号的一致性(保证签名一致才干升级) 签名 ...
- 【bzoj4602】[Sdoi2016]齿轮
dfs,连边,边权为比值,赋值搜索,遇到矛盾时退出 #include<algorithm> #include<iostream> #include<cstdlib> ...
- a high-level neural networks AP
Keras Documentation https://keras.io/ You have just found Keras. Keras is a high-level neural networ ...
- P1196 [NOI2002]银河英雄传说(并查集)
P1196 [NOI2002]银河英雄传说(并查集) 本题关键 用两个一维数组表示了一个稀疏的二维数组. 这两个一维数组一个表示祖先(就是最前面那个),一个表示距离祖先的距离. 并且还有一个关键点是, ...
- [疑问] C# 多线程程序,如果在并行程序块中开空间会远远慢于将空间开在并行块之外
// int[,] label = new int[m, n]; Parallel.For(, thread_num, (n) => { ]; i++) { int[] tmp = new in ...
- IDEA中 Spark 读Hbase 报错处理:
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory] // :: ERROR RecoverableZooKeepe ...
- 如何彻底卸载Vs2015
当我们卸载了VS2015想再安装VS软件的时候,发现安装路径根本更改不了. 网上查的由很多方法. 要真的找注册表去完全删除时非常繁琐的这里可以使用的方法就是 先下载卸载软件 https://githu ...