Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3036431
  • 博文数量: 167
  • 博客积分: 613
  • 博客等级: 中士
  • 技术积分: 5473
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-13 21:35
个人简介

人, 既无虎狼之爪牙,亦无狮象之力量,却能擒狼缚虎,驯狮猎象,无他,唯智慧耳。

文章分类
文章存档

2015年(19)

2014年(70)

2013年(54)

2012年(14)

2011年(10)

分类: Python/Ruby

2014-07-10 15:32:53

   今天来学习使用Python实现对任意文件进行AES加密。Python强大之处一是表现在语法简单,内容小巧,却可以实现复杂的程序功能;二是其优秀的跨平台能力,尤其涉及系统API调用时,统一封装为PythonAPI,我们所需要的仅仅是在运行机器上有Python解释器;三是丰富的库模块,使得我们可以使用Python实现多种多样的功能。所有的这一切,都使得我们摆脱程序开发时的细枝末节,而将重点放在程序逻辑和技术实现之上。我们准备使用Python来对一个文件进行AES加密,当然我们可以自己来实现这个加密函数(其实推荐C/C++的实现,因为速度快),但是我们完全可以调用现成的模块。这里我们使用的模块是PyCrypto。

一、关于PyCrypto
   由于我们今天主要调用PyCrypto库来实现加密,所以这里还是稍微做些介绍吧!PyCrypto是一个免费的加密算法库,支持常见的DES、AES加密以及MD5、SHA各种HASH运算。我们可以在其官方主页下载最新版本:,我编写程序时最新的是v2.6.1,下载之后得到一个zip的发布包,其中提供了setup.py文件,可以知道该程序包是使用Distutils分发的,基本的命令应该是:

点击(此处)折叠或打开

  1. python setup.py build
  2. python setup.py install
    如果你的系统时Linux,那么恭喜你可以轻松地完成上面的安装过程,当然你的系统上应当有Python2;如果你的系统是Winodws7,那么恭喜你和楼主一样,需要在Windows上重新编译该包了。具体的过程比较繁琐,比如设置系统PATH变量,更改编译器MSVC设置等,这里给大家推荐一篇详细的安装教程:http://blog.victorjabur.com/2011/06/05/compiling-python-2-7-modules-on-windows-32-and-64-using-msvc-2008-express/,如果你打不开,那么恭喜你需要“翻墙”才行了。当然为诸多不方便上网或者不方便阅读的童鞋,我也准备了两个Windows下的自动配置包程序,但是是针对win7下的python2.7和PyCrypto2.6.1的,编译器使用的是VS2008,如果你的也是这个版本,可以拿来直接用了:
pycrypto.zip
    这些工作都做完之后,在PowerShell中启动python,输入import Crypto运行,如果没有提升错误则说明安装PyCrypto成功。PyCrypto中包含了许多加密方法,我们可以查阅官方文档:https://www.dlitz.net/software/pycrypto/api/current/,比如DES加密就包含说明+使用方法+函数参数说明:


二、AES加密小程序
   这部分我们来对一个文件(任何类型)进行加密,这里给出一个测试程序,虽然功能尚未完善,但是已经可以实现针对文件的AES加密。按照我们以往的惯例,我们先给出实现的代码,然后再逐行讲解。

点击(此处)折叠或打开

  1. # -*- coding: cp936 -*-
  2. #A Test to Return a AES-File of a Common File

  3. from Crypto.Cipher import AES
  4. from Crypto import Random
  5. import binascii

  6. def AES_File(fs):
  7.     key = b'1234567890!@#$%^' #16-bytes password
  8.     iv = Random.new().read(AES.block_size)
  9.     cipher = AES.new(key, AES.MODE_CBC, iv)
  10.     print 'if fs is a multiple of 16...'
  11.     #if fs is a multiple of 16
  12.     x = len(fs) % 16
  13.     print 'fs的长度是: ', len(fs)
  14.     print 'The num to padded is : ', x
  15.     if x != 0:
  16.         fs_pad = fs + '0'*(16 - x) #It shoud be 16-x not
  17.         print 'fs_pad is : ', fs_pad
  18.         print len(fs_pad)
  19.         print len(fs_pad)%16
  20.     msg = iv + cipher.encrypt(fs_pad)
  21.     print 'File after AES is like...', binascii.b2a_hex(msg[:10])
  22.     return msg

  23. #Create a Test Src File and Get FileSteam
  24. fs = open('test', 'w+')
  25. fs.write('啊,我爱你,我的祖国!')
  26. fs.write('凌晨三时开始进攻!')
  27. fs.seek(0,0)
  28. fs_msg = fs.read()
  29. print fs_msg
  30. fs.close()

  31. #Crypt Src FileStream
  32. fc = open('fc', 'wb')
  33. fc_msg = AES_File(fs_msg)
  34. fc.writelines(fc_msg)
  35. fc.close()

  36. raw_input('Enter for Exit...')
