我正在编写一段代码来使用对称加密来加密文本。但它没有以正确的结果回来......
from Crypto.Cipher import AES
import os
crypto = AES.new(os.urandom(32), AES.MODE_CTR, counter = lambda : os.urandom(16))
encrypted = crypto.encrypt("aaaaaaaaaaaaaaaa")
print crypto.decrypt(encrypted)
此处,解密文本与原始文本不同。
我对密码学的了解并不多,所以请耐心等待。我理解CTR模式需要一个“计数器”功能来每次提供一个随机计数器,但为什么当我的密钥是32字节时它需要它是16个字节并且它坚持我的消息也是16字节的倍数?这是正常的吗?
我猜它没有回到原始邮件,因为计数器在加密和解密之间发生了变化。但那么,它究竟应该在理论上如何运作呢?我究竟做错了什么?无论如何,我被迫回到欧洲央行,直到我弄明白:(
答案 0 :(得分:12)
counter
必须在解密时返回与加密时相同的内容,就像你直觉一样,因此,一个(不安全全部)的方式是:
>>> secret = os.urandom(16)
>>> crypto = AES.new(os.urandom(32), AES.MODE_CTR, counter=lambda: secret)
>>> encrypted = crypto.encrypt("aaaaaaaaaaaaaaaa")
>>> print crypto.decrypt(encrypted)
aaaaaaaaaaaaaaaa
点击率是一个块密码,因此看起来让您感到惊讶的“一次16个”约束非常自然。
当然,所谓的“计数器”会在每次调用is grossly insecure时返回相同的值。不需要做太多改善,例如......:
import array
class Secret(object):
def __init__(self, secret=None):
if secret is None: secret = os.urandom(16)
self.secret = secret
self.reset()
def counter(self):
for i, c in enumerate(self.current):
self.current[i] = c + 1
if self.current: break
return self.current.tostring()
def reset(self):
self.current = array.array('B', self.secret)
secret = Secret()
crypto = AES.new(os.urandom(32), AES.MODE_CTR, counter=secret.counter)
encrypted = crypto.encrypt(16*'a' + 16*'b' + 16*'c')
secret.reset()
print crypto.decrypt(encrypted)
答案 1 :(得分:4)
AES是block cipher:它是一种算法(更准确地说,是一对算法),它接受密钥和消息块,并加密或解密块。无论密钥大小如何,块的大小始终为16字节。
点击率是mode of operation。它是一对基于分组密码构建的算法,用于生成流密码,可以加密和解密任意长度的消息。
CTR通过将连续的消息块与计数器的连续值的加密相结合来工作。计数器的大小需要是一个块,因此它是块密码的有效输入。
compilation:
cloud_properties:
instance_type: m3.medium
availability_zone: us-east-1d
network: consul1
reuse_compilation_vms: true
workers: 6
director_uuid: $YOUR_ID
jobs:
- instances: 3
name: consul
networks:
- name: consul1
persistent_disk: 4096
properties:
consul:
join_host: 0.consul-z1.consul1.consul-aws.microbosh
services:
example: {}
networks:
apps: consul1
resource_pool: small_z1
templates:
- consumes:
consul_servers:
from: consul_leaders
name: consul
provides:
consul_servers:
as: consul_leaders
release: consul
update:
canaries: 0
max_in_flight: 50
name: consul-aws
networks:
- cloud_properties: {}
name: floating
type: vip
- cloud_properties:
subnet: $YOUR_SUBNET
security_groups:
- default
availability_zone: us-east-1d
name: consul1
type: dynamic
properties: {}
releases:
- name: consul
version: latest
resource_pools:
- cloud_properties:
instance_type: m3.medium
availability_zone: us-east-1d
name: small_z1
network: consul1
stemcell:
name: bosh-aws-xen-hvm-ubuntu-trusty-go_agent
version: latest
update:
canaries: 0
canary_watch_time: 1000-60000
max_in_flight: 50
serial: true
update_watch_time: 1000-60000
开始。但是如果密钥被多次使用,则第二条消息不允许重用第一条消息使用的任何计数器值,并且最简单的方法是确保随机生成初始计数器值(使用2 ^ 128)空间,碰撞的可能性可以忽略不计。)通过让呼叫者选择一个计数器功能,PyCrypto库可以为您提供足够的绳索。你应该使用'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
,而不仅仅是“为了更好的性能”,正如文档所说,但是因为它比你自己想象的更安全。即便如此,请注意使用随机初始值,这不是默认值。
Crypto.Util.Counter
答案 2 :(得分:2)
为什么当我的密钥是32字节
时需要16字节
它必须与密码的块大小相同。 CTR模式只是对计数器进行加密,并使用加密的计数器块对明文进行异或。
备注:强>
标准免责声明:加密很难。如果您不明白自己在做什么,将弄错。
我只想在会话中存储一些密码。
使用scrypt。 scrypt包含encrypt
和decrypt
,其中使用AES-CTR和密码派生密钥。
$ pip install scrypt
$ python
>>> import scrypt
>>> import getpass
>>> pw = getpass.getpass("enter password:")
enter password:
>>> encrypted = scrypt.encrypt("Guido is a space alien.",pw)
>>> out = scrypt.decrypt(encrypted,pw)
>>> out
'Guido is a space alien.'
答案 3 :(得分:1)
初始化向量(“计数器”)需要与加密和解密之间的密钥保持一致。它被用于使您可以对相同的文本进行一百万次编码,并且每次都获得不同的密文(防止一些已知的明文攻击和模式匹配/攻击)。解密时仍需要使用与加密时相同的IV。通常,当您开始解密流时,将IV初始化为与开始加密该流时开始时相同的值。
有关初始化向量的信息,请参阅http://en.wikipedia.org/wiki/Initialization_vector。
请注意,os.urandom(16)不是'确定性',这是计数器函数的要求。我建议你使用增量函数,因为这就是CTR模式的设计方式。初始计数器值应该是随机的,但连续值应该从初始值(确定性)完全可预测。甚至可以为您处理初始值(我不知道细节)
关于键,IV和输入大小,听起来您选择的密码的块大小为16个字节。你描述的一切都符合这一点,对我来说似乎很正常。
答案 4 :(得分:1)
我可能肯定迟到了,我可能忽略了以前的答案,但我没有找到一个明确的陈述,应该如何(至少恕我直言)根据PyCrypto包完成。
Crypto.Util.Counter包提供了可调用的有状态计数器,这非常有用,但至少对我来说很容易使用它们。
您必须创建一个计数器,例如ctr = Counter.new('parameters here')
。每次计数器模式密码对象调用计数器来加密消息时,它都会递增。这是良好的加密实践所必需的,否则有关相等块的信息可能会从密文泄漏。
现在你无法在同一个密码对象上调用解密函数,因为它会再次调用同一个计数器,同时计数器已经增加了几次。您需要做的是使用使用相同参数初始化的不同计数器创建新的密码对象。通过这种方式,解密工作正常,从加密完成的同一点开始计数。
下面的工作示例:
# Import modules
from Crypto.Cipher import AES
from Crypto import Random
from Crypto.Util import Counter
# Pad for short keys
pad = '# constant pad for short keys ##'
# Generate a random initialization vector, to be used by both encryptor and decryptor
# This may be sent in clear in a real communication
random_generator = Random.new()
IV = random_generator.read(8)
# Encryption steps
# Ask user for input and pad or truncate to a 32 bytes (256 bits) key
prompt = 'Input your key. It will padded or truncated at 32 bytes (256 bits).\n-: '
user_keye = raw_input(prompt)
keye = (user_keye + pad)[:32]
# Create counter for encryptor
ctr_e = Counter.new(64, prefix=IV)
# Create encryptor, ask for plaintext to encrypt, then encrypt and print ciphertext
encryptor = AES.new(keye, AES.MODE_CTR, counter=ctr_e)
plaintext = raw_input('Enter message to cipher: ')
ciphertext = encryptor.encrypt(plaintext)
print ciphertext
print
# Decryption steps
# Ask user for key: it must be equal to that used for encryption
prompt = 'Input your key. It will padded or truncated at 32 bytes (256 bits).\n-: '
user_keyd = raw_input(prompt)
keyd = (user_keyd + pad)[:32]
# Create counter for decryptor: it is equal to the encryptor, but restarts from the beginning
ctr_d = Counter.new(64, prefix=IV)
# Create decryptor, then decrypt and print decoded text
decryptor = AES.new(keyd, AES.MODE_CTR, counter=ctr_d)
decoded_text = decryptor.decrypt(ciphertext)
print decoded_text