這幾天作了一個需求,讀取上傳的公私鑰,而後利用私鑰採用RSA加密摘要,發送給後端。其中運用到了base64
的加解密,RSA加密採用的是node的Crypto
模塊,base64
的轉碼採用的是js-base64, 然而萬萬沒有想到,這裏面有坑啊。 javascript
(開個玩笑,這個庫仍是很不錯的)html
首先是文件的讀取,採用的是FileReader
, 而且二進制文件的讀取應爲readAsArrayBuffer
,不然會亂碼。java
readFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = function(evt) {
resolve(evt.target.result)
};
reader.readAsArrayBuffer(file);
})
}
複製代碼
從這裏讀取到的數據,調用Object.prototype.toString.call([data])
,結果爲[object Uint8Array]
。node
ArrayBuffer
對象用來表示通用的、固定長度的原始二進制數據緩衝區。ArrayBuffer
不能直接操做,而是要經過類型數組對象或DataView
對象來操做,它們會將緩衝區中的數據表示爲特定的格式,並經過這些格式來讀寫緩衝區的內容。
Uint8Array
數組類型表示一個8位無符號整型數組,建立時內容被初始化爲0。建立完後,能夠以對象的方式或使用數組下標索引的方式引用數組中的元素。git
類型數組對象就是Uint8Array
之類的TypedArray
,咱們須要這些對象操做ArrayBuffer
。github
這是一個摸索了好久的點,由於有的公私鑰讀取能夠直接採用reader.readAsText(file)
。後端
另外一個摸索了好久的東西即是ArrayBuffer
與base64
的轉換,原因於js-base64
對這兩個的轉換並不支持。api
很使人捉🐔的是,調用庫中的方法Base64.encode([ArrayBuffer])
,得出的數據一直不對。 略微看了一下源碼。數組
var _Base64 = global.Base64;
var version = "2.5.1";
// if node.js and NOT React Native, we use Buffer
var buffer;
if (typeof module !== 'undefined' && module.exports) {
try {
buffer = eval("require('buffer').Buffer");
} catch (err) {
buffer = undefined;
}
}
...
var _encode = buffer ?
buffer.from && Uint8Array && buffer.from !== Uint8Array.from
? function (u) {
return (u.constructor === buffer.constructor ? u : buffer.from(u))
.toString('base64')
}
: function (u) {
return (u.constructor === buffer.constructor ? u : new buffer(u))
.toString('base64')
}
: function (u) { return btoa(utob(u)) }
;
var encode = function(u, urisafe) {
return !urisafe
? _encode(String(u))
: _encode(String(u)).replace(/[+\/]/g, function(m0) {
return m0 == '+' ? '-' : '_';
}).replace(/=/g, '');
};
複製代碼
不知是否是採用IIFE
的關係,buffer
恆爲undefined
,而且此時!urisafe
爲真,會流入第一個條件,而的u
爲Uint8Array
,強制類型轉換會致使亂碼。學習
故而不用庫,用原生方法。 Buffer.from([key]).toString('base64')
這是ArrayBuffer
轉base64
的原生方法。
function _base64ToArrayBuffer(base64) {
var binary_string = window.atob(base64);
var len = binary_string.length;
var bytes = new Uint8Array( len );
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes.buffer;
}
複製代碼
const encryptDigest = crypto.privateEncrypt({ key: privateKey },
Buffer.from(_base64ToArrayBuffer(digest))
);
const res = crypto.publicDecrypt({ key: publicKey }, encryptDigest);
res.toString('base64') === digest ?
複製代碼
只要用私鑰加密的摘要用公鑰能成功解密,而且數據不變,就證實這個流程正確了。
其實用window.atob
也能夠將ArrayBuffer
轉base64
,可是有大小限制,碼位應在 0x00 ~ 0xFF 範圍內。然然後端小哥說業務不支持ascii
格式的,只能做罷。