-1. 第一行的‘-*-coding:cp936 -*-’用于告诉Python解释器源码文件的编码方式,Python的默认方式是ASCii的,但是由于代码中出现了汉字,因此运行时解释器会提示我们是否要注明解码的方式,也可以使用'coding:utf-8';
-2. 第四行到第六行主要引入一些必要的模块,Crypto.Cipher模块中的AES模块以及Crypto中的Random模块,binascii模块为查看二进制下的数据提供了各种方法。
-3. 第八行到二十四行主要实现调用AES加密API的函数,我们命名为AES_File(),其中:
   第九行用于指定AES加密的初始密钥,根据AES规范,可以是16字节、24字节和32字节长,这里我们使用了一个16字节的初始密钥,其实完全可以由用户输入的口令+salt获得;
   第十行是用于生成iv,这里使用了Crypto模块中的Random模块,读取其16字节的数据作为iv的值,AES的分块大小固定为16字节;
   第十一行用于生成了加密时需要的实际密码,主要使用了AES.new(key, AES.MODE_CBC,iv)函数,key和iv由前两步生成,这步可以指定加密模式,这里选择的是CBC模式;
   第十二到第二十一行主要用于判断数据长度是否为16字节块的整数倍,从而进行适当的Padding,这里的关键是利用'%'运算判断是否是16字节的整数倍,然后在尾部追加(16-x)个填充字符;
   其实这里还有一个关键的问题,那就是Python中列表和字符串的转换问题。使用list()函数可以将字符串转换成列表,使用str()函数可以将列表转换成字符串,但是前后的转换并不是等价的,比如:

   可以看到a与c并不相等,将字符串转换成列表时,二者还是元素个数相等的,但是将列表转换成字符串时,会包含进“无关字符”,即'[]'、' '(空格)等,因此元素个数会发生改变。因此列表和字符串是不等价的转换。之所以想到了这个问题,是因为自己在考虑填充时开始想利用列表的append()方法写个循环,结果行不通,最后还是利用了序列乘法予以了解决。
-4. 第二十二行使用生成的cipher对象的encrypt方法加密文件流,得到密文文件流msg,注意这里与iv进行了一次异或,另一个需要注意的是encrypt方法的输入和输出都是一个字符串;
-5. 第二十三行主要是为了调试的需要,导入binascii模块查看密文文件流的前10个字节,每个二进制字节以十六进制的形式表示;
-6. 第二十七行到第三十三行的工作是生成一个明文文件,获取其明文数据流,这里可以直接从打开的文件中读取,不过为了测试的需要,这里临时写入一个文件,因此复制数据流的时候记得重置指针到文件开头,即fs.seek(0, 0);
-7. 第三十五行到第三十九行主要是将获取的明文数据流调用AES加密得到密文数据流,然后关闭文件;
   好了,现在我们看看结果,这里指定编码的时候不影响程序执行,但是会影响程序的print结果,第一次使用utf-8编码,第二次使用ascii编码:





阅读(35027) | 评论(1) | 转发(3) |
给主人留下些什么吧!~~

windhawkgyang2014-07-11 09:42:00

上述AES加密测试程序中 if流程中遗失了对不需要Padding时候的处理,添加一个else语句即可:
    if x != 0:
        fs_pad = fs + '0'*(16 - x)  #It shoud be 16-x not x!
        print 'fs_pad is : ', fs_pad
        print len(fs_pad)
        print len(fs_pad)%16
    else:
  &nb