首先是密钥交换的过程,Diffie-Hellman密钥交换算法参考维基百科的文档:
http://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange
client端js语言,服务端php语言 用DH密钥交换算法交换密钥。
var g = "2";
var p = "106025087133488299239129351247929016326438167258746469805890028339770628303789813787064911279666129";
function doStaff() {
var big_a = randBigInt(100);
var big_p = str2bigInt(p, 10, 0);
var big_g = str2bigInt(g, 10, 0);
var A = powMod(big_g, big_a, big_p);
var str_A = bigInt2str(A, 10);
var B;
var secret;
$.ajax({
url: 'server.php',
type: 'GET',
async: false,
data: {"A":str_A},
cache: false,
timeout: 5 * 1000,
dataType: 'json',
success: function(data, status, xhr) {
B = str2bigInt(data.B, 10, 0);
},
error: function() {
alert(2);
}
});
secret = powMod(B, big_a, big_p);
secret = bigInt2str(secret, 10);
上边的代码中,最后的到的secret,就是最终和服务端协商一致的密钥(这是一个很多位数字的字符串,我们的密钥使用16字节,那么我们可以考虑对它md5,作为对称加密的密钥)。
上述代码中big int相关的js,直接使用的一个开源的bigint.js(js代码有不开源的吗?^_^) 。 参见这里:http://leemon.com/crypto/BigInt.js
服务端php代码:
<?php
$g = "2";
$p = "106025087133488299239129351247929016326438167258746469805890028339770628303789813787064911279666129";
$b = "86410430023"; // TODO: change this to a random generated number.
$A = $_REQUEST['A'];
$B = bcpowmod($g, $b, $p);
$secret = bcpowmod($A, $b, $p);
echo '{"B":"' . $B .'"}';
?>
可见我们采用固定的g和p,这2个变量是公开的,不怕泄漏。
js端首先生成一个100bit长的整数a,并依据公式计算出A, 用ajax的形式发送到服务端php。 服务端收到A,自己生成变量b,依据公式计算出B,响应给客户端js。
此时,服务端和客户端分别可以依据公式计算出一个相同的secret。 这个secret没有在网络中传输过,双方可说是“心照不宣”,且双方自己选定的a和b是保密的,第三方无法根据公开传输的数据推算出a,b,当然也无法得到secret。 这就是DH算法的原理。
======================================================华丽的分割线========================================================
密钥交换完成后,我们假定双方拥有了对称加密的密钥。 接下来是一个AES算法的demo。 分别有js加密php解密,和反过来php加密js解密。
这里采用了一个js库,叫做crypt-js.它的官方地址是 http://code.google.com/p/crypto-js/#CryptoJS_3.1
js加密:
var pwd = "123456";
var key = CryptoJS.enc.Utf8.parse("96e79218965eb72c92a549dd5a330112"); //CryptoJS.MD5("111111");
var iv = CryptoJS.enc.Utf8.parse('1234567812345678');
var encrypted = CryptoJS.AES.encrypt(pwd, key, {iv:iv});
document.write(encrypted.toString());
这里write出来的值,就是加密过后的密文,toString方法得到,按照crypto-js文档中说,是兼容openssl格式的可见文本形式。(加密通常得到的结果是byte数组,要以某种形式转换为可见字符,如base64,hex等方式。 这个形式转换也可以自定义)。
此值在php端解密:
$iv='1234567812345678';
$key = "96e79218965eb72c92a549dd5a330112";
$data = "FBPJjTRA4MEkMcMDg7eOng==";
$data = base64_decode($data);
$ttt = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
echo "decrypted : $ttt";
接下来的代码demo是php端加密, js解密。
php加密:
$svrMessage = "server message . .. adfasdfsdaf adfasdfsdaf";
$iv='1234567812345678';
$key = "96e79218965eb72c92a549dd5a330112";
$enc = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $svrMessage, MCRYPT_MODE_CBC, $iv);
$enc = base64_encode($enc);
echo "<br/>$enc";
输出的结果是经过base64转换为可见字符的。
js解密:
var received = "UQhDUzgusxZiejMuuVjh78BcpoQt82swQvSqyCvnuTdb3drBJTPghFBmTORflU6h";
var eee = CryptoJS.enc.Base64.parse(received);
var ddd = CryptoJS.AES.decrypt({
ciphertext: CryptoJS.enc.Base64.parse(received),
salt: ""
}, key, {iv:iv});
document.write(ddd.toString(CryptoJS.enc.Utf8) + "<br />");