AES加解密在php接口请求过程中的应用示例


Posted in PHP onOctober 26, 2016

在php请求接口的时候,我们经常需要考虑的一个问题就是数据的安全性,因为数据传输过程中很有可能会被用fillder这样的抓包工具进行截获。一种比较好的解决方案就是在客户端请求发起之前先对要请求的数据进行加密,服务端api接收到请求数据后再对数据进行解密处理,返回结果给客户端的时候也对要返回的数据进行加密,客户端接收到返回数据的时候再解密。因此整个api请求过程中数据的安全性有了一定程度的提高。

今天结合一个简单的demo给大家分享一下AES加解密技术在php接口请求中的应用。

首先,准备一个AES加解密的基础类:

<?php

/**
 * 加密基础类
 */

class Crypt_AES
{
  protected $_cipher = "rijndael-128";
  protected $_mode = "cbc";
  protected $_key;
  protected $_iv = null;
  protected $_descriptor = null;

  /**
   * 是否按照PKCS #7 的标准进行填充
   * 为否默认将填充“\0”补位
   * @var boolean
   */
  protected $_PKCS7 = false;

  /**
   * 构造函数,对于密钥key应区分2进制字符串和16进制的。
   * 如需兼容PKCS#7标准,应选项设置开启PKCS7,默认关闭
   * @param string $key 
   * @param mixed $iv   向量值
   * @param array $options
   */
  public function __construct($key = null, $iv = null, $options = null)
  {
    if (null !== $key) {
      $this->setKey($key);
    }

    if (null !== $iv) {
      $this->setIv($iv);
    }

    if (null !== $options) {
      if (isset($options['chipher'])) {
        $this->setCipher($options['chipher']);
      }

      if (isset($options['PKCS7'])) {
        $this->isPKCS7Padding($options['PKCS7']);
      }

      if (isset($options['mode'])) {
        $this->setMode($options['mode']);
      }
    }
  }

  /**
   * PKCS#7状态查看,传入Boolean值进行设置
   * @param boolean $flag
   * @return boolean
   */
  public function isPKCS7Padding($flag = null)
  {
    if (null === $flag) {
      return $this->_PKCS7;
    }
    $this->_PKCS7 = (bool) $flag;
  }

  /**
   * 开启加密算法
   * @param string $algorithm_directory locate the encryption 
   * @param string $mode_directory
   * @return Crypt_AES
   */
  public function _openMode($algorithm_directory = "" , $mode_directory = "") 
  {
    $this->_descriptor = mcrypt_module_open($this->_cipher, 
                        $algorithm_directory, 
                        $this->_mode,
                        $mode_directory);
    return $this;
  }

  public function getDescriptor()
  {
    if (null === $this->_descriptor) {
      $this->_openMode();
    }
    return $this->_descriptor;
  }

  protected function _genericInit()
  {
    return mcrypt_generic_init($this->getDescriptor(), $this->getKey(), $this->getIv());
  }

  protected function _genericDeinit()
  {
    return mcrypt_generic_deinit($this->getDescriptor());
  }

  public function getMode()
  {
    return $this->_mode;
  }
  
  public function setMode($mode)
  {
    $this->_mode = $mode;
    return $this;
  }

  public function getCipher()
  {
    return $this->_cipher;
  }
  
  public function setCipher($cipher)
  {
    $this->_cipher = $cipher;
    return $this;
  }  
  /**
   * 获得key
   * @return string
   */
  public function getKey()
  {
    return $this->_key;
  }
  
  /**
   * 设置可以
   * @param string $key
   */
  public function setKey($key)
  {
    $this->_key = $key;
    return $this;
  }  


  /**
   * 获得加密向量块,如果其为null时将追加当前Descriptor的IV大小长度
   *
   * @return string
   */
  public function getIv()
  {
    if (null === $this->_iv && in_array($this->_mode, array( "cbc", "cfb", "ofb", ))) {
      $size = mcrypt_enc_get_iv_size($this->getDescriptor());
      $this->_iv = str_pad("", 16, "\0");
    }
    return $this->_iv;
  }

  /**
   * 获得向量块
   *
   * @param string $iv
   * @return Crypt_AES $this
   */
  public function setIv($iv)
  {
    $this->_iv = $iv;
    return $this;
  }  

  /**
   * 加密
   * @param string $str 被加密文本
   * @return string
   */
  public function encrypt($str){
    $td = $this->getDescriptor();
    $this->_genericInit();
    $bin = mcrypt_generic($td, $this->padding($str));
    $this->_genericDeinit();

    return $bin;
  }
 
