一个以ajax请求为主的应用,数据传输加密的解决方案

首先是密钥交换的过程,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 />");

(全文完)

《一个以ajax请求为主的应用,数据传输加密的解决方案》有1个想法

  1. 你好,首先非常感谢你的分享。不过我按照你的代码最后得出的两个秘钥并不一致。我用的是js加C# 能详细请教一下你吗

发表评论

电子邮件地址不会被公开。 必填项已用*标注