pythonencryptioncaesar-ciphercryptdecrypt

How can find key while decrypting the ciphertext? (unknown key)


I want to decrypt the following classical Cipher-text without knowing key:

Cq ligss’v vcjandd ujw, wuqjwgausjkq cv ulxucdd zrj mhuouahj lbh nuvl upgoqlm rx mhfmllcyw cqxiueuwaiq kbdjyg ghoahh, xlre jhjmrfuo vuws nr fuwaiqsf vwwuwnv. Sm fqvhj nkjydlm ewwrey lfwuwuvahjds vgjkamwawdlyg, ulbhnryldhbb whdtfhk mdxy fggpmhluuwaiq, hlrlyflcqy yywlblblfa ijip ghoahh tuqccqy nushvswwaiqk uqv ghvcfsf uwwrjxv li sjcysnh uiqnyukuwaiqk. Cw hlrncgwm wzy eswntiqw zrj xdlu lfnhyllls dfx fghiaxhfnlsflls, hfmxjcqy nksn rffb ahwwhgwx uwwlhchfnv uuq swfwmv kyqkcwaph vuws uqv nksn ll lheulfm xfwkshjwx gmllfa wjuqkglkmlgh. Fjsslijjuszs qgn rffb kuiwaxslgk cqvcyaxxsfv’ hllnufq vxl uoki xfxhjjlfm jdiesf fggpwlfw, mhuouw wregxfcfsnlghv, shg fuwaiqsf vwwxjcwq, gdccqy cw ahgamswhvsvow cq s qrjfg lbdl lhdchk iq vcjandd nummw.

So far I have used different online-tools unsucessfully.

I inspired from this source using Pythonic code to reach:

import argparse

class CaesarAlgorithm:

    def encrypt(self, message, key, alphabet):

        # start with empty ciphertext
        ciphertext = ""

        # iterate through each character in message
        for old_character in message:
            new_character = ""

            # if character is in alphabet -> append to ciphertext
            if(old_character in alphabet):
                index = alphabet.index(old_character)
                new_index = (index + key) % len(alphabet)
                new_character = alphabet[new_index]

            # Note: characters not defined in the alphabet are ignored

            # add new character to ciphertext
            ciphertext = ciphertext + new_character

        # return ciphertext to calling function
        return ciphertext


    def decrypt(self, message, key, alphabet):

        # decrypting is like encrypting but with negative key
        plaintext = self.encrypt(message, 0 - key, alphabet)

        # return plaintext to calling function
        return plaintext


# parse the arguments (args) given via the command line
parser = argparse.ArgumentParser()
parser.add_argument("-e", "--encrypt", dest="encrypt_or_decrypt", action="store_true")
parser.add_argument("-d", "--decrypt", dest="encrypt_or_decrypt", action="store_false")
parser.add_argument("-m", "--message", help="message for encrypt / decrypt", type=str)
parser.add_argument("-k", "--key",     help="key for encrypt / decrypt", type=int)
parser.add_argument("-a", "--alphabet", help="defined alphabet", type=str)


ciphertext="Cq ligss’v vcjandd ujw, wuqjwgausjkq cv ulxucdd zrj mhuouahj lbh nuvl upgoqlm rx mhfmllcyw cqxiueuwaiq kbdjyg ghoahh, xlre jhjmrfuo vuws nr fuwaiqsf vwwuwnv. Sm fqvhj nkjydlm ewwrey lfwuwuvahjds vgjkamwawdlyg, ulbhnryldhbb whdtfhk mdxy fggpmhluuwaiq, hlrlyflcqy yywlblblfa ijip ghoahh tuqccqy nushvswwaiqk uqv ghvcfsf uwwrjxv li sjcysnh uiqnyukuwaiqk. Cw hlrncgwm wzy eswntiqw zrj xdlu lfnhyllls dfx fghiaxhfnlsflls, hfmxjcqy nksn rffb ahwwhgwx uwwlhchfnv uuq swfwmv kyqkcwaph vuws uqv nksn ll lheulfm xfwkshjwx gmllfa wjuqkglkmlgh. Fjsslijjuszs qgn rffb kuiwaxslgk cqvcyaxxsfv’ hllnufq vxl uoki xfxhjjlfm jdiesf fggpwlfw, mhuouw wregxfcfsnlghv, shg fuwaiqsf vwwxjcwq, gdccqy cw ahgamswhvsvow cq s qrjfg lbdl lhdchk iq vcjandd nummw."

# Simulate command-line arguments
# Replace with your desired values
args = parser.parse_args(["-d", "-m", ciphertext, "-k", "3", "-a", "abcdefghijklmnopqrstuvwxyz"])  

# create caesar instance
caesar = CaesarAlgorithm()

# if --encrypt -> call encrypt function
if(args.encrypt_or_decrypt == True):
    print(caesar.encrypt(args.message, args.key, args.alphabet))

# if --decrypt -> call decrypt function
else:
    print(caesar.decrypt(args.message, args.key, args.alphabet))

So far, I could not achieve Plain Text and find the identified key Even I have tried to use the following online tools and find the key mannually but I could not yet:


Solution

  • Your cipher text is not encoded with a Caesar Cipher, it is encoded with a Vigenere Cipher with the key uds.


    You can write a Vigenere Cipher:

    class VigenereCipher:
        @staticmethod
        def _key_iter(key):
            while True:
                for ch in key:
                    yield ch
    
        @staticmethod
        def _lookup_table(key, alphabet):
            if any(ch not in alphabet for ch in key):
                raise ValueError("Key should be in the alphabet")
            return {
                ch: alphabet[alphabet.index(ch):] + alphabet[:alphabet.index(ch)]
                for ch in key
            }
            
        @staticmethod
        def encode(message, key, alphabet="abcdefghijklmnopqrstuvwxyz"):
            lookup = VigenereCipher._lookup_table(key, alphabet)
            key_iter = VigenereCipher._key_iter(key)
            return "".join(
                lookup[next(key_iter)][alphabet.index(ch)]
                if ch in alphabet else ch
                for ch in message
            )
    
        @staticmethod
        def decode(message, key, alphabet="abcdefghijklmnopqrstuvwxyz"):
            lookup = VigenereCipher._lookup_table(key, alphabet)
            key_iter = VigenereCipher._key_iter(key)
            return "".join(
                alphabet[lookup[next(key_iter)].index(ch)]
                if ch in alphabet else ch
                for ch in message
            )
    

    Then if you know the key (and alphabet), you can decode the string:

    ciphertext="Cq ligss’v vcjandd ujw, wuqjwgausjkq cv ulxucdd zrj mhuouahj"
    print(VigenereCipher.decode(ciphertext.lower(), "uds"))
    

    Which outputs:

    in today’s digital age, cryptography is crucial for securing
    

    If you want to find an unknown key then you will have to:

    1. Generate all possible combinations of keys.
    2. Decode the text using the key.
    3. Perform some sort of analysis on the text to see if it makes sense.

    For example, if you know the key is length 3 and has no repeated characters then you can generate potential solutions using:

    from itertools import permutations
    
    common_two_letter_words = ("as", "to", "be", "in", "by", "is", "it", "at", "of", "or", "on", "an", "us", "if", "my", "do", "no", "he", "up", "so", "am", "me", "go", "hi")
    
    def has_vowel(word):
        return any(vowel in word for vowel in "aeiouy")
    
    for key in permutations(alphabet, 3):
        output = VigenereCipher.decode(ciphertext.lower(), key)
        if (
            output[:2] in common_two_letter_words
            and output[9] == "s"
            and all(has_vowel(word) for word in output.split(" "))
            and output[37:39] in common_two_letter_words
        ):
            print("".join(key), output)
    

    Which looks to see if the input decodes the two-letter words to common words and that the character after the apostrophe is an s and that each word contains at least one vowel and outputs:

    cdo an xgdeq’s hagmlap sgi, urchtsyrehhc as gjugaap xov kegmrmfg
    cds an tgdaq’s dagilal sge, uryhtoyrahhy as cjucaal xor kecmrifg
    cdy an ngduq’s xagclaf sgy, urshtiyruhhs as wjuwaaf xol kewmrcfg
    udc in jodqy’s tigytab agu, croptegrqpho is srusiab foh sesuryng
    ude in hodoy’s rigwtaz ags, crmptcgrophm is qruqiaz fof sequrwng
    udg in fodmy’s pigutax agq, crkptagrmphk is oruoiax fod seourung
    udi in dodky’s nigstav ago, criptygrkphi is mrumiav fob semursng
    udk in bodiy’s ligqtat agm, crgptwgriphg is krukiat foz sekurqng
    udm in zodgy’s jigotar agk, creptugrgphe is iruiiar fox seiurong
    udo in xodey’s higmtap agi, crcptsgrephc is grugiap fov segurmng
    udq in vodcy’s figktan agg, craptqgrcpha is erueian fot seeurkng
    uds in today’s digital age, cryptography is crucial for securing
    udw in podwy’s zigetah aga, cruptkgrwphu is yruyiah fon seyureng
    udy in noduy’s xigctaf agy, crsptigruphs is wruwiaf fol sewurcng
    

    The correct solution is in there and the other solutions are gibberish. With more analysis (and a dictionary of more and longer words) you can narrow down the solution to the correct key. That is left as an exercise for the reader.

    fiddle