文本加密
实现原理
使用零宽字符实现文本隐藏和加密.
- 什么是零宽字符
零宽字符是一种在浏览器中不打印的字符,大致相当于 display-none,在许多文本应用中也不显示,比如邮箱、QQ、微信、文本编辑器等;
这里有三种零宽字符 -- 零宽空格、零宽连字、零宽不连字
零宽字符在浏览器中对应的转义字符
零宽空格 --- ​
零宽不连字 --- ‌
零宽连字 --- ‍
- 摩斯电码
摩斯电码采用长短两种符号进行文本加密,通过字典进行加密和解码,摩斯电码字典如下图所示

我们使用/作为分隔符将 morse 这个单词转换为摩斯电码就是
morse -> --/---/.-./.../.
现在我们再将 / 替换为零宽空格; - 替换问零宽连字;. 替换为零宽不连字
/ --> ​
. --> ‌
- --> ‍
那么就能将 morse 这个单词转换为如下零宽字符
‍‍​‍‍‍​‌‍‌​‌‌‌​‌
将这段零宽字符粘贴进一个 HTML 文件当中
<p>前</p>
<div>‍‍​‍‍‍​‌‍‌​‌‌‌​‌</div>
<p>后</p>
在浏览器中打开这个 HTML 文件,你只能看到 "前后" 两个字
到这了原理已经可见一斑了
密码字典
我们要建立两个字典,一个加密字典,一个解密字典
建两个数组分别存储 a-z 和 0-9 对应的莫斯码
const morseWords = [
".-",
"-...",
"-.-.",
"-..",
".",
"..-.",
"--.",
"....",
"..",
".---",
"-.-",
".-..",
"--",
"-.",
"---",
".--.",
"--.-",
".-.",
"...",
"-",
"..-",
"...-",
".--",
"-..-",
"-.--",
"--..",
];
const morseNumber = [
"-----",
".----",
"..---",
"...--",
"....-",
".....",
"-....",
"--...",
"---..",
"----.",
];
使用索引对象来做字典,for 循环生成对应加解密字典
let wordsToMorse = {};
let morseToWords = {};
let morseToNum = {};
//a-z数组
let words = [];
for (let i = 10; i < 36; i++) {
let j = i.toString(36);
words.push(j);
}
//数字加密字典
let numToMorse = morseNumber;
//数字解密字典
for (let i in morseNumber) {
morseToNum[morseNumber[i]] = i;
}
//字母加密字典
for (let i in words) {
wordsToMorse[words[i]] = morseWords[i];
}
//字母解密字典
for (let i in wordsToMorse) {
morseToWords[wordsToMorse[i]] = i;
}
//合并解密字典
let decodeWords = Object.assign(morseToWords, morseToNum);
导出字典,这样就可以通过索引来查询字典
/* morse.js */
export { wordsToMorse, morseToWords, morseToNum, numToMorse, decodeWords };
/* encrypt.js */
import { wordsToMorse, morseToWords, morseToNum, numToMorse, decodeWords } form 'morse.js'
console.log(wordsToMorse['z']) //--..
console.log(decodeWords['--..']) //z
加密函数
主要两步,先转换摩斯电码,再使用零宽字符替换摩斯电码
- 循环输入字符串,判断每个字符是字母还是数字,分别调用不同的数字/字母加密字典
- 将转换后的摩斯字符串使用 、 、 进行替换
function incode(str) {
if(typeOf str != 'string'){
return ;
}
let res = [];
let l = "‍";
let s = "‌";
let q = "​";
for (let i in str) {
let val = str[i];
if (!!parseInt(val) || parseInt(val) == 0) {
res.push(numToMorse[str[i]]);
} else {
res.push(wordsToMorse[str[i]]);
}
}
let encrypt = res.join("/");
encrypt = encrypt.replace(/\//g, q)
encrypt = encrypt.replace(/\./g, s)
encrypt = encrypt.replace(/\-/g, l)
return encrypt;
}
解密函数
首先从待解密字符串中匹配零宽字符, 零宽字符在 Unicode 中的编码为 \u200B \u200C \u200D| 在 HTML 中有两种显示
​
<!-- &zwnj -->
‌ --> &zwnj
<!-- &zwj -->
‍ --> &zwj
- 匹配出文本中的零宽字符
- 转换零宽字符为摩斯字符串
- 调用解密字典把摩斯码转换普通文本
function decode(text) {
if(typeOf str != 'string'){
return ;
}
let decode = [];
//匹配文本中的零宽字符,并转换为摩斯码
text.match(/(\&\#8203\;|\&\#8204\;|\&\#8205\;|\u200B|\u200C|\u200D|\&zwnj\;|\&zwj\;)+/g).map(temp => {
temp = temp.replace(/\&\#8203\;|\u200B/g, "/");
temp = temp.replace(/\&\#8204\;|\u200C|\&zwnj\;/g, ".");
temp = temp.replace(/\&\#8205\;|\u200D|\&zwj\;/g, "-");
let arr = temp.split("/");
//调用解密字典转码
for (let i in arr) {
decode.push(decodeWords[arr[i]]);
}
})
return decode;
}
中文支持
TIP
到这里我们已经实现对普通字符串的加密了,但是只支持英文和数字。
要实现中文加解密,主要思路是先对文本进行正则匹配,将其中的中文进行 Unicode 转码,转码后整段文本就变成了字母、数字、'' 的组合,我们在加解密字典中加上对 \ 的支持,在 morse.js 文件中加上
/* morse.js */
/* 附件字符 */
decodeWords["-...-"] = " "; //顺便把空格也加进字典
decodeWords[".--.-"] = "\\";
wordsToMorse["\\"] = ".--.-";
使用
- pc 版 QQ 不支持零宽字符,会显示空格,但手机版不会
- 中文转码长度大约是相同长度英文字符的 6 倍
- 零宽字符虽然看不见但是,但也是一个个字符,所以删除含有零宽字符的文本时,删到零宽字符时会删不动要把零宽字符全部删完才能接着往下删
自定义
通过编辑 morse.js 文件,可在摩斯电码的基础上自定义自己独一无二的密码字典
应用场景
- 存储隐藏信息
零宽字体可以用来存储隐藏信息, 比如:隐藏加密存储比特币钱包,或者在你的代码里埋下一个彩蛋
- 秘密传达消息
零宽字符在大部分应用都支持,pc 版 QQ 会显示零宽字符,但移动端不显示;
你可以将密文加密到普通文本中,然后邮件发送,表面上看起来是普通文本,只有对方复制明文进行解密后才能看出隐藏信息!
- 为文章添加隐藏水印
你可以在你写的文章插入隐藏字符,将作者信息嵌入其中;
当别人复制你的文章时,并不会发现这篇文章已经被你悄悄打下水印;
在写代码时的坑
零宽字体通常是不可见的,所以在写代码的时候,如果在代码中加入了这些零宽字符,是非常坑的!
所以在打印日志的时候,推荐使用:fmt.Printf("%#v", info)来打印日志!
fmt.Printf("%#v", info);
本站在线地址
参考: