js 實現 base58加密/解密(包含中文)
阿新 • • 發佈:2018-12-18
1. Base58加密原理:和通常base64編碼一樣,base58編碼的作用也是將非可視字元視覺化(ASCII化)。但不同的是base58編碼去掉了幾個看起來會產生歧義的字元,如 0 (零), O (大寫字母O), I (大寫的字母i) and l (小寫的字母L) ,和幾個影響雙擊選擇的字元,如/, +。結果字符集正好58個字元(包括9個數字,24個大寫字母,25個小寫字母)。
2. 直接上程式碼(加密):
a 先存ALPHABET_MAP(方便後期檢查當前是否為Base58編碼)
var ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; var ALPHABET_MAP = {}; var BASE = 58; for (var i = 0; i < ALPHABET.length; i++) { ALPHABET_MAP[ALPHABET.charAt(i)] = i; } // 得到物件ALPHABET_MAP ---> {1: 0, 2: 1, 3: 2, 4: 3, 5: 4, 6: 5, 7: 6, 8: 7, 9: 8, A: 9, B: 10, C: 11, D: 12, E: 13, …, y: 56, z: 57}
b 將字串轉為byte位元組陣列(這裡用的UTF8)
// 字串轉utf8格式的位元組陣列(英文和數字直接返回的unicode碼,中文轉%xx之後打斷當成16進位制轉10進位制) function ToUTF8(str) { var result = new Array(); var k = 0; for (var i = 0; i < str.length; i++) { var j = encodeURI(str[i]); if (j.length==1) { // 未轉換的字元 result[k++] = j.charCodeAt(0); } else { // 轉換成%XX形式的字元 var bytes = j.split("%"); for (var l = 1; l < bytes.length; l++) { result[k++] = parseInt("0x" + bytes[l]); } } } return result; } // 如果有特殊需求,要轉成utf16,可以用以下函式 function ToUTF16(str) { var result = new Array(); var k = 0; for (var i = 0; i < str.length; i++) { var j = str[i].charCodeAt(0); result[k++] = j & 0xFF; result[k++] = j >> 8; } return result; }
c Base58加密函式
// 傳進已經轉成位元組的陣列 -->buffer(utf8格式) function encode(buffer) { if (buffer.length === 0) return ''; var i, j, digits = [0]; for (i = 0; i < buffer.length; i++) { for (j = 0; j < digits.length; j++){ // 將資料轉為二進位制,再位運算右邊添8個0,得到的數轉二進位制 // 位運算-->相當於 digits[j].toString(2);parseInt(10011100000000,2) digits[j] <<= 8; } digits[0] += buffer[i]; var carry = 0; for (j = 0; j < digits.length; ++j) { digits[j] += carry; carry = (digits[j] / BASE) | 0; digits[j] %= BASE; } while (carry) { digits.push(carry % BASE); carry = (carry / BASE) | 0; } } // deal with leading zeros for (i = 0; buffer[i] === 0 && i < buffer.length - 1; i++) digits.push(0); return digits .reverse() .map(function(digit) { return ALPHABET[digit]; }) .join(''); } // ToUTF8('6嘎') --->[54, 229, 152, 142] // encode(ToUTF8('6嘎')) ---> 2QPT7B console.log(encode(ToUTF8('6嘎')))
3 直接上程式碼(解密):
a 解密成位元組陣列
// string ---> 加密後的字串
function decode(string) {
if (string.length === 0) return [];
var i,
j,
bytes = [0];
for (i = 0; i < string.length; i++) {
var c = string[i];
// c是不是ALPHABET_MAP的key
if (!(c in ALPHABET_MAP)) throw new Error('Non-base58 character');
for (j = 0; j < bytes.length; j++) bytes[j] *= BASE;
bytes[0] += ALPHABET_MAP[c];
var carry = 0;
for (j = 0; j < bytes.length; ++j) {
bytes[j] += carry;
carry = bytes[j] >> 8;
// 0xff --> 11111111
bytes[j] &= 0xff;
}
while (carry) {
bytes.push(carry & 0xff);
carry >>= 8;
}
}
// deal with leading zeros
for (i = 0; string[i] === '1' && i < string.length - 1; i++) bytes.push(0);
return bytes.reverse();
}
// [54, 229, 152, 142]
console.log(decode(2QPT7B))
b 將位元組陣列解密成字串
// 傳位元組陣列
function byteToString(arr) {
if (typeof arr === 'string') {
return arr;
}
var str = '',
_arr = arr;
for (var i = 0; i < _arr.length; i++) {
// 陣列中每個數字轉為二進位制 匹配出開頭為1的直到0的字元
// eg:123-->"1111011"-->{0:"1111",groups: undefined, index: 0, input: "1111011"}
var one = _arr[i].toString(2),
v = one.match(/^1+?(?=0)/);
if (v && one.length == 8) {
var bytesLength = v[0].length;
var store = _arr[i].toString(2).slice(7 - bytesLength);
for (var st = 1; st < bytesLength; st++) {
store += _arr[st + i].toString(2).slice(2);
}
str += String.fromCharCode(parseInt(store, 2));
i += bytesLength - 1;
} else {
str += String.fromCharCode(_arr[i]);
}
}
return str;
}
// 6嘎
console.log([54, 229, 152, 142])