@@ -1832,5 +1832,171 @@ public void testStoreToByteArrayThreaded()
18321832 }
18331833 }
18341834 }
1835+
1836+ /**
1837+ * Test concurrent access to engineGetCertificateAlias() while other
1838+ * threads are modifying the same KeyStore instance via setKeyEntry()
1839+ * and setCertificateEntry().
1840+ */
1841+ @ Test
1842+ public void testGetCertificateAliasConcurrent ()
1843+ throws KeyStoreException , IOException , FileNotFoundException ,
1844+ NoSuchProviderException , NoSuchAlgorithmException ,
1845+ CertificateException , InvalidKeySpecException ,
1846+ UnrecoverableKeyException , InterruptedException {
1847+
1848+ int numThreads = 10 ;
1849+ int iterationsPerThread = 100 ;
1850+ final KeyStore store = KeyStore .getInstance (storeType ,
1851+ storeProvider );
1852+ final LinkedBlockingQueue <Integer > results =
1853+ new LinkedBlockingQueue <>();
1854+ final CountDownLatch startLatch = new CountDownLatch (1 );
1855+ final CountDownLatch doneLatch = new CountDownLatch (numThreads );
1856+
1857+ /* Initialize KeyStore with some initial entries */
1858+ store .load (null , null );
1859+ store .setKeyEntry ("initialKey" , serverKeyRsa ,
1860+ storePass .toCharArray (), rsaServerChain );
1861+ store .setCertificateEntry ("initialCert" , serverCertRsa );
1862+
1863+ /* Create thread pool */
1864+ ExecutorService executor = Executors .newFixedThreadPool (numThreads );
1865+
1866+ /* Start reader threads that call getCertificateAlias() */
1867+ for (int i = 0 ; i < numThreads / 2 ; i ++) {
1868+ final int threadId = i ;
1869+ executor .submit (new Runnable () {
1870+ @ Override
1871+ public void run () {
1872+ try {
1873+ /* Wait for all threads to be ready */
1874+ startLatch .await ();
1875+
1876+ for (int j = 0 ; j < iterationsPerThread ; j ++) {
1877+ /* Look up alias for existing certificate */
1878+ String alias = store .getCertificateAlias (
1879+ serverCertRsa );
1880+ if (alias == null ) {
1881+ /* Certificate should exist, either as
1882+ * initialCert or in a key entry chain */
1883+ results .add (1 );
1884+ return ;
1885+ }
1886+
1887+ /* Verify alias is valid */
1888+ if (!alias .equals ("initialCert" ) &&
1889+ !alias .startsWith ("writerKey" )) {
1890+ results .add (1 );
1891+ return ;
1892+ }
1893+
1894+ /* Look up alias for certificate that might
1895+ * be added/removed by writers */
1896+ alias = store .getCertificateAlias (
1897+ clientCertRsa );
1898+ /* Result can be null or valid alias, both OK */
1899+ if (alias != null &&
1900+ !alias .startsWith ("writerCert" )) {
1901+ results .add (1 );
1902+ return ;
1903+ }
1904+ }
1905+
1906+ /* Success */
1907+ results .add (0 );
1908+
1909+ } catch (Exception e ) {
1910+ e .printStackTrace ();
1911+ results .add (1 );
1912+
1913+ } finally {
1914+ doneLatch .countDown ();
1915+ }
1916+ }
1917+ });
1918+ }
1919+
1920+ /* Start writer threads that modify KeyStore */
1921+ for (int i = numThreads / 2 ; i < numThreads ; i ++) {
1922+ final int threadId = i ;
1923+ executor .submit (new Runnable () {
1924+ @ Override
1925+ public void run () {
1926+ try {
1927+ /* Wait for all threads to be ready */
1928+ startLatch .await ();
1929+
1930+ for (int j = 0 ; j < iterationsPerThread ; j ++) {
1931+ String keyAlias = "writerKey" + threadId +
1932+ "_" + j ;
1933+ String certAlias = "writerCert" + threadId +
1934+ "_" + j ;
1935+
1936+ /* Add key entry */
1937+ store .setKeyEntry (keyAlias , serverKeyRsa ,
1938+ storePass .toCharArray (), rsaServerChain );
1939+
1940+ /* Add certificate entry */
1941+ store .setCertificateEntry (certAlias ,
1942+ clientCertRsa );
1943+
1944+ /* Delete some entries periodically */
1945+ if (j % 10 == 0 && j > 0 ) {
1946+ String oldKeyAlias = "writerKey" + threadId +
1947+ "_" + (j - 10 );
1948+ String oldCertAlias = "writerCert" +
1949+ threadId + "_" + (j - 10 );
1950+ try {
1951+ store .deleteEntry (oldKeyAlias );
1952+ store .deleteEntry (oldCertAlias );
1953+ } catch (KeyStoreException e ) {
1954+ /* Entry might not exist, ignore */
1955+ }
1956+ }
1957+ }
1958+
1959+ /* Success */
1960+ results .add (0 );
1961+
1962+ } catch (Exception e ) {
1963+ e .printStackTrace ();
1964+ results .add (1 );
1965+
1966+ } finally {
1967+ doneLatch .countDown ();
1968+ }
1969+ }
1970+ });
1971+ }
1972+
1973+ /* Start all threads simultaneously */
1974+ startLatch .countDown ();
1975+
1976+ /* Wait for all threads to complete */
1977+ doneLatch .await ();
1978+
1979+ /* Shutdown executor */
1980+ executor .shutdown ();
1981+
1982+ /* Check results - all threads should have succeeded */
1983+ assertEquals ("Expected " + numThreads + " results" ,
1984+ numThreads , results .size ());
1985+
1986+ Iterator <Integer > listIterator = results .iterator ();
1987+ while (listIterator .hasNext ()) {
1988+ Integer cur = listIterator .next ();
1989+ if (cur != 0 ) {
1990+ fail ("Threading error in concurrent " +
1991+ "getCertificateAlias test" );
1992+ }
1993+ }
1994+
1995+ /* Verify KeyStore is still in valid state */
1996+ assertNotNull (store .getCertificate ("initialCert" ));
1997+ Certificate [] chain = store .getCertificateChain ("initialKey" );
1998+ assertNotNull (chain );
1999+ assertTrue (chain .length > 0 );
2000+ }
18352001}
18362002
0 commit comments