今天下午和一個(gè)妹子聊到在前端做AES加密,妹子說(shuō)有一些開源的庫(kù),我想webcrypto提出來(lái)這么多年,應(yīng)該主流瀏覽器很多也應(yīng)該有原生的支持了吧?寫個(gè)加測(cè)試頁(yè)面試試看:
測(cè)試下來(lái)感覺(jué)各個(gè)瀏覽器支持程度還是參差不齊,IE11和Edge都是不同的實(shí)現(xiàn)方式,IE11甚至還會(huì)隨機(jī)出現(xiàn)oncomplete的時(shí)候result數(shù)據(jù)都還沒(méi)有ready的情況,甚至于有的時(shí)候完全相同的入?yún)ⅲòS機(jī)數(shù)也相同)加密出來(lái)的結(jié)果居然會(huì)不同,也就是說(shuō)有的時(shí)候加密算法自己會(huì)加密錯(cuò),出來(lái)的結(jié)果根本就無(wú)法被正確解密,實(shí)在是個(gè)半成品:

(第一次加密錯(cuò)了,第二次加密對(duì)了解密錯(cuò)了,第三次終于加解密都對(duì)了)

(第一次加密錯(cuò)了,第二次對(duì)了)
<!DOCTYPE html>
<html>
<head>
<META NAME="Author" CONTENT="emu">
<META NAME="Keywords" CONTENT="webcrypto AES-CBC AES-GCM">
</head>
<body>
<div id="out"></div>
<script type="text/javascript">
function output(sign) {
document.getElementById("out").innerHTML += sign + "<br>";
}
function bufferToHex(b){
var dataview = new DataView(b);
result = "";
for (var i = 0; i < b.byteLength; i += 4) {
tmp = dataview.getUint32(i).toString(16);
result += (tmp.length == 8 ? "" : "0") + tmp;
}
return result;
}
function bufferToString(b){
//new TextDecoder().decode(b)
var hex=bufferToHex(b);
var result=unescape(hex.replace(/(..)/g,"%$1"));
return result;
}
function stringToBuffer(s){
//new TextEncoder().encode(s);
var a = s.split("");
for (var i = 0; i < a.length; i++) {
a[i] = a[i].charCodeAt(0)
};
var result = new Uint8Array(a);
return result;
}
var textToBeEncrypted = 'Hello World!';
var bufferToBeEncrypted=stringToBuffer(textToBeEncrypted);
var bufferToBeDecrypted;
var pwd="let me try this password"
var password = stringToBuffer(pwd);
var sAlg="AES-CBC";//AES-GCM部分瀏覽器不支持
var c = window.crypto || window.msCrypto;
var subtle = c.subtle || c.webkitSubtle;
var iv = c.getRandomValues(new Uint8Array(16));
var alg = { name: sAlg, iv: iv };
var op=c.subtle.digest('SHA-256', password)
var encryptKey,decryptKey,hash;
if(("then" in op)){
op.then(function(buffer){
hash=buffer;
c.subtle.importKey('raw', hash, alg, false, ['encrypt']).then(function(buffer){
encryptKey=buffer;
c.subtle.importKey('raw', hash, alg, false, ['decrypt']).then(function(buffer){
decryptKey=buffer;
doEncrypt(alg,encryptKey);
})
})
});
}else{
op.oncomplete=function(e){
var hash=e.target.result;
var op=c.subtle.importKey('raw', hash, alg, false, ['encrypt']);
op.oncomplete=function(e){
encryptKey=e.target.result;
op=c.subtle.importKey('raw', hash, alg, false, ['decrypt']);
op.oncomplete=function(e){
decryptKey=e.target.result;
encryptWithCryptoOperation(alg,encryptKey);
}
}
}
}
function doEncrypt(alg,encryptKey){
var op = c.subtle.encrypt(alg, encryptKey, bufferToBeEncrypted);
op.then(function(buffer){
bufferToBeDecrypted=buffer;
output("pwd: <b><i>"+pwd+"</i></b> alg:<b>"+sAlg+"</b> <br>encrypt("+textToBeEncrypted + ")="+ bufferToHex(buffer));
doDecrypt(alg,decryptKey);
})
}
function doDecrypt(alg,decryptKey){
var op = c.subtle.decrypt(alg, decryptKey, bufferToBeDecrypted);
op.then(function(buffer){
output("pwd:<b><i>"+pwd+"</i></b> alg:<b>"+sAlg+"</b> <br>decrypt("+bufferToHex(bufferToBeDecrypted) + ")="+ bufferToString(buffer));
})
}
function encryptWithCryptoOperation(alg,encryptKey){
var op = c.subtle.encrypt(alg, encryptKey);
op.onerror=function(e){output(sAlg+" unsupported")}
op.process(bufferToBeEncrypted);
op.oncomplete=function(e){
bufferToBeDecrypted=e.target.result
output("pwd: <b><i>"+pwd+"</i></b> alg:<b>"+sAlg+"</b> <br>encrypt("+textToBeEncrypted + ")="+ bufferToHex(bufferToBeDecrypted));
//decryptWithCryptoOperation(alg,decryptKey)
setTimeout(decryptWithCryptoOperation,0,alg,decryptKey)
}
op.finish();
}
function decryptWithCryptoOperation (alg,decryptKey){
var op = c.subtle.decrypt(alg, decryptKey);
op.onerror=function(e){(sAlg+" unsupported")}
op.process(bufferToBeDecrypted);
op.oncomplete=function(e){
if(!e.target.result){
output("IE is crazy!<br>");
encryptWithCryptoOperation(alg,encryptKey)
}else
output("pwd:<b><i>"+pwd+"</i></b> alg:<b>"+sAlg+"</b> <br>decrypt("+bufferToHex(bufferToBeDecrypted) + ")="+ bufferToString(e.target.result));
}
op.finish();
}
</script>
</body>
</html>
<html>
<head>
<META NAME="Author" CONTENT="emu">
<META NAME="Keywords" CONTENT="webcrypto AES-CBC AES-GCM">
</head>
<body>
<div id="out"></div>
<script type="text/javascript">
function output(sign) {
document.getElementById("out").innerHTML += sign + "<br>";
}
function bufferToHex(b){
var dataview = new DataView(b);
result = "";
for (var i = 0; i < b.byteLength; i += 4) {
tmp = dataview.getUint32(i).toString(16);
result += (tmp.length == 8 ? "" : "0") + tmp;
}
return result;
}
function bufferToString(b){
//new TextDecoder().decode(b)
var hex=bufferToHex(b);
var result=unescape(hex.replace(/(..)/g,"%$1"));
return result;
}
function stringToBuffer(s){
//new TextEncoder().encode(s);
var a = s.split("");
for (var i = 0; i < a.length; i++) {
a[i] = a[i].charCodeAt(0)
};
var result = new Uint8Array(a);
return result;
}
var textToBeEncrypted = 'Hello World!';
var bufferToBeEncrypted=stringToBuffer(textToBeEncrypted);
var bufferToBeDecrypted;
var pwd="let me try this password"
var password = stringToBuffer(pwd);
var sAlg="AES-CBC";//AES-GCM部分瀏覽器不支持
var c = window.crypto || window.msCrypto;
var subtle = c.subtle || c.webkitSubtle;
var iv = c.getRandomValues(new Uint8Array(16));
var alg = { name: sAlg, iv: iv };
var op=c.subtle.digest('SHA-256', password)
var encryptKey,decryptKey,hash;
if(("then" in op)){
op.then(function(buffer){
hash=buffer;
c.subtle.importKey('raw', hash, alg, false, ['encrypt']).then(function(buffer){
encryptKey=buffer;
c.subtle.importKey('raw', hash, alg, false, ['decrypt']).then(function(buffer){
decryptKey=buffer;
doEncrypt(alg,encryptKey);
})
})
});
}else{
op.oncomplete=function(e){
var hash=e.target.result;
var op=c.subtle.importKey('raw', hash, alg, false, ['encrypt']);
op.oncomplete=function(e){
encryptKey=e.target.result;
op=c.subtle.importKey('raw', hash, alg, false, ['decrypt']);
op.oncomplete=function(e){
decryptKey=e.target.result;
encryptWithCryptoOperation(alg,encryptKey);
}
}
}
}
function doEncrypt(alg,encryptKey){
var op = c.subtle.encrypt(alg, encryptKey, bufferToBeEncrypted);
op.then(function(buffer){
bufferToBeDecrypted=buffer;
output("pwd: <b><i>"+pwd+"</i></b> alg:<b>"+sAlg+"</b> <br>encrypt("+textToBeEncrypted + ")="+ bufferToHex(buffer));
doDecrypt(alg,decryptKey);
})
}
function doDecrypt(alg,decryptKey){
var op = c.subtle.decrypt(alg, decryptKey, bufferToBeDecrypted);
op.then(function(buffer){
output("pwd:<b><i>"+pwd+"</i></b> alg:<b>"+sAlg+"</b> <br>decrypt("+bufferToHex(bufferToBeDecrypted) + ")="+ bufferToString(buffer));
})
}
function encryptWithCryptoOperation(alg,encryptKey){
var op = c.subtle.encrypt(alg, encryptKey);
op.onerror=function(e){output(sAlg+" unsupported")}
op.process(bufferToBeEncrypted);
op.oncomplete=function(e){
bufferToBeDecrypted=e.target.result
output("pwd: <b><i>"+pwd+"</i></b> alg:<b>"+sAlg+"</b> <br>encrypt("+textToBeEncrypted + ")="+ bufferToHex(bufferToBeDecrypted));
//decryptWithCryptoOperation(alg,decryptKey)
setTimeout(decryptWithCryptoOperation,0,alg,decryptKey)
}
op.finish();
}
function decryptWithCryptoOperation (alg,decryptKey){
var op = c.subtle.decrypt(alg, decryptKey);
op.onerror=function(e){(sAlg+" unsupported")}
op.process(bufferToBeDecrypted);
op.oncomplete=function(e){
if(!e.target.result){
output("IE is crazy!<br>");
encryptWithCryptoOperation(alg,encryptKey)
}else
output("pwd:<b><i>"+pwd+"</i></b> alg:<b>"+sAlg+"</b> <br>decrypt("+bufferToHex(bufferToBeDecrypted) + ")="+ bufferToString(e.target.result));
}
op.finish();
}
</script>
</body>
</html>
測(cè)試下來(lái)感覺(jué)各個(gè)瀏覽器支持程度還是參差不齊,IE11和Edge都是不同的實(shí)現(xiàn)方式,IE11甚至還會(huì)隨機(jī)出現(xiàn)oncomplete的時(shí)候result數(shù)據(jù)都還沒(méi)有ready的情況,甚至于有的時(shí)候完全相同的入?yún)ⅲòS機(jī)數(shù)也相同)加密出來(lái)的結(jié)果居然會(huì)不同,也就是說(shuō)有的時(shí)候加密算法自己會(huì)加密錯(cuò),出來(lái)的結(jié)果根本就無(wú)法被正確解密,實(shí)在是個(gè)半成品:

(第一次加密錯(cuò)了,第二次加密對(duì)了解密錯(cuò)了,第三次終于加解密都對(duì)了)

(第一次加密錯(cuò)了,第二次對(duì)了)