前言
- 这是什么?
一张马赛克,马赛克的背后往往隐匿着令人兴奋的真相。这是一张埋藏着秘密的文本加密图。
汉字的转化
加密
听起来一直是一个颇具难度的词,进行加密操作似乎需要你学习某个太平洋岛国的小众方言或者精通各类密码学;其实加密的原理简单粗暴,无非是两步:
-
对汉字(字符)进行转化;
-
通过特定的公式进行加密和解密。
那么今天我们就来尝试下用最简单的方式制作一张文本加密图吧。
使用Unicode将汉字转换为数字
Unicode编码
Unicode一开始就是为了解决不同语言之间的障碍而出现的。于是乎,世界上的每个汉字和不同语言的文字都拥有了属于自己的unicode码,进行汉字加密的第一步便是将汉字转为unicode码,比如下面的这段话:
晚上一起去喝酒别告诉你老婆
经过charCodeAt方法的转化,顺利转成了
data() {
return {
encryption:'晚上一起去喝酒别告诉你老婆',
}
},
mounted () {
// 遍历字符串
for(let i=0;i<this.encryption.length;i++){
console.log(this.encryption.charAt(i))
console.log(this.encryption.charAt(i).charCodeAt(0).toString())
}
},
初步加密
转化为Unicode码后的文本,已经初步得到了加密,但是经验丰富的程序员还是能一眼看出这其中的含义,我们需要对这些编码进行更进一步(更花里胡哨)的加密;
进一步转化为rgb颜色值似乎是一个够骚的办法;但是rgb的颜色值由三个0-255的数字组成,而转换后的编码包含0~9的数字,怎样才能完成Unicode码到rgb的转化呢?
有了!对Unicode编码进行三进制转换不就能将每个数字都控制在0-2之间吗?于是这串汉字在进行Unicode编码转换后,又进行了一次三进制转换,得到了这样的结果:
// 遍历字符串
for(let i=0;i<this.encryption.length;i++){
console.log(this.encryption.charAt(i))
console.log(this.encryption.charAt(i).charCodeAt(0).toString(3))// 转化为三进制
}
进一步地
我们对每一个编码进行截取,终于得到了类似rgb一样数值;
// 遍历字符串
for(let i=0;i<this.encryption.length;i++){
console.log(this.encryption.charAt(i))
let a = this.encryption.charAt(i).charCodeAt(0).toString(3)// 转化为三进制
console.log(a.substring(0,3)+','+a.substring(3,6)+','+a.substring(6,9)+','+a.substring(9,10))// 将得到的三进制数字切割为4份
}
这样的数字,是不是已经很像我们所需要的rgb颜色值了,但是每个编码都由10位数字组成,似乎比rgb的9位多了一位!别急,最后的一位我们之后再处理;在这之前我们先进行最令人兴奋的一步,将这些编码转化为颜色;
首先,我们先去掉之前的多余操作,对获取到的数据进行整合,得到了passwords数组;
// 遍历字符串
for(let i=0;i<this.encryption.length;i++){
this.$set(this.passwords,i,this.encryption.charAt(i).charCodeAt(0).toString(3))
}
console.log(this.encryption)
console.log('passwords:'+this.passwords)
然后遍历passwords数组创建一个个p标签;
<p class="p" v-for="(item,index) in passwords" :key="index">
,,
</p>
下一步
就是通过每个数据的前三个值,为p标签染色啦,当当当,经过染色的p标签终于成为了多姿多彩的自己;
<p class="p"
v-for="(item,index) in passwords"
:key="index"
:style="{backgroundColor: 'rgb('+item.substring(0,3)+','+item.substring(3,6)+','+item.substring(6,9)+')'}">
</p>
接下来
只需要将p标签的规格改动一下,一个个类似像素点的标签骤然出现;这个时候,我们已经初步有了一个经过加密的文字加密图;
然而
当前的加密数据仍然不够准确,因为刚刚,我们漏掉了字符编码的最后一位没有处理。那我们该怎么处理这最后一位字符编码呢?
其实依靠上面的思路,处理最后一位的方式有很多,可以转化为像素的圆角,宽度等,这里我选择将它们转换为透明度也就是rgba中的A
三进制数字的最后一位只有三种可能,即0、1和2;通过简单的数学公式(-x+4)*0.25便能将这三者转化为对应的透明度1、0.75和0.5;说干就干,一个带透明度的加密图片出现了;
<p class="p"
v-for="(item,index) in passwords"
:key="index"
:style="{backgroundColor: 'rgba('+item.substring(0,3)+','+item.substring(3,6)+','+item.substring(6,9)+','+ (-item.substring(9,10)+4)*0.25+')'}"
></p>
进一步优化
字母和符号
我们发现,以上方法只适用于汉字,对于Unicode编码很短的常见数字和字母并不适用;为了适配数字和字母,我们应该怎样给数字和字母进行单独的加密操作呢?
上文的介绍中,我们提到,rgb的颜色值在0-255之间,而我们使用了三进制加密使得颜色值最高只能达到222;222以上的数值并没出现的可能;那么解决思路就来了,给数字和字母加上255作为标识符不就可以的了吗!
单独对数字和字母进行特殊的操作(这里我在处理数字和字母时改用十进制编码,同时在编码的前面三位加上255作为标识,编码的后面补足够的0,达到长度为10字符的要求)
例如
字母’a’的unicode为97,增加前缀255,在后面补足0;我们便得到了专属于’a’的加密编码
255,097,000,0
英文逗号’,’的unicode为44,增加前缀255,在后面补足0;我们便得到了专属于’,’的加密编码
255,044,000,0
// 遍历字符串
for(let i=0;i<this.encryption.length;i++){
// 汉字
if(this.encryption.charAt(i).charCodeAt(0).toString()>255){
this.$set(this.passwords,i,this.encryption.charAt(i).charCodeAt(0).toString(3))
}
// 数字和字母
else{
// unicode码为0~9
if(this.encryption.charAt(i).charCodeAt(0).toString().length == 1){
this.$set(this.passwords,i,'25500' + this.encryption.charAt(i).charCodeAt(0).toString() + '0000')
}
// unicode码为10~99
else if(this.encryption.charAt(i).charCodeAt(0).toString().length == 2){
this.$set(this.passwords,i,'2550' + this.encryption.charAt(i).charCodeAt(0).toString() + '0000')
}
// unicode码为100~255
else{
this.$set(this.passwords,i,'255' + this.encryption.charAt(i).charCodeAt(0).toString() + '0000')
}
}
}
最后
完成
最后简单加上按钮和输入框,一个前端文本加密工具就完成啦;
当我们把代表一个汉字的每一小格子缩小到一个像素,加密图片信息存储量便达到了惊人的高度;一张800w像素的图片,可以储存800w字相当于数十本书的字数;
改进
使用p标签进行渲染有诸多坏处,比如你并不能保存为图片,而且图片格式也不利于JS直接解密,可以改为使用canvas进行渲染,这里使用p标签进行演示纯粹是因为我懒;
延伸
上面只是提供了一种进行简单加密的思路,举一反三,我们同样可以将数字编码转换为对应的圆角、旋转角度、长度等等视觉表现;是不是很高(zhuang)级(bi)快搞一个试试吧,冲啊。
解密
如此这般自己解吧