美文网首页程序员
PHP与OpenSSL工具包 AES+RSA

PHP与OpenSSL工具包 AES+RSA

作者: 坚果jimbowhy | 来源:发表于2018-12-16 00:49 被阅读0次

    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"]);
    

    相关文章

      网友评论

        本文标题:PHP与OpenSSL工具包 AES+RSA

        本文链接:https://www.haomeiwen.com/subject/wlgzhqtx.html