Cryptography Assignment(Ⅰ)


前言

本次作业共4个题目,分别为Week1 PA、PA1 option、cryptopals set 1、MTC3 SHA1-Hashed Passwords


一、Week1 PA

1.题目要求

Many Time Pad
让我们看看当一个流密码密钥被使用多次时会出现什么问题。下面是11个十六进制编码的密文,它们是用流密码对11个明文进行加密的结果,它们都使用相同的流密码密钥。您的目标是解密最后的密文,并将其中的秘密消息作为解决方案提交。
11个密文具体内容可在题目页看到。

2.解题思路

1.流加密的加密算法核心是按位异或操作,而如果要解密数据,只需要将密文与密钥串进行按位异或,即可得到原数据。
假设A、B两串明文数据,key为加密数据A、B所使用的密钥。加密后的数据A用EA表示(EA = A ⊕ key),加密后的数据B用EB表示(EB = B ⊕ key),符号⊕表示异或操作。
利用相同的值异或结果为0可以得出:
EA ⊕ EB = (A ⊕ key)⊕ (B ⊕ key)= A ⊕ B ⊕ (key ⊕ key)= A ⊕ B
此时只要知道任一明文串就可以对所有的明文串解密:
EA ⊕ EB ⊕ B = A ⊕ B ⊕ B = A
2.在ASCII表中,空格的16进制数0x20,字母A-Z的16进制数范围是0x41-0x5A,a-z的16进制数范围是0x61-0x7A,按位异或可以发现,一个大写字母与空格异或可以得到对应的小写字母;一个小写字母与空格异或可以得到对应大写字母。
3.基于以上两种特点,我们可以对11个密文两两做异或操作,判断不同明文中可能存在空格的位置,
EA ⊕ EB = (A ⊕key)⊕ (B ⊕ key)= A ⊕ B ⊕ (key ⊕ key)= A ⊕ B
当获取足够多的位置信息后,将对应位置上的密文和空格做异或操作,就可得到对应位置的密钥信息

3.代码

代码如下:

#密文为十六进制字符串,应该先将其处理
ciphertexts = [
    "315c4eeaa8b5f8aaf9174145bf43e1784b8fa00dc71d885a804e5ee9fa40b16349c146fb778cdf2d3aff021dfff5b403b510d0d0455468aeb98622b137dae857553ccd8883a7bc37520e06e515d22c954eba5025b8cc57ee59418ce7dc6bc41556bdb36bbca3e8774301fbcaa3b83b220809560987815f65286764703de0f3d524400a19b159610b11ef3e",
    "234c02ecbbfbafa3ed18510abd11fa724fcda2018a1a8342cf064bbde548b12b07df44ba7191d9606ef4081ffde5ad46a5069d9f7f543bedb9c861bf29c7e205132eda9382b0bc2c5c4b45f919cf3a9f1cb74151f6d551f4480c82b2cb24cc5b028aa76eb7b4ab24171ab3cdadb8356f",
    "32510ba9a7b2bba9b8005d43a304b5714cc0bb0c8a34884dd91304b8ad40b62b07df44ba6e9d8a2368e51d04e0e7b207b70b9b8261112bacb6c866a232dfe257527dc29398f5f3251a0d47e503c66e935de81230b59b7afb5f41afa8d661cb",
    "32510ba9aab2a8a4fd06414fb517b5605cc0aa0dc91a8908c2064ba8ad5ea06a029056f47a8ad3306ef5021eafe1ac01a81197847a5c68a1b78769a37bc8f4575432c198ccb4ef63590256e305cd3a9544ee4160ead45aef520489e7da7d835402bca670bda8eb775200b8dabbba246b130f040d8ec6447e2c767f3d30ed81ea2e4c1404e1315a1010e7229be6636aaa",
    "3f561ba9adb4b6ebec54424ba317b564418fac0dd35f8c08d31a1fe9e24fe56808c213f17c81d9607cee021dafe1e001b21ade877a5e68bea88d61b93ac5ee0d562e8e9582f5ef375f0a4ae20ed86e935de81230b59b73fb4302cd95d770c65b40aaa065f2a5e33a5a0bb5dcaba43722130f042f8ec85b7c2070",
    "32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd2061bbde24eb76a19d84aba34d8de287be84d07e7e9a30ee714979c7e1123a8bd9822a33ecaf512472e8e8f8db3f9635c1949e640c621854eba0d79eccf52ff111284b4cc61d11902aebc66f2b2e436434eacc0aba938220b084800c2ca4e693522643573b2c4ce35050b0cf774201f0fe52ac9f26d71b6cf61a711cc229f77ace7aa88a2f19983122b11be87a59c355d25f8e4",
    "32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd90f1fa6ea5ba47b01c909ba764896cf606ef40c04afe1ac0aa81dd066592ded9f8774b529c7ea125d298e8883f5e9305f4b44f915cb2bd05af51373fd9b4af511039fa2d96f83414aaaf261bda2e97b170fb5cce2a53e675c154c0d9681596934777e2275b381ce2e40582afe67650b13e72287ff2270abcf73bb028932836fbdecfecee0a3b894473c1bbeb6b4913a536ce4f9b13f1efff71ea313c8661dd9a4ce",
    "315c4eeaa8b5f8bffd11155ea506b56041c6a00c8a08854dd21a4bbde54ce56801d943ba708b8a3574f40c00fff9e00fa1439fd0654327a3bfc860b92f89ee04132ecb9298f5fd2d5e4b45e40ecc3b9d59e9417df7c95bba410e9aa2ca24c5474da2f276baa3ac325918b2daada43d6712150441c2e04f6565517f317da9d3",
    "271946f9bbb2aeadec111841a81abc300ecaa01bd8069d5cc91005e9fe4aad6e04d513e96d99de2569bc5e50eeeca709b50a8a987f4264edb6896fb537d0a716132ddc938fb0f836480e06ed0fcd6e9759f40462f9cf57f4564186a2c1778f1543efa270bda5e933421cbe88a4a52222190f471e9bd15f652b653b7071aec59a2705081ffe72651d08f822c9ed6d76e48b63ab15d0208573a7eef027",
    "466d06ece998b7a2fb1d464fed2ced7641ddaa3cc31c9941cf110abbf409ed39598005b3399ccfafb61d0315fca0a314be138a9f32503bedac8067f03adbf3575c3b8edc9ba7f537530541ab0f9f3cd04ff50d66f1d559ba520e89a2cb2a83",
    "32510ba9babebbbefd001547a810e67149caee11d945cd7fc81a05e9f85aac650e9052ba6a8cd8257bf14d13e6f0a803b54fde9e77472dbff89d71b57bddef121336cb85ccb8f3315f4b52e301d16e9f52f904"
]

NUM_CIPHER = len(ciphertexts)#NUM_CIPHER=11
THRESHOLD_VALUE = 7#如果两两异或的结果为字母数大于7次,就认为该字符为空格,该值更改会影响最终结果

def strxor(a, b):
    #两个字符串的异或
    if len(a) > len(b):
        # 形成二元组,异或,返回新字符串
        return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a[:len(b)], b)])
    else:
        return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b[:len(a)])])

def letter_position(s):
    #返回给定字符串中给定字母的位置
    position = []
    for idx in range(len(s)):
        #考虑到空格异或为0的情况可能较多
        if (s[idx] >= 'A' and s[idx] <= 'Z') or (s[idx] >= 'a' and s[idx] <= 'z') or s[idx] == chr(0):
            position.append(idx)
    return position

def find_space(cipher):
    #寻找空字符
    space_position = {}
    space_possible = {}
    #双重循环,每一条密文均与其他密文异或
    for cipher_idx_1 in range(NUM_CIPHER):
        space_xor = []#用于保存可能空格符对应的位置
        c = ''.join([chr(int(d, 16)) for d in [cipher[cipher_idx_1][i:i + 2] for i in range(0, len(cipher[cipher_idx_1]), 2)]])
        for cipher_idx_2 in range(NUM_CIPHER):
            #将十六进制字符串处理成对应ascii字符(每两个字符代表一个ascii符号)
            e = ''.join([chr(int(d, 16)) for d in [cipher[cipher_idx_2][i:i+2] for i in range(0, len(cipher[cipher_idx_2]), 2)]])
            plain_xor = strxor(c, e)
            if cipher_idx_2 != cipher_idx_1:
                # 记录明文中可能空格符的位置
                space_xor.append(letter_position(plain_xor))
        space_possible[cipher_idx_1] = space_xor  #形成三维列表,新列表为11*10*n

    #进一步判断已记录的位置是否为空字符,其准确性受到文本数量的影响
    for cipher_idx_1 in range(NUM_CIPHER):
        spa = []
        for position in range(400):
            count = 0
            for cipher_idx_2 in range(NUM_CIPHER - 1):
                if position in space_possible[cipher_idx_1][cipher_idx_2]:
                    count += 1
            if count > THRESHOLD_VALUE:  # 如果异或后字母出现次数大于7次,认为明文中此处为空格
                spa.append(position)
        space_position[cipher_idx_1] = spa  #构成二维列表,11 * n
    return space_position

#计算获得对应密钥Key
def calculate_key(cipher):
    key = [0] * 200  #存放key
    space = find_space(cipher)
    #print(space)
    for cipher_idx_1 in range(NUM_CIPHER):
        for position in range(len(space[cipher_idx_1])):
            idx = space[cipher_idx_1][position] * 2 #十六进制,用2位表示
            a = cipher[cipher_idx_1][idx] + cipher[cipher_idx_1][idx + 1]
            key[space[cipher_idx_1][position]] = int(a ,16) ^ ord(' ') # 计算密钥,获得结果十进制(ascii码)

    key_str = ""#空串用于存放密钥
    for k in key:
        key_str += chr(k)#转化为
    return key_str  #获得密钥串

result = ""
key = calculate_key(ciphertexts)
key_hex = ''.join([hex(ord(c)).replace('0x', '') for c in key])#十六进制key
print("key=",key)
print("key_hex=",key_hex)

f = ''.join([chr(int(d, 16)) for d in [ciphertexts[10][i:i+2] for i in range(0, len(ciphertexts[10]), 2)]])
for letter in strxor(f,key):
         if (letter>=' ' and letter<='~ '):#打印从32-126的可见字符
             result+=letter
         else:
             result+='0'#不可打印字符用0代替,以区别空格符
print(result)


#打印所有密文对应的明文
for j in range(11):
    f = ''.join([chr(int(d, 16)) for d in [ciphertexts[j][i:i + 2] for i in range(0, len(ciphertexts[j]), 2)]])
    for letter in strxor(f,key):
         if (letter>=' ' and letter<='~ '):
             result+=letter
         else:
             result+='0'
    print(result)
    result = '' #将result清空,再次使用

4.运行结果

在这里插入图片描述


二、PA1 option

1.题目要求

Write a program that allows you to “crack” ciphertexts generated using a Vigenere-like cipher, where byte-wise XOR is used instead of addition modulo 26.
编写一个程序, 破解使用类似Vigenere密码加密的密文,其中使用逐字节的异或而不是模26加法。

Ciphertext:
F96DE8C227A259C87EE1DA2AED57C93FE5DA36ED4EC87EF2C63AAE5B9A7EFFD673BE4ACF7BE8923CAB1ECE7AF2DA3DA44FCF7AE29235A24C963FF0DF3CA3599A70E5DA36BF1ECE77F8DC34BE129A6CF4D126BF5B9A7CFEDF3EB850D37CF0C63AA2509A76FF9227A55B9A6FE3D720A850D97AB1DD35ED5FCE6BF0D138A84CC931B1F121B44ECE70F6C032BD56C33FF9D320ED5CDF7AFF9226BE5BDE3FF7DD21ED56CF71F5C036A94D963FF8D473A351CE3FE5DA3CB84DDB71F5C17FED51DC3FE8D732BF4D963FF3C727ED4AC87EF5DB27A451D47EFD9230BF47CA6BFEC12ABE4ADF72E29224A84CDF3FF5D720A459D47AF59232A35A9A7AE7D33FB85FCE7AF5923AA31EDB3FF7D33ABF52C33FF0D673A551D93FFCD33DA35BC831B1F43CBF1EDF67F0DF23A15B963FE5DA36ED68D378F4DC36BF5B9A7AFFD121B44ECE76FEDC73BE5DD27AFCD773BA5FC93FE5DA3CB859D26BB1C63CED5CDF3FE2D730B84CDF3FF7DD21ED5ADF7CF0D636BE1EDB79E5D721ED57CE3FE6D320ED57D469F4DC27A85A963FF3C727ED49DF3FFFDD24ED55D470E69E73AC50DE3FE5DA3ABE1EDF67F4C030A44DDF3FF5D73EA250C96BE3D327A84D963FE5DA32B91ED36BB1D132A31ED87AB1D021A255DF71B1C436BF479A7AF0C13AA14794

2.解题思路

1.枚举密钥长度(从1到13)
2.将十六进制解码后的密文按密钥长度分块,取出每一块中的第一个字符作为第一组,取出每块中的第二个字符作为第二组,以次类推。
(根据Vigenere加密的特点可以知道,第一组中的字符是由密钥的第一个字符加密得到的,第二组中的字符是由密钥的第二个字符加密得到的……)
3.从0-255穷举当前密钥长度下密钥中的每一个字符,例如:将这个字符与第一组的字符依次异或,直到异或出来的结果都是大小写字母、标点符号和空格,记录这个字符,作为密钥的第一个字符,以此类推去穷举第二个、第三个字符……
4.如果在当前密钥长度下,能找出满足明文要求的密钥的所有字符,则可能找到了正确的密钥长度和密钥。若找不全密钥的所有字符,则说明当前密钥长度错误,将密钥长度加一,再次返回第2步。
5.用密钥与密文异或解密出原文

3.代码

代码如下:

import string

# 使用穷举密钥法
# 1.选择密钥长度
# 2.确定密钥中每个字符的值(0x00-0xFF)
ciphertext = 'F96DE8C227A259C87EE1DA2AED57C93FE5DA36ED4EC87EF2C63AAE5B9A\
7EFFD673BE4ACF7BE8923CAB1ECE7AF2DA3DA44FCF7AE29235A24C963FF0DF3CA3599A\
70E5DA36BF1ECE77F8DC34BE129A6CF4D126BF5B9A7CFEDF3EB850D37CF0C63AA2509A\
76FF9227A55B9A6FE3D720A850D97AB1DD35ED5FCE6BF0D138A84CC931B1F121B44ECE\
70F6C032BD56C33FF9D320ED5CDF7AFF9226BE5BDE3FF7DD21ED56CF71F5C036A94D96\
3FF8D473A351CE3FE5DA3CB84DDB71F5C17FED51DC3FE8D732BF4D963FF3C727ED4AC8\
7EF5DB27A451D47EFD9230BF47CA6BFEC12ABE4ADF72E29224A84CDF3FF5D720A459D4\
7AF59232A35A9A7AE7D33FB85FCE7AF5923AA31EDB3FF7D33ABF52C33FF0D673A551D9\
3FFCD33DA35BC831B1F43CBF1EDF67F0DF23A15B963FE5DA36ED68D378F4DC36BF5B9A\
7AFFD121B44ECE76FEDC73BE5DD27AFCD773BA5FC93FE5DA3CB859D26BB1C63CED5CDF\
3FE2D730B84CDF3FF7DD21ED5ADF7CF0D636BE1EDB79E5D721ED57CE3FE6D320ED57D4\
69F4DC27A85A963FF3C727ED49DF3FFFDD24ED55D470E69E73AC50DE3FE5DA3ABE1EDF\
67F4C030A44DDF3FF5D73EA250C96BE3D327A84D963FE5DA32B91ED36BB1D132A31ED8\
7AB1D021A255DF71B1C436BF479A7AF0C13AA14794'


# 密文为16进制编码 密文中两个十六进制数对应一个字符
def hexDecode(text):
    cList = []
    for i in range(0, len(text), 2):
        cList.append(int(text[i:2 + i], 16))
    return cList


# 从0x00-0xFF验证每一位密钥
def keyfind(strGroup):
    # 由题目得知明文中包含大小写字母、标点符号和空格,但不包含数字
    possibleChars = string.ascii_letters + ',' + '.' + ' '
    testKeys = []
    trueKeys = []
    for i in range(0x00, 0xFF):
        testKeys.append(i)
        trueKeys.append(i)
    for i in testKeys:
        for j in strGroup:
            if chr(i ^ j) not in possibleChars:
                trueKeys.remove(i)
                break
    return trueKeys


# 密文中两个十六进制数对应一个字符
cipherList = hexDecode(ciphertext)
# 枚举密钥长度从1-13位
trueKeylen = 0
truevigenerekey = []
for keyLen in range(1, 14):
    vigenerelikeKey = []
    for i in range(0, keyLen):
        # 每隔一个keyLen取一个字符保存在strGroup中,strGroup中的所有字符都是由同一个密钥字符加密的
        strGroup = cipherList[i::keyLen]
        ansKeys = keyfind(strGroup)
        if not ansKeys:
            break
        else:
            vigenerelikeKey.insert(i, ansKeys)
    if vigenerelikeKey:
        trueKeylen = keyLen
        truevigenerekey = vigenerelikeKey
        print('枚举出的密钥长度为', keyLen)
        print('密钥为(十进制ASCII码表示)', vigenerelikeKey)

# 用密钥解密密文
plaintext = ''
for i in range(0, len(cipherList)):
    plaintext = plaintext + chr(cipherList[i] ^ truevigenerekey[i % trueKeylen][0])
print('解密后的明文为', plaintext)

4.运行结果

在这里插入图片描述


三、cryptopals set 1

1.题目要求

给出了一个文件,该文件首先经过repeating-key XOR,然后base64编码得到。要求我们给出密钥和明文。
文件内容如下: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

2.解题思路

题目中给出了提示:
1.确定KEYSIZE,也就是密钥的长度,比如说可以尝试从2到40的值
2.实现一个函数,该函数功能是计算两个字符串的汉明距
3.每次尝试KEYSIZE时,分别计算两个长度为KEYSIZE的字节的串,计算其汉明距
4.选取2-3个最小汉明距离的KEYSIZE(如果选取了正确的密钥长度,两两块之间的汉明距离的值应该趋于小),在确定时可以采用一些技巧,如选取4个块,两两组合计算汉明距,再除以组合数,再除以key_size,进行规格化。
5.知道KEYSIZE大小后,将密文按照KEYSIZE大小分到每个块里面
6.然后可以重新组合,将每个块的第一个字节取出来重新组成一个块,将每个块的第二个字节取出来重新组成第二个块。。
7.对于新组成的块而言,此时解密就相当于是单字符的异或,我们之前已经学过怎么处理这种情况了
8.将每个单字符异或的key组合起来就是我们要求的repeating-key XOR的key了,将明文拼接起来就是所求的完整明文了

3.代码

代码如下:

import base64
import itertools

# 字母频率表
charfreq = {
    'a': 0.0651738, 'b': 0.0124248, 'c': 0.0217339,
    'd': 0.0349835, 'e': 0.1041442, 'f': 0.0197881,
    'g': 0.0158610, 'h': 0.0492888, 'i': 0.0558094,
    'j': 0.0009033, 'k': 0.0050529, 'l': 0.0331490,
    'm': 0.0202124, 'n': 0.0564513, 'o': 0.0596302,
    'p': 0.0137645, 'q': 0.0008606, 'r': 0.0497563,
    's': 0.0515760, 't': 0.0729357, 'u': 0.0225134,
    'v': 0.0082903, 'w': 0.0171272, 'x': 0.0013692,
    'y': 0.0145984, 'z': 0.0007836, ' ': 0.1918182
}

# 计算文本的分值:该分值是所有在输入字符串中出现的字符对应在字符频率表中的频率相加
def engScore(tbytes):
    score = 0
    for byte in tbytes:
        score += charfreq.get(chr(byte).lower(), 0)
    return score

# 字符串与密钥(同一个)异或函数
def charXor(tbytes, keyvalue):
    output = b''
    for char in tbytes:
        output += bytes([char ^ keyvalue])
    return output

# 密钥从0-255暴力破解函数
def singlecharXor(ciphertext):
    candidates = []
    for keyCandidates in range(256):
        plainCandidates = charXor(ciphertext, keyCandidates)
        candidateScore = engScore(plainCandidates)
        result = {
            'key': keyCandidates,
            'score': candidateScore,
            'plaintext': plainCandidates
        }
        candidates.append(result)
    return sorted(candidates, key=lambda c: c['score'], reverse=True)[0]

# 字符串与重复密钥异或函数
def repeatkeyXor(tbytes, key):
    output = b''
    i = 0
    for byte in tbytes:
        output += bytes([byte ^ key[i]])
        i = i + 1 if i < len(key) - 1 else 0
    return output

# 计算两个字符串的hamming距离
def hammingDistance(str1, str2):
    assert len(str1) == len(str2)
    dist = 0
    for x, y in zip(str1, str2):
        b = x ^ y  # 转换为二进制(以字符串形式表示,如“0b100000”,0b表示二进制)
        dist += sum([1 for bit in bin(b) if bit == '1'])
    return dist

def breakrepeatingkeyXor(binarydata):
    normalizedDistances = {}
    for keySize in range(2, 41):
        # 取出根据keySize划分出的4组数据
        group = [binarydata[i:i + keySize] for i in range(0, len(binarydata), keySize)][:4]
        distance = 0
        # 使用迭代器itertools中的combinations对4组中进行两两任意组合
        pairs = itertools.combinations(group, 2)
        for (x, y) in pairs:
            distance += hammingDistance(x, y)
        distance /= 6
        normalizedDistance = distance / keySize
        normalizedDistances[keySize] = normalizedDistance
    possiblekeySizes = sorted(normalizedDistances, key=normalizedDistances.get)[:3]
    print(possiblekeySizes)

    possiblePlaintexts = []
    for d in possiblekeySizes:
        key = b''
        for i in range(d):
            block = b''
            for j in range(i, len(binarydata), d):
                block += bytes([binarydata[j]])
            key += bytes([singlecharXor(block)['key']])
        possiblePlaintexts.append((repeatkeyXor(binarydata, key), key))
    return max(possiblePlaintexts, key=lambda k: engScore(k[0]))

def main():
    with open("ctext.txt") as fp:
        data = base64.b64decode(fp.read())
    result = breakrepeatingkeyXor(data)
    print("key = ", result[1].decode())
    print("长度= ", len(result[1].decode()))
    print(result[0].decode().rstrip())

if __name__ == "__main__":
    main()

4.运行结果

在这里插入图片描述


四、MTC3 SHA1-Hashed Passwords:

1.题目要求

根据键盘上的密码输入情况来破解密码,密码的hash值为67ae1a64661ac8b4494666f58c4822408dd0a3e4
在这里插入图片描述
在这里插入图片描述

2.解题思路

根据键盘上的按键分布,可以看出右边的数字键只有2486,很有可能是当做上下左右的功能,然后对剩余的按照每个按键出现1次进行暴力,发现能得出解。

3.代码

代码如下:

import hashlib
import itertools
import datetime

start = datetime.datetime.now()
SHA1hash = "67ae1a64661ac8b4494666f58c4822408dd0a3e4"
testStr = [['Q', 'q'], ['W', 'w'], ['5', '%'], ['8', '('], ['=', '0'], ['I', 'i'], ['*', '+'], ['n', 'N']]


def sha1Encrypt(str):
    sha = hashlib.sha1(str.encode())
    encrypts = sha.hexdigest()
    return encrypts

#按照每个键按了一次进行暴力破解
liststr = "0" * 8
tempstr = ""
testPassword = list(liststr)
for a in range(0, 2):
    testPassword[0] = testStr[0][a]
    for b in range(0, 2):
        testPassword[1] = testStr[1][b]
        for c in range(0, 2):
            testPassword[2] = testStr[2][c]
            for d in range(0, 2):
                testPassword[3] = testStr[3][d]
                for e in range(0, 2):
                    testPassword[4] = testStr[4][e]
                    for f in range(0, 2):
                        testPassword[5] = testStr[5][f]
                        for g in range(0, 2):
                            testPassword[6] = testStr[6][g]
                            for h in range(0, 2):
                                testPassword[7] = testStr[7][h]
                                temp = "".join(testPassword)
                                for i in itertools.permutations(temp, 8):
                                    tempPw="".join(i)
                                    tempstr = sha1Encrypt(tempPw)
                                    if tempstr == SHA1hash:
                                        print("密码为:",tempPw)
                                        end = datetime.datetime.now()
                                        print("运行时间为:",end - start)
                                        exit(0)


4.运行结果

在这里插入图片描述


总结

通过这次实验学习了很多与流密码相关的攻击方式,当我们实际需要应用到流密码时,不应该自己去实现,而是直接使用库中的流密码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值