1. 程式人生 > >一個以ajax請求為主的應用,資料傳輸加密的解決方案

一個以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 />");