用Google API来取得Google帐户的联系人列表

标签 : ,

昨天写了一篇从Web Mail里取得用户通讯录的方法的文章,里面提到了Google的Account Authentication API,今天我们就用Account Authentication APIGoogle Contacts Data API来做一个取得Google帐户联系人的测试。

首先我们要看一下Account Authentication API,对于网络应用来说我们选择对网络应用程序的验证,对网络应用程序的验证也提供了 OAuthAuthSub 两种认证方式,我们选择AuthSub的认证方式,认证过程如下图

Google Authsub Diagram

用户在第三方Web应用上向Google Accounts Authentication发送AuthSub的HTTP请求,如果用户没有登录Google,则会显示登录页面,用户登录之后,会提示用户是否接受或拒绝这个第三方Web应用的访问请求,如果用户同意,Google就会生成一个token,转回第三方Web应用,第三方Web应用凭此token,可以请求Google的相关Sevice,比如联系人的服务,从而取得相关数据。

AuthSub有两种接口,一个是AuthSubRequest(A call to this method sends the user to a Google Accounts web page, where the user is given the opportunity to log in and grant Google account access to the web application. If successful, Google provides a single-use authentication token, which the web application can use to access the user’s Google service data.)另一个是AuthSubSessionToken(A call to this method allows the web application to exchange a single-use token for a session token),按照Google文档的理解,AuthSubRequest是一个单次的认证,AuthSubSessionToken应该是带会话(Session)的。

我们就举Google提供的例子

https://www.google.com/accounts/AuthSubRequest?
   next=http%3A%2F%2Fwww.yourwebapp.com%2Fshowcalendar.html
   &scope=http%3A%2F%2Fwww.google.com%2Fcalendar%2Ffeeds%2F
   &session=1
   &secure=1

https://www.google.com/accounts/AuthSubRequest就是AuthSub请求的地址,next表示认证之后要转回的地址,一般就是第三方Web应用的地址,也就是你网站的一个地址,Google会把token附带到这个地址后面,scope是你要请求的Google服务地址,这个例子里是要访问Google日历的数据,另外两个参数看Google的文档吧。

接下来,我们要取得Google帐户的联系人,我们先看看Google提供了多少可以访问的服务吧,访问Google数据API,Google提供的数据还着不少,有日历、文档、图书搜索、网路相册等等,当然也包括我们所需要的联系人的API,Google数据API要好好了解一下,总体来说Google提供一个Gdata的数据格式,和RSS的feed类似的格式,通过相关服务的访问地址,就可以返回Gdata数据,至于Gdata的读取,已经有了很多程序语言的封装好的程序(http://code.google.com/intl/zh-CN/apis/gdata/clientlibs.html),直接用就可以了,我们用PHP举例,PHP对Gdata的封装,是Zend Framework里的Gdata包,在http://framework.zend.com/download/gdata下载就可以了,但是现在Zend Gdata的包里没有直接的Google contacts的组件,但不要紧,通过Zend Gdata里基础数据的访问,可以取得Google contacts。

我们看看Google Contacts Data API的开发人员指南吧,取得联系人的Feed URL是

http://www.google.com/m8/feeds/contacts/userEmail/full
或 

http://www.google.com/m8/feeds/contacts/default/full

那我们就用PHP来写一个取得联系人的程序吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
require_once 'Zend/Loader.php';
Zend_Loader::loadClass ( 'Zend_Gdata' );
Zend_Loader::loadClass ( 'Zend_Gdata_AuthSub' );
Zend_Loader::loadClass ( 'Zend_Gdata_ClientLogin' );
Zend_Loader::loadClass ( 'Zend_Gdata_Query' );
Zend_Loader::loadClass ( 'Zend_Gdata_Feed' );
 
$my_contacts = 'http://www.google.com/m8/feeds/contacts/default/full';
 
if (! isset ( $_SESSION ['cal_token'] )) {
	if (isset ( $_GET ['token'] )) {
		// You can convert the single-use token to a session token.
		$session_token = Zend_Gdata_AuthSub::getAuthSubSessionToken ( $_GET ['token'] );
		// Store the session token in our session.
		$_SESSION ['cal_token'] = $session_token;
	} else {
		// Display link to generate single-use token
		$googleUri = Zend_Gdata_AuthSub::getAuthSubTokenUri ( 'http://' . $_SERVER ['SERVER_NAME'] . $_SERVER ['REQUEST_URI'], $my_contacts, 0, 1 );
		echo "Click <a href='$googleUri'>here</a> " . "to authorize this application.";
		exit ();
	}
}
 
// Create an authenticated HTTP Client to talk to Google.
$client = Zend_Gdata_AuthSub::getHttpClient ( $_SESSION ['cal_token'] );
 
$gdata = new Zend_Gdata ( $client );
$query = new Zend_Gdata_Query ( $my_contacts );
//$query->setMaxResults(10);
$query->setMaxResults ( 2000 );
$feed = $gdata->getFeed ( $query );
 
foreach ( $feed as $entry ) {
 
	$parts = $entry->getExtensionElements ();
	foreach ( $parts as $p ) {
		$element = $p->getDOM ();
		switch ($element->tagName) {
			case 'email' :
				print ( "Email: " . $element->getAttribute ( 'address' ) . "<br/>" );
				break;
			case 'phoneNumber' :
				print ( "Phone: " . $element->nodeValue . "<br/>" );
				break;
			default :
				continue;
		}
	}
 
}

放在你服务器上运行一下吧(别忘了Zend Gdata包要加进去)。

从邮件服务商取得用户通讯录的方法

标签 : , , , ,

我们在使用facebook开心网这些SNS网站的时候,里面有一项功能,就是通过邮件邀请好友,它可以导出你在Hotmail、Gmail、Yahoo、网易、新浪等Web Mail里的通讯录,然后给这些好友发送邀请信,这种病毒式营销推广方式起到了非常好的效果,facebook、开心网这些网站得以迅速壮大,这项功能也成了SNS网站的标配,从技术角度,我们就来探讨一下实现这项功能的原理和方法。

先说Gmail、Hotmail,Google和MS还是非常强大的,Google提供了Account Authentication API,它允许第三方的网站通过它的认证服务后,通过Google的Service取得用户的数据,包括通讯录等,MS也有Windows Live ID服务,不过我对微软的东西研究的不深,MSDN上找到的Windows Live ID SDK的资料,http://msdn.microsoft.com/en-us/library/bb404787.aspx

接下来说说其他邮件系统的取得方式,主要的原理就是通过模拟用户行为,登陆Web Mail系统,取得用户通讯录的信息,很多Web Mail都提供通讯录CSV格式的下载服务,我们取得这个CSV文件就可以了,网易的邮件系统似乎已经不提供CSV文件的下载,那只能通过通讯录页面的HTML分析,来取得信息,所以相对复杂一些,而Sohu的通讯录虽然前端采用了Ajax,但后端取得通讯返回的格式是Json,应该是最方便。

接下来说模拟用户行为,其实就是通过在自己服务器端向Web Mail发送相对的HTTP请求,而模拟出用户登陆的行为,在Linux下,我们用cURL这个组件,在编译PHP的时候把cURL编译进去,使PHP支持cURL,cURL非常好用,也非常强大,在libcurl里有大多数开发语言的支持,如果使用JAVA的话,还可以使用Apache的HttpClient

我们要模拟出用户登陆Web Mail的过程,我们就要对通常情况下,登陆Web Mail的HTTP请求过程有所了解,HttpWatch是一个非常好的工具,在做为插件安装倒IE和FireFox上,在IE或是FireFox里就可以看到所有的HTTP请求的信息了。

httpwatch

我们看看网易163的邮件的登陆过程

00:00:00.000    跳转提示
+ 0.000        0.231    1543    2352    POST    200    text/html; charset=UTF-8    http://reg.163.com/login.jsp?type=1&url=http://entry.mail.163.com/coremail/fcg/ntesdoor2?lightweight%3D1%26verifycookie%3D1%26language%3D-1%26style%3D35
0.231    1543    2352    1 request
00:00:00.462        0.177    0    0    GET    (History Cache)    image/x-icon    http://reg.163.com/favicon.ico
00:00:00.469    网易电子邮箱 – 简约3.0Beta
+ 0.000        0.118    1703    604    GET    302    Redirect to http://g4a30.mail.163.com/jy3/main.jsp?sid=gAJQIqzzbBwvsaVoDhzztmnXQvBRiMuz    http://entry.mail.163.com/coremail/fcg/ntesdoor2?lightweight=1&verifycookie=1&language=-1&style=35&username=xxx
+ 0.245        0.366    1664    6788    GET    200    text/html;charset=GBK    http://g4a30.mail.163.com/jy3/main.jsp?sid=gAJQIqzzbBwvsaVoDhzztmnXQvBRiMuz
+ 0.422        0.158    0    0    GET    (Cache)    text/css    http://mimg.126.net/p/jy3style/lib/0903090940/jscss/globle.css
+ 0.659        0.087    0    0    GET    (Cache)    application/x-javascript    http://mimg.126.net/p/jy3style/js/0903090940/tools.js
+ 0.816        0.067    0    0    GET    (Cache)    text/css    http://mimg.126.net/p/jy3style/lib/0903090940/cmcss/skin_blue.css
+ 0.998        0.578    1586    5521    GET    200    text/html;charset=GBK    http://g4a30.mail.163.com/jy3/top.jsp?sid=gAJQIqzzbBwvsaVoDhzztmnXQvBRiMuz
+ 1.015        0.624    1589    24844    GET    200    text/html;charset=GBK    http://g4a30.mail.163.com/jy3/folder.jsp?sid=gAJQIqzzbBwvsaVoDhzztmnXQvBRiMuz
+ 1.025        0.316    1590    10490    GET    200    text/html;charset=GBK    http://g4a30.mail.163.com/jy3/welcome.jsp?sid=gAJQIqzzbBwvsaVoDhzztmnXQvBRiMuz
+ 1.034        3.174    1592    2043    GET    200    text/html;charset=GBK    http://g4a30.mail.163.com/jy3/justfresh.jsp?sid=gAJQIqzzbBwvsaVoDhzztmnXQvBRiMuz
+ 1.280        0.439    0    0    GET    (Cache)    text/css    http://mimg.126.net/p/jy3style/lib/0903090940/jscss/globle.css
+ 1.632        0.407    0    0    GET    (Cache)    text/css    http://mimg.126.net/p/jy3style/lib/0903090940/jscss/globle.css
+ 1.647        0.432    0    0    GET    (Cache)    text/css    http://mimg.126.net/p/jy3style/lib/0903090940/jscss/welcome2.css
+ 1.876        0.235    0    0    GET    (Cache)    text/css    http://mimg.126.net/p/jy3style/lib/0903090940/jscss/globle.css
+ 2.179        0.117    0    0    GET    (Cache)    application/x-javascript    http://mimg.126.net/p/jy3style/js/0903090940/tools.js
+ 2.263        0.167    0    0    GET    (Cache)    application/x-javascript    http://mimg.126.net/p/jy3style/js/0903090940/tools.js
+ 2.354        0.207    0    0    GET    (Cache)    application/x-javascript    http://mimg.126.net/p/jy3style/js/0903090940/tools.js
+ 2.488        0.209    0    0    GET    (Cache)    text/css    http://mimg.126.net/p/jy3style/lib/0903090940/cmcss/skin_blue.css
+ 2.614        0.152    0    0    GET    (Cache)    text/css    http://mimg.126.net/p/jy3style/lib/0903090940/cmcss/skin_blue.css
+ 2.619        0.192    0    0    GET    (Cache)    application/x-javascript    http://mimg.126.net/p/jy3style/js/0903090940/welcome.js
+ 2.740        0.325    0    0    GET    (Cache)    text/css    http://mimg.126.net/p/jy3style/lib/0903090940/cmcss/skin_blue.css
4.208    9724    50290    20 requests
00:00:00.878        0.124    0    0    GET    (History Cache)    image/x-icon    http://www.163.com/favicon.ico
00:00:03.572    网易电子邮箱 – 简约3.0Beta
+ 0.000        0.541    0    0    GET    (Cache)    text/html; charset=GB2312    http://mimg.163.com/tianqi/city_simple/58367_090311.html
+ 0.531        0.715    1424    250    GET    304    text/css    http://mimg.163.com/jy3style/lib/jscss/ifr_weather2.css
+ 1.614        0.245    0    0    GET    (Cache)    image/gif    http://mimg.163.com/jy3style/lib/images/weather2.gif
1.859    1424    250    3 requests

首先,将用户名和密码POST到http://reg.163.com/login.jsp?type=1&url=http://entry.mail.163.com/coremail/fcg/ntesdoor2?lightweight%3D1%26verifycookie%3D1%26language%3D-1%26style%3D35,reg.163.com是网易通行证,后面的url是用户登陆网易通行证后要转向的地址,在这里我们看到就是http://entry.mail.163.com/coremail/fcg/ntesdoor2,这就是网易邮件的入口,在我们模拟登陆的时候,可以不传这个url过去,至于用户名和密码表单的name,看mail.163.com的HTML代码就知道了。相关错误处理,在POST到http://reg.163.com/login.jsp这个地址之后,返回的信息要取得的,因为如果用户输错了用户名或是密码,还是应该要提示用户的,而且在错误的情况下,也不用往下进行了,reg.163.com返回的错误页面里有“您的密码不正确”或是“用户名不正确”这样的信息,以此就可以判断用户是否成功登陆了。

之后我们要模拟用户访问http://entry.mail.163.com/coremail/fcg/ntesdoor2?lightweight=1&verifycookie=1&language=-1&style=35&username=xxx,就是登陆邮件的过程,注意,这里是个GET方法,用POST方法是不行的,访问entry.mail.163.com这个URL之后,会返回一个302码,就是要Redirect转向,到http://g4a30.mail.163.com/jy3/main.jsp?sid=gAJQIqzzbBwvsaVoDhzztmnXQvBRiMuz这个地址,这也就是大家看到的邮件登陆进去的首页了,注意这个sid要抓出来,应该是登陆的一个token,还有http://g4a30.mail.163.com这个地址,网易邮件是个集群,有非常多的服务器,你们每次上去的服务器是随随机的,用你的sid访问其他服务器也是没问题的,比如http://g1a98.mail.163.com/jy3/main.jsp?sid=gAJQIqzzbBwvsaVoDhzztmnXQvBRiMuz,所以这个服务器地址你可以写死。

解析来我们要看看网易邮件通讯录的URL,在UI看到是http://g4a30.mail.163.com/jy3/address/addrlist.jsp?sid=gAJQIqzzbBwvsaVoDhzztmnXQvBRiMuz&gid=all,我们模拟用户访问这个URL,然后抓取这个页面的内容HTML,分析一下,就可以了,在PHP里有个很好的HTML Parser工具PHP Simple HTML DOM Parser,可以像jQuery一样取得页面元素,这样就很方便的取得页面信息了。

其他一些Web Mail的登陆过程分析也是类似的,如果是取得CSV文件就更简单了,通过取到的通讯录内容,就可以在SNS网站上邀请了。

在这里我向国内这些大的门户网站或是Mail服务商提个建议,现在的互联网是一个开放化、平台化的趋势,网站应该开放自己的资源,可以让第三方的的应用更方便的访问,固步自封是不能发展的,试想如果QQ开放他的好友平台,引入第三方应用,那它将轻松超过Facebook,成为世界第一大SNS社区。

Kohana的Events和Hooks研究

标签 : ,

Kohana是一个使用PHP5的面向对象的MVC框架,是从CodeIgniter衍生出来,研究了几天,感觉还不错,就是它的文档实在太简陋了,在看到它的Events机制和Hooks机制时,确实有点不明白,文档上说的实在太简单了,后来我在Google上搜索了一下,找到两篇文章《Events and hooks in Kohana》《Practical Kohana Hooks example: PHPIDS》,上面讲了一下Kohana的Events和Hooks,并举了例子,我也开始慢慢理解了它的含义。

在Kohana的Events中,默认定义了很多的Events(例如system.ready、system.pre_controller等等),中文理解就是事件,也就是触发点,在程序在运行到某个位置时,会被触发,具体就是调用Event::run方法,我们在Kohana.php里可以看到在不同的位置执行了不同的Event::run方法,那么Hooks的意义在于当一个事件被触发之前,可以通过已加载的Hooks来修改事件的回调,有点绕口,说白了就是在事件发生之前,做点事情,举个例子,你安排了今天的日程,下午3点要开会,那么在3点开会就是一个Event(事件),那么到3点的时刻,你希望提醒你一下,你在你的手机里定了一个闹钟,在3点的时候会响,这就是一个Hook,可以看出Hook是基于Event的。

我们可以创建自己的Hook程序,并加载如默认的Events里,但是默认的Events不一定能满足我们的需要,比如我们在Web应用中经常使用的权限校验,判断这个用户是否登陆过,我们虽然可以使用默认Events里的system.pre_controller,但是这个Event是针对所有的Controller的,有些Controller是不需要校验用户的,还好Kohana允许自己定义Event,下面我们还是举例说明吧。

要使用Hooks,首先要在application/config/config.php里将$config['enable_hooks']置为TRUE。

之后我们定义自己的Event,我们在application/controllers目录下建立base.php,代码如下:

1
2
3
4
5
6
7
class Base_Controller extends Controller  {
 
	public function __construct() {
		parent::__construct();
		Event::run("base.construct");
	}	
}

我们继承了Kohana的Controller,在构造函数里定义了Event,名字叫“base.construct”,以后我们的Controller都继承自Base_Controller,那么在对象创建的时候都会触发base.construct事件。

Hook的文件放在application/hooks下,我们就创建一个hook文件sessioncheck.php,代码如下:

1
2
3
4
5
6
7
8
9
class SessionCheck {
 
	public function check() {
		echo "check session ...";
	}
 
}
 
Event::add('base.construct', array('SessionCheck','check'));

我们将SessionCheck的check方法加载到了base.construct事件上,也就是在触发base.construct事件之前,会执行SessionCheck的check方法。

接下来我们写一个Controller,在application/controllers目录下建立first.php,代码如下:

1
2
3
4
5
6
7
class First_Controller extends Base_Controller  {
 
	public function index() {
		echo "First - index";
		exit();
	}
}

我们执行一下看看会有什么提示,http://localhost/kohana/first,显示

check session …First – index

没问题了,在Controller构造时,执行了Hook里的方法。

PHP框架选择

标签 : , ,

离最初用PHP编程序已经有8、9年时间了,后来这6、7年的时间一直研究Java,对PHP有些生疏了,但PHP的生命力却依旧顽强,对于面向Web开发时Java的繁琐,我最近又将注意力集中到了PHP上,但已经习惯了Struts这样的MVC框架,我也要寻找一个适合的PHP MVC框架,选择的标准有几个:1、性能;2、易用性;3、文档;4、长期支持度

我最开始看了Zend Framework,Zend的东西,毕竟带有官方特性,他的framework应该是代表着主流,看了之后,Zend Framework可以说是纷繁复杂,但是面面俱到,Web应用方面的问题基本都可以解决,我唯一担心的就是性能,虽没有做过测试,但也确实担心。

后来有一天在JavaEye上逛,看到一篇帖子《PHP框架的繁荣是正确的发展方向吗?》,讨论了PHP的运行机制、与ROR的比较、性能等等,非常热闹,同时也列举出了一些PHP的框架,特别是一些性能比较,让我很吃惊,CakePHP、Symfony可以不用考虑了。

接下来我看了看CodeIgniter,感觉不错,简单,相比Zend Framework要简单得多,大多数问题也都能解决,性能在一些资料描述中也表现的尚可(比Zend Framework要快几倍),而且其文档比较细,学习起来不难,后来又发现了Kohana,Kohana是从CodeIgniter衍生出来,由于CodeIgniter是兼容PHP4和5的,而Kohana只支持PHP5,是完全的OO方式,其文档并还没有仔细研究,看到了一个比较的文章《Notes on Choosing a PHP Framework: A Quick Comparison of CodeIgniter and Kohana》,看上去Kohana有些特性还是很优秀的,但不知道Kohana社区对于这个开源产品的支持有多好。

后来又看到文章《Performance of Yii》,发现Yii这个框架的性能更强劲啊,比CodeIgniter还要好几倍,不可思议,看了看Yii的文档,它也是完全OO的,要PHP5以上,核心应该也比较简单,能保持比较好的性能,但我觉得它的Guide文档比较粗,学习起来似乎要费点功夫,其性能应该是我最感兴趣的地方。

再说说国内的PHP框架,在JavaEye的文章里,QeePHP的作者也在推荐自己的框架,简单测试下比Yii还要快,好NB啊,但从社区反应出来其文档不够详细,其代码我也没有细看,似乎和Yii有很多相近的地方,另一个国内的PHP框架ThinkPHP文档比较详尽,但没有测试报告,不知道性能如何,而且在PHPChina的社区里和QeePHP有激烈的争论,挺有意思的。

看了一大圈,我也没有决定采用何种PHP的框架,他们各有长处,也各有缺陷,但综合考虑,我还是应该会在CodeIgniter、Kohana和Yii中选择最终的方案。

3DES加密

标签 : , ,

DES加密算法是一种标准算法,应该可以通过不同程序语言实现,我在异构系统里,可能需要用不同的语言加密解密,整理不同语言的DES算法还是有必要的,先前我发过一个Java的3DES算法代码,我再发一遍

import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;

public class DES {

public static int _DES = 1;
public static int _DESede = 2;
public static int _Blowfish = 3;

private Cipher p_Cipher;
private SecretKey p_Key;
private String p_Algorithm;

private void selectAlgorithm(int al) {
switch (al) {
default:
case 1:
this.p_Algorithm = “DES”;
break;
case 2:
this.p_Algorithm = “DESede”;
break;
case 3:
this.p_Algorithm = “Blowfish”;
break;
}
}

public DES(int algorithm) throws Exception {
this.selectAlgorithm(algorithm);
Security.addProvider(new com.sun.crypto.provider.SunJCE());
this.p_Cipher = Cipher.getInstance(this.p_Algorithm);
}

public byte[] getKey() {
return this.checkKey().getEncoded();
}

private SecretKey checkKey() {
try {
if (this.p_Key == null) {
KeyGenerator keygen = KeyGenerator.getInstance(this.p_Algorithm);
/*
SecureRandom sr = new SecureRandom(key.getBytes());
keygen.init(168, sr);*/
this.p_Key = keygen.generateKey();
}
}
catch (Exception nsae) {}
return this.p_Key;
}

public void setKey(byte[] enckey) {
this.p_Key = new SecretKeySpec(enckey, this.p_Algorithm);
}

public byte[] encode(byte[] data) throws Exception {
this.p_Cipher.init(Cipher.ENCRYPT_MODE, this.checkKey());
return this.p_Cipher.doFinal(data);
}

public byte[] decode(byte[] encdata, byte[] enckey) throws Exception {
this.setKey(enckey);
this.p_Cipher.init(Cipher.DECRYPT_MODE, this.p_Key);
return this.p_Cipher.doFinal(encdata);
}

public String byte2hex(byte[] b) {
String hs = “”;
String stmp = “”;
for (int i = 0; i < b.length; i++) {
stmp = Integer.toHexString(b[i] & 0xFF);
if (stmp.length() == 1) {
hs += “0″ + stmp;
}
else {
hs += stmp;
}
}
return hs.toUpperCase();
}

public byte[] hex2byte(String hex) throws IllegalArgumentException {
if (hex.length() % 2 != 0) {
throw new IllegalArgumentException();
}
char[] arr = hex.toCharArray();
byte[] b = new byte[hex.length() / 2];
for (int i = 0, j = 0, l = hex.length(); i < l; i++, j++) {
String swap = “” + arr[i++] + arr[i];
int byteint = Integer.parseInt(swap, 16) & 0xFF;
b[j] = new Integer(byteint).byteValue();
}
return b;
}

public static void main(String[] args) throws Exception {

String info = “要加密的字串”;
System.out.println(“region string:” + info);
byte[] key; //密钥文件(byte)
DES des = new DES(DES._DESede); // 声明DES
key = des.getKey(); //获取随机生成的密钥
System.out.println(“encrypted key(byte):” + new String(key));
String hexkey = des.byte2hex(key); //生成十六进制密钥
System.out.println(“encrypted key(hex):” + hexkey);
byte[] enc = des.encode(info.getBytes()); //生成加密文件(byte)
System.out.println(“encrypted string(byte):” + new String(enc));

String hexenc = des.byte2hex(enc); //生成十六进制加密文件
System.out.println(“encrypted string(hex):” + hexenc);

byte[] dec = des.decode(enc, des.hex2byte(hexkey)); //解密文件,其中转换十六进制密钥为byte
System.out.println(“decrypted string:” + new String(dec)); //生成解密文件字符串, 与info相同
}

}

最近我在看PHP的时候,研究了一下PHP对于3DES的加密代码

<?php

function fmt3DESEncode($s,$base64key){

$key = base64_decode($base64key);

$td = mcrypt_module_open(MCRYPT_3DES, ”, MCRYPT_MODE_ECB, ”);

$iv_size = mcrypt_enc_get_iv_size($td);

$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);

mcrypt_generic_init($td, $key,$iv);

$encrypted_data = mcrypt_generic($td, $s);

mcrypt_generic_deinit($td);

mcrypt_module_close($td);

return $encrypted_data;

}

function PaddingPKCS7($data) {

$block_size = mcrypt_get_block_size(‘tripledes’, ‘ecb’);

$padding_char = $block_size – (strlen($data) % $block_size);

$data .= str_repeat(chr($padding_char),$padding_char);

return $data;

}

function fmt3DESDecode($s,$base64key) {

$key = base64_decode($base64key);

$td = mcrypt_module_open(MCRYPT_3DES, ”, MCRYPT_MODE_ECB, ”);

$iv_size = mcrypt_enc_get_iv_size($td);

$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);

mcrypt_generic_init($td, $key,$iv);

$p_t = mdecrypt_generic($td, $s);

mcrypt_generic_deinit($td);

mcrypt_module_close($td);

return $p_t;

}

function unPaddingPKCS7($data) {

$pad = ord($data{strlen($data)-1});

if ($pad > strlen($data)) {

return false;

}

if (strspn($data, chr($pad), strlen($data) – $pad) != $pad) {

return false;

}

$srcdata = substr($data, 0, -1 * $pad);

return $srcdata;

}

$S = “laoer”;

$str = BASE64_Encode(fmt3DESEncode(PaddingPKCS7($S),”XXXXXXXXX-BASE64-密钥”));

echo $str;

echo”<br/>”;

$str = fmt3DESDecode(base64_decode($str), “XXXXXXXXX-BASE64-密钥”);

echo unPaddingPKCS7($str);

?>

有几点注意,在Java里3DES默认ENCRYPT_MODE是ECB,并采用PKCS7补码,关于着两个概念,我不是专业人士,也解释不清楚,需要研究加密解密了,所以在PHP里也进行了相应的处理,还有就是Java代码里密钥和加密结果是用16进制编码了,PHP代码里用了Base64,大家用的时候相应改一下就可以了。

现在可以测试一下,JAVA和PHP的加密解密结果是否一致!

补:.NET下3DES的算法也没有问题,我不做这方面,但以前同事做过,都测试通过。