【问题标题】:Encryption/Decryption using AES/CBC/PKCS7Padding [closed]使用 AES/CBC/PKCS7Padding 加密/解密 [关闭]
【发布时间】:2018-10-08 07:33:09
【问题描述】:

我在使用 AES/CBC/PKCS7Padding 进行加密方面需要帮助。我需要像 here 这样的结果。

【问题讨论】:

  • 您好,请向我们展示您的代码/您尝试过的内容。这样,我们可以更快地帮助您。
  • 将您的代码(正在使用的实际代码)直接放在问题中。
  • @user1318741,是的,您的代码不起作用。因此,请将您的代码放在帖子中,以便我们可以看到哪里出错并帮助您修复它。您是否在代码中使用了库?
  • @user1318741 这不是这里的工作方式。请求库或其他第三方代码显然是题外话。
  • 那只是一个类文件。就其本身而言,它什么也不做。您需要展示如何亲自实施它,以及您期望的结果是什么,以及结果实际是什么。跨度>

标签: php android python encryption cryptography


【解决方案1】:

以下是如何使用流行的 cryptography 库来完成此任务。此代码改编自他们的文档。它使用问题中最初给出的数据、密钥和 IV。

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend
from base64 import b64decode, b64encode

backend = default_backend()
padder = padding.PKCS7(128).padder()
unpadder = padding.PKCS7(128).unpadder()

data = b'demo'
data = padder.update(data) + padder.finalize()
key = b64decode('HJkPmTz+uY7wd0p1+w//DABgbvPq9/230RwEG2sJ9mo=')
iv = b64decode('AAAAAAAAAAAAAAAAAAAAAA==')

cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend)
encryptor = cipher.encryptor()
ct = encryptor.update(data) + encryptor.finalize()
ct_out = b64encode(ct)
print(ct_out)

decryptor = cipher.decryptor()
plain = decryptor.update(ct) + decryptor.finalize()
plain = unpadder.update(plain) + unpadder.finalize()
print(plain)

输出

b'W2FEImF2qrAjaJ/LV+bgQA=='
b'demo'

只是为了好玩,这是一个不需要 3rd 方包的实现。 通常,人们不会做这种事情,因为加密的第一条规则是“不要推出自己的加密!”。但这是我在 Cryptopals 挑战赛中编写的一些 AES 代码。 ;) 它通过 ctypes 直接调用标准 OpenSSL 库来进行 AES ECB 编码,然后使用它来执行 CBC 加密/解密。

此代码是在运行 Python 3.6.0 的 Linux 系统上开发和测试的,但它也应该在 Windows 上运行。我认为它也可以在 OSX 上正确运行。

import os
from base64 import b64decode, b64encode
from ctypes import *

AES_MAXNR = 14
AES_BLOCK_SIZE = 16

DECODE = 0
ENCODE = 1

class AES_KEY(Structure):
    _fields_ = [
        ("rd_key", c_long * 4 *(AES_MAXNR + 1)),
        ("rounds", c_int),
    ]

crypto = cdll.LoadLibrary("libeay32.dll" if os.name == "nt" else "libssl.so")

# Function prototypes
AES_set_encrypt_key = crypto.AES_set_encrypt_key
AES_set_encrypt_key.restype = c_int
# userKey, bits, key
AES_set_encrypt_key.argtypes = [c_char_p, c_int, POINTER(AES_KEY)]

AES_set_decrypt_key = crypto.AES_set_decrypt_key
AES_set_decrypt_key.restype = c_int
# userKey, bits, key
AES_set_decrypt_key.argtypes = [c_char_p, c_int, POINTER(AES_KEY)]

AES_ecb_encrypt = crypto.AES_ecb_encrypt
AES_ecb_encrypt.restype = None
#in, out, key, enc(1=encode, 0=decode) 
AES_ecb_encrypt.argtypes = [c_char_p, c_char_p, POINTER(AES_KEY), c_int]

set_key = (AES_set_decrypt_key, AES_set_encrypt_key)

def set_aes_key(key, encode):
    ''' Create an AES encoding or decoding key '''
    keylen = len(key)
    valid = {16, 24, 32}
    if keylen not in valid:
        msg = f'Key length must be one of {valid}, not {keylen}'
        raise ValueError(msg)

    aes_key = AES_KEY()
    rc = set_key[encode](c_char_p(key), keylen * 8, byref(aes_key))
    if rc != 0:
        # I don't think we can get here...
        raise ValueError('Error generating AES key', rc)
    return aes_key

def aes_ecb(block, aes_key, encode):
    ''' Encrypt or decrypt a single block '''
    outbuff = create_string_buffer(AES_BLOCK_SIZE)
    AES_ecb_encrypt(c_char_p(block), outbuff, byref(aes_key), encode)
    return outbuff.raw

def PKCS7_pad(data):
    padsize = AES_BLOCK_SIZE - len(data) % AES_BLOCK_SIZE
    return data + bytes([padsize]) * padsize

def PKCS7_unpad(data):
    offset = data[-1]
    return data[:-offset]

def xor_bytes(a, b):
    size = len(a)
    a = int.from_bytes(a, 'big')
    b = int.from_bytes(b, 'big')
    return (a ^ b).to_bytes(size, 'big')

def aes_cbc_encode(data, key, iv):
    ekey = set_aes_key(key, ENCODE)

    data = PKCS7_pad(data)
    cipher = []
    for block in zip(*[iter(data)] * AES_BLOCK_SIZE):
        block = bytes(block)
        coded = aes_ecb(xor_bytes(iv, block), ekey, ENCODE)
        cipher.append(coded)
        iv = coded

    return b''.join(cipher)

def aes_cbc_decode(data, key, iv):
    dkey = set_aes_key(key, DECODE)

    plain = []
    for block in zip(*[iter(data)] * AES_BLOCK_SIZE):
        block = bytes(block)
        decoded = aes_ecb(block, dkey, DECODE)
        plain.append(xor_bytes(iv, decoded))
        iv = block

    plain[-1] = PKCS7_unpad(plain[-1])
    return b''.join(plain)

# Test

data = b'demo'
key = b64decode('HJkPmTz+uY7wd0p1+w//DABgbvPq9/230RwEG2sJ9mo=')
iv = b64decode('AAAAAAAAAAAAAAAAAAAAAA==')
cipher = aes_cbc_encode(data, key, iv)
out = b64encode(cipher)
print(out)

plain = aes_cbc_decode(cipher, key, iv)
print(plain)

输出

b'W2FEImF2qrAjaJ/LV+bgQA=='
b'demo'

【讨论】:

  • 非常感谢。我马上试试。
  • @user1318741 很高兴!
  • 很棒的代码,但使用pycryptodomecryptography 不是更简单/更好吗?还是不允许 OP 使用第三方库?
  • @t.m.adam 当然,通常人们会使用加密包来执行此操作,并且生成的代码会更紧凑且更易于阅读。但是无需安装 3rd 方包就可以很方便地做一些事情,而且正如我所说,我已经编写并测试了这些功能,所以我很容易将它们组合到上面的代码中。此外,我的代码清楚地显示了 CBC 是如何从 ECB 构建的,这很高兴知道。
  • 是的,我很欣赏(来自 ECB 的 CBC),尽管您可以在 ECB 模式下对 pycryptodome 的 AES 类做同样的事情。同样,代码看起来很棒,我只是想知道您为什么选择这种方法。
猜你喜欢
  • 2016-08-21
  • 2019-04-07
  • 2015-05-27
  • 1970-01-01
  • 2014-08-20
  • 2013-08-11
  • 2011-12-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多