|
1 | 1 | package com.thealgorithms.ciphers;
|
2 | 2 |
|
3 |
| -import java.util.Scanner; |
| 3 | +public class HillCipher { |
4 | 4 |
|
5 |
| -/* |
6 |
| - * Java Implementation of Hill Cipher |
7 |
| - * Hill cipher is a polyalphabetic substitution cipher. Each letter is represented by a number |
8 |
| - * belonging to the set Z26 where A=0 , B=1, ..... Z=25. To encrypt a message, each block of n |
9 |
| - * letters (since matrix size is n x n) is multiplied by an invertible n × n matrix, against |
10 |
| - * modulus 26. To decrypt the message, each block is multiplied by the inverse of the matrix used |
11 |
| - * for encryption. The cipher key and plaintext/ciphertext are user inputs. |
12 |
| - * @author Ojasva Jain |
13 |
| - */ |
14 |
| -public final class HillCipher { |
15 |
| - private HillCipher() { |
16 |
| - } |
17 |
| - |
18 |
| - static Scanner userInput = new Scanner(System.in); |
19 |
| - |
20 |
| - /* Following function encrypts the message |
21 |
| - */ |
22 |
| - static void encrypt(String message) { |
23 |
| - message = message.toUpperCase(); |
24 |
| - // Get key matrix |
25 |
| - System.out.println("Enter key matrix size"); |
26 |
| - int matrixSize = userInput.nextInt(); |
27 |
| - System.out.println("Enter Key/encryptionKey matrix "); |
28 |
| - int[][] keyMatrix = new int[matrixSize][matrixSize]; |
29 |
| - for (int i = 0; i < matrixSize; i++) { |
30 |
| - for (int j = 0; j < matrixSize; j++) { |
31 |
| - keyMatrix[i][j] = userInput.nextInt(); |
32 |
| - } |
33 |
| - } |
34 |
| - // check if det = 0 |
| 5 | + // Encrypts the message using the key matrix |
| 6 | + public String encrypt(String message, int[][] keyMatrix) { |
| 7 | + message = message.toUpperCase().replaceAll("[^A-Z]", ""); |
| 8 | + int matrixSize = keyMatrix.length; |
35 | 9 | validateDeterminant(keyMatrix, matrixSize);
|
36 | 10 |
|
37 |
| - int[][] messageVector = new int[matrixSize][1]; |
38 |
| - String cipherText = ""; |
39 |
| - int[][] cipherMatrix = new int[matrixSize][1]; |
40 |
| - int j = 0; |
41 |
| - while (j < message.length()) { |
| 11 | + StringBuilder cipherText = new StringBuilder(); |
| 12 | + int[] messageVector = new int[matrixSize]; |
| 13 | + int[] cipherVector = new int[matrixSize]; |
| 14 | + int index = 0; |
| 15 | + |
| 16 | + while (index < message.length()) { |
42 | 17 | for (int i = 0; i < matrixSize; i++) {
|
43 |
| - if (j >= message.length()) { |
44 |
| - messageVector[i][0] = 23; |
| 18 | + if (index < message.length()) { |
| 19 | + messageVector[i] = message.charAt(index++) - 'A'; |
45 | 20 | } else {
|
46 |
| - messageVector[i][0] = (message.charAt(j)) % 65; |
| 21 | + messageVector[i] = 'X' - 'A'; // Padding with 'X' if needed |
47 | 22 | }
|
48 |
| - System.out.println(messageVector[i][0]); |
49 |
| - j++; |
50 | 23 | }
|
51 |
| - int x; |
52 |
| - int i; |
53 |
| - for (i = 0; i < matrixSize; i++) { |
54 |
| - cipherMatrix[i][0] = 0; |
55 | 24 |
|
56 |
| - for (x = 0; x < matrixSize; x++) { |
57 |
| - cipherMatrix[i][0] += keyMatrix[i][x] * messageVector[x][0]; |
| 25 | + for (int i = 0; i < matrixSize; i++) { |
| 26 | + cipherVector[i] = 0; |
| 27 | + for (int j = 0; j < matrixSize; j++) { |
| 28 | + cipherVector[i] += keyMatrix[i][j] * messageVector[j]; |
58 | 29 | }
|
59 |
| - System.out.println(cipherMatrix[i][0]); |
60 |
| - cipherMatrix[i][0] = cipherMatrix[i][0] % 26; |
61 |
| - } |
62 |
| - for (i = 0; i < matrixSize; i++) { |
63 |
| - cipherText += (char) (cipherMatrix[i][0] + 65); |
| 30 | + cipherVector[i] = cipherVector[i] % 26; |
| 31 | + cipherText.append((char) (cipherVector[i] + 'A')); |
64 | 32 | }
|
65 | 33 | }
|
66 |
| - System.out.println("Ciphertext: " + cipherText); |
| 34 | + |
| 35 | + return cipherText.toString(); |
67 | 36 | }
|
68 | 37 |
|
69 |
| - // Following function decrypts a message |
70 |
| - static void decrypt(String message) { |
71 |
| - message = message.toUpperCase(); |
72 |
| - // Get key matrix |
73 |
| - System.out.println("Enter key matrix size"); |
74 |
| - int n = userInput.nextInt(); |
75 |
| - System.out.println("Enter inverseKey/decryptionKey matrix "); |
76 |
| - int[][] keyMatrix = new int[n][n]; |
77 |
| - for (int i = 0; i < n; i++) { |
78 |
| - for (int j = 0; j < n; j++) { |
79 |
| - keyMatrix[i][j] = userInput.nextInt(); |
80 |
| - } |
81 |
| - } |
82 |
| - // check if det = 0 |
83 |
| - validateDeterminant(keyMatrix, n); |
| 38 | + // Decrypts the message using the inverse key matrix |
| 39 | + public String decrypt(String message, int[][] inverseKeyMatrix) { |
| 40 | + message = message.toUpperCase().replaceAll("[^A-Z]", ""); |
| 41 | + int matrixSize = inverseKeyMatrix.length; |
| 42 | + validateDeterminant(inverseKeyMatrix, matrixSize); |
| 43 | + |
| 44 | + StringBuilder plainText = new StringBuilder(); |
| 45 | + int[] messageVector = new int[matrixSize]; |
| 46 | + int[] plainVector = new int[matrixSize]; |
| 47 | + int index = 0; |
84 | 48 |
|
85 |
| - // solving for the required plaintext message |
86 |
| - int[][] messageVector = new int[n][1]; |
87 |
| - String plainText = ""; |
88 |
| - int[][] plainMatrix = new int[n][1]; |
89 |
| - int j = 0; |
90 |
| - while (j < message.length()) { |
91 |
| - for (int i = 0; i < n; i++) { |
92 |
| - if (j >= message.length()) { |
93 |
| - messageVector[i][0] = 23; |
| 49 | + while (index < message.length()) { |
| 50 | + for (int i = 0; i < matrixSize; i++) { |
| 51 | + if (index < message.length()) { |
| 52 | + messageVector[i] = message.charAt(index++) - 'A'; |
94 | 53 | } else {
|
95 |
| - messageVector[i][0] = (message.charAt(j)) % 65; |
| 54 | + messageVector[i] = 'X' - 'A'; // Padding with 'X' if needed |
96 | 55 | }
|
97 |
| - System.out.println(messageVector[i][0]); |
98 |
| - j++; |
99 | 56 | }
|
100 |
| - int x; |
101 |
| - int i; |
102 |
| - for (i = 0; i < n; i++) { |
103 |
| - plainMatrix[i][0] = 0; |
104 | 57 |
|
105 |
| - for (x = 0; x < n; x++) { |
106 |
| - plainMatrix[i][0] += keyMatrix[i][x] * messageVector[x][0]; |
| 58 | + for (int i = 0; i < matrixSize; i++) { |
| 59 | + plainVector[i] = 0; |
| 60 | + for (int j = 0; j < matrixSize; j++) { |
| 61 | + plainVector[i] += inverseKeyMatrix[i][j] * messageVector[j]; |
107 | 62 | }
|
108 |
| - |
109 |
| - plainMatrix[i][0] = plainMatrix[i][0] % 26; |
110 |
| - } |
111 |
| - for (i = 0; i < n; i++) { |
112 |
| - plainText += (char) (plainMatrix[i][0] + 65); |
| 63 | + plainVector[i] = plainVector[i] % 26; |
| 64 | + plainText.append((char) (plainVector[i] + 'A')); |
113 | 65 | }
|
114 | 66 | }
|
115 |
| - System.out.println("Plaintext: " + plainText); |
| 67 | + |
| 68 | + return plainText.toString(); |
116 | 69 | }
|
117 | 70 |
|
118 |
| - // Determinant calculator |
119 |
| - public static int determinant(int[][] a, int n) { |
120 |
| - int det = 0; |
121 |
| - int sign = 1; |
122 |
| - int p = 0; |
123 |
| - int q = 0; |
| 71 | + // Validates that the determinant of the key matrix is not zero modulo 26 |
| 72 | + private void validateDeterminant(int[][] keyMatrix, int n) { |
| 73 | + int det = determinant(keyMatrix, n) % 26; |
| 74 | + if (det == 0) { |
| 75 | + throw new IllegalArgumentException("Invalid key matrix. Determinant is zero modulo 26."); |
| 76 | + } |
| 77 | + } |
124 | 78 |
|
| 79 | + // Computes the determinant of a matrix recursively |
| 80 | + private int determinant(int[][] matrix, int n) { |
| 81 | + int det = 0; |
125 | 82 | if (n == 1) {
|
126 |
| - det = a[0][0]; |
127 |
| - } else { |
128 |
| - int[][] b = new int[n - 1][n - 1]; |
129 |
| - for (int x = 0; x < n; x++) { |
130 |
| - p = 0; |
131 |
| - q = 0; |
132 |
| - for (int i = 1; i < n; i++) { |
133 |
| - for (int j = 0; j < n; j++) { |
134 |
| - if (j != x) { |
135 |
| - b[p][q++] = a[i][j]; |
136 |
| - if (q % (n - 1) == 0) { |
137 |
| - p++; |
138 |
| - q = 0; |
139 |
| - } |
140 |
| - } |
| 83 | + return matrix[0][0]; |
| 84 | + } |
| 85 | + int sign = 1; |
| 86 | + int[][] subMatrix = new int[n - 1][n - 1]; |
| 87 | + for (int x = 0; x < n; x++) { |
| 88 | + int subI = 0; |
| 89 | + for (int i = 1; i < n; i++) { |
| 90 | + int subJ = 0; |
| 91 | + for (int j = 0; j < n; j++) { |
| 92 | + if (j != x) { |
| 93 | + subMatrix[subI][subJ++] = matrix[i][j]; |
141 | 94 | }
|
142 | 95 | }
|
143 |
| - det = det + a[0][x] * determinant(b, n - 1) * sign; |
144 |
| - sign = -sign; |
| 96 | + subI++; |
145 | 97 | }
|
| 98 | + det += sign * matrix[0][x] * determinant(subMatrix, n - 1); |
| 99 | + sign = -sign; |
146 | 100 | }
|
147 | 101 | return det;
|
148 | 102 | }
|
149 |
| - |
150 |
| - // Function to implement Hill Cipher |
151 |
| - static void hillCipher(String message) { |
152 |
| - System.out.println("What do you want to process from the message?"); |
153 |
| - System.out.println("Press 1: To Encrypt"); |
154 |
| - System.out.println("Press 2: To Decrypt"); |
155 |
| - short sc = userInput.nextShort(); |
156 |
| - if (sc == 1) { |
157 |
| - encrypt(message); |
158 |
| - } else if (sc == 2) { |
159 |
| - decrypt(message); |
160 |
| - } else { |
161 |
| - System.out.println("Invalid input, program terminated."); |
162 |
| - } |
163 |
| - } |
164 |
| - |
165 |
| - static void validateDeterminant(int[][] keyMatrix, int n) { |
166 |
| - if (determinant(keyMatrix, n) % 26 == 0) { |
167 |
| - System.out.println("Invalid key, as determinant = 0. Program Terminated"); |
168 |
| - } |
169 |
| - } |
170 |
| - |
171 |
| - // Driver code |
172 |
| - public static void main(String[] args) { |
173 |
| - // Get the message to be encrypted |
174 |
| - System.out.println("Enter message"); |
175 |
| - String message = userInput.nextLine(); |
176 |
| - hillCipher(message); |
177 |
| - } |
178 | 103 | }
|
0 commit comments