前言
在区块链的整个体系中大量使用了密码学算法,比如用于 PoW 的哈希算法,用于完整性验证的 Merkle Tree,用于交易签名与验证的数字签名算法,用于隐私保护的零知识证明等等。
可以说密码学是保证区块链安全的基石,而区块链的广泛应用也推进了密码学的发展。在区块链内核 CITA 的 v0.18 中,新增了「基于 Rust 语言的国密算法库」新特性。这次更新,使用户在尊重版权的前提下,即可自由调用 Rust 实现的国密算法库,来匹配业务场景所需的国密签名算法,大幅降低企业用户及开发者获得高性能区块链底层设计服务的成本,方便用户打造最贴近业务需求的区块链。
现代信息安全的基本要求:
- 信息的保密性 Confidentiality:防止信息泄漏给未经授权的人(加密解密技术)
- 信息的完整性 Integrity:防止信息被未经授权的篡改(消息认证码,数字签名)
- 认证性 Authentication:保证信息来自正确的发送者(消息认证码,数字签名)
- 不可否认性 Non-repudiation:保证发送者不能否认他们已发送的消息(数字签名)
目录
一、什么是密码学
密码学的英语单词是 Cryptograghy,是由希腊单词 Kryptos(隐藏)和 Graphin(写)派生出来的,最初代表的意思是用来隐秘的传递信息。隐藏和写就是隐写,在古典密码学的发展中就有一门称为隐写术的技术,比如说藏头诗就是一种隐写术。在《巨人的陨落》中,艾瑟尔和弟弟比利就是通过每隔两个单词就会加一个单词来作为加密后的密文,这也是隐写术的一个例子。隐写术发展到今天演变为数字水印技术,一般在文件中加一个标识信息(即数字水印),可以起到追踪溯源,防伪和版权保护的作用。
密码学是网络安全、信息安全、区块链等产品的基础,常见的非对称加密、对称加密、散列函数等,都属于密码学范畴。
密码学一开始的功能是在有恶意攻击者存在的环境下,保护双方通信安全,现在是用来保护信息安全的核心技术。
所以密码学旨在保护协议免受攻击者攻击的一种安全措施,使之成为安全协议
二、古典密码学
以时间划分,1976 年以前的密码算法都属于古典密码学,基本使用在军事机密和外交领域,它的特点就是加解密过程简单,一般用手工或机械就可以完成。古典密码学现在已经很少采用了,然而,研究古典密码的原理对于理解构造和分析现代密码都是十分有益的。尤其是对称加密技术,它就是从古典密码学中演化进来的。
密码学术语
- 明文:没有经过加密,任何人都能看得懂的文字
- 密文:经过加密后的文字
- 密钥:明文与密文转换的参数
古典密码学中最经典的两种算法
置换密码
又称换位密码,加密过程中明文的字母保持相同,但是顺序被打乱。只要把位置恢复,就能得到明文。
一个典型置换密码的例子:公元前 500 年的古希腊斯巴达邦城,存在一种叫做「棍子加密」的加密方法。找一个腰带,将信息横着写在腰带上,但是这个信息是完全打乱的,需要一个可以解密的棍子,将腰带缠绕在棍子上,就可以恢复出明文。这也是最简单的置换密码的方式。后来随着抽象代数的出现,有了矩阵之后,就可以做一个复杂的置换加密,对运算做一个置换,用置换矩阵来解密,恢复出明文。
代换密码
又称替换密码,明文中的每一个字符被替换成密文中的另一个字符。接收者对密文做反向替换就可以恢复明文。
位移密码
位移密码是代换密码其中的一种,最简单最典型的就是凯撒密码
**凯撒密码:**它是一种单表代换密码,加密方式就是通过对字母的位移进行加密,比如把字母表右移三位,上面是明文表,下面是对应的密文表。如下图所示:
代换密码的破解和发展
像凯撒密码一样,每一个都有相应代表的位置,像 A 代表着 D,B 代表这 E。单表代换密码就很容易被破解,只要用频率分析表就可以破解,这是根据人类自然语言中,字母出现的频率不同来进行破解的。比如英文 E 是使用最频繁的,其次是 T/R/N/I/O/A/S 等;有些字母使用的很少,例如 Z/J/K/Q/X 等,这样就可以获得英文字母使用频率分布表,这个表是根据几本书获得的频率分析来获得的,同时,统计双字母组合和三字母组合的使用频率也非常有用。
有了破解的方法后,密码学中也会相对应的出现防止破解的方法。
- 多名或同音代替密码
与简单代替密码类似,只是映射是一对多的,每个明文字母可以加密成多个密文字母。用已知明文攻击比较容易破解
- 多字母代替密码
每次对 L 个字母进行代换,隐藏或均匀化字母的自然频度,用于抵抗频率分析。比如 hill 密码,用已知明文攻击比较容易破解。
- 多表代替密码
多代替密码可以说是古典密码学的巅峰之作,是以一系列(两个以上)代换表依次对明文消息的字母进行代换的加密操作。先把明文分成多份,代换表有很多个,根据序列依次更换代换序列,对每个序列进行加密。典型的代换密码:vigenere cipher,博福特密码,enigma 密码机(这个密码机可以说是古典密码学中最厉害的一种,但是在 1940 年时,被图灵给破解了)。
古典密码学的特点
- 计算强度小
- 出现在 DES 之前
- 数据安全基于算法的保密。这和现代密码有很大的差距,只要知道加密方法,就能轻易的获取明文。现代的密码基于密钥的加密,算法都是公开的,而且公开的密码算法安全性更高,能被更多人评论和使用,加强漏洞的修补。
- 以字母表为主要加密对象。古典密码大多数是对有意义的文字进行加密,而现代密码是对比特序列进行加密。这也是现代密码和古典密码的区别,而且古典密码的分析方法也是用字母频率分析表来破解的。
- 替换和置换技术
- 密码分析方法基于字母与字母组合的频率特性以及明文的可读性
三、古典密码的代码案例
用html+js+node实现简单的置换密码、代换密码、位移密码、凯撒密码的加密以及解密
置换密码案例
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<label for="plainText">明文</label>
<input type="text" name="" id="plainText">
<button onclick="encipher()">加密</button>
<br>
密文<input type="text" id="cipherText">
<button onclick="decrypt()">解密</button>
<br>
原文<input type="text" name="" id="original">
</body>
<script>
// 置换规则是反转
function encipher() {
let plainText = document.getElementById('plainText').value
// 第一种:js自带反转方法
/*
split(""):根据空字符串拆分数组
reverse():数组反转元素位置
join(""):数组转回字符串,且不带分隔符
*/
let newPlainText = plainText.split("").reverse().join("")
document.getElementById('cipherText').value = newPlainText
// 第二种
/*
使用遍历以及api charAt()
定义新的空字符串,遍历str,charAt()是取字符串的一个字符(charAt() 方法可返回指定位置的字符。),
先去最后一个字符,再取倒数第二个...以此类推。都放到新的字符串前面。这样就是倒序的了.
*/
let newPlainTexts = ""
for(let i = 1; i<=plainTextl.length; i++){
newPlainTexts = newPlainTexts + plainText.charAt(plainText.length-i)
}
document.getElementById('cipherText').value = newPlainTexts
}
function decrypt() {
let cipherText = document.getElementById('cipherText').value
let originalText = cipherText.split("").reverse().join("")
document.getElementById('original').value = originalText
}
</script>
</html>
代换密码案例
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<label for="plainText">明文:</label>
<input type="text" id="plainText">
<button onclick="encipher()">加密</button>
<br>
<label for="cipherText">密文:</label>
<input type="text" id="cipherText">
<button onclick="decrypt()">解密</button>
<br>
<label for="original">原文</label>
<input type="text" name="" id="original">
</body>
<script>
//加密
function encipher() {
let plainText = document.getElementById("plainText").value;
//替换表
const key = {
'a': 'w', 'b': 'v', 'c': 'q', 'd': 'k', 'e': 'i', 'f': 'g', 'g': 'c',
'h': 'x', 'i': 'u', 'j': 'r', 'k': 'l', 'l': 'j', 'm': 'e', 'n': 'b',
'o': 'y', 'p': 't', 'q': 'p', 'r': 'm', 's': 'h', 't': 'd', 'u': 'a',
'v': 'z', 'w': 's', 'x': 'o', 'y': 'n', 'z': 'f'
};
//将输入的明文统一转换为小写,便于匹配替换表
/*
js自带toLowerCase() 将字符串大写转换成小写
js自带toUpperCase() 将字符串大写转换成小写
*/
plainText = plainText.toLowerCase();
let cipherText = '';
for (let i = 0; i < plainText.length; i++) {
const char = plainText[i];
cipherText += key[char];
}
document.getElementById("cipherText").value = cipherText;
}
// 解密
function decrypt() {
let cipherText = document.getElementById("cipherText").value;
const key = {
'w': 'a', 'v': 'b', 'q': 'c', 'k': 'd', 'i': 'e', 'g': 'f', 'c': 'g',
'x': 'h', 'u': 'i', 'r': 'j', 'l': 'k', 'j': 'l', 'e': 'm', 'b': 'n',
'y': 'o', 't': 'p', 'p': 'q', 'm': 'r', 'h': 's', 'd': 't', 'a': 'u',
'z': 'v', 's': 'w', 'o': 'x', 'n': 'y', 'f': 'z'
};
let original = ""
for(let i = 0; i<cipherText.length; i++){
let char = cipherText[i]
char = key[char]
original += char
}
document.getElementById("original").value = original;
}
</script>
</html>
当然不一定是用对象,也可以用数组,数组更简单一点
位移密码案例
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=, initial-scale=1.0">
<title>移位密码</title>
</head>
<body>
<h1>移位密码</h1>
<label for="plainText">明文:</label>
<input type="text" id="plainText">
<br>
<label for="shift">偏移量:</label>
<input type="number" id="shift">
<input type="checkbox" id="shiftDirection">左移位
<button onclick="encipher()">加密or解密</button>
<br>
<label for="cipherText">密文:</label>
<input type="text" id="cipherText" >
</body>
<script>
// 加密
function encipher() {
let plainText = document.getElementById("plainText").value;
const shift = parseInt(document.getElementById("shift").value);
const isLeftShift = document.getElementById("shiftDirection").checked;
plainText = plainText.toLowerCase();
var cipherText = "";
for (var i = 0; i < plainText.length; i++) {
var char = plainText[i];
/*
match是js自带的一个函数
用于匹配字符串内的元素
正则:match(/元素/可选参数) 普通:match("元素")
可选参数: g全局匹配 i不区分大小写匹配 gi全局不分大小写
*/
if (char.match(/[a-z]/i)) {
// 移位右加左减
const code = plainText.charCodeAt(i);
var shiftedCode;
if (isLeftShift) {
shiftedCode = code - shift;
} else {
shiftedCode = code + shift;
}
// 解决头尾问题
if (char.match(/[a-z]/)) {
if (char.match(/[a-z]/) && shiftedCode < 97) {
shiftedCode += 26;
} else if (char.match(/[a-z]/) && shiftedCode > 122) {
shiftedCode -= 26;
}
}
// 将unicode值转回字符 String.fromCharCode()
char = String.fromCharCode(shiftedCode);
}
cipherText += char;
}
document.getElementById("cipherText").value = cipherText;
}
</script>
</html>
右移的可以使用左移恢复明文,左移的可以使用右移恢复
bug:不区分大小写
以下凯撒密码解决该bug
凯撒密码案例
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>凯撒密码</h1>
明文:
<input type="text" id="plaintext">
<br>
偏移值:
<input type="number" id="shift">
<br>
<button onclick="encrypt()">加密</button>
<button onclick="decrypt()">解密</button>
<br>
密文:
<input type="text" id="ciphertext" readonly>
<br>
解密后:
<input type="text" id="decryptedText" readonly>
</body>
</html>
<script>
// 处理加密按钮点击事件
function encrypt() {
let plaintext = document.getElementById("plaintext").value;
let shift = parseInt(document.getElementById("shift").value);
let ciphertext = caesarCipherEncrypt(plaintext, shift);
document.getElementById("ciphertext").value = ciphertext;
}
// 处理解密按钮点击事件
function decrypt() {
let ciphertext = document.getElementById("ciphertext").value;
let shift = parseInt(document.getElementById("shift").value);
let decryptedText = caesarCipherDecrypt(ciphertext, shift);
document.getElementById("decryptedText").value = decryptedText;
}
// 凯撒密码加密函数
function caesarCipherEncrypt(text, shift) {
let encryptedText = "";
for (let i = 0; i < text.length; i++) {
let char = text[i];
if (char.match(/[a-z]/i)) {
let code = char.charCodeAt();
if (code >= 65 && code <= 90) {
char = String.fromCharCode(((code - 65 + shift) % 26) + 65);
} else if (code >= 97 && code <= 122) {
char = String.fromCharCode(((code - 97 + shift) % 26) + 97);
}
} encryptedText += char;
} return encryptedText;
}
// 凯撒密码解密函数
function caesarCipherDecrypt(text, shift) {
let decryptedText = "";
for (let i = 0; i < text.length; i++) {
let char = text[i];
if (char.match(/[a-z]/i)) {
let code = char.charCodeAt();
if (code >= 65 && code <= 90) {
char = String.fromCharCode(((code - 65 - shift + 26) % 26) + 65);
}
else if (code >= 97 && code <= 122) { // 小写字母
char = String.fromCharCode(((code - 97 - shift + 26) % 26) + 97);
}
} decryptedText += char;
} return decryptedText;
}
</script>