当前位置

翻墙代理的加密部分

就是怎么在 PHP/mcrypt 和 PyCrypto 之间 DES(或其他加密算法,比如3DES/RSA/..) 通信的问题,我这里还额外考察了下 .NET 平台的算法

网上询问相关问题的还挺多,尤其是 PHP 和 .NET 之间的 DES 转换。主要是 PHP/mcrypt 隐藏了 padding 的细节,且只保留了 ZERO_PADDING 模式,所以不明白cyrpto原理的不太容易找到症结所在。我的传输方案统一用 pkcs#7 padding.

首先是给服务器端增加的 PKCS#7 PADDING 函数,来自PHP官方函数手册上某人的注释

  1. function padding_pkcs7($crypto, $mode, $dat)
  2. {
  3.         $block = mcrypt_get_block_size($crypto, $mode);
  4.         $len = strlen($dat);
  5.         $padding = $block - ($len % $block);
  6.         $dat .= str_repeat(chr($padding),$padding);
  7.         return $dat;
  8. }
  9.  
  10. function strip_pkcs7($crypto, $mode, $text)
  11. {
  12.         $block = mcrypt_get_block_size($crypto, $mode);
  13.         $packing = ord($text{strlen($text) - 1});
  14.         if($packing and ($packing < $block)){
  15.                 for($P = strlen($text) - 1; $P >= strlen($text) - $packing; $P--){
  16.                         if(ord($text{$P}) != $packing){
  17.                                 $packing = 0;
  18.                         }
  19.                 }
  20.         }
  21.         return substr($text,0,strlen($text) - $packing);
  22. }

本地端 .NET 平台的 descrypto 封装

  1. # -*- coding: utf-8 -*-
  2. # ipcrypto.py
  3. from System import Array, Byte
  4. from System.Security.Cryptography import DESCryptoServiceProvider, CryptoStream, CryptoStreamMode, PaddingMode
  5. from System.IO import MemoryStream, StreamWriter, StreamReader
  6.  
  7. from hashlib import md5
  8.  
  9. def s2ab(s):
  10.     return Array[Byte](tuple(Byte(ord(c)) for c in s))
  11.  
  12. def ab2s(ab, len=0):
  13.     if len == 0: len = ab.Length
  14.     return ''.join([chr(ab[i]) for i in range(len)])
  15.  
  16. class descrypto():
  17.     def __init__(self, password):
  18.         pwmd5 = md5(password).digest()
  19.         self.key = s2ab(pwmd5[:8])
  20.         self.iv = s2ab(pwmd5[8:])
  21.         self.des = DESCryptoServiceProvider() # 缺省 des.Mode = CipherMode.CBC
  22.  
  23.     def enc(self, input):
  24.         ms = MemoryStream()
  25.         encStream = CryptoStream(ms, self.des.CreateEncryptor(self.key, self.iv), CryptoStreamMode.Write)
  26.         sw = StreamWriter(encStream)
  27.         sw.Write(input)
  28.         sw.Flush()
  29.         encStream.FlushFinalBlock()
  30.         return ab2s(ms.GetBuffer(), ms.Length)
  31.  
  32.     def dec(self, input):
  33.         ms = MemoryStream(s2ab(input))
  34.         length = len(input)
  35.         decStream = CryptoStream(ms, self.des.CreateDecryptor(self.key, self.iv), CryptoStreamMode.Read)
  36.         byteArray = Array.CreateInstance(Byte, length)
  37.         length = decStream.Read(byteArray, 0, length)
  38.         return ab2s(byteArray, length)

本地端 PyCrypto 的封装

  1. # -*- coding: utf-8 -*-
  2. # pycrypto.py
  3. from hashlib import md5
  4. from Crypto.Cipher import DES
  5.  
  6. class descrypto():
  7.     def __init__(self, password):
  8.         pwmd5 = md5(password).digest()
  9.         self.key = pwmd5[:8]
  10.         self.iv = pwmd5[8:]
  11.  
  12.     def enc(self, input):
  13.         des = DES.new(self.key, DES.MODE_CBC, self.iv)
  14.         lastblock = len(input) % 8
  15.         if lastblock > 0:
  16.             padding = 8 - lastblock
  17.             input += padding * chr(padding)
  18.  
  19.         return des.encrypt(input)
  20.  
  21.     def dec(self, input):
  22.         des = DES.new(self.key, DES.MODE_CBC, self.iv)
  23.         ret = des.decrypt(input)
  24.         padding = ord(ret[-1])
  25.         for i in range(padding):
  26.             if ord(ret[-1 - i]) != padding:
  27.                 padding = 0
  28.                 break
  29.         if padding > 0:
  30.             ret = ret[:-padding]
  31.         return ret
Topic: 

评论

如果php空间设置了mbstring.func_overload,strlen返回的就不一定是字节数。这种情况下应该用mb_strlen($string, '8bit');
http://us.php.net/manual/en/function.mb-strlen.php#77040