古典密码学总结及代码实现

Posted 小圣.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了古典密码学总结及代码实现相关的知识,希望对你有一定的参考价值。

1. 概述

虽然用近代密码学的观点来看,许多古典密码是很不安全的。但是许多编制古典密码学的基本方法对于编制现代密码学仍有效果。其中主要的方法有:置换方法、代替方法

2. 置换方法

把明文中的字母重新排列,字母本身不变,但其位置改变了,这样编成的密码称为置换密码。

2.1 倒序加密

最简单的置换密码是把明文中的字母顺序倒过来,然后截成固定长度的字母组成密文。

// 置换倒序加密
func reverseOrder(plainText string) string {
	var cipherText strings.Builder
	// 把明文倒序,得到密文
	for i := len(plainText) - 1; i > 0; i-- {
		cipherText.WriteString(string(plainText[i]))
	}
	return cipherText.String()
}

2.2 矩阵加密

倒序的置换密码显然是很弱的。另外一种置换密码是把明文按照某一顺序排成一个矩阵,然后按另一顺序选出矩阵中的字母以形成密文,最后截成固定长度的字母组作为密文。例如下面的例子:

1. 设明文为:wo yao gao su ni yi ge mi mi

2. 转换成明文矩阵:
			w o y a 
			o g a o
			s u n i
			y i g e 
			m i m i 

3. 然后按照一定顺序重新排列矩阵,这个顺序可以认为是密钥。
		假设这个顺序是2314。意思就是矩阵的列按照这个顺序排序
			o y w a
			g a o o
			u n s i
			i g y e
			i m m i
			
4. 矩阵转换成字符串就是密文了
	密文: oywagaoounsiigyeimmi

代码如下:

func matrixEncrypt(plainText string, key []byte) string {
	// 把明文要求排成矩阵,设矩阵为4列

	// 计算需要多少行
	row := len(plainText) / 4
	if len(plainText)%4 != 0 {
		row++
	}

	// 创建密文矩阵
	var plainTextMatrix [][4]string = make([][4]string, row)

	var a, b int
	// 循环倒明文的最后
	for i := 0; i < len(plainText); i++ {
		plainTextMatrix[a][b] = string(plainText[i])

		b++
		if b%4 == 0 && b != 0 {
			b = 0
			a++
		}
	}

	// 如果明文不够一行,就填充明文矩阵
	excr := len(plainText) % 4
	if excr != 0 {
		paddingNum := 4 - excr
		fmt.Println("padin", paddingNum)
		for i := 0; i < paddingNum; i++ {
			plainTextMatrix[row-1][3-i] = "@"
		}
	}

	// 生成密文矩
	var cipherText string
	// var test string
	var cipherTextMatrix [][4]string = make([][4]string, row)
	for i := 0; i < 4; i++ {
		for j := 0; j < len(plainTextMatrix); j++ {
			cipherTextMatrix[j][i] = plainTextMatrix[j][key[i]-1]
			// 这里可以直接把密文矩阵转换为密文
			// test += plainTextMatrix[j][key[i]-1]
		}

	}

	// 密文矩阵转化为密文
	for i := 0; i < 4; i++ {
		for j := 0; j <= len(cipherTextMatrix)-1; j++ {
			cipherText += cipherTextMatrix[j][i]
		}
	}

	// 返回密文
	return cipherText
}

解密:

func matrixDecrypt(cipherText string, key []byte) string {
	row := len(cipherText) / 4
	if len(cipherText)%4 != 0 {
		row++
	}
	var cipherTextMatrix [][4]string = make([][4]string, row)

	var a, b int
	// 根据密文得到密文矩阵
	for j := 0; j < len(cipherText); j++ {
		cipherTextMatrix[a][b] = string(cipherText[j])
		a++
		if a == row {
			a = 0
			b++
		}
	}

	// 把密文矩阵转换成明文矩阵
	var plainTextMatrix [][4]string = make([][4]string, row)
	for i := 0; i < 4; i++ {
		for j := 0; j < len(cipherTextMatrix); j++ {
			plainTextMatrix[j][key[i]-1] = cipherTextMatrix[j][i]
		}
	}

	// 明文矩阵转换成明文
	var plainText string
	for i := 0; i < row; i++ {
		for j := 0; j < 4; j++ {
			plainText += string(plainTextMatrix[i][j])
		}

	}
	// 返回明文
	return plainText
}

运行结果:

置换密码比较简单,经不起已知明文攻击。但是,把它与其它密码技术相结合,可以得到十分有效的密码。

3. 代替方法

首先构造一个或多个密文字母表,然后密文字母表中的字母或字母组来代替明文字母或字母组,各字母或字母组的相对位置不变,但其本身改变了。这样编成的密码称为代替密码。

按代替所使用的密文字母表的个数可以将代替密码分为单表代替密码和多表代替密码。

3.1 单表代替密码

单表代替密码包括:加法密码、乘法密码和密钥词组代替密码。其主要思想就是用一个字符去代替另一个字符,以此来达到加密的效果。接收者对密文做反向替换就可以恢复出明文。

其中最有的名的加法密码就是凯撒加密,工作原理大概如下:
图中使用D代替A、E代替B、E代替C,以此后推,当需要加密时,就是使用下面的字母表。例如要加密bad这个单词时,使用下面的字母可以加密成edg
表中字母往后的偏移量可以当作密钥,接收者需要解密时,只需要按照密钥,把字母向前移动就可以得到密钥。话不多说,上代码:

/*
	加密
	filename: 要加密的明文地址
	des: 存放加密后密文的地址
	key: 密钥
*/
func encrypt(plainTextFile string, dest string, key int) {

	// 从文件中读取明文
	plaintText, err := ioutil.ReadFile(plainTextFile)
	if err != nil {
		fmt.Println("read file err: ", err)
		return
	}
	// 得到密文切片,方便偏移
	ciphertextSlice := []uint8{}
	for _, v := range plaintText {
		// 把明文偏移key个单位
		intv := v + uint8(key)
		ciphertextSlice = append(ciphertextSlice, intv)
	}
	// 把密文写入文件
	err = ioutil.WriteFile(dest, ciphertextSlice, 0666)
	if err != nil {
		fmt.Println("write file err: ", err)
		return
	}

	fmt.Println("successfully encrypt")
}

/*
	解密
	cipherTextFile:密文路径
	plaintTextFile:解密后明文的存放路径
	key:明文
*/
func decrypt(cipherTextFile string, plainTextFile string, key int) {
	// 读取密文
	cipherText, err := ioutil.ReadFile(cipherTextFile)
	if err != nil {
		fmt.Println("read file err: ", err)
		return
	}

	plainTextSlice := []uint8{}
	// 根据密钥解密
	for _, v := range cipherText {
		intV := v - uint8(key)
		plainTextSlice = append(plainTextSlice, intV)
	}

	// 转换成明文字符串
	plaintText := string(plainTextSlice)

	// 写入本地
	err = ioutil.WriteFile(plainTextFile, []byte(plaintText), 0666)
	if err != nil {
		fmt.Println("write file err: ", err)
		return
	}

	fmt.Println("successfully decrypt")

}

/*
	破解
	cipherTextFile:加密文件
	crackTextFile:破解后文件的存储路径
*/
// 暴力破解
func cracking(cipherTextFile string, crackTextFile string) {
	// 读取密文
	cipherText, err := ioutil.ReadFile(cipherTextFile)
	if err != nil {
		fmt.Println("read file err: ", err)
		return
	}

	crackedTextSlice := []uint8{}
	var crackedText string
	file, err := os.OpenFile(crackTextFile, os.O_APPEND|os.O_WRONLY, 0666)
	if err != nil {
		fmt.Println("open file err: ", err)
		return
	}
	defer file.Close()
	writer := bufio.NewWriter(file)

	// 暴力破解密文
	for i := 1; i <= 26; i++ {
		for _, v := range cipherText {
			intV := v - uint8(i)
			crackedTextSlice = append(crackedTextSlice, intV)
		}
		crackedText = string(crackedTextSlice)
		_, err = writer.WriteString(crackedText + "\\n")
		if err != nil {
			fmt.Println("write file err: ", err)
			return
		}
		writer.Flush()
		crackedTextSlice = []uint8{}
	}

	fmt.Println("successfully cracke")
}

4. 最后

完整代码: https://github.com/bigzoro/cryptography

以上是关于古典密码学总结及代码实现的主要内容,如果未能解决你的问题,请参考以下文章

古典密码学总结及代码实现

1.密码学概念及古典密码

单表代替密码原理及算法实现

密码学——几种典型的古典密码体制(Caesar体制Playfair体制Vigenere体制Beaufort体制以及Hill体制)

古典密码简介

密码学(杨义先)- 古典密码