OpenSSL readme ========================================
OpenSSL 加密解密工具包
Linux 需要安装 openssl 工具包, 传送门 http://www.openssl.org/source/
Windows 下需要安装 openssl 的程序, 传送门 http://slproweb.com/products/Win32OpenSSL.html
ASN.1 key structures in DER and PEM -
RSA 算法原理 (一) 阮一峰 -
RSA 算法原理 (二) 阮一峰 -
开始之前需要将 PHP.INI 配置文件的; extension=php_openssl.dll 改为 extension=php_openssl.dll.
RSA 加密算法是一种非对称加密算法, 在公开密钥加密和电子商业中 RSA 被广泛使用. 非对称指的是加密解密用的是不同的一组密钥, 这就是与对称加密的最大区别. 非对称加密算法的实现使得密码可以明文传输而没有泄密风险, 基本原理是:
+ A 与 B 双方生成各自的公钥私钥
+ 双方交换公钥, 可以明文传输
+ 各方用对方提供的公钥加密消息后发送给对方, 这个密文只有拥有密钥方才能解开
+ 只要密钥不泄漏可保公钥明文传输的安全性
RSA 是 1977 年由麻省理工学院的罗纳德. 李维斯特 (Ron Rivest), 阿迪. 萨莫尔(Adi Shamir) 和伦纳德. 阿德曼 (Leonard Adleman) 一起设计的, RSA 就是他们三人姓氏开头字母拼在一起组成的.
在非对称加密系统出现之前, 所有加密和解密使用同样规则, 这些规则相当于密钥, 称为对称加密算法(Symmetric-key algorithm). 其中又以高级加密标准为代表(英语: Advanced Encryption Standard, 缩写: AES), 在密码学中又称 Rijndael 加密法, 是美国联邦政府采用的一种区块加密标准. 这个标准用来替代原先的 DES, 已经被多方分析且广为全世界所使用.
密钥生成
openssl genrsa 用于生成 rsa 私钥文件, 生成是可以指定私钥长度, 具体参数请参考文档.
openssl genrsa -out 2048_rsa_private_key.pem 2048
Rsa 命令用于处理 Rsa 密钥生成公钥, 格式转换和打印信息
openssl rsa -in 2048_rsa_private_key.pem -pubout -out 2048_rsa_public_key.pem
-in filename: 输入的 RSA 密钥文件, 在此为上面生成的密钥 rsa_private_key.pem.
-pubout: 设置此选项后, 保存公钥值到输出文件中.
-out filename: 输出文件, 在此我们定义成 rsa_public_key.pem
java 开发使用的 PKCS8 格式转换命令
openssl pkcs8 -topk8 -inform PEM -in 2048_rsa_private_key.pem -outform PEM -nocrypt -out 2048_rsa_private_key_pkcs8.pem
PHP 与 OpenSSL AES 对称加密
- openssl_encrypt()
- openssl_decrypt()
微信公众平台 / 小程序使用的 AES 算法是 AES-128-CBC + OPENSSL_RAW_DATA.
信息摘要算法
Message Digest Algorithm 消息摘要算法缩写为 MD, 一种被广泛使用的密码散列函数, 其中以 MD5 消息摘要算法为普遍.
Secure Hash Algorithm 缩写为 SHA, 密码散列函数. 能计算出一个数字消息所对应到的, 固定长度字符串的算法, 也是消息摘要算法的一种.
这些算法 (md,sha) 之所以称作安全算法基于以下两点:
(1)由消息摘要反推原输入消息, 从计算理论上来说是很困难的. 但目前有人制造出碰撞的可能了, 大大减弱了安全性.
(2)想要找到两组不同的消息对应到相同的消息摘要, 从计算理论上来说是很困难的. 任何对输入消息的变动, 都会很高概率导致其产生的消息摘要迥异.
HMAC: 散列消息身份验证码 Hashed Message Authentication Code .
根据 RFC 2316,HMAC 以及 IPSec 被认为是 Interact 安全的关键性核心协议. 它不是散列函数, 而是采用了将 MD5 或 SHA1 散列函数与共享机密密钥 (与公钥 / 私钥对不同) 一起使用的消息身份验证机制. 基本来说, 消息与密钥组合并运行散列函数. 然后运行结果与密钥组合并再次运行散列函数. 这个 128 位的结果被截断成 96 位, 成为 Mac. 然后创建两个 B 长的不同字符串:
innerpad = 长度为 B 的 0*36
outterpad = 长度为 B 的 0*5C
计算输入字符串 str 的 HMAC:
hash(key ^ outterpad, hash(key ^ innerpad, str))
hmac 主要应用在身份验证中, 它的使用方法是这样的:
客户端发出登录请求(假设是浏览器的 GET 请求)
服务器返回一个随机值, 并在会话中记录这个随机值
客户端将该随机值作为密钥, 用户密码进行 hmac 运算, 然后提交给服务器
服务器读取用户数据库中的用户密码和步骤 2 中发送的随机值做与客户端一样的 hmac 运算, 然后与用户发送的结果比较, 如果结果一致则验证用户合法
在这个过程中, 可能遭到安全攻击的是服务器发送的随机值和用户发送的 hmac 结果, 而对于截获 了这两个值的黑客而言这两个值是没有意义的, 绝无获取用户密码的可能性, 随机值的引入使 hmac 只在当前会话中有效, 大大增强了安全性和实用性. 大多数的 语言都实现了 hmac 算法, 比如 PHP 的 mhash,python 的 hmac.py,java 的 MessageDigest 类, 在 Web 验证中使用 hmac 也是可行的, 用 JS 进行 md5 运算的速度也是比较快的.
PHP 与 OpenSSL RSA 非对称加解密
RSA 使用非对称加解密字符长度是 密钥长度 / 8bit = 字节的长度, 如 1024 对应的数据分组长度 128 字节, 2048 对数据分组 256 字节. RSA 加密解密有四个配置的方法, 使用私钥加密就对应公钥解密, 反之公钥加密就用私钥解密, 配套使用.
- openssl_private_encrypt() - Encrypts data with private key
- openssl_private_decrypt() - Decrypts data with private key
- openssl_public_encrypt() - Encrypts data with public key
- openssl_public_decrypt() - Decrypts data with public key
PEM 密钥文件读取配套方法
- openssl_pkey_get_private(file_get_contents($path));
- openssl_pkey_get_public(file_get_contents($path));
OpenSSL 模块提供丰富的功能, 包括密钥生成 API 都有.
签名与验证
使用配套方法
- openssl_sign()
- openssl_verify()
注意, 阿里支付使用的签名算法是 OPENSSL_ALGO_SHA256, 默认的是 OPENSSL_ALGO_SHA1.
完整参考代码:
- class Crypto{
- const KEYSIZE = 2048;
- const CONF = 'alipay/openssl/openssl.cnf';
- const PRIVATEKEY = "./ranking/rsa/2048_private_key.pem";
- const PUBLICKEY = "./ranking/rsa/2048_public_key.pem";
- static function keygen(){
- // Windows 系统要设置 openssl 环境变量或通过配置信息指定配置文件
- $conf = array(
- 'private_key_bits' => self::KEYSIZE,
- 'config' => self::CONF,
- );
- $res = openssl_pkey_new($conf);
- if( $res ) {
- $d= openssl_pkey_get_details($res);
- $pub = $d['key'];
- $bits = $d['bits'];
- $filepath = $bits.'_rsa_private_key.pem';
- openssl_pkey_export($res, $pri, null, $conf);
- openssl_pkey_export_to_file($res, $filepath, null, $conf);
- print_r(["private_key"=>$pri, "public_key"=>$pub, "keysize"=>$bits]);
- }else echo "openssl_pkey_new falls";
- }
- static function encrypt($msg, $key, $method="AES-128-CBC", $options=OPENSSL_RAW_DATA){
- $ivlen = openssl_cipher_iv_length($method);
- $iv = openssl_random_pseudo_bytes($ivlen);
- $cipher = openssl_encrypt($msg, $method, $key, $options, $iv);
- $hmac = hash_hmac('sha256', $cipher, $key, $as_binary=true);
- $cipher = base64_encode( $iv.$hmac.$cipher );
- return $cipher;
- }
- static function decrypt($cipher, $key, $method="AES-128-CBC", $options=OPENSSL_RAW_DATA){
- $c = base64_decode($cipher);
- $ivlen = openssl_cipher_iv_length($method);
- $iv = substr($c, 0, $ivlen);
- $hmac = substr($c, $ivlen, $sha2len=32);
- $cipher = substr($c, $ivlen+$sha2len);
- $msg = openssl_decrypt($cipher, $method, $key, $options, $iv);
- $calcmac = hash_hmac('sha256', $cipher, $key, $as_binary=true);
- if( hash_equals($hmac, $calcmac) ) return $msg;//PHP 5.6+ timing attack safe comparison
- return false;
- }
- static function getPublicKey()
- {
- $pem = file_get_contents(self::PUBLICKEY);
- // $pem = chunk_split(base64_encode($pem),64,"\n"); // transfer to pem format
- // $pem = "-----BEGIN CERTIFICATE-----\n".$pem."-----END CERTIFICATE-----\n";
- $publicKey = openssl_pkey_get_public($pem);
- return $publicKey;
- }
- static function getPrivateKey()
- {
- $pem = file_get_contents(self::PRIVATEKEY);
- // $pem = chunk_split($pem,64,"\n"); // transfer to pem format
- // $pem = "-----BEGIN PRIVATE KEY-----\n".$pem."-----END PRIVATE KEY-----\n";
- $privateKey = openssl_pkey_get_private($pem);
- return $privateKey;
- }
- static function sign($msg, $algorithm=OPENSSL_ALGO_SHA256){
- $sign = "";
- $key = self::getPrivateKey();
- // OPENSSL_ALGO_SHA256 OPENSSL_ALGO_MD5 OPENSSL_ALGO_SHA1
- openssl_sign($msg, $sign, $key, $algorithm);
- $sign = base64_encode($sign);
- openssl_free_key($key);
- return $sign;
- }
- static function verify($msg, $sign, $algorithm=OPENSSL_ALGO_SHA256){
- $sign = base64_decode($sign);
- $key = self::getPublicKey();
- $result = openssl_verify($msg, $sign, $key, $algorithm);
- openssl_free_key($key);
- return $result;
- }
- static function publicEncrypt($source_data) {
- $data = "";
- $key = self::getPublicKey();
- $dataArray = str_split($source_data, self::KEYSIZE/8);
- foreach ($dataArray as $value) {
- $encryptedTemp = "";
- openssl_public_encrypt($value,$encryptedTemp,$key);
- $data .= $encryptedTemp;
- }
- openssl_free_key($key);
- return base64_encode($data);
- }
- static function privateDecrypt($eccryptData) {
- $decrypted = "";
- $decodeStr = base64_decode($eccryptData);
- $key = self::getPrivateKey();
- $enArray = str_split($decodeStr, self::KEYSIZE/8);
- foreach ($enArray as $va) {
- $decryptedTemp = "";
- openssl_private_decrypt($va,$decryptedTemp,$key);
- $decrypted .= $decryptedTemp;
- }
- openssl_free_key($key);
- return $decrypted;
- }
- static function privateEncrypt($source_data) {
- $data = "";
- $dataArray = str_split($source_data, self::KEYSIZE/8);
- $key = self::getPrivateKey();
- foreach ($dataArray as $value) {
- $encryptedTemp = "";
- openssl_private_encrypt($value,$encryptedTemp,$key);
- var_dump( strlen($encryptedTemp));
- $data .= $encryptedTemp;
- }
- openssl_free_key($key);
- return base64_encode($data);
- }
- static function publicDecrypt($eccryptData) {
- $decrypted = "";
- $decodeStr = base64_decode($eccryptData);
- $key = self::getPublicKey();
- $enArray = str_split($decodeStr, self::KEYSIZE/8);
- foreach ($enArray as $va) {
- $decryptedTemp = "";
- openssl_public_decrypt($va,$decryptedTemp,$key);
- $decrypted .= $decryptedTemp;
- }
- openssl_free_key($key);
- return $decrypted;
- }
- }
- $plain = "Some secret here for you ...";
- $key = openssl_random_pseudo_bytes(16);
- $cipher = Crypto::encrypt($plain, $key);
- $msg = Crypto::decrypt($cipher, $key);
- print_r(['明文'=>$plain, '解密'=>$msg, '密文'=>$cipher]);
- $plain = "利用公钥加密, 私钥解密做数据保密通信!";
- $cipher = Crypto::publicEncrypt($plain);
- $msg = Crypto::privateDecrypt($cipher);
- print_r(['明文'=>$plain, '解密'=>$msg, '密文'=>$cipher]);
- $plain = "利用私钥加密, 公钥解密可以做身份验证";
- $cipher = Crypto::privateEncrypt($plain);
- $msg = Crypto::publicDecrypt($cipher);
- print_r(['明文'=>$plain, '解密'=>$msg, '密文'=>$cipher]);
- $msg = 'a=123';
- $sign = Crypto::sign($msg);
- $verify = Crypto::verify($msg, $sign);
- print_r(['预签'=>$msg, '签名'=>$sign, '验证'=>$verify==1?"PASS":"FAIL"]);
来源: http://www.jianshu.com/p/ac0e05f00269