用Python手搓一个维吉尼亚密码加解密工具(附完整代码和查表法实现)
维吉尼亚密码作为古典密码学的经典代表,至今仍是理解加密原理的绝佳案例。不同于简单的凯撒移位,它通过多表替换机制有效对抗频率分析攻击。本文将带您从零实现一个完整的维吉尼亚密码工具包,包含数学计算和查表法两种实现方式,最终打包成可直接调用的命令行工具。
1. 密码原理深度解析
维吉尼亚密码的核心在于动态位移。与凯撒密码固定位移量不同,它通过密钥字符循环决定每个明文字母的位移量。例如密钥"KEY"表示三个位移量:K(10)、E(4)、Y(24),这三个值会循环应用于明文的每个字母。
数学本质:
- 加密公式:
密文 = (明文ASCII值 + 密钥ASCII值 - 2*65) % 26 + 65 - 解密公式:
明文 = (密文ASCII值 - 密钥ASCII值 + 26) % 26 + 65
查表法则是通过预先生成的维吉尼亚方阵(26x26字母矩阵)进行坐标查找。第一行为标准字母表,后续每行前移一位:
# 维吉尼亚方阵生成代码片段 def generate_vigenere_square(): square = [] for i in range(26): row = [chr((j + i) % 26 + 65) for j in range(26)] square.append(''.join(row)) return square2. 基础数学实现
我们先实现最直接的数学计算版本,适合理解算法本质:
def vigenere_encrypt(plaintext: str, key: str) -> str: ciphertext = [] key_repeated = (key * (len(plaintext) // len(key) + 1))[:len(plaintext)] for p_char, k_char in zip(plaintext.upper(), key_repeated.upper()): if p_char.isalpha(): shift = ord(k_char) - 65 encrypted = chr((ord(p_char) - 65 + shift) % 26 + 65) ciphertext.append(encrypted) else: ciphertext.append(p_char) return ''.join(ciphertext) def vigenere_decrypt(ciphertext: str, key: str) -> str: plaintext = [] key_repeated = (key * (len(ciphertext) // len(key) + 1))[:len(ciphertext)] for c_char, k_char in zip(ciphertext.upper(), key_repeated.upper()): if c_char.isalpha(): shift = ord(k_char) - 65 decrypted = chr((ord(c_char) - 65 - shift + 26) % 26 + 65) plaintext.append(decrypted) else: plaintext.append(c_char) return ''.join(plaintext)关键改进点:
- 支持非字母字符保留原样
- 自动处理大小写统一
- 密钥循环机制优化
3. 查表法高级实现
查表法更接近历史实现方式,适合教学演示:
class VigenereCipher: def __init__(self): self.square = self._generate_square() def _generate_square(self): return [ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'BCDEFGHIJKLMNOPQRSTUVWXYZA', # 剩余24行... 'ZABCDEFGHIJKLMNOPQRSTUVWXY' ] def encrypt(self, plaintext: str, key: str) -> str: encrypted = [] key_index = 0 for char in plaintext.upper(): if char.isalpha(): row = ord(key[key_index % len(key)].upper()) - 65 col = ord(char) - 65 encrypted.append(self.square[row][col]) key_index += 1 else: encrypted.append(char) return ''.join(encrypted) def decrypt(self, ciphertext: str, key: str) -> str: decrypted = [] key_index = 0 for char in ciphertext.upper(): if char.isalpha(): row = ord(key[key_index % len(key)].upper()) - 65 decrypted.append(chr(self.square[0].index(char) - row + 65)) key_index += 1 else: decrypted.append(char) return ''.join(decrypted)4. 命令行工具封装
将核心功能封装为可直接调用的命令行工具:
import argparse def main(): parser = argparse.ArgumentParser(description='维吉尼亚密码工具') parser.add_argument('mode', choices=['encrypt', 'decrypt'], help='加密或解密') parser.add_argument('-k', '--key', required=True, help='加密密钥') parser.add_argument('-t', '--text', help='直接输入文本') parser.add_argument('-f', '--file', help='从文件读取内容') args = parser.parse_args() if not args.text and not args.file: print("错误:必须提供文本或文件输入") return content = args.text if args.text else open(args.file, 'r').read() cipher = VigenereCipher() if args.mode == 'encrypt': result = cipher.encrypt(content, args.key) else: result = cipher.decrypt(content, args.key) print("结果:") print(result) if __name__ == '__main__': main()使用示例:
# 加密 python vigenere.py encrypt -k SECRET -t "HELLO WORLD" # 解密文件 python vigenere.py decrypt -k SECRET -f encrypted.txt5. 实战技巧与优化建议
密钥安全性:
- 密钥长度应至少与明文相当
- 避免使用常见单词作为密钥
- 示例安全密钥生成代码:
import secrets def generate_key(length=16): return ''.join(secrets.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ') for _ in range(length))
性能优化:
- 预计算字母偏移量
- 使用列表推导替代循环
- 优化后的加密函数:
def optimized_encrypt(plaintext, key): key_stream = (key * (len(plaintext) // len(key) + 1))[:len(plaintext)] return ''.join( chr((ord(p) + ord(k) - 130) % 26 + 65) if p.isalpha() else p for p, k in zip(plaintext.upper(), key_stream.upper()) )
扩展功能:
- 添加文件批量处理功能
- 支持多种编码格式
- 实现自动密钥模式
在CTF比赛中遇到维吉尼亚密码题时,建议先分析密钥长度(可通过Kasiski测试),再尝试频率分析。实际使用中发现,当密钥长度等于明文长度且密钥完全随机时,维吉尼亚密码理论上可以达到完美保密。