OpenSSL readme ========================================
OpenSSL加密解密工具包
linux 需要安装openssl工具包,传送门 http://www.openssl.org/source/
window 下需要安装openssl的程序,传送门 http://slproweb.com/products/Win32OpenSSL.html
ASN.1 key structures in DER and PEM - https://tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pem
RSA算法原理(一)阮一峰 - http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html
RSA算法原理(二)阮一峰 - http://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html
开始之前需要将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主要应用在身份验证中,它的使用方法是这样的:
1. 客户端发出登录请求(假设是浏览器的GET请求)
2. 服务器返回一个随机值,并在会话中记录这个随机值
3. 客户端将该随机值作为密钥,用户密码进行hmac运算,然后提交给服务器
4. 服务器读取用户数据库中的用户密码和步骤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(){
// window系统要设置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"]);