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