1 /******************************************************************************* 2 * Copyright (c) 2005, 2019 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.osgi.tests.security; 15 16 import java.io.File; 17 import java.io.FileOutputStream; 18 import java.io.IOException; 19 import java.io.OutputStream; 20 import java.net.URL; 21 import java.security.KeyStore; 22 import java.security.KeyStoreException; 23 import java.security.cert.Certificate; 24 import java.security.cert.CertificateException; 25 import java.util.ArrayList; 26 import junit.framework.Test; 27 import junit.framework.TestCase; 28 import junit.framework.TestSuite; 29 import org.eclipse.osgi.internal.service.security.KeyStoreTrustEngine; 30 import org.eclipse.osgi.service.security.TrustEngine; 31 import org.eclipse.osgi.tests.OSGiTestsActivator; 32 33 public class KeyStoreTrustEngineTest extends TestCase { 34 35 private static char[] PASSWORD_DEFAULT = { 'c', 'h', 'a', 'n', 'g', 'e', 'i', 't' }; 36 private static String TYPE_DEFAULT = "JKS"; //$NON-NLS-1$ 37 38 private static TestCase[] s_tests = { 39 /* findTrustAnchor tests */ 40 new KeyStoreTrustEngineTest("findTrustAnchor positive test: self signed trusted", "ca1_root") { //$NON-NLS-1$ //$NON-NLS-2$ 41 public void runTest() { 42 testFindTrustAnchor0(); 43 } 44 }, new KeyStoreTrustEngineTest("findTrustAnchor positive test: chain with root trusted", "ca1_root") { //$NON-NLS-1$ //$NON-NLS-2$ 45 public void runTest() { 46 testFindTrustAnchor1(); 47 } 48 }, new KeyStoreTrustEngineTest("findTrustAnchor positive test: chain with intermediate trusted", "ca1_ou") { //$NON-NLS-1$ //$NON-NLS-2$ 49 public void runTest() { 50 testFindTrustAnchor2(); 51 } 52 }, new KeyStoreTrustEngineTest("findTrustAnchor positive test: chain with leaf trusted", "ca1_leafb") { //$NON-NLS-1$ //$NON-NLS-2$ 53 public void runTest() { 54 testFindTrustAnchor3(); 55 } 56 }, new KeyStoreTrustEngineTest("findTrustAnchor negative test: untrusted self signed") { //$NON-NLS-1$ 57 public void runTest() { 58 testFindTrustAnchor4(); 59 } 60 }, new KeyStoreTrustEngineTest("findTrustAnchor negative test: untrusted chain") { //$NON-NLS-1$ 61 public void runTest() { 62 testFindTrustAnchor5(); 63 } 64 }, new KeyStoreTrustEngineTest("findTrustAnchor negative test: invalid chain") { //$NON-NLS-1$ 65 public void runTest() { 66 testFindTrustAnchor6(); 67 } 68 }, new KeyStoreTrustEngineTest("findTrustAnchor negative test: incomplete-able chain") { //$NON-NLS-1$ 69 public void runTest() { 70 testFindTrustAnchor7(); 71 } 72 }, new KeyStoreTrustEngineTest("findTrustAnchor negative test: null chain") { //$NON-NLS-1$ 73 public void runTest() { 74 testFindTrustAnchor8(); 75 } 76 }, 77 /* addTrustAnchor tests */ 78 new KeyStoreTrustEngineTest("addTrustAnchor positive test: add with alias") { //$NON-NLS-1$ 79 public void runTest() { 80 testAddTrustAnchor0(); 81 } 82 }, /* 83 * , new 84 * KeyStoreTrustEngineTest("addTrustAnchor positive test: add with autogenerated alias" 85 * , null) { public void runTest() { testAddTrustAnchor1(); } } 86 */ 87 new KeyStoreTrustEngineTest("addTrustAnchor negative test: null cert specified") { //$NON-NLS-1$ 88 public void runTest() { 89 testAddTrustAnchor2(); 90 } 91 }, new KeyStoreTrustEngineTest("addTrustAnchor negative test: existing cert specified", "ca1_root") { //$NON-NLS-1$ //$NON-NLS-2$ 92 public void runTest() { 93 testAddTrustAnchor3(); 94 } 95 }, new KeyStoreTrustEngineTest("addTrustAnchor negative test: existing alias specified", "ca1_root") { //$NON-NLS-1$ //$NON-NLS-2$ 96 public void runTest() { 97 testAddTrustAnchor4(); 98 } 99 } 100 /* removeTrustAnchor tests */ 101 , new KeyStoreTrustEngineTest("removeTrustAnchor positive test: remove by alias", "ca1_root") { //$NON-NLS-1$ //$NON-NLS-2$ 102 public void runTest() { 103 testRemoveTrustAnchor0(); 104 } 105 }, new KeyStoreTrustEngineTest("removeTrustAnchor positive test: remove by cert", "ca1_root") { //$NON-NLS-1$ //$NON-NLS-2$ 106 public void runTest() { 107 testRemoveTrustAnchor1(); 108 } 109 }, new KeyStoreTrustEngineTest("removeTrustAnchor negative test: cert not found") { //$NON-NLS-1$ 110 public void runTest() { 111 testRemoveTrustAnchor2(); 112 } 113 }, new KeyStoreTrustEngineTest("removeTrustAnchor negative test: by alias not found") { //$NON-NLS-1$ 114 public void runTest() { 115 testRemoveTrustAnchor3(); 116 } 117 }, new KeyStoreTrustEngineTest("removeTrustAnchor negative test: remove by null alias") { //$NON-NLS-1$ 118 public void runTest() { 119 testRemoveTrustAnchor4(); 120 } 121 }, new KeyStoreTrustEngineTest("removeTrustAnchor negative test: remove by null certificate") { //$NON-NLS-1$ 122 public void runTest() { 123 testRemoveTrustAnchor5(); 124 } 125 }, 126 /* getTrustAnchor tests */ 127 new KeyStoreTrustEngineTest("getTrustAnchor positive test: get by alias", "ca1_root") { //$NON-NLS-1$ //$NON-NLS-2$ 128 public void runTest() { 129 testGetTrustAnchor0(); 130 } 131 }, new KeyStoreTrustEngineTest("getTrustAnchor negative test: get by null alias") { //$NON-NLS-1$ 132 public void runTest() { 133 testGetTrustAnchor1(); 134 } 135 }, new KeyStoreTrustEngineTest("getTrustAnchor negative test: does not exist") { //$NON-NLS-1$ 136 public void runTest() { 137 testGetTrustAnchor2(); 138 } 139 }, 140 /* getAliases tests */ 141 new KeyStoreTrustEngineTest("getAliases positive test: get the alias list", "ca1_root", "ca2_root") { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 142 public void runTest() { 143 testGetAliases0(); 144 } 145 } }; 146 suite()147 public static Test suite() { 148 TestSuite suite = new TestSuite("Unit tests for TrustEngine"); //$NON-NLS-1$ 149 for (TestCase s_test : s_tests) { 150 suite.addTest(s_test); 151 } 152 return suite; 153 } 154 155 private static KeyStore supportStore; 156 static { 157 try { 158 URL supportUrl = OSGiTestsActivator.getContext().getBundle().getEntry("test_files/security/keystore.jks"); //$NON-NLS-1$ 159 supportStore = KeyStore.getInstance(TYPE_DEFAULT); supportUrl.openStream()160 supportStore.load(supportUrl.openStream(), PASSWORD_DEFAULT); 161 } catch (Exception e) { 162 e.printStackTrace(); 163 } 164 } 165 166 private String[] aliases; 167 private KeyStore testStore; 168 private File testStoreFile; 169 TrustEngine engine; 170 KeyStoreTrustEngineTest()171 public KeyStoreTrustEngineTest() { 172 // placeholder 173 } 174 KeyStoreTrustEngineTest(String name, String... aliases)175 public KeyStoreTrustEngineTest(String name, String... aliases) { 176 super(name); 177 this.aliases = aliases; 178 } 179 setUp()180 protected void setUp() throws Exception { 181 if (supportStore == null) { 182 fail("Could not open keystore with test certificates!"); //$NON-NLS-1$ 183 } 184 185 testStore = KeyStore.getInstance(TYPE_DEFAULT); 186 testStore.load(null, PASSWORD_DEFAULT); 187 if (aliases != null) { 188 for (String alias : aliases) { 189 testStore.setCertificateEntry(alias, getTestCertificate(alias)); 190 } 191 } 192 testStoreFile = File.createTempFile("teststore", "jks"); //$NON-NLS-1$ //$NON-NLS-2$ 193 final FileOutputStream out = new FileOutputStream(testStoreFile); 194 try { 195 testStore.store(out, PASSWORD_DEFAULT); 196 } finally { 197 safeClose(out); 198 } 199 engine = new KeyStoreTrustEngine(testStoreFile.getPath(), TYPE_DEFAULT, PASSWORD_DEFAULT, "teststore", null); //$NON-NLS-1$ 200 } 201 202 /** 203 * Closes a stream and ignores any resulting exception. This is useful when 204 * doing stream cleanup in a finally block where secondary exceptions are not 205 * worth logging. 206 */ safeClose(OutputStream out)207 protected static void safeClose(OutputStream out) { 208 try { 209 if (out != null) 210 out.close(); 211 } catch (IOException e) { 212 // ignore 213 } 214 } 215 tearDown()216 protected void tearDown() { 217 engine = null; 218 testStore = null; 219 testStoreFile.delete(); 220 } 221 getTestCertificate(String alias)222 private static Certificate getTestCertificate(String alias) throws KeyStoreException { 223 return supportStore.getCertificate(alias); 224 } 225 getTestCertificateChain(String... aliases)226 private static Certificate[] getTestCertificateChain(String... aliases) throws KeyStoreException { 227 ArrayList<Certificate> certs = new ArrayList<>(aliases.length); 228 for (String alias : aliases) { 229 certs.add(getTestCertificate(alias)); 230 } 231 return certs.toArray(new Certificate[] {}); 232 } 233 234 // findTrustAnchor positive test: self signed trusted testFindTrustAnchor0()235 public void testFindTrustAnchor0() { 236 try { 237 Certificate cert = engine.findTrustAnchor(new Certificate[] { getTestCertificate("ca1_root") }); //$NON-NLS-1$ 238 assertNotNull("Did not return a cert for self-signed case", cert); //$NON-NLS-1$ 239 assertEquals("Input and output certs not equal for self-signed case", cert, getTestCertificate("ca1_root")); //$NON-NLS-1$ //$NON-NLS-2$ 240 } catch (Throwable t) { 241 fail("Unexpected exception testing trusted self-signed cert: " + t.getMessage()); //$NON-NLS-1$ 242 } 243 } 244 245 // findTrustAnchor positive test: chain with root trusted testFindTrustAnchor1()246 public void testFindTrustAnchor1() { 247 try { 248 Certificate cert = engine.findTrustAnchor(getTestCertificateChain("ca1_leafb", "ca1_ou", "ca1_root")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 249 assertNotNull("Certificate did not come back in trusted root case", cert); //$NON-NLS-1$ 250 assertEquals("Output cert is not root trusted cert", cert, getTestCertificate("ca1_root")); //$NON-NLS-1$ //$NON-NLS-2$ 251 } catch (Throwable t) { 252 fail("Unexpected exception testing trusted root from complete chain: " + t.getMessage()); //$NON-NLS-1$ 253 } 254 } 255 256 // findTrustAnchor positive test: chain with intermediate trusted testFindTrustAnchor2()257 public void testFindTrustAnchor2() { 258 try { 259 Certificate cert = engine.findTrustAnchor(getTestCertificateChain("ca1_leafb", "ca1_ou", "ca1_root")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 260 assertNotNull("Certificate did not come back in trusted intermediate case", cert); //$NON-NLS-1$ 261 assertEquals("Output cert is not intermediate trusted cert", cert, getTestCertificate("ca1_ou")); //$NON-NLS-1$ //$NON-NLS-2$ 262 } catch (Throwable t) { 263 fail("Unexpected exception testing trusted root from complete chain: " + t.getMessage()); //$NON-NLS-1$ 264 } 265 } 266 267 // findTrustAnchor positive test: chain with leaf trusted testFindTrustAnchor3()268 public void testFindTrustAnchor3() { 269 try { 270 Certificate cert = engine.findTrustAnchor(getTestCertificateChain("ca1_leafb", "ca1_ou", "ca1_root")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 271 assertNotNull("Certificate did not come back in trusted leaf case", cert); //$NON-NLS-1$ 272 assertEquals("Output cert is not leaf trusted cert", cert, getTestCertificate("ca1_leafb")); //$NON-NLS-1$ //$NON-NLS-2$ 273 } catch (Throwable t) { 274 fail("Unexpected exception testing trusted root from complete chain: " + t.getMessage()); //$NON-NLS-1$ 275 } 276 } 277 278 // findTrustAnchor negative test: untrusted self signed testFindTrustAnchor4()279 public void testFindTrustAnchor4() { 280 try { 281 Certificate cert = engine.findTrustAnchor(new Certificate[] { getTestCertificate("ca2_root") }); //$NON-NLS-1$ 282 assertNull("Incorrectly returned a certificate for untrusted self-signed case", cert); //$NON-NLS-1$ 283 } catch (Throwable t) { 284 fail("Unexpected exception testing untrusted self-signed cert: " + t.getMessage()); //$NON-NLS-1$ 285 } 286 } 287 288 // findTrustAnchor negative test: untrusted chain testFindTrustAnchor5()289 public void testFindTrustAnchor5() { 290 try { 291 Certificate cert = engine.findTrustAnchor(getTestCertificateChain("ca2_leafb", "ca2_ou", "ca2_root")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 292 assertNull("Incorrectly returned a certificate for untrusted chain case", cert); //$NON-NLS-1$ 293 } catch (Throwable t) { 294 fail("Unexpected exception testing untrusted chain: " + t.getMessage()); //$NON-NLS-1$ 295 } 296 } 297 298 // findTrustAnchor negative test: invalid chain testFindTrustAnchor6()299 public void testFindTrustAnchor6() { 300 try { 301 Certificate cert = engine.findTrustAnchor(getTestCertificateChain("ca2_leafa", "ca1_root")); //$NON-NLS-1$ //$NON-NLS-2$ 302 assertNull("Incorrectly returned a certificate on invalid certificate chain", cert); //$NON-NLS-1$ 303 } catch (Throwable t) { 304 assertNull("Incorrectly thrown exception thrown on invalid certificate chain", t); //$NON-NLS-1$ 305 } 306 } 307 308 // findTrustAnchor negative test: incomplete-able chain testFindTrustAnchor7()309 public void testFindTrustAnchor7() { 310 try { 311 Certificate cert = engine.findTrustAnchor(getTestCertificateChain("ca1_leafb", "ca1_root")); //$NON-NLS-1$ //$NON-NLS-2$ 312 assertNull("Incorrectly returned a certificate on incomplete-able certificate chain", cert); //$NON-NLS-1$ 313 } catch (Throwable t) { 314 assertNull("Incorrectly thrown exception thrown on incomplete-able certificate chain", t); //$NON-NLS-1$ 315 } 316 } 317 318 // findTrustAnchor negative test: null chain testFindTrustAnchor8()319 public void testFindTrustAnchor8() { 320 try { 321 engine.findTrustAnchor(null); 322 fail("Did not throw IllegalArgumentException on NULL certificate"); //$NON-NLS-1$ 323 } catch (Throwable t) { 324 assertTrue("Incorrect exception thrown on NULL certificate", t instanceof IllegalArgumentException); //$NON-NLS-1$ 325 } 326 } 327 328 // testAddTrustAnchor positive test: add with alias testAddTrustAnchor0()329 public void testAddTrustAnchor0() { 330 try { 331 String alias = engine.addTrustAnchor(getTestCertificate("ca1_root"), "ca1_root"); //$NON-NLS-1$ //$NON-NLS-2$ 332 assertEquals("Alias returned does not equal alias input", alias, "ca1_root"); //$NON-NLS-1$ //$NON-NLS-2$ 333 334 } catch (Throwable t) { 335 fail("Unexpected exception adding trusted root: " + t.getMessage()); //$NON-NLS-1$ 336 } 337 } 338 339 // testAddTrustAnchor positive test: add with autogenerated alias testAddTrustAnchor1()340 public void testAddTrustAnchor1() { 341 try { 342 String alias = engine.addTrustAnchor(getTestCertificate("ca1_root"), null); //$NON-NLS-1$ 343 assertNotNull("Generated alias was not correctly returned", alias); //$NON-NLS-1$ 344 } catch (Throwable t) { 345 fail("Unexpected exception adding trusted root (autogen alias): " + t.getMessage()); //$NON-NLS-1$ 346 } 347 } 348 349 // testAddTrustAnchor negative test: null cert specified testAddTrustAnchor2()350 public void testAddTrustAnchor2() { 351 try { 352 engine.addTrustAnchor(null, "ca1_root"); //$NON-NLS-1$ 353 fail("Did not throw IllegalArgumentException on NULL certificate"); //$NON-NLS-1$ 354 } catch (Throwable t) { 355 assertTrue("Incorrect exception thrown on NULL certificate", t instanceof IllegalArgumentException); //$NON-NLS-1$ 356 } 357 } 358 359 // testAddTrustAnchor negative test: existing cert specified testAddTrustAnchor3()360 public void testAddTrustAnchor3() { 361 try { 362 engine.addTrustAnchor(getTestCertificate("ca1_root"), "new_root"); //$NON-NLS-1$ //$NON-NLS-2$ 363 assertTrue("Did not throw CertificateException on duplicate cert", false); //$NON-NLS-1$ 364 } catch (Throwable t) { 365 assertTrue("Incorrect exception thrown on duplicate cert", t instanceof CertificateException); //$NON-NLS-1$ 366 return; 367 } 368 fail("Expected exception when adding trust anchor"); //$NON-NLS-1$ 369 } 370 371 // testAddTrustAnchor negative test: existing alias specified testAddTrustAnchor4()372 public void testAddTrustAnchor4() { 373 try { 374 engine.addTrustAnchor(getTestCertificate("ca2_root"), "ca1_root"); //$NON-NLS-1$ //$NON-NLS-2$ 375 assertTrue("Did not throw CertificateException on duplicate alias", false); //$NON-NLS-1$ 376 } catch (Throwable t) { 377 assertTrue("Incorrect exception thrown on duplicate alias", t instanceof CertificateException); //$NON-NLS-1$ 378 return; 379 } 380 fail("Expected exception when adding trust anchor"); //$NON-NLS-1$ 381 } 382 383 // removeTrustAnchor positive test: remove by alias testRemoveTrustAnchor0()384 public void testRemoveTrustAnchor0() { 385 try { 386 engine.removeTrustAnchor("ca1_root"); //$NON-NLS-1$ 387 } catch (Throwable t) { 388 fail("Unexpected exception thrown when removing by alias: " + t.getMessage()); //$NON-NLS-1$ 389 } 390 } 391 392 // removeTrustAnchor positive test: remove by cert testRemoveTrustAnchor1()393 public void testRemoveTrustAnchor1() { 394 try { 395 engine.removeTrustAnchor(getTestCertificate("ca1_root")); //$NON-NLS-1$ 396 } catch (Throwable t) { 397 fail("Unexpected exception thrown when removing by cert: " + t.getMessage()); //$NON-NLS-1$ 398 } 399 } 400 401 // removeTrustAnchor negative test: cert not found testRemoveTrustAnchor2()402 public void testRemoveTrustAnchor2() { 403 try { 404 engine.removeTrustAnchor(getTestCertificate("ca1_root")); //$NON-NLS-1$ 405 fail("Did not throw CertificateException on cert not found"); //$NON-NLS-1$ 406 } catch (Throwable t) { 407 assertTrue("Incorrect exception thrown on remove by cert", t instanceof CertificateException); //$NON-NLS-1$ 408 } 409 } 410 411 // removeTrustAnchor negative test: by alias not found testRemoveTrustAnchor3()412 public void testRemoveTrustAnchor3() { 413 try { 414 engine.removeTrustAnchor("ca2_root"); //$NON-NLS-1$ 415 assertTrue("Did not throw CertificateException on alias not found", false); //$NON-NLS-1$ 416 } catch (Throwable t) { 417 assertTrue("Incorrect exception thrown on remove by alias", t instanceof CertificateException); //$NON-NLS-1$ 418 return; 419 } 420 fail("Expected exception when removing trust anchor"); //$NON-NLS-1$ 421 } 422 423 // removeTrustAnchor negative test: remove by null alias testRemoveTrustAnchor4()424 public void testRemoveTrustAnchor4() { 425 try { 426 engine.removeTrustAnchor((String) null); 427 fail("Did not throw CertificateException on alias null"); //$NON-NLS-1$ 428 } catch (Throwable t) { 429 assertTrue("Incorrect exception thrown on remove by null alias", t instanceof IllegalArgumentException); //$NON-NLS-1$ 430 } 431 } 432 433 // removeTrustAnchor negative test: remove by null certificate testRemoveTrustAnchor5()434 public void testRemoveTrustAnchor5() { 435 try { 436 engine.removeTrustAnchor((Certificate) null); 437 fail("Did not throw IllegalArgumentException on remove by cert null"); //$NON-NLS-1$ 438 } catch (Throwable t) { 439 assertTrue("Incorrect exception thrown on remove by null cert", t instanceof IllegalArgumentException); //$NON-NLS-1$ 440 } 441 } 442 443 // getTrustAnchor positive test: get by alias testGetTrustAnchor0()444 public void testGetTrustAnchor0() { 445 try { 446 Certificate cert = engine.getTrustAnchor("ca1_root"); //$NON-NLS-1$ 447 assertEquals("Did not get expected certificate", getTestCertificate("ca1_root"), cert); //$NON-NLS-1$ //$NON-NLS-2$ 448 } catch (Throwable t) { 449 fail("Unexpected exception when retrieving trust anchor: " + t.getMessage()); //$NON-NLS-1$ 450 } 451 } 452 453 // getTrustAnchor negative test: get by null alias testGetTrustAnchor1()454 public void testGetTrustAnchor1() { 455 try { 456 engine.getTrustAnchor(null); 457 fail("Did not throw IllegalArgumentException on get by alias null"); //$NON-NLS-1$ 458 } catch (Throwable t) { 459 assertTrue("Incorrect exception thrown on remove by null alias", t instanceof IllegalArgumentException); //$NON-NLS-1$ 460 } 461 } 462 463 // getTrustAnchor negative test: does not exist testGetTrustAnchor2()464 public void testGetTrustAnchor2() { 465 try { 466 Certificate cert = engine.getTrustAnchor("ca2_root"); //$NON-NLS-1$ 467 assertNull("Incorrectly returned a certificate on certificate does not exist", cert); //$NON-NLS-1$ 468 } catch (Throwable t) { 469 assertNull("Incorrectly thrown exception on alias does not exist", t); //$NON-NLS-1$ 470 return; 471 } 472 } 473 474 // getAliases positive test: get the alias list testGetAliases0()475 public void testGetAliases0() { 476 try { 477 engine.getAliases(); 478 } catch (Throwable t) { 479 fail("Unexpected exception when retrieving alias list: " + t.getMessage()); //$NON-NLS-1$ 480 } 481 482 } 483 // TODO: thread safety tests 484 // TODO: performance tests 485 } 486