  public function padding($dat)
  {
    if ($this->isPKCS7Padding()) {
      $block = mcrypt_enc_get_block_size($this->getDescriptor());
   
      $len = strlen($dat);
      $padding = $block - ($len % $block);
      $dat .= str_repeat(chr($padding),$padding);      
    }

    return $dat;
  }

  public function unpadding($str)
  {
    if ($this->isPKCS7Padding()) {
      $pad = ord($str[($len = strlen($str)) - 1]);
      $str = substr($str, 0, strlen($str) - $pad);
    }
    return $str;
  }

  /**
   * 解密
   * @param string $str 
   * @return string
   */
  public function decrypt($str){
    $td = $this->getDescriptor();

    $this->_genericInit();
    $text = mdecrypt_generic($td, $str);
    $this->_genericDeinit();

    return $this->unpadding($text);
  }
  
  /**
   * 16进制转成2进制数据
   * @param string $hexdata 16进制字符串
   * @return string
   */
  public static function hex2bin($hexdata) 
  {
    return pack("H*" , $hexdata);
  }

  /**
   * 字符串转十六进制
   * @param string $hexdata 16进制字符串
   * @return string
   */
  public static function strToHex($string)
  {
    $hex='';
    for($i=0;$i<strlen($string);$i++)
      $hex.=dechex(ord($string[$i]));
    $hex=strtoupper($hex);
    return $hex;
  }

  /**
   * 十六进制转字符串
   * @param string $hexdata 16进制字符串
   * @return string
   */
  function hexToStr($hex)
  {
    $string='';
    for($i=0;$i<strlen($hex)-1;$i+=2)
      $string.=chr(hexdec($hex[$i].$hex[$i+1]));
    return $string;
  }
}

客户端请求部分:

<?php 

include 'AES.php';

$md5Key = 'ThisIsAMd5Key';               // 对应服务端:$md5key = 'ThisIsAMd5Key';
$aesKey = Crypt_AES::strToHex('1qa2ws4rf3edzxcv');   // 对应服务端:$aesKey = '3171613277733472663365647A786376';
$aesKey = Crypt_AES::hex2bin($aesKey);
$aesIV = Crypt_AES::strToHex('dfg452ws');       // 对应服务端:$aesIV = '6466673435327773';
$aes = new Crypt_AES($aesKey,$aesIV,array('PKCS7'=>true, 'mode'=>'cbc'));

// var_dump($aes);

$data['name'] = 'idoubi';
$data['sex']= 'male';
$data['age'] = 23;
$data['signature'] = '白天我是一个程序员,晚上我就是一个有梦想的演员。';

$content = base64_encode($aes->encrypt(json_encode($data)));
$content = urlencode($content);
$sign = md5($content.$md5Key);

$url = 'http://localhost/aesdemo/api.php';
$params = "version=1.0&sign=$sign&content=$content";

// 请求接口
post($url, $params);

/**
 * 接口请求函数
 */
function post($url, $params) {
  $curlPost= $params;
  $ch = curl_init();   //初始化curl
  curl_setopt($ch, CURLOPT_URL, $url);  //提交到指定网页
  curl_setopt($ch, CURLOPT_HEADER, 0);  //设置header
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);  //要求结果为字符串且输出到屏幕上
  curl_setopt($ch, CURLOPT_POST, 1);  //post提交方式
  curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);
  $result = curl_exec($ch);//运行curl
  curl_close($ch);
  var_dump(json_decode($result, true));
}

接口处理逻辑:

<?php 


include 'AES.php';

$data = $_POST; // 接口请求得到的数据
$content = $data['content'];
$sign = $data['sign'];

$aesKey = '3171613277733472663365647A786376';
$aesIV = '6466673435327773';
$md5key = 'ThisIsAMd5Key';

// 校验数据
if(strcasecmp(md5(urlencode($content).$md5key),$sign) == 0) {
  // 数据校验成功
  $key = Crypt_AES::hex2bin($aesKey);
  $aes = new Crypt_AES($key, $aesIV, array('PKCS7'=>true, 'mode'=>'cbc'));

  $decrypt = $aes->decrypt(base64_decode($content));
  if (!$decrypt) {   // 解密失败
    echo json_encode('can not decrypt the data');
  } else {
    echo json_encode($decrypt);   // 解密成功
  }
} else{
  echo json_encode('data is not integrity');    // 数据校验失败
}

上述接口请求过程中定义了三个加解密需要用到的参数:$aesKey、$aesIV、$md5key,在实际应用过程中,只要与客户端用户约定好这三个参数,客户端程序员利用这几个参数对要请求的数据进行加密后再请求接口,服务端程序员在接收到数据后利用同样的加解密参数对数据进行解密,整个api请求过程中的数据就很安全了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
php模拟post行为代码总结(POST方式不是绝对安全)
Feb 22 PHP
MySQL的FIND_IN_SET函数使用方法分享
Mar 27 PHP
ThinkPHP实现事务回滚示例代码
Jun 23 PHP
php实现汉字验证码和算式验证码的方法
Mar 07 PHP
PHP数组操作类实例
Jul 11 PHP
Smarty使用自定义资源的方法
Aug 08 PHP
关于php中的json_encode()和json_decode()函数的一些说明
Nov 20 PHP
php实现多维数组排序的方法示例
Mar 23 PHP
php数据库的增删改查 php与javascript之间的交互
Aug 31 PHP
PHP学习记录之数组函数
Jun 01 PHP
Yii框架的redis命令使用方法简单示例
Oct 15 PHP
PHP 自动加载类原理与用法实例分析
Apr 14 PHP
centos+php+coreseek+sphinx+mysql之一coreseek安装篇
Oct 25 #PHP
在Thinkphp中使用ajax实现无刷新分页的方法
Oct 25 #PHP
PHP上传Excel文件导入数据到MySQL数据库示例
Oct 25 #PHP
详解PHP中foreach的用法和实例
Oct 25 #PHP
php array_keys 返回数组的键名
Oct 25 #PHP
php array_key_exists() 与 isset() 的区别
Oct 24 #PHP
PHP实现简易blog的制作
Oct 24 #PHP
You might like
php htmlentities()函数的定义和用法
2016/05/13 PHP
Laravel5中Cookie的使用详解
2017/05/03 PHP
thinkPHP5框架导出Excel文件简单操作示例
2018/08/03 PHP
Smarty缓存机制实例详解【三种缓存方式】
2019/07/20 PHP
[转]JS宝典学习笔记
2007/02/07 Javascript
不能再简单的无闪刷新验证码原理很简单
2007/11/05 Javascript
表格 隔行换色升级版
2009/11/07 Javascript
JavaScript Event学习第六章 事件的访问
2010/02/07 Javascript
js实现兼容IE6与IE7的DIV高度
2010/05/13 Javascript
浅析JavaScript中两种类型的全局对象/函数
2013/12/05 Javascript
jQuery中element选择器用法实例
2014/12/29 Javascript
javascript数组输出的两种方式
2015/01/13 Javascript
JS+CSS实现六级网站导航主菜单效果
2015/09/28 Javascript
深入理解ECMAScript的几个关键语句
2016/06/01 Javascript
Node.js中常规的文件操作总结
2016/10/13 Javascript
Vue2实现组件props双向绑定
2016/12/02 Javascript
JavaScript学习笔记--常用的互动方法
2016/12/07 Javascript
JavaScript实现AOP详解(面向切面编程,装饰者模式)
2017/12/19 Javascript
简单明了区分escape、encodeURI和encodeURIComponent
2018/05/26 Javascript
微信小程序第三方框架对比 之 wepy / mpvue / taro
2019/04/10 Javascript
NodeJs crypto加密制作token的实现代码
2019/11/15 NodeJs
JavaScript 绘制饼图的示例
2021/02/19 Javascript
Python获取运行目录与当前脚本目录的方法
2015/06/01 Python
Python的网络编程库Gevent的安装及使用技巧
2016/06/24 Python
Python SMTP发送邮件遇到的一些问题及解决办法
2018/10/24 Python
python selenium 查找隐藏元素 自动播放视频功能
2019/07/24 Python
Django 拆分model和view的实现方法
2019/08/16 Python
tensorflow实现从.ckpt文件中读取任意变量
2020/05/26 Python
SpringBoot首页设置解析(推荐)
2021/02/11 Python
python lambda的使用详解
2021/02/26 Python
青年文明号申报材料
2014/12/23 职场文书
考勤制度通知
2015/04/25 职场文书
催款律师函范文
2015/05/27 职场文书
合理化建议书范文
2015/09/14 职场文书
Win11右下角图标点了没反应怎么办?Win11点击右下角图标无反应解决方法汇总
2022/07/07 数码科技
Spring中bean集合注入的方法详解
2022/07/07 Java/Android