Encrypting/Decrypting a string using a Affine cipher using the original 128 ASCII table

≯℡__Kan透↙ 提交于 2019-12-11 03:58:45

问题


So I am trying to encrypt a message using an affine cipher. I want to use all 128 ASCII characters to do it. I am getting an error when it comes to particular letters, Q, R, S, T, and U. They are not converting back correctly and displaying the incorrect decryption. Any ideas what is going on?

Encrypted Message

RUX[^adgjmpsvy|

Decrypted Message

ABCDEFGHIJKLMNOP/.-,+VWXYZ

My code;

public class EncryptionProject {

   public static void main(String[] args) {

        StringBuilder s = new StringBuilder("ABCDEFGHIJKLMNOPQRSTUVWXYZ");

        System.out.println("Encrypted Message");
        StringBuilder encryptedMessage = affineEncryption(s);
        System.out.print(encryptedMessage);

        System.out.println("\nDecrypted Message");
        StringBuilder decryptedMessage = affineDecryption(encryptedMessage);
        System.out.print(decryptedMessage);
    }

    /* use affine cipher to encrypt message */
    public static StringBuilder affineEncryption(StringBuilder s) {
        StringBuilder encryption = new StringBuilder();
        final int A = 3, B = 15, M = 128; // a * p + b (mod m)

        /* loop through string and compute affine cipher */
        for (int i = 0; i < s.length(); i++) {
            encryption.append((char)((A * s.charAt(i) + B) % M));
        }

        return encryption;
    }

    /* decryption method */
    public static StringBuilder affineDecryption(StringBuilder s) {
        final int A = 43, B = 15, M = 128;
        StringBuilder decryption = new StringBuilder();

        /* loop through and undo affine cipher using inverse of A */
        for (int i = 0; i < s.length(); i++) {
            decryption.append((char)((A * Math.abs((s.charAt(i) - B))) % M));
        }

        return decryption;
    }
}

回答1:


The cause of the problem is that for negative dividends a and/or divisors n the mathematical definition of the modulo operation generally may differ from the definition in the respective programming language.

The result of the modulo-operation is mathematically defined as the remainder of the Euclidean division. That remainder is always larger than or equal to zero. For positive divisors n the remainder is given by:

a mod n = a - n * floor(a/n) 

where floor(a/n) is the floor-function which gives as output the greatest integer less than or equal to its input (I don't consider negative divisors n since n = 128 > 0 in the question).

Examples for a positive divisor n = 128 with a positive dividend a = 559 (above) and a negative dividend a = -559 (below):

559 mod 128 = 559 - 128 * floor(559/128) = 559 - 128 * floor(4.37) = 559 -128 * 4 = 47
-559 mod 128 = -559 - 128 * floor(-559/128) = -559 - 128 * floor(-4.37) = -559 -128 * (-5) = 81

However, in many programming languages (including Java) a different definition for the modulo-operation (sometimes referred to as symmetrical variant) is used:

a mod n = a - n * trunc(a/n) 

Here, trunc(a/n) means the truncated division where the quotient a/n is rounded towards zero.

Example for a positive divisor n = 128 with a positive dividend a = 559 (above) and a negative dividend a = -559 (below):

559 mod 128 = 559 - 128 * trunc(559/128) = 559 - 128 * trunc(4.37) = 559 -128 * 4 = 47      
-559 mod 128 = -559 - 128 * trunc(-559/128) = -559 - 128 * trunc(-4.37) = -559 -128 * (-4) = -47

As one can see, both definitions, the mathematical and the symmetrical, provide different results for negative dividends.

The immediate cause of the problem is that in the formula of the affine cipher, the mathematical definition is meant whereas in your code the symmetrical variant is used (because Java uses the symmetrical variant). This can be nicely demonstrated by the example of the letter Q: In your code the letter Q (= 81 dec) is encrypted (A = 3, B = 15, M = 128) to

(3 * 81 + 15) % 128 = 2

The decryption (A = 43, B = 15, M = 128) is

(43 * (2 - 15)) % 128 = -559 % 128

Depending on the modulo variant the result is -47 and 81 for the symmetrical and the mathematical variant, respectively (see above). Since the symmetrical variant is used in the code you get the "wrong" result -47.

Thus, the solution is to switch the modulo operation in your code to the mathematical definiton. This can be achieved by the following formula:

a mMod n  = ((a sMod n) + n) sMod n

where mMod and sMod denote the mathematical and symmetrical modulo-operator, respectively. For this, define a new method:

private static int mathematicalMod(int a, int n) {
    return ((a % n) + n) % n;
}

and replace in the affineEncryption-method

encryption.append((char)((A * s.charAt(i) + B) % M));

with

encryption.append((char)mathematicalMod(A * s.charAt(i) + B, M));

and in the affineDecryption-method

decryption.append((char)((A * Math.abs((s.charAt(i) - B))) % M));

with

decryption.append((char)mathematicalMod(A * (s.charAt(i) - B),  M));

Note, that in the latter replacement also the Math.abs-method is removed since it doesn't belong to the affine-cipher-decrypt-algorithm .

With those changes, and the following input

StringBuilder s = new StringBuilder("AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0=1!2\"34$5%6&7/8(9)^`+*#'-_.:,;<>\\[]~{}|@");

the output in the console becomes:

In Java there is also an implementation of the symmetrical variant: int Math.floorMod(int a, int n) which, of course, can also be used instead of the custom implementation int mathematicalMod(int a, int n).



来源:https://stackoverflow.com/questions/54583472/encrypting-decrypting-a-string-using-a-affine-cipher-using-the-original-128-asci

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!