我需要什么类型的加密?

时间:2015-03-04 04:30:57

标签: encryption aes des encryption-symmetric

好的,原来的任务是跟踪2"友好"能够共享用户cookie的网站(比方说,我有example.com,我的朋友有mysite.com,他还有一个域simple.example.com,所以他可以在.example.com上设置cookie)。 / p>

要跟踪我们想要设置唯一Cookie的用户活动,此Cookie应该是唯一的并且长度为32个字节(ascii)。从这个角度来看非常简单,可以这样实现:

md5(microtime)

就是这样,但现在我们有新的约束

  1. 我们应该能够确定究竟是谁设置了cookie:exmaple.com引擎或mysite.com引擎

  2. 32个字节的长度是必须的,仍然是

  3. 我们应该能够加密时间戳(发布cookie时)

  4. 生成的cookie值的第一个和最后一个字符应该不同,所以我们可以根据cookie进行A / B测试(所以我们总是可以说,如果cookie的最后一个字符是"> K& #34;,显示此用户"功能A")

  5. 鉴于结果字符串的长度应始终为32个或更少,并且数据应加密和解密(当然不是用户),并且字符串对于用户应该是唯一的,这会使任务变得非常复杂。 p>

    我的想法和问题:

    • 我们应该使用对称密钥加密(解决约束 1和3),但在这种情况下我们如何确保结果字符串不超过32个字符(约束< / em> 2)?

    • 是否有其他解决问题的方法,因为我们需要加密的数据量是:时间戳和微秒(14字节),站点发布者标志(1个字节)=总共15个字节

    我的第一个想法是将数据打包成二进制字符串而不是base64编码。结果将是8-chars long base64-encoded string:

    def encode():
        base64( pack('Lv', timestamp, microseconds) )
    

    在开头和结尾添加site-issuer标志和字符:

    def getCookie():
        rand('a'...'Z') + encode() + issuerFlagChar() + rand('a'...'Z')
    

    因此,结果是11个字符长,我们很容易遇到约束2

    但问题是:这个算法肯定不安全,我不确定数百万网站用户的结果字符串是否是唯一的。

    我想知道我是否可以将DES或AES用于此目的,但我不确定结果字符串是否始终符合约束2 (结果字符串不应超过32个ascii字符)。

    是否存在对称密钥算法以确保类似&#34;如果使用M字节密钥加密N个字节,则结果数据长度为Math.Ceil(N * 2 + 1 / M)字节&#34;?结果长度是可以预测的吗?

2 个答案:

答案 0 :(得分:6)

撇开您确实应该咨询安全顾问这一事实,您可以轻松回答您提出的实际问题:

  

是否存在对称密钥算法,以确保“如果使用M字节密钥加密N个字节,那么结果数据长度为Math.Ceil(N * 2 + 1 / M)字节”?结果长度是可以预测的吗?

是的。它们被称为Block Ciphers

根据定义,每个块密码都具有以下属性:密文的长度等于纯文本的长度。实际上,大多数分组密码(包含DES和AES)都有些作弊,因为它们在开始加密之前需要明文为padded to the length of the block

换句话说,如果明文为N个字节且块大小为B,则密文的长度为B*(Math.ceil(N/B))个字节。

请注意我是如何讨论块大小的,它与密钥大小不同。在这种情况下,密钥大小实际上是无关紧要的。

例如,AES uses a block size of 128 bits或16个字节。这意味着如果您的纯文本长度在17到32个字节之间,AES将保证您的密文长度为32个字节。这与您选择的密钥大小无关,密钥大小可以是128,192或256位(16,24或32字节)之一。

答案 1 :(得分:3)

首先,您需要知道是否要加密签署数据。

加密会阻止用户查看数据,但他们仍然可以根据加密类型以某种方式对其进行修改。例如,解密修改后的密文只会提供损坏的数据,它不会失败。

另一方面,签名将阻止用户修改数据,也就是说,您的代码将能够检测到已修改的数据。一个简单的算法是HMAC

我假设你想要两者。我的解决方案同时做到了。

您的cookie必须是32个字节长,即256位。我们将使用128位加密数据和128位用于HMAC。

对于数据,我将时间戳编码为64位整数(即使您希望将其存储为微秒精度,也足够了)。如果您有两个站点,则发布cookie的站点可以存储为1位,但我会将它存储在32位整数中,因为我们有足够的空间。对于可用于a / b测试的标记也是如此。

所有数据都是128位,16个字节。这是AES块的确切大小。所以,我们将使用AES加密它!

其他16个字节将是密文的MAC(Encrypt then MAC)。我使用HMAC-SHA256,它有256位输出。我们只有128位的空间,所以我截断了它。从理论上讲,这使得安全性降低,但实际上128bit足以让蛮力企图无法实现。

解密cookie是相反的过程:计算给定密文的HMAC并检查它是否与给定的MAC匹配。如果是,则继续解密密文并解压缩数据。

以下是代码:

from struct import pack, unpack
from Crypto.Cipher import AES
import hashlib
import hmac


AES_KEY = hashlib.sha256(b"secret key 1 asdfasdf").digest()
HMAC_KEY = hashlib.sha256(b"secret key 2 asdfasdf").digest()

# timestamp: 64bit unix timestamp
# site: 32bit integer, which site issued the cookie
# tag: 32bit integer, tag used for a/b testing.
def encrypt_cookie(timestamp, site, tag):

    # Pack the data
    data = pack('QII', timestamp, site, tag)

    # Encrypt it
    aes = AES.new(AES_KEY, AES.MODE_ECB, 'This is an IV456')
    ciphertext = aes.encrypt(data)

    # Do HMAC of the ciphertext
    sig = hmac.new(HMAC_KEY, ciphertext, hashlib.sha256).digest()
    sig = sig[:16]   # Truncate to only first 16 bytes.

    return ciphertext + sig

def decrypt_cookie(cookie):

    # Do HMAC of the ciphertext
    sig = hmac.new(HMAC_KEY, cookie[:16], hashlib.sha256).digest()
    sig = sig[:16]   # Truncate to only first 16 bytes.

    # Check the HMAC is ok
    if sig != cookie[16:]:
        raise Exception("Cookie has been tampered with")

    # Decrypt it
    aes = AES.new(AES_KEY, AES.MODE_ECB, 'This is an IV456')
    data = aes.decrypt(cookie[:16])

    # unPack the data
    timestamp, site, tag = unpack('QII', data)

    return timestamp, site, tag

cookie = encrypt_cookie(1, 2, 3)
print(len(cookie))  # prints: 32
print(decrypt_cookie(cookie))  # prints: 1, 2, 3

# Change a single byte in the cookie, the last one
cookie = cookie[:31] + b'0'
print(decrypt_cookie(cookie))  # raises the exception

我很想知道为什么cookie必须是32字节。似乎有一个奇怪的要求,如果你没有,你可以使用许多旨在解决这个问题的库,例如Django signing如果你正在使用它Django的。