---
layout: post
title: 如何破解我上次写的加密程序？
tags: [Python, 加密, 破解]
---

  不按规则使用加密算法是非常不安全的！<!--more-->    

# 起因
  前几天我用Python写了一个[很简单的加密程序](/2022/10/08/tinyaes.html)，用的是tinyaes提供的AES-128-CTR加密方式，然后因为嫌麻烦没有整iv。最近几天我一直在搜关于AES中关于iv的信息，我一直没想明白那个iv到底是干啥用的。   
  在我看了几天那些关于AES的5种加密模式之后，关于ECB和CBC的加密模式讲的很多，而对其他加密模式讲的很不清楚……但唯一写的很清楚的就是ECB很不安全，重复使用相同的密钥和iv加密数据很不安全，尤其是CTR模式，直接完全丧失了机密性😂，这不是在完全否定我前几天写的东西嘛，然而除了这些就再没有写别的了……那它到底是怎么个丧失了机密性啊，又不说清楚，所以就只好我自己想了😓。

# 探索
  首先我先看了看AES中5种加密模式的区别，发现CTR模式和其他模式有个不同的地方是它加密的是密码，而不是数据，它是把密码和iv通过AES加密算法加密之后把获得的东西和明文数据异或后来得到密文的，怪不得它可以加密和解密用相同的方法，原来它真的就是异或加密啊😂，那异或加密安全吗？我在网上也搜了搜，很多人说这是一种非常不安全的加密方式，只有不懂的人和刚入门密码学的人才会用，巧了，我就是不懂密码学的人😂，然而异或加密又怎么不安全网上又不说……我真的是无语了。   
  不说我就只能自己想了……首先，他们说CTR不安全的加密仅限于相同的iv和密码去加密不同的数据，而加密的方式是通过异或，那么我们先这样去算一下：   
  K = AES(key, iv)   
  C1 = K xor P1  
  C2 = K xor P2  
  其中K是用AES加密密码和iv获得的值，C代表密文，P代表明文。然后想着想着，如果用C1 xor C2会怎么样呢？最终用异或运算律算了一下，大概明白了为什么它不安全了，因为：   
  C1 xor C2 = P1 xor P2   
  我们知道，在这种情况下假如你用P1或P2去异或上面式子得到的值，那你就能得到另一个明文，也就是：   
  (C1 xor C2) xor P1 = P2   
  这样你只要知道一组用这个密钥加密的密文和明文，就能破解所有其他密文了！   
  于是根据这个原理我试着用Python写了一个根据已知明文密文对获得密文的程序出来：
```python
import sys

if not len(sys.argv) == 4:
    exit(f"Usage: {sys.argv[0]} [enc_file1] [enc_file2] [plain_file2]")
with open(sys.argv[1], "rb") as enc_file1:
    with open(sys.argv[2], "rb") as enc_file2:
        with open(sys.argv[3], "rb") as plain_file2:
            with open(sys.argv[1] + ".crack", "wb") as crack_file1:
                crack_file1.write(bytes(a ^ b for (a, b) in zip(bytes(a ^ b for (a, b) in zip(enc_file1.read(), enc_file2.read())), plain_file2.read())))
```
  写完之后发现和加密的程序用了一样多的行数😂，试了一下，真的可以获得另外密文的对应的明文！而且是在不需要知道密码的情况，不过我不知道是我程序写的有问题还是推算的有问题，当想要破解的文件长度比已知明文密文的文件长时，破解出来的明文就只能破解到和已知明文密文一样长的位置……总之能证明之前写的加密确实“完全丧失了机密性”就行了。   

# 解决办法
  为了弥补上一篇文章写的加密程序所遇到的问题，这次我还是乖乖的按照官方说明使用iv吧，根据这个原理我重新写了一下我的程序：
```python
import hashlib, tinyaes, sys, os

if not len(sys.argv) == 3:
    exit(f"Usage: {sys.argv[0]} [filepath] [key]")
enc = False
if len(sys.argv[1]) > 4:
    if sys.argv[1][-4:] == ".enc":
        enc = True
with open(sys.argv[1], 'rb') as orig:
    iv = os.urandom(16)
    key = tinyaes.AES(hashlib.md5(sys.argv[2].encode()).digest(), orig.read(16) if enc else iv)
    with open(sys.argv[1][:-4] if enc else sys.argv[1] + ".enc", 'wb') as targetfile:
        if not enc:
            targetfile.write(iv)
        for byte_block in iter(lambda: orig.read(4096), b''):
            targetfile.write(key.CTR_xcrypt_buffer(byte_block))
```
  这次我把随机生成的iv存到了文件的开头，不过也正是如此，我没办法用完全相同的办法去进行加密和解密了……所以用了`.enc`后缀名作为明文和密文的区分。还有程序的行数几乎增加了一倍😂。   

# 感想
  看来对于不太了解的学科还是好好的按照人家说明书上写的做比较好，不然不懂装懂就会达不到预期的想法了。
