【问题标题】:Making Vigenére encryption / decryption skip whitespace使 Vigenére 加密/解密跳过空格
【发布时间】:2020-02-04 00:30:15
【问题描述】:

我使用 Vigenére 密码制作了一个基于菜单的加密工具。截至目前该程序加密空格,我怎样才能让程序跳过空格。

#creating variables to be used
text_in_use = ''
encrypt_key = ''
decrypt_key = ''

#function to encypypt input text
def encrypt(plaintext, key):
    keyLength = len(key)
    keyAsIntegers = [ord(i) for i in key] #create list with the ASCII value for each charachter in key
    plaintextAsIntegers = [ord(i) for i in plaintext] #create list with the ASCII value for each charachter in text
    encyptedtext = '' 
    for i in range(len(plaintextAsIntegers)): #
        encryptvalue = (plaintextAsIntegers[i] + keyAsIntegers[i % keyLength]) % 26 #execute encryption or characters according to vigenere definition
        encyptedtext += chr(encryptvalue + 65)
    return encyptedtext #return the encyptes tex

#function to decrypt the encrypted text
def decrypt(encyptedtext, key):
    keyLength = len(key)
    keyAsIntegers = [ord(i) for i in key] #create list with the ASCII value for each charachter in key
    encryptedTextAsIntegers = [ord(i) for i in encyptedtext] #create list with the ASCII value for each charachter in text
    plaintext = ''
    for i in range(len(encryptedTextAsIntegers)):
        value = (encryptedTextAsIntegers[i] - keyAsIntegers[i % keyLength]) % 26 #decryption of encrypted characters
        plaintext += chr(value + 65)
    return plaintext #return decrypted text

#check if user input is valid
def check_value(userEntry):
    while True:
        try: #check if userinput is an integer
            userInput = int(input(userEntry))
            if userInput not in range(1,6): #check if userinput is in valid range
                print("Invalid choice, valid choices are 1-5! Try again! \n")
        except ValueError:
            print("Invalid choice! Input can't be empty or a string! \n")
            print("""1: Input text to work with
2: Print the current text
3: Encrypt the current text
4: Decrypt the current text
5: Exit""")
        else:
            return userInput #return valid userinput


def menu():
    while True:
        print("""1: Input text to work with
2: Print the current text
3: Encrypt the current text
4: Decrypt the current text
5: Exit""")

        choice = check_value("Enter Choice: ")

        if choice == 1: #allows user to input text for use
            text_in_use = str(input("Enter Text: ")).upper()
            print("Text is set to:",text_in_use,"\n")
        elif choice == 2: #prints set text
            print("Your text:",text_in_use,"\n") 
        elif choice == 3: #ask user to set encryptionkey
            encrypt_key = str(input("Enter an encryptionkey: ")).upper()
            text_in_use = encrypt(text_in_use, encrypt_key)
            print("Your text:", text_in_use)
        elif choice == 4: #ask user for decryptionkey
            decrypt_key = str(input("Enter a the decryptionkey: ")).upper()
            text_in_use = decrypt(text_in_use, decrypt_key)
            print("Your text:", text_in_use)
        elif choice == 5:
            exit()

menu()

我希望程序像它已经做的那样工作,但它应该跳过加密中的空格。

如:

"HELLO MY MAN" --> encryption(key = asd) --> "HWOLG MQ MSQ"

换句话说,空格应该仍然存在于加密文本中。

【问题讨论】:

  • 加密的全部目的是使密文看起来尽可能随机。如果你保留空间,它会更容易破解。无论如何,常规算法也会加密空格,因此您应该修改算法或单独加密每个单词
  • 这有一些骗局,但是这个措辞真的很好,而其他的似乎更糟糕的是代码错误/附加问题/更具体的代码特定问题,并且通常没有得到回答,所以工藤为此。但是,请特别注意标题正确,并在发布前查看您的帖子。

标签: python python-3.x encryption whitespace vigenere


【解决方案1】:

当明文为“HELLO MY MAN”且密钥为“asd”时,不确定如何获得“HWOLG MQ MSQ”。我得到了别的东西。

无论如何,可能是这样的:

def encrypt(plaintext, key):
    from itertools import cycle
    from string import ascii_uppercase as alphabet

    offset = ord("A")

    key_char = cycle(key)

    encrypted_plaintext = ""
    for char in plaintext:
        # If the current character is any kind of whitespace...
        if char.isspace():
            # Append it to the final string with no changes.
            encrypted_plaintext += char
        else:
            # The character is not whitespace, so you have to encrypt it.
            char_sum = ord(char) + ord(next(key_char))
            char_sum_wrapped = char_sum % len(alphabet)
            encrypted_plaintext += chr(char_sum_wrapped + offset)
    return encrypted_plaintext

如果当前字符是空格,只需将其附加到最终字符串而不做任何更改。 str.isspace 如果字符串中的每个字符(当前字符)都是某种空格(空格、制表符、换行符、回车符等),则返回 true。

我尽量避免使用不雅和硬编码的数字,所以我改变了一些东西。例如,我没有像您那样在做任何其他事情之前将明文和密钥中的所有字符都转换为整数,而是在循环中转换字符。顺便说一句,循环也不同——我正在遍历明文中的字符,而不是进行基于范围的 for 循环,然后将 i 视为当前字符的索引。其余基本相同(除了key_charitertools.cycle 的东西,请阅读下面我的笔记)。

要注意的另一件事是,使用此实现,key_char 迭代器仅在明文中的当前字符不是空格时才会前进 - 但是,您可能希望它无论如何都前进。只是需要记住的一点。

没关系,这似乎是这个密码的期望行为。

另外,请注意,您的程序以以下几行开头:

#creating variables to be used
text_in_use = ''
encrypt_key = ''
decrypt_key = ''

它们根本没有贡献,您可以安全地删除它们。

编辑 - 更多信息:

itertools.cycle 是一个函数,给定一个可迭代对象(如字符串或列表),它返回一个迭代器,该迭代器产生该可迭代对象中的元素。例如:

>>> from itertools import cycle
>>> char_iterator = cycle("ABC")
>>> next(char_iterator)
'A'
>>> next(char_iterator)
'B'
>>> next(char_iterator)
'C'
>>> next(char_iterator)
'A'
>>> next(char_iterator)
'B'
>>> next(char_iterator)
'C'
>>> next(char_iterator)
'A'
>>> 

如您所见,循环无限重复。为此,我选择使用itertools.cycle 来替换您原始代码中的keyAsIntegers[i % keyLength]

string.ascii_uppercase 只是一个由 A-Z 之间的所有大写字母组成的字符串。在我的代码中,我导入 ascii_uppercase 并在同一行中将其重命名为 alphabet,但它们是相同的。

>>> from string import ascii_uppercase as alphabet
>>> alphabet
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
>>> 

【讨论】:

  • 很好,我绝对喜欢里面的isspace()cycle。这使得代码更容易阅读,因为它解释了代码试图完成的what。当然,对于可能无法预期的初学者课程:)
  • 嗨,你似乎知道你在说什么!但是,您能否在代码中添加一些 cmet,因为我是初学者并且很难跟踪正在发生的事情,如上面的评论中所述:)
  • 感谢两位的反馈。我已经用更多信息更新了我的帖子。如果我能更清楚地解释其他任何事情,请告诉我。
【解决方案2】:

您可以忽略空格的加密方法,或重构代码以仅对实际单词运行加密,例如使用plaintext.split(' ') 获取要加密的单词列表,然后对每个项目运行加密/解密在列表中。

以下是在加密期间忽略空格的方法。请注意,此实现假定“跳过空白”意味着您通常仍会加密空白。空白的关键仍然是进步,这不是完全正确的行为。

def encrypt(plain_text, key):
    ...
    for i in range(len(plaintextAsIntegers)):
        if plain_text[i] == ' ':
            encryptedtext += ' '
        else:
            encryptvalue = (plaintextAsIntegers[i] + keyAsIntegers[i % keyLength]) % 26 
            encyptedtext += chr(encryptvalue + 65)

解密应该是相反的过程。

...
if encryptedtext[i] == ' ':
    plain_text += ' ':
else:
...

这使得加密更弱,因为可以根据它们的长度猜测哪些单词可能是哪个单词。将所有空格(包括制表符等)作为要加密的字符包含在内要好得多。

【讨论】:

  • 我已经投了赞成票,但是这个问题是不正确的,你需要保留一个单独的偏移量来指示要使用的键中的下一个字符。请修复。
  • @MaartenBodewes 很确定这就是他们用这段代码所做的,不是吗? keyAsIntegers[i % keyLength]...
  • 问题是i即使找到空格也会前进。这不是 Vigenére 通常认为的运作方式。密文应相同,有空格或无空格。至少应该在答案中注明这一点,但老实说,我认为当前的答案会被大多数学者认为是错误的。
  • 取决于您对“skip” tbh 的解释
  • @MaartenBodewes 已更新以包含假设等,感谢您指出这一点。