问题
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