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加解密(二) #236

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

AES加解密(二) #236

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

Comments

@soapgu
Copy link
Owner

soapgu commented Jan 14, 2024

  • 前言

上一篇是对AES的快速入门,这次通过和其他端的加解密来详细解说一下。
这一次用的模式为CBC模式

PS:其实最简单的应该是ECB模式,但是从安全性角度来说已经被判了死刑。

Warning
The ECB mode should not be used because it is semantically insecure. For one, it exposes correlation between blocks.

  • 关于CBC使用参数

  • IV
    向量值和加密块的长度一致(16byte),如果不指定api会随机生成一个。

  • block size
    加密块数据大小(16byte)

  • AES的padding

AES加密一次加密的数据量为16byte,如果不足16byte需要“凑整”,过大需要“切割”。
图片
我们暂时不谈切割,我们说“凑整”padding

  1. ANSI X.923

在ANSI X.923的方式下,先是填充00,最后一个字节填充padded的字节个数。

例子: | DD DD DD DD DD DD DD DD | DD DD DD 00 00 00 00 05 |

  1. ISO 10126

在ISO 10126的方式下,先是填充随机值,最后一个字节填充padded的字节个数。

例子: | DD DD DD DD DD DD DD DD | DD DD DD 95 81 28 A7 05 |

  1. PKCS7

在PKCS7的方式下,如果一共需要padded多少个字节,所有填充的地方都填充这个值。

例子: | DD DD DD DD DD DD DD DD | DD DD DD 05 05 05 05 05 |

  1. ISO/IEC 7816-4

在ISO/IEC 7816-4方式下,第一个填充的字节是80,后面的都填充00。

例子: | DD DD DD DD DD DD DD DD | DD DD DD 80 00 00 00 00 |

  1. Zero padding

在Zero padding方式下,每一个需要填充的字节都填00。

例子: | DD DD DD DD DD DD DD DD | DD DD DD 00 00 00 00 00 |

我们默认使用PKCS7

  • 有趣的PKCS7极端例子
    如果最后一行数据正好是怎么样?

不如写一个例子实验一下

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad
import binascii


keys = '30980f98296b77f00a55f3c92b35322d898ae2ffcdb906de40336d2cf3d556a0'
iv = 'e5889166bb98ba01e1a6bc9b32dbf3e6'

def aes_string( input:str ):
    cipher = AES.new(binascii.unhexlify(keys), AES.MODE_CBC,iv=binascii.unhexlify(iv))
    decipher = AES.new(binascii.unhexlify(keys), AES.MODE_CBC,iv=binascii.unhexlify(iv))
    message = input.encode()
    pad_message = pad(message, AES.block_size)
    print(f"message:{message},pad message{pad_message}")
    text_secret = cipher.encrypt(pad_message)
    print(f"text_secret:{text_secret}")
    original_message = unpad(decipher.decrypt(text_secret),AES.block_size)
    print("Decrypted original message:", original_message)

aes_string('testtesttesttest')
图片

可以看出补了16个0x10“废数据”,如果unpad,正好把整个数据块全部拿掉。

  • 加解密完整实现

  • 文件加密

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad
import binascii
import os
import argparse


parser = argparse.ArgumentParser(description='Process some files.')
parser.add_argument('-f', '--input-file', required=True, help='The input file')

args = parser.parse_args()

print(f"Input file: {args.input_file}")

keys = '30980f98296b77f00a55f3c92b35322d898ae2ffcdb906de40336d2cf3d556a0'
iv = 'e5889166bb98ba01e1a6bc9b32dbf3e6'
download_path = args.input_file
output_filename = f"encrypt_{args.input_file}"

#print(f"key lenght:{len(binascii.unhexlify(keys))}")

cipher = AES.new(binascii.unhexlify(keys), AES.MODE_CBC,iv=binascii.unhexlify(iv))
in_file_length = os.path.getsize(download_path)
block_count = in_file_length / AES.block_size
print(in_file_length)

count = 0
with open(download_path, 'rb') as in_file:
    with open(output_filename, 'wb') as out_file:
        while content := in_file.read(AES.block_size):
            if(len(content) < AES.block_size):
                content = pad(content,AES.block_size)
                hex_string = binascii.hexlify(content).decode()
                print(f"pad content:{hex_string}")
            crypted_chunk = cipher.encrypt(content)
            out_file.write(crypted_chunk)
            #print(f"计数{count}/总数{block_count},length:{len(crypted_chunk)}")

out_file_length = os.path.getsize(output_filename)
print(f"encrypt ok,size {out_file_length}")

注意这里最后一块数据块需要执行pad函数进行凑整处理

  • 文件解密
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import binascii
import os
import argparse

parser = argparse.ArgumentParser(description='Process some files.')
parser.add_argument('-f', '--input-file', required=True, help='The input file')
args = parser.parse_args()
print(f"Input file: {args.input_file}")

keys = '30980f98296b77f00a55f3c92b35322d898ae2ffcdb906de40336d2cf3d556a0'
iv = 'e5889166bb98ba01e1a6bc9b32dbf3e6'
download_path = args.input_file
output_filename = f"restore_{download_path}"


cipher = AES.new(binascii.unhexlify(keys), AES.MODE_CBC,iv=binascii.unhexlify(iv))

in_file_length = os.path.getsize(download_path)

block_count = in_file_length / AES.block_size

count = 0
with open(download_path, 'rb') as in_file:
    with open(output_filename, 'wb') as out_file:
        while content := in_file.read(AES.block_size):  # 每次读取数据
            count = count + 1
            decrypted_chunk = cipher.decrypt(content)
            if(block_count == count):
                print(f"before pad content:{decrypted_chunk}")
                decrypted_chunk = unpad( decrypted_chunk,AES.block_size )
            hex_string = binascii.hexlify(decrypted_chunk).decode()
            print(f"content:{hex_string}")
            out_file.write(decrypted_chunk)
            print(f"计数{count}/总数{block_count},length:{len(decrypted_chunk)}")

print(f"decrypt {output_filename} OK")

out_file_length = os.path.getsize(output_filename)
print(f"in file size:{in_file_length},out file size{out_file_length}")

同样,解密最后一块数据块需要执行unpad。

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