Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AES加解密(一) #235

Open
soapgu opened this issue Jan 10, 2024 · 0 comments
Open

AES加解密(一) #235

soapgu opened this issue Jan 10, 2024 · 0 comments
Labels

Comments

@soapgu
Copy link
Owner

soapgu commented Jan 10, 2024

  • 前言

鉴于RSA及其低下的效率不适合文件内容直接加密。好像HTTPS也不是这样玩的,人家RSA只是协商密钥,传输用的还是AES。所以还是要重回AES研究

  • 实现步骤

可以用RSA时候用的python库来实现

参考

aes需要key和nonce两个参数构建

先试水单文件字符串加解密

print("begin to encrypt...")

data = 'secret data to transmit'.encode()

aes_key = open("aes_key","rb").read()
aes_iv = open("aes_iv","rb").read()

print(aes_key)
print(aes_iv)
cipher = AES.new(aes_key, AES.MODE_CTR,nonce=aes_iv)
ciphertext = cipher.encrypt(data)

print("Ciphertext:", ciphertext)

decipher = AES.new(aes_key, AES.MODE_CTR,nonce=aes_iv)
original_message = decipher.decrypt( ciphertext )
print("Decrypted original message:", original_message.decode('utf-8'))

再实验单个文件加密和解密,也没问题。

最近写到服务里,果然出问题了!

文件解出来,文件还原得不对。
对了一下,似乎文件大小没错!那只能是内容错了

  • 诡异的解密

问题我单个实验的时候是成功的!以此为抓手开始排除法,发现一个奇怪现象,我解密必须先解一次字符串,再解密文件就是成功的。直接解密是失败的,似乎和解密顺序有关?不太理解。
后面再次缩小实验范围对同一段数据进行两次解密会如何?

aes_key = open("aes_key","rb").read()
aes_iv = open("aes_iv","rb").read()

#cipher_aes = AES.new(aes_key, AES.MODE_CTR,nonce=aes_iv)
decipher_aes = AES.new(aes_key, AES.MODE_CTR,nonce=aes_iv)

text_secret = open("string.data","rb").read()
print(text_secret)
original_message = decipher_aes.decrypt(text_secret)
print("Decrypted original message:", original_message)

original_message = decipher_aes.decrypt(text_secret)
print("Decrypted original message:", original_message)

是的。果然解密出来的内容不一样!毁三观!难道程序是不确定的?

  • AES工作模式

基础知识缺乏害死人,原理AES还有一个关键参数是MODE,我这里就是无脑抄Example的代码觉得没问题。

AES.MODE_CTR 是一个关键参数

ECB模式(电子密码本模式:Electronic codebook)
  ECB是最简单的块密码加密模式,加密前根据加密块大小(如AES为128位)分成若干块,之后将每块使用相同的密钥单独加密,解密同理。

CBC模式(密码分组链接:Cipher-block chaining)
  CBC模式对于每个待加密的密码块在加密前会先与前一个密码块的密文异或然后再用加密器加密。第一个明文块与一个叫初始化向量的数据块异或
  CBC加密:https://www.cnblogs.com/phoenixy/p/15793339.html

CTR模式(计数器模式)
  CTR模式是一种通过将逐次累加的计数器进行加密来生成密钥流的流密码。自增的算子用密钥加密之后的输出和明文异或的结果得到密文,相当于一次一密。
  这种加密方式简单快速,安全可靠,而且可以并行加密,但是在计算器不能维持很长的情况下,密钥只能使用一次。

CFB模式(密文反馈:Cipher feedback)
  与ECB和CBC模式只能够加密块数据不同,CFB能够将块密文(Block Cipher)转换为流密文(Stream Cipher)

OFB模式(输出反馈:Output feedback)
  OFB是先用块加密器生成密钥流(Keystream),然后再将密钥流与明文流异或得到密文流,解密是先用块加密器生成密钥流,再将密钥流与密文流异或得到明文,由于异或操作的对称性所以加密和解密的流程是完全一样的。

我们这里的CRT模式还有一个隐藏的参数——自增的算子,解密顺序和加密顺序必须完全一样。由于我这里写的aes加密器和解密器是公共变量就不能保证顺序了。比较合适的做法是一次加解密都新建一个变量

图片

参考链接

这里注意加密解密对象生命周期都是per request

BLOCK_SIZE = 16 * 1024
aes_key = open("aes_key","rb").read()
aes_iv = open("aes_iv","rb").read()

@app.post("/aes/upload")
async def ase_upload( file: UploadFile ):
    print(file.filename)
    start_time = time.time()
    save_path = os.path.join(os.getcwd(), "downloads" , file.filename)
    cipher_aes = AES.new(aes_key, AES.MODE_CTR,nonce=aes_iv)
    with open(save_path, 'wb') as out_file:
        while content := await file.read(BLOCK_SIZE):  # 每次读取RSA加密最大值
            encrypted_chunk = cipher_aes.encrypt(content)
            out_file.write(encrypted_chunk)
    end_time = time.time()
    run_time = end_time - start_time
    print(f"AES加密运行时间: {run_time} 秒")
    return {"filename": file.filename}

@app.get("/aes/download")
async def ase_get_file( file: str ):
    download_path = os.path.join(os.getcwd(), "downloads" , file)
    def iterfileaes():
        start_time = time.time()
        count = 0
        decipher_aes = AES.new(aes_key, AES.MODE_CTR,nonce=aes_iv)
        with open(download_path, 'rb') as in_file:
            while content := in_file.read(BLOCK_SIZE):  # 每次读取数据
                decrypted_chunk = decipher_aes.decrypt(content)
                count = count + 1
                #print(f"计数{count},length:{len(decrypted_chunk)}")
                yield decrypted_chunk
        end_time = time.time()
        run_time = end_time - start_time
        print(f"AES解密运行时间: {run_time} 秒")
    headers = {
       "Content-Disposition": f"attachment; filename={file}",  # 设置下载文件名
    }
    return StreamingResponse(iterfileaes(), headers=headers , media_type="application/octet-stream")
  • 实验结果

图片

16710673437381651.jpg
大小:69K
加密: 0.0089 秒
解密: 0.0026秒

123.png
大小:3.6M
加密:0.0564秒
解密:0.0557秒

eBook.pdf
大小:8.3M
加密:0.0905秒
解密:0.0901秒

big_zip.zip
大小:96.3M
加密:0.8684秒
解密:0.8952秒

@soapgu soapgu changed the title AES加解密 AES加解密(一) Jan 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant