1 import java.io.BufferedReader;
2 import java.io.FileReader;
3 import java.io.IOException;
4 import java.math.BigInteger;
5 import java.security.AlgorithmParameters;
6 import java.security.GeneralSecurityException;
7 import java.security.KeyFactory;
8 import java.security.PrivateKey;
9 import java.security.Security;
10 import java.security.spec.AlgorithmParameterSpec;
11 import java.security.spec.MGF1ParameterSpec;
12 import java.security.spec.RSAPrivateKeySpec;
13 import java.util.Arrays;
14 
15 import javax.crypto.Cipher;
16 import javax.crypto.spec.OAEPParameterSpec;
17 import javax.crypto.spec.PSource;
18 import javax.xml.bind.DatatypeConverter;
19 
20 import org.bouncycastle.jce.provider.BouncyCastleProvider;
21 
22 class TestVectorData {
23     public BigInteger pub_key_modulus;
24     public BigInteger pub_key_exponent;
25     public BigInteger priv_key_public_exponent;
26     public BigInteger priv_key_modulus;
27     public BigInteger priv_key_exponent;
28     public BigInteger priv_key_prime_1;
29     public BigInteger priv_key_prime_2;
30     public BigInteger priv_key_prime_exponent_1;
31     public BigInteger priv_key_prime_exponent_2;
32     public BigInteger priv_key_coefficient;
33     public byte[] plaintext;
34     public byte[] ciphertext;
35 }
36 
37 class TestVectorLoader {
38     private static final String FILE_HEADER = "# RSA OAEP SHA2 vectors built";
39     private static final String EXAMPLE_HEADER = "# =====";
40     private static final String EXAMPLE = "# Example";
41     private static final String PUBLIC_KEY = "# Public key";
42     private static final String PUB_MODULUS = "# Modulus:";
43     private static final String PUB_EXPONENT = "# Exponent:";
44     private static final String PRIVATE_KEY = "# Private key";
45     private static final String PRIV_MODULUS = "# Modulus:";
46     private static final String PRIV_PUBLIC_EXPONENT = "# Public exponent:";
47     private static final String PRIV_EXPONENT = "# Exponent:";
48     private static final String PRIV_PRIME_1 = "# Prime 1:";
49     private static final String PRIV_PRIME_2 = "# Prime 2:";
50     private static final String PRIV_PRIME_EXPONENT_1 = "# Prime exponent 1:";
51     private static final String PRIV_PRIME_EXPONENT_2 = "# Prime exponent 2:";
52     private static final String PRIV_COEFFICIENT = "# Coefficient:";
53     private static final String OAEP_EXAMPLE_HEADER = "# OAEP Example";
54     private static final String MESSAGE = "# Message:";
55     private static final String ENCRYPTION = "# Encryption:";
56 
57     private BufferedReader m_reader = null;
58     private FileReader m_file_reader = null;
59     private TestVectorData m_data = null;
60 
TestVectorLoader()61     TestVectorLoader() {
62 
63     }
64 
finalize()65     protected void finalize() {
66         close();
67     }
68 
open(String path)69     public void open(String path) throws IOException {
70         close();
71         m_file_reader = new FileReader(path);
72         m_reader = new BufferedReader(m_file_reader);
73         m_data = new TestVectorData();
74     }
75 
close()76     public void close() {
77         try {
78             if (m_reader != null) {
79                 m_reader.close();
80                 m_reader = null;
81             }
82             if (m_file_reader != null) {
83                 m_file_reader.close();
84                 m_file_reader = null;
85             }
86             m_data = null;
87         } catch (IOException e) {
88             System.out.println("Exception closing files");
89             e.printStackTrace();
90         }
91     }
92 
loadNextTest()93     public TestVectorData loadNextTest() throws IOException {
94         if (m_file_reader == null || m_reader == null || m_data == null) {
95             throw new IOException("A test vector file must be opened first");
96         }
97 
98         String line = m_reader.readLine();
99 
100         if (line == null) {
101             // end of file
102             return null;
103         }
104 
105         if (line.startsWith(FILE_HEADER)) {
106             // start of file
107             skipFileHeader(m_reader);
108             line = m_reader.readLine();
109         }
110 
111         if (line.startsWith(OAEP_EXAMPLE_HEADER)) {
112             // Next example, keep existing keys and load next message
113             loadMessage(m_reader, m_data);
114             return m_data;
115         }
116 
117         // otherwise it's a new example
118         if (!line.startsWith(EXAMPLE_HEADER)) {
119             throw new IOException("Test Header Missing");
120         }
121         startNewTest(m_reader);
122         m_data = new TestVectorData();
123 
124         line = m_reader.readLine();
125         if (!line.startsWith(PUBLIC_KEY))
126             throw new IOException("Public Key Missing");
127         loadPublicKey(m_reader, m_data);
128 
129         line = m_reader.readLine();
130         if (!line.startsWith(PRIVATE_KEY))
131             throw new IOException("Private Key Missing");
132         loadPrivateKey(m_reader, m_data);
133 
134         line = m_reader.readLine();
135         if (!line.startsWith(OAEP_EXAMPLE_HEADER))
136             throw new IOException("Message Missing");
137         loadMessage(m_reader, m_data);
138 
139         return m_data;
140     }
141 
unhexlify(String line)142     private byte[] unhexlify(String line) {
143         byte[] bytes = DatatypeConverter.parseHexBinary(line);
144         return bytes;
145     }
146 
readBigInteger(BufferedReader br)147     private BigInteger readBigInteger(BufferedReader br) throws IOException {
148         return new BigInteger(br.readLine(), 16);
149     }
150 
skipFileHeader(BufferedReader br)151     private void skipFileHeader(BufferedReader br) throws IOException {
152         br.readLine(); // # # Derived from the NIST OAEP SHA1 vectors
153         br.readLine(); // # # Verified against the Bouncy Castle OAEP SHA2 implementation
154         br.readLine(); // #
155     }
156 
startNewTest(BufferedReader br)157     private void startNewTest(BufferedReader br) throws IOException {
158         String line = br.readLine();
159         if (!line.startsWith(EXAMPLE))
160             throw new IOException("Example Header Missing");
161     }
162 
loadPublicKey(BufferedReader br, TestVectorData data)163     private void loadPublicKey(BufferedReader br, TestVectorData data) throws IOException {
164         String line = br.readLine();
165         if (!line.startsWith(PUB_MODULUS))
166             throw new IOException("Public Key Modulus Missing");
167         data.pub_key_modulus = readBigInteger(br);
168 
169         line = br.readLine();
170         if (!line.startsWith(PUB_EXPONENT))
171             throw new IOException("Public Key Exponent Missing");
172         data.pub_key_exponent = readBigInteger(br);
173     }
174 
loadPrivateKey(BufferedReader br, TestVectorData data)175     private void loadPrivateKey(BufferedReader br, TestVectorData data) throws IOException {
176         String line = br.readLine();
177         if (!line.startsWith(PRIV_MODULUS))
178             throw new IOException("Private Key Modulus Missing");
179         data.priv_key_modulus = readBigInteger(br);
180 
181         line = br.readLine();
182         if (!line.startsWith(PRIV_PUBLIC_EXPONENT))
183             throw new IOException("Private Key Public Exponent Missing");
184         data.priv_key_public_exponent = readBigInteger(br);
185 
186         line = br.readLine();
187         if (!line.startsWith(PRIV_EXPONENT))
188             throw new IOException("Private Key Exponent Missing");
189         data.priv_key_exponent = readBigInteger(br);
190 
191         line = br.readLine();
192         if (!line.startsWith(PRIV_PRIME_1))
193             throw new IOException("Private Key Prime 1 Missing");
194         data.priv_key_prime_1 = readBigInteger(br);
195 
196         line = br.readLine();
197         if (!line.startsWith(PRIV_PRIME_2))
198             throw new IOException("Private Key Prime 2 Missing");
199         data.priv_key_prime_2 = readBigInteger(br);
200 
201         line = br.readLine();
202         if (!line.startsWith(PRIV_PRIME_EXPONENT_1))
203             throw new IOException("Private Key Prime Exponent 1 Missing");
204         data.priv_key_prime_exponent_1 = readBigInteger(br);
205 
206         line = br.readLine();
207         if (!line.startsWith(PRIV_PRIME_EXPONENT_2))
208             throw new IOException("Private Key Prime Exponent 2 Missing");
209         data.priv_key_prime_exponent_2 = readBigInteger(br);
210 
211         line = br.readLine();
212         if (!line.startsWith(PRIV_COEFFICIENT))
213             throw new IOException("Private Key Coefficient Missing");
214         data.priv_key_coefficient = readBigInteger(br);
215     }
216 
loadMessage(BufferedReader br, TestVectorData data)217     private void loadMessage(BufferedReader br, TestVectorData data) throws IOException {
218         String line = br.readLine();
219         if (!line.startsWith(MESSAGE))
220             throw new IOException("Plaintext Missing");
221         data.plaintext = unhexlify(br.readLine());
222 
223         line = br.readLine();
224         if (!line.startsWith(ENCRYPTION))
225             throw new IOException("Ciphertext Missing");
226         data.ciphertext = unhexlify(br.readLine());
227     }
228 
229 }
230 
231 public class VerifyRSAOAEPSHA2 {
232 
233     public enum SHAHash {
234         SHA1, SHA224, SHA256, SHA384, SHA512
235     }
236 
237     private SHAHash m_mgf1_hash;
238     private SHAHash m_alg_hash;
239     private Cipher m_cipher;
240     private PrivateKey m_private_key;
241     private AlgorithmParameters m_algo_param;
242 
VerifyRSAOAEPSHA2(SHAHash mgf1_hash, SHAHash alg_hash, TestVectorData test_data)243     VerifyRSAOAEPSHA2(SHAHash mgf1_hash, SHAHash alg_hash, TestVectorData test_data) throws Exception {
244 
245         m_mgf1_hash = mgf1_hash;
246         m_alg_hash = alg_hash;
247 
248         MGF1ParameterSpec mgf1_spec = getMGF1ParameterSpec(m_mgf1_hash);
249         AlgorithmParameterSpec algo_param_spec = getAlgorithmParameterSpec(m_alg_hash, mgf1_spec);
250 
251         m_algo_param = AlgorithmParameters.getInstance("OAEP");
252         m_algo_param.init(algo_param_spec);
253 
254         m_private_key = loadPrivateKey(test_data);
255 
256         m_cipher = getCipher(m_alg_hash);
257     }
258 
getCipher(SHAHash alg_hash)259     private Cipher getCipher(SHAHash alg_hash) throws GeneralSecurityException {
260         Cipher cipher = null;
261 
262         switch (alg_hash) {
263 
264         case SHA1:
265             cipher = Cipher.getInstance("RSA/ECB/OAEPwithSHA1andMGF1Padding", "BC");
266             break;
267 
268         case SHA224:
269             cipher = Cipher.getInstance("RSA/ECB/OAEPwithSHA-224andMGF1Padding", "BC");
270             break;
271 
272         case SHA256:
273             cipher = Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding", "BC");
274             break;
275 
276         case SHA384:
277             cipher = Cipher.getInstance("RSA/ECB/OAEPwithSHA-384andMGF1Padding", "BC");
278             break;
279 
280         case SHA512:
281             cipher = Cipher.getInstance("RSA/ECB/OAEPwithSHA-512andMGF1Padding", "BC");
282             break;
283         }
284 
285         return cipher;
286     }
287 
getMGF1ParameterSpec(SHAHash mgf1_hash)288     private MGF1ParameterSpec getMGF1ParameterSpec(SHAHash mgf1_hash) {
289         MGF1ParameterSpec mgf1 = null;
290 
291         switch (mgf1_hash) {
292 
293         case SHA1:
294             mgf1 = MGF1ParameterSpec.SHA1;
295             break;
296         case SHA224:
297             mgf1 = MGF1ParameterSpec.SHA224;
298             break;
299 
300         case SHA256:
301             mgf1 = MGF1ParameterSpec.SHA256;
302             break;
303 
304         case SHA384:
305             mgf1 = MGF1ParameterSpec.SHA384;
306             break;
307 
308         case SHA512:
309             mgf1 = MGF1ParameterSpec.SHA512;
310             break;
311         }
312 
313         return mgf1;
314     }
315 
getAlgorithmParameterSpec(SHAHash alg_hash, MGF1ParameterSpec mgf1_spec)316     private AlgorithmParameterSpec getAlgorithmParameterSpec(SHAHash alg_hash, MGF1ParameterSpec mgf1_spec) {
317 
318         OAEPParameterSpec oaep_spec = null;
319 
320         switch (alg_hash) {
321 
322         case SHA1:
323             oaep_spec = new OAEPParameterSpec("SHA1", "MGF1", mgf1_spec, PSource.PSpecified.DEFAULT);
324             break;
325 
326         case SHA224:
327             oaep_spec = new OAEPParameterSpec("SHA-224", "MGF1", mgf1_spec, PSource.PSpecified.DEFAULT);
328             break;
329 
330         case SHA256:
331             oaep_spec = new OAEPParameterSpec("SHA-256", "MGF1", mgf1_spec, PSource.PSpecified.DEFAULT);
332             break;
333 
334         case SHA384:
335             oaep_spec = new OAEPParameterSpec("SHA-384", "MGF1", mgf1_spec, PSource.PSpecified.DEFAULT);
336             break;
337 
338         case SHA512:
339             oaep_spec = new OAEPParameterSpec("SHA-512", "MGF1", mgf1_spec, PSource.PSpecified.DEFAULT);
340             break;
341         }
342 
343         return oaep_spec;
344     }
345 
loadPrivateKey(TestVectorData test_data)346     private PrivateKey loadPrivateKey(TestVectorData test_data) throws Exception {
347         KeyFactory kf = KeyFactory.getInstance("RSA");
348 
349         RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(test_data.priv_key_modulus, test_data.priv_key_exponent);
350 
351         return kf.generatePrivate(keySpec);
352     }
353 
testDecrypt(byte[] plaintext, byte[] ciphertext)354     public void testDecrypt(byte[] plaintext, byte[] ciphertext) throws Exception {
355         System.out.println("Verifying OAEP with mgf1_hash: " + m_mgf1_hash + " alg_hash: " + m_alg_hash + " - "
356                 + ciphertext.length + " bytes ciphertext - "
357                 + plaintext.length + " bytes plaintext");
358 
359         m_cipher.init(Cipher.DECRYPT_MODE, m_private_key, m_algo_param);
360         byte[] java_plaintext = m_cipher.doFinal(ciphertext);
361 
362         if (Arrays.equals(java_plaintext, plaintext) == false) {
363             throw new Exception("Verification failure - plaintext does not match after decryption.");
364         }
365     }
366 
main(String[] args)367     public static void main(String[] args) {
368         Security.addProvider(new BouncyCastleProvider());
369 
370         // assume current directory if no path given on command line
371         String vector_path = "./vectors/cryptography_vectors/asymmetric/RSA/oaep-custom";
372 
373         if (args.length > 0) {
374             vector_path = args[0];
375         }
376 
377         System.out.println("Vector file path: " + vector_path);
378 
379         try {
380             // loop over each combination of hash loading the vector file
381             // to verify for each
382             for (SHAHash mgf1_hash : SHAHash.values()) {
383                 for (SHAHash alg_hash : SHAHash.values()) {
384                     if (mgf1_hash.name().toLowerCase().equals("sha1") &&
385                         alg_hash.name().toLowerCase().equals("sha1")) {
386                         continue;
387                     }
388                     String filename = "oaep-" + mgf1_hash.name().toLowerCase() +
389                                           "-" + alg_hash.name().toLowerCase() + ".txt";
390 
391                     System.out.println("Loading " + filename + "...");
392 
393                     TestVectorLoader loader = new TestVectorLoader();
394                     loader.open(vector_path + filename);
395 
396                     TestVectorData test_data;
397 
398                     // load each test in the file and verify
399                     while ((test_data = loader.loadNextTest()) != null) {
400                         VerifyRSAOAEPSHA2 verify = new VerifyRSAOAEPSHA2(mgf1_hash, alg_hash, test_data);
401                         verify.testDecrypt(test_data.plaintext, test_data.ciphertext);
402                     }
403 
404                     System.out.println("Verifying " + filename + " completed successfully.");
405                 }
406             }
407 
408             System.out.println("All verification completed successfully");
409 
410         } catch (Exception e) {
411             // if any exception is thrown the verification has failed
412             e.printStackTrace();
413             System.out.println("Verification Failed!");
414         }
415     }
416 }
417