zoukankan      html  css  js  c++  java
  • php 的rsa密匙生成!加密解密

    最近研究了一下crypt rsa for php!

    RSA方法,简单地说,是生成一组数字n、e和d。用n和e加密明文(明文要求小于n),用n和d解密密文。
      寻找一组n、e、d很容易,但当n足够大时,由n和e求出d很难。这是RSA的安全性所在。
      https不需说了。这里说的是,如何在non-https环境使用JavaScript和PHP实现RSA加密/解密:在浏览器使用JavaScript加密信息,在服务器用PHP解密信息。

    一,用JavaScript加密

    有关RSA,比较好的JavaScript见[url]http://ohdave.com/rsa/[/url](该页面还有密钥生成程序下载)。用法也简单:

    1,设置参数

    setMaxDigits(d);

    函数的参数d跟n的位数有关。关系是

    d=n的十六进制位数/2+3

    比如n是十六进制16位(对应二进制125~128位),d就设成19。

    2,设置密钥

    key=new RSAKeyPair(e,d,n);

    参数e、d、n是十六进制表示。加密只需要e和n,d给空串。如:

    key=new RSAKeyPair('3d','','a090f4fdaba1c60975fb3b9ea6937a27');

    3,加密

    ct=encryptedString(key,pt+'\x01');

    这个函数把明文pt加密为密文ct(十六进制字符串)。对长信息,则分段加密(但PEAR::Crypt_RSA不认可,下文将解决这个问题)。
    '\x01'这个字节,是PEAR::Crypt_RSA要求的结尾符。保证PEAR::Crypt_RSA可以成功解密的一组明文长度(字节)为:

    n的十六进制位数/2-3

    对2的密钥,一次可以加密13个字节(14个也可能成功,看密钥和明文的情况)。

    二,用PHP解密

    PEAR::Crypt_RSA的安装很简单,这里不说了。

    1,打开数学库

    require_once('Crypt/RSA.php');//一般是这个路径
    $math_obj = &Crypt_RSA_MathLoader::loadWrapper();

    PHP 的数学库有GMP、BigInt、BCMath。如果不给loadWrapper指定参数,他会按照这个顺序选择已经加载的数学库(分别对应 Crypt_RSA_Math_GMP类、Crypt_RSA_Math_BigInt类和Crypt_RSA_Math_BCMath类)。 BCMath是php的默认配置,但比起GMP,实在是慢多了。不了解BigInt,不好说。

    2,载入密钥

    $dd = $math_obj->int2bin('0x'.$d);
    $nn = $math_obj->int2bin('0x'.$n);
    $pk = new Crypt_RSA_Key($nn, $dd, 'private');

    $rsa_obj = new Crypt_RSA;
    $rsa_obj->setParams(array('dec_key' => $pk));

    3,解密

    $pt = $rsa_obj->decryptBinary($math_obj->int2bin('0x'.$ct));

    PEAR::Crypt_RSA可以使用十六进制密钥,也可以解密十六进制密文。但对Crypt_RSA_Math_BCMath类必须修改一下int2bin的代码,使他能够把十六进制数字转换为内部格式。
    当然,如果是十进制密钥/密文,'0x'前缀就不用加了。

    对Crypt_RSA_Math_BCMath::int2bin()的改动是在函数的开头加上:

    $p=substr($num,0,2);
    if($p=='0x'||$p=='0X'){
      $len=strlen($num);
      $result = 0;
      $factor = 1;
      for($i=$len-1;$i>=2;$i--){
      $result=bcadd($result,bcmul(hexdec($num[$i]),$factor));
      $factor=bcmul($factor,16);
      }
      $num=$result;
    }




    三,解决JavaScript加密功能浪费问题

    JavaScript的encryptedString()函数对长信息分段加密,密文用空格分割各个段落。而Crypt_RSA类的decryptBinary()函数不能适应这一点。为了利用充分JavaScript的加密功能,只能给Crypt_RSA类增加一个函数:

    function decryptString($s$key = null)
    {
      $result = ‘’;
      if($key==null)
      $key = $this->_dec_key;
      $exp = $this->_math_obj->bin2int($key->getExponent());
      $modulus = $this->_math_obj->bin2int($key->getModulus());
      $chunk_len = $key->getKeyLength() - 1;
      $block_len = (int) ceil($chunk_len / 8);

      $radix=10;
      if(substr($s,0,2)=='0x'){
      $radix=16;
      $s=substr($s,2);
      }
      $blocks = explode(" ",$s);

      for($i = 0; $i < count($blocks); ++$i){
      $enc_data=$blocks[$i];
      if ($radix == 16)
      $enc_data='0x'.$enc_data;
      $enc_data=$this->_math_obj->int2bin($enc_data);

      $data_len = strlen($enc_data);
      $curr_pos = 0;
      $bit_pos = 0;
      $plain_data = $this->_math_obj->bin2int("\0");
      while ($curr_pos < $data_len){
      $tmp = $this->_math_obj->bin2int(substr($enc_data$curr_pos$block_len));
      $tmp = $this->_math_obj->powmod($tmp$exp$modulus);
      $plain_data = $this->_math_obj->bitOr($plain_data$tmp$bit_pos);
      $bit_pos += $chunk_len;
      $curr_pos += $block_len;
      }
      $result .= $this->_math_obj->int2bin($plain_data);
      }
      if (ord($result{strlen($result) - 1}) == 0)
      $result = substr($result,0, strlen($result) - 1);
      return $result;
    }



     

    这个函数对密文分段解密。加密时也不必在末尾增加字节('\x01')了。解密调用如下:

    $rsa_obj->decryptString('0x'.$ct);//十进制不加0x前缀

    四,用PHP生成密钥

    PEAR::Crypt_RSA的Crypt_RSA_KeyPair类可以生成密钥。调用步骤如下:

    require_once('Crypt/RSA.php');
    $math_obj = &Crypt_RSA_MathLoader::loadWrapper();
    $key_pair = new Crypt_RSA_KeyPair($key_lenth);
    if (!$key_pair->isError()){
      $public_key = $key_pair->getPublicKey();
      $private_key = $key_pair->getPrivateKey();
      $e =$math_obj->hexstr($math_obj->bin2int($public_key->getExponent()));
      $d =$math_obj->hexstr($math_obj->bin2int($private_key->getExponent()));
      $n =$math_obj->hexstr($math_obj->bin2int($public_key->getModulus()));
    }




    hexstr()是自己添加的函数,用来把十进制字符串转换为十六进制。对Crypt_RSA_Math_GMP很简单,只需:

    function hexstr($num){
      return gmp_strval($num,16);
    }





    对Crypt_RSA_Math_BCMath略麻烦些:

    function hexstr($num){
      $result = '';
      do{
      $result = sprintf('%02x',intval(bcmod($num,256))).$result;
      $num = bcdiv($num, 256);
      }while(bccomp($num, 0));
      return ltrim($result,'0');
    }





    五,用php生成密钥(二)

    为了提高加密速度,一般选一个较小的e。比较常用的是3、17、257、65537几个素数。

    generate()生成密钥的算法是依次计算p,q,n,e,d。因此做了如下改动,以便可以自己选e值:

    原来的:
    function Crypt_RSA_KeyPair($key_len, $wrapper_name = 'default', $error_handler = '')
    改后增加一个参数e:
    function Crypt_RSA_KeyPair($key_len, $e = null, $wrapper_name = 'default', $error_handler = '')
    这个函数调用generate()。效应地:
    function generate($key_len = null)
    也增加一个参数e:
    function generate($key_len = null, $e = null)

    把CRYPT_RSA-1.0.0的KeyPair.php中属于generate()的245~271行改动顺序,由e确定p和q:

    if($e != null&&$this->_math_obj->cmpAbs($e,2)>0)
      $e = $this->_math_obj->nextPrime($this->_math_obj->dec($e));//取个素数
    else
    {
      while(true)
      {
      $e = $this->_math_obj->getRand($q_len$this->_random_generator);
      if ($this->_math_obj->cmpAbs($e,2)<=0)
      continue;
      $e = $this->_math_obj->nextPrime($this->_math_obj->dec($e));
      break;
      }
    }
    do{
      $p = $this->_math_obj->getRand($p_len$this->_random_generator, true);
      $p = $this->_math_obj->nextPrime($p);
      do{
      do{
      $q = $this->_math_obj->getRand($q_len$this->_random_generator, true);
      $tmp_len = $this->_math_obj->bitLen($this->_math_obj->mul($p$q));
      if ($tmp_len < $key_len)
      $q_len++;
      elseif ($tmp_len > $key_len)
      $q_len--;
      } while ($tmp_len != $key_len);
      $q = $this->_math_obj->nextPrime($q);
      $tmp = $this->_math_obj->mul($p$q);
      } while ($this->_math_obj->bitLen($tmp) != $key_len);
      // $n - is shared modulus
      $n = $this->_math_obj->mul($p$q);
      // generate public ($e) and private ($d) keys
      $pq = $this->_math_obj->mul($this->_math_obj->dec($p), $this->_math_obj->dec($q));
      if($this->_math_obj->isZero($this->_math_obj->dec($this->_math_obj->gcd($e$pq))))
      break;
    }while(true);






    (网易的服务真体贴啊,连pre标记里面的东西都给改。还改不好)这样,如果要生成e为3的1024位密钥,可以如下调用:

    $key_pair = new Crypt_RSA_KeyPair(1024,3);

    六,干什么用

      加密比较重要的数据。比如注册时用户输入的密码。
      登录时把密码hmac一下就可以防止重放攻击(replay attack)了。对注册不存在这种攻击,但有密码泄露的危险。上传密码hash那点安全性根本不算什么。这个可以用RSA加密解决。
      不过,对中间人攻击还是没办法。




    另外一个

  • 相关阅读:
    Vuforia7+Unity2017.3.f3实践练习
    pureMVC+unity
    sql语法小结
    ScreenToViewportPoint,WorldToViewportPoint,ViewportToWorldPoint的运用,实现一个简单的对三维中物体的拖拽移动效果
    Unity游戏开发学习之路——数据持久化
    Unity游戏开发之路上的那些坑——NullReferenceException
    ffmpeg-音频视频处理
    微信开发之(五)接收语音识别结果
    微信开发之(五)获取media_id的值
    微信开发之(五)微信获取自定义菜单
  • 原文地址:https://www.cnblogs.com/mutuan/p/2281534.html
Copyright © 2011-2022 走看看