| /******************************************************************************* |
| * Copyright (c) 2005, 2019 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.osgi.tests.security; |
| |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.net.URL; |
| import java.security.KeyStore; |
| import java.security.KeyStoreException; |
| import java.security.cert.Certificate; |
| import java.security.cert.CertificateException; |
| import java.util.ArrayList; |
| import junit.framework.Test; |
| import junit.framework.TestCase; |
| import junit.framework.TestSuite; |
| import org.eclipse.osgi.internal.service.security.KeyStoreTrustEngine; |
| import org.eclipse.osgi.service.security.TrustEngine; |
| import org.eclipse.osgi.tests.OSGiTestsActivator; |
| |
| public class KeyStoreTrustEngineTest extends TestCase { |
| |
| private static char[] PASSWORD_DEFAULT = { 'c', 'h', 'a', 'n', 'g', 'e', 'i', 't' }; |
| private static String TYPE_DEFAULT = "JKS"; //$NON-NLS-1$ |
| |
| private static TestCase[] s_tests = { |
| /* findTrustAnchor tests */ |
| new KeyStoreTrustEngineTest("findTrustAnchor positive test: self signed trusted", "ca1_root") { //$NON-NLS-1$ //$NON-NLS-2$ |
| public void runTest() { |
| testFindTrustAnchor0(); |
| } |
| }, new KeyStoreTrustEngineTest("findTrustAnchor positive test: chain with root trusted", "ca1_root") { //$NON-NLS-1$ //$NON-NLS-2$ |
| public void runTest() { |
| testFindTrustAnchor1(); |
| } |
| }, new KeyStoreTrustEngineTest("findTrustAnchor positive test: chain with intermediate trusted", "ca1_ou") { //$NON-NLS-1$ //$NON-NLS-2$ |
| public void runTest() { |
| testFindTrustAnchor2(); |
| } |
| }, new KeyStoreTrustEngineTest("findTrustAnchor positive test: chain with leaf trusted", "ca1_leafb") { //$NON-NLS-1$ //$NON-NLS-2$ |
| public void runTest() { |
| testFindTrustAnchor3(); |
| } |
| }, new KeyStoreTrustEngineTest("findTrustAnchor negative test: untrusted self signed") { //$NON-NLS-1$ |
| public void runTest() { |
| testFindTrustAnchor4(); |
| } |
| }, new KeyStoreTrustEngineTest("findTrustAnchor negative test: untrusted chain") { //$NON-NLS-1$ |
| public void runTest() { |
| testFindTrustAnchor5(); |
| } |
| }, new KeyStoreTrustEngineTest("findTrustAnchor negative test: invalid chain") { //$NON-NLS-1$ |
| public void runTest() { |
| testFindTrustAnchor6(); |
| } |
| }, new KeyStoreTrustEngineTest("findTrustAnchor negative test: incomplete-able chain") { //$NON-NLS-1$ |
| public void runTest() { |
| testFindTrustAnchor7(); |
| } |
| }, new KeyStoreTrustEngineTest("findTrustAnchor negative test: null chain") { //$NON-NLS-1$ |
| public void runTest() { |
| testFindTrustAnchor8(); |
| } |
| }, |
| /* addTrustAnchor tests */ |
| new KeyStoreTrustEngineTest("addTrustAnchor positive test: add with alias") { //$NON-NLS-1$ |
| public void runTest() { |
| testAddTrustAnchor0(); |
| } |
| }, /* |
| * , new |
| * KeyStoreTrustEngineTest("addTrustAnchor positive test: add with autogenerated alias" |
| * , null) { public void runTest() { testAddTrustAnchor1(); } } |
| */ |
| new KeyStoreTrustEngineTest("addTrustAnchor negative test: null cert specified") { //$NON-NLS-1$ |
| public void runTest() { |
| testAddTrustAnchor2(); |
| } |
| }, new KeyStoreTrustEngineTest("addTrustAnchor negative test: existing cert specified", "ca1_root") { //$NON-NLS-1$ //$NON-NLS-2$ |
| public void runTest() { |
| testAddTrustAnchor3(); |
| } |
| }, new KeyStoreTrustEngineTest("addTrustAnchor negative test: existing alias specified", "ca1_root") { //$NON-NLS-1$ //$NON-NLS-2$ |
| public void runTest() { |
| testAddTrustAnchor4(); |
| } |
| } |
| /* removeTrustAnchor tests */ |
| , new KeyStoreTrustEngineTest("removeTrustAnchor positive test: remove by alias", "ca1_root") { //$NON-NLS-1$ //$NON-NLS-2$ |
| public void runTest() { |
| testRemoveTrustAnchor0(); |
| } |
| }, new KeyStoreTrustEngineTest("removeTrustAnchor positive test: remove by cert", "ca1_root") { //$NON-NLS-1$ //$NON-NLS-2$ |
| public void runTest() { |
| testRemoveTrustAnchor1(); |
| } |
| }, new KeyStoreTrustEngineTest("removeTrustAnchor negative test: cert not found") { //$NON-NLS-1$ |
| public void runTest() { |
| testRemoveTrustAnchor2(); |
| } |
| }, new KeyStoreTrustEngineTest("removeTrustAnchor negative test: by alias not found") { //$NON-NLS-1$ |
| public void runTest() { |
| testRemoveTrustAnchor3(); |
| } |
| }, new KeyStoreTrustEngineTest("removeTrustAnchor negative test: remove by null alias") { //$NON-NLS-1$ |
| public void runTest() { |
| testRemoveTrustAnchor4(); |
| } |
| }, new KeyStoreTrustEngineTest("removeTrustAnchor negative test: remove by null certificate") { //$NON-NLS-1$ |
| public void runTest() { |
| testRemoveTrustAnchor5(); |
| } |
| }, |
| /* getTrustAnchor tests */ |
| new KeyStoreTrustEngineTest("getTrustAnchor positive test: get by alias", "ca1_root") { //$NON-NLS-1$ //$NON-NLS-2$ |
| public void runTest() { |
| testGetTrustAnchor0(); |
| } |
| }, new KeyStoreTrustEngineTest("getTrustAnchor negative test: get by null alias") { //$NON-NLS-1$ |
| public void runTest() { |
| testGetTrustAnchor1(); |
| } |
| }, new KeyStoreTrustEngineTest("getTrustAnchor negative test: does not exist") { //$NON-NLS-1$ |
| public void runTest() { |
| testGetTrustAnchor2(); |
| } |
| }, |
| /* getAliases tests */ |
| new KeyStoreTrustEngineTest("getAliases positive test: get the alias list", "ca1_root", "ca2_root") { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| public void runTest() { |
| testGetAliases0(); |
| } |
| } }; |
| |
| public static Test suite() { |
| TestSuite suite = new TestSuite("Unit tests for TrustEngine"); //$NON-NLS-1$ |
| for (TestCase s_test : s_tests) { |
| suite.addTest(s_test); |
| } |
| return suite; |
| } |
| |
| private static KeyStore supportStore; |
| static { |
| try { |
| URL supportUrl = OSGiTestsActivator.getContext().getBundle().getEntry("test_files/security/keystore.jks"); //$NON-NLS-1$ |
| supportStore = KeyStore.getInstance(TYPE_DEFAULT); |
| supportStore.load(supportUrl.openStream(), PASSWORD_DEFAULT); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| private String[] aliases; |
| private KeyStore testStore; |
| private File testStoreFile; |
| TrustEngine engine; |
| |
| public KeyStoreTrustEngineTest() { |
| // placeholder |
| } |
| |
| public KeyStoreTrustEngineTest(String name, String... aliases) { |
| super(name); |
| this.aliases = aliases; |
| } |
| |
| protected void setUp() throws Exception { |
| if (supportStore == null) { |
| fail("Could not open keystore with test certificates!"); //$NON-NLS-1$ |
| } |
| |
| testStore = KeyStore.getInstance(TYPE_DEFAULT); |
| testStore.load(null, PASSWORD_DEFAULT); |
| if (aliases != null) { |
| for (String alias : aliases) { |
| testStore.setCertificateEntry(alias, getTestCertificate(alias)); |
| } |
| } |
| testStoreFile = File.createTempFile("teststore", "jks"); //$NON-NLS-1$ //$NON-NLS-2$ |
| final FileOutputStream out = new FileOutputStream(testStoreFile); |
| try { |
| testStore.store(out, PASSWORD_DEFAULT); |
| } finally { |
| safeClose(out); |
| } |
| engine = new KeyStoreTrustEngine(testStoreFile.getPath(), TYPE_DEFAULT, PASSWORD_DEFAULT, "teststore", null); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Closes a stream and ignores any resulting exception. This is useful when |
| * doing stream cleanup in a finally block where secondary exceptions are not |
| * worth logging. |
| */ |
| protected static void safeClose(OutputStream out) { |
| try { |
| if (out != null) |
| out.close(); |
| } catch (IOException e) { |
| // ignore |
| } |
| } |
| |
| protected void tearDown() { |
| engine = null; |
| testStore = null; |
| testStoreFile.delete(); |
| } |
| |
| private static Certificate getTestCertificate(String alias) throws KeyStoreException { |
| return supportStore.getCertificate(alias); |
| } |
| |
| private static Certificate[] getTestCertificateChain(String... aliases) throws KeyStoreException { |
| ArrayList<Certificate> certs = new ArrayList<>(aliases.length); |
| for (String alias : aliases) { |
| certs.add(getTestCertificate(alias)); |
| } |
| return certs.toArray(new Certificate[] {}); |
| } |
| |
| // findTrustAnchor positive test: self signed trusted |
| public void testFindTrustAnchor0() { |
| try { |
| Certificate cert = engine.findTrustAnchor(new Certificate[] { getTestCertificate("ca1_root") }); //$NON-NLS-1$ |
| assertNotNull("Did not return a cert for self-signed case", cert); //$NON-NLS-1$ |
| assertEquals("Input and output certs not equal for self-signed case", cert, getTestCertificate("ca1_root")); //$NON-NLS-1$ //$NON-NLS-2$ |
| } catch (Throwable t) { |
| fail("Unexpected exception testing trusted self-signed cert: " + t.getMessage()); //$NON-NLS-1$ |
| } |
| } |
| |
| // findTrustAnchor positive test: chain with root trusted |
| public void testFindTrustAnchor1() { |
| try { |
| Certificate cert = engine.findTrustAnchor(getTestCertificateChain("ca1_leafb", "ca1_ou", "ca1_root")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| assertNotNull("Certificate did not come back in trusted root case", cert); //$NON-NLS-1$ |
| assertEquals("Output cert is not root trusted cert", cert, getTestCertificate("ca1_root")); //$NON-NLS-1$ //$NON-NLS-2$ |
| } catch (Throwable t) { |
| fail("Unexpected exception testing trusted root from complete chain: " + t.getMessage()); //$NON-NLS-1$ |
| } |
| } |
| |
| // findTrustAnchor positive test: chain with intermediate trusted |
| public void testFindTrustAnchor2() { |
| try { |
| Certificate cert = engine.findTrustAnchor(getTestCertificateChain("ca1_leafb", "ca1_ou", "ca1_root")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| assertNotNull("Certificate did not come back in trusted intermediate case", cert); //$NON-NLS-1$ |
| assertEquals("Output cert is not intermediate trusted cert", cert, getTestCertificate("ca1_ou")); //$NON-NLS-1$ //$NON-NLS-2$ |
| } catch (Throwable t) { |
| fail("Unexpected exception testing trusted root from complete chain: " + t.getMessage()); //$NON-NLS-1$ |
| } |
| } |
| |
| // findTrustAnchor positive test: chain with leaf trusted |
| public void testFindTrustAnchor3() { |
| try { |
| Certificate cert = engine.findTrustAnchor(getTestCertificateChain("ca1_leafb", "ca1_ou", "ca1_root")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| assertNotNull("Certificate did not come back in trusted leaf case", cert); //$NON-NLS-1$ |
| assertEquals("Output cert is not leaf trusted cert", cert, getTestCertificate("ca1_leafb")); //$NON-NLS-1$ //$NON-NLS-2$ |
| } catch (Throwable t) { |
| fail("Unexpected exception testing trusted root from complete chain: " + t.getMessage()); //$NON-NLS-1$ |
| } |
| } |
| |
| // findTrustAnchor negative test: untrusted self signed |
| public void testFindTrustAnchor4() { |
| try { |
| Certificate cert = engine.findTrustAnchor(new Certificate[] { getTestCertificate("ca2_root") }); //$NON-NLS-1$ |
| assertNull("Incorrectly returned a certificate for untrusted self-signed case", cert); //$NON-NLS-1$ |
| } catch (Throwable t) { |
| fail("Unexpected exception testing untrusted self-signed cert: " + t.getMessage()); //$NON-NLS-1$ |
| } |
| } |
| |
| // findTrustAnchor negative test: untrusted chain |
| public void testFindTrustAnchor5() { |
| try { |
| Certificate cert = engine.findTrustAnchor(getTestCertificateChain("ca2_leafb", "ca2_ou", "ca2_root")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| assertNull("Incorrectly returned a certificate for untrusted chain case", cert); //$NON-NLS-1$ |
| } catch (Throwable t) { |
| fail("Unexpected exception testing untrusted chain: " + t.getMessage()); //$NON-NLS-1$ |
| } |
| } |
| |
| // findTrustAnchor negative test: invalid chain |
| public void testFindTrustAnchor6() { |
| try { |
| Certificate cert = engine.findTrustAnchor(getTestCertificateChain("ca2_leafa", "ca1_root")); //$NON-NLS-1$ //$NON-NLS-2$ |
| assertNull("Incorrectly returned a certificate on invalid certificate chain", cert); //$NON-NLS-1$ |
| } catch (Throwable t) { |
| assertNull("Incorrectly thrown exception thrown on invalid certificate chain", t); //$NON-NLS-1$ |
| } |
| } |
| |
| // findTrustAnchor negative test: incomplete-able chain |
| public void testFindTrustAnchor7() { |
| try { |
| Certificate cert = engine.findTrustAnchor(getTestCertificateChain("ca1_leafb", "ca1_root")); //$NON-NLS-1$ //$NON-NLS-2$ |
| assertNull("Incorrectly returned a certificate on incomplete-able certificate chain", cert); //$NON-NLS-1$ |
| } catch (Throwable t) { |
| assertNull("Incorrectly thrown exception thrown on incomplete-able certificate chain", t); //$NON-NLS-1$ |
| } |
| } |
| |
| // findTrustAnchor negative test: null chain |
| public void testFindTrustAnchor8() { |
| try { |
| engine.findTrustAnchor(null); |
| fail("Did not throw IllegalArgumentException on NULL certificate"); //$NON-NLS-1$ |
| } catch (Throwable t) { |
| assertTrue("Incorrect exception thrown on NULL certificate", t instanceof IllegalArgumentException); //$NON-NLS-1$ |
| } |
| } |
| |
| // testAddTrustAnchor positive test: add with alias |
| public void testAddTrustAnchor0() { |
| try { |
| String alias = engine.addTrustAnchor(getTestCertificate("ca1_root"), "ca1_root"); //$NON-NLS-1$ //$NON-NLS-2$ |
| assertEquals("Alias returned does not equal alias input", alias, "ca1_root"); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| } catch (Throwable t) { |
| fail("Unexpected exception adding trusted root: " + t.getMessage()); //$NON-NLS-1$ |
| } |
| } |
| |
| // testAddTrustAnchor positive test: add with autogenerated alias |
| public void testAddTrustAnchor1() { |
| try { |
| String alias = engine.addTrustAnchor(getTestCertificate("ca1_root"), null); //$NON-NLS-1$ |
| assertNotNull("Generated alias was not correctly returned", alias); //$NON-NLS-1$ |
| } catch (Throwable t) { |
| fail("Unexpected exception adding trusted root (autogen alias): " + t.getMessage()); //$NON-NLS-1$ |
| } |
| } |
| |
| // testAddTrustAnchor negative test: null cert specified |
| public void testAddTrustAnchor2() { |
| try { |
| engine.addTrustAnchor(null, "ca1_root"); //$NON-NLS-1$ |
| fail("Did not throw IllegalArgumentException on NULL certificate"); //$NON-NLS-1$ |
| } catch (Throwable t) { |
| assertTrue("Incorrect exception thrown on NULL certificate", t instanceof IllegalArgumentException); //$NON-NLS-1$ |
| } |
| } |
| |
| // testAddTrustAnchor negative test: existing cert specified |
| public void testAddTrustAnchor3() { |
| try { |
| engine.addTrustAnchor(getTestCertificate("ca1_root"), "new_root"); //$NON-NLS-1$ //$NON-NLS-2$ |
| assertTrue("Did not throw CertificateException on duplicate cert", false); //$NON-NLS-1$ |
| } catch (Throwable t) { |
| assertTrue("Incorrect exception thrown on duplicate cert", t instanceof CertificateException); //$NON-NLS-1$ |
| return; |
| } |
| fail("Expected exception when adding trust anchor"); //$NON-NLS-1$ |
| } |
| |
| // testAddTrustAnchor negative test: existing alias specified |
| public void testAddTrustAnchor4() { |
| try { |
| engine.addTrustAnchor(getTestCertificate("ca2_root"), "ca1_root"); //$NON-NLS-1$ //$NON-NLS-2$ |
| assertTrue("Did not throw CertificateException on duplicate alias", false); //$NON-NLS-1$ |
| } catch (Throwable t) { |
| assertTrue("Incorrect exception thrown on duplicate alias", t instanceof CertificateException); //$NON-NLS-1$ |
| return; |
| } |
| fail("Expected exception when adding trust anchor"); //$NON-NLS-1$ |
| } |
| |
| // removeTrustAnchor positive test: remove by alias |
| public void testRemoveTrustAnchor0() { |
| try { |
| engine.removeTrustAnchor("ca1_root"); //$NON-NLS-1$ |
| } catch (Throwable t) { |
| fail("Unexpected exception thrown when removing by alias: " + t.getMessage()); //$NON-NLS-1$ |
| } |
| } |
| |
| // removeTrustAnchor positive test: remove by cert |
| public void testRemoveTrustAnchor1() { |
| try { |
| engine.removeTrustAnchor(getTestCertificate("ca1_root")); //$NON-NLS-1$ |
| } catch (Throwable t) { |
| fail("Unexpected exception thrown when removing by cert: " + t.getMessage()); //$NON-NLS-1$ |
| } |
| } |
| |
| // removeTrustAnchor negative test: cert not found |
| public void testRemoveTrustAnchor2() { |
| try { |
| engine.removeTrustAnchor(getTestCertificate("ca1_root")); //$NON-NLS-1$ |
| fail("Did not throw CertificateException on cert not found"); //$NON-NLS-1$ |
| } catch (Throwable t) { |
| assertTrue("Incorrect exception thrown on remove by cert", t instanceof CertificateException); //$NON-NLS-1$ |
| } |
| } |
| |
| // removeTrustAnchor negative test: by alias not found |
| public void testRemoveTrustAnchor3() { |
| try { |
| engine.removeTrustAnchor("ca2_root"); //$NON-NLS-1$ |
| assertTrue("Did not throw CertificateException on alias not found", false); //$NON-NLS-1$ |
| } catch (Throwable t) { |
| assertTrue("Incorrect exception thrown on remove by alias", t instanceof CertificateException); //$NON-NLS-1$ |
| return; |
| } |
| fail("Expected exception when removing trust anchor"); //$NON-NLS-1$ |
| } |
| |
| // removeTrustAnchor negative test: remove by null alias |
| public void testRemoveTrustAnchor4() { |
| try { |
| engine.removeTrustAnchor((String) null); |
| fail("Did not throw CertificateException on alias null"); //$NON-NLS-1$ |
| } catch (Throwable t) { |
| assertTrue("Incorrect exception thrown on remove by null alias", t instanceof IllegalArgumentException); //$NON-NLS-1$ |
| } |
| } |
| |
| // removeTrustAnchor negative test: remove by null certificate |
| public void testRemoveTrustAnchor5() { |
| try { |
| engine.removeTrustAnchor((Certificate) null); |
| fail("Did not throw IllegalArgumentException on remove by cert null"); //$NON-NLS-1$ |
| } catch (Throwable t) { |
| assertTrue("Incorrect exception thrown on remove by null cert", t instanceof IllegalArgumentException); //$NON-NLS-1$ |
| } |
| } |
| |
| // getTrustAnchor positive test: get by alias |
| public void testGetTrustAnchor0() { |
| try { |
| Certificate cert = engine.getTrustAnchor("ca1_root"); //$NON-NLS-1$ |
| assertEquals("Did not get expected certificate", getTestCertificate("ca1_root"), cert); //$NON-NLS-1$ //$NON-NLS-2$ |
| } catch (Throwable t) { |
| fail("Unexpected exception when retrieving trust anchor: " + t.getMessage()); //$NON-NLS-1$ |
| } |
| } |
| |
| // getTrustAnchor negative test: get by null alias |
| public void testGetTrustAnchor1() { |
| try { |
| engine.getTrustAnchor(null); |
| fail("Did not throw IllegalArgumentException on get by alias null"); //$NON-NLS-1$ |
| } catch (Throwable t) { |
| assertTrue("Incorrect exception thrown on remove by null alias", t instanceof IllegalArgumentException); //$NON-NLS-1$ |
| } |
| } |
| |
| // getTrustAnchor negative test: does not exist |
| public void testGetTrustAnchor2() { |
| try { |
| Certificate cert = engine.getTrustAnchor("ca2_root"); //$NON-NLS-1$ |
| assertNull("Incorrectly returned a certificate on certificate does not exist", cert); //$NON-NLS-1$ |
| } catch (Throwable t) { |
| assertNull("Incorrectly thrown exception on alias does not exist", t); //$NON-NLS-1$ |
| return; |
| } |
| } |
| |
| // getAliases positive test: get the alias list |
| public void testGetAliases0() { |
| try { |
| engine.getAliases(); |
| } catch (Throwable t) { |
| fail("Unexpected exception when retrieving alias list: " + t.getMessage()); //$NON-NLS-1$ |
| } |
| |
| } |
| // TODO: thread safety tests |
| // TODO: performance tests |
| } |