Skip to content

Commit d7339f0

Browse files
authored
Merge pull request #168 from cconlon/wksSecretKeyIterations
Fix SecretKey decryption to use stored PBKDF2 iteration count in WKS
2 parents 5ccd2ed + de50da5 commit d7339f0

File tree

2 files changed

+377
-1
lines changed

2 files changed

+377
-1
lines changed

src/main/java/com/wolfssl/provider/jce/WolfSSLKeyStore.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2773,7 +2773,7 @@ protected synchronized byte[] getDecryptedKey(char[] password)
27732773
* PBKDF2-HMAC-SHA512. Generate a 96 byte key in total, split
27742774
* between 32-byte AES-CBC-256 and 64-byte HMAC-SHA512 key */
27752775
derivedKey = deriveKeyFromPassword(password, this.kdfSalt,
2776-
WKS_PBKDF2_ITERATION_COUNT,
2776+
this.kdfIterations,
27772777
WKS_ENC_KEY_LENGTH + WKS_HMAC_KEY_LENGTH);
27782778

27792779
if (derivedKey == null) {

src/test/java/com/wolfssl/provider/jce/test/WolfSSLKeyStoreTest.java

Lines changed: 376 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1832,5 +1832,381 @@ public void testStoreToByteArrayThreaded()
18321832
}
18331833
}
18341834
}
1835+
1836+
/*
1837+
* Test that SecretKey entries can be stored with one iteration count,
1838+
* then retrieved successfully after the iteration count changes.
1839+
* This verifies that SecretKey decryption uses the stored kdfIterations
1840+
* instead of the current global WKS_PBKDF2_ITERATION_COUNT.
1841+
*/
1842+
@Test
1843+
public void testSecretKeyWithIterationCountChange()
1844+
throws KeyStoreException, IOException, FileNotFoundException,
1845+
NoSuchProviderException, NoSuchAlgorithmException,
1846+
CertificateException, InvalidKeySpecException,
1847+
UnrecoverableKeyException {
1848+
1849+
KeyStore store = null;
1850+
KeyGenerator kg = null;
1851+
SecretKey aesKey1 = null;
1852+
SecretKey aesKey2 = null;
1853+
SecretKey hmacKey = null;
1854+
Key keyOut = null;
1855+
ByteArrayOutputStream baos = null;
1856+
ByteArrayInputStream bais = null;
1857+
String origIterationCount =
1858+
Security.getProperty("wolfjce.wks.iterationCount");
1859+
1860+
try {
1861+
/* Create KeyStore and load empty */
1862+
store = KeyStore.getInstance(storeType, storeProvider);
1863+
store.load(null, storePass.toCharArray());
1864+
1865+
/* Set iteration count to 20000 */
1866+
Security.setProperty("wolfjce.wks.iterationCount", "20000");
1867+
1868+
/* Generate and store AES key with 20000 iterations */
1869+
kg = KeyGenerator.getInstance("AES");
1870+
assertNotNull(kg);
1871+
kg.init(256, rand);
1872+
aesKey1 = kg.generateKey();
1873+
assertNotNull(aesKey1);
1874+
store.setKeyEntry("aesKey1", aesKey1,
1875+
storePass.toCharArray(), null);
1876+
1877+
/* Store KeyStore to byte array */
1878+
baos = new ByteArrayOutputStream();
1879+
store.store(baos, storePass.toCharArray());
1880+
1881+
/* Change iteration count to 15000 (LOWER) */
1882+
Security.setProperty("wolfjce.wks.iterationCount", "15000");
1883+
1884+
/* Load KeyStore from byte array */
1885+
bais = new ByteArrayInputStream(baos.toByteArray());
1886+
store = KeyStore.getInstance(storeType, storeProvider);
1887+
store.load(bais, storePass.toCharArray());
1888+
1889+
/* Retrieve and verify key works with lower iteration count */
1890+
keyOut = store.getKey("aesKey1", storePass.toCharArray());
1891+
assertNotNull(keyOut);
1892+
assertTrue(keyOut instanceof SecretKey);
1893+
assertTrue(Arrays.equals(aesKey1.getEncoded(),
1894+
keyOut.getEncoded()));
1895+
1896+
/* Add new key with lower iteration count (15000) */
1897+
aesKey2 = kg.generateKey();
1898+
assertNotNull(aesKey2);
1899+
store.setKeyEntry("aesKey2", aesKey2,
1900+
storePass.toCharArray(), null);
1901+
1902+
/* Store KeyStore again */
1903+
baos = new ByteArrayOutputStream();
1904+
store.store(baos, storePass.toCharArray());
1905+
1906+
/* Change iteration count to 30000 (higher than both) */
1907+
Security.setProperty("wolfjce.wks.iterationCount", "30000");
1908+
1909+
/* Load KeyStore from byte array */
1910+
bais = new ByteArrayInputStream(baos.toByteArray());
1911+
store = KeyStore.getInstance(storeType, storeProvider);
1912+
store.load(bais, storePass.toCharArray());
1913+
1914+
/* Verify both keys work with higher iteration count set */
1915+
keyOut = store.getKey("aesKey1", storePass.toCharArray());
1916+
assertNotNull(keyOut);
1917+
assertTrue(keyOut instanceof SecretKey);
1918+
assertTrue(Arrays.equals(aesKey1.getEncoded(),
1919+
keyOut.getEncoded()));
1920+
1921+
keyOut = store.getKey("aesKey2", storePass.toCharArray());
1922+
assertNotNull(keyOut);
1923+
assertTrue(keyOut instanceof SecretKey);
1924+
assertTrue(Arrays.equals(aesKey2.getEncoded(),
1925+
keyOut.getEncoded()));
1926+
1927+
/* Add HMAC key with new higher iteration count (30000) */
1928+
kg = KeyGenerator.getInstance("HmacSHA256");
1929+
assertNotNull(kg);
1930+
kg.init(256, rand);
1931+
hmacKey = kg.generateKey();
1932+
assertNotNull(hmacKey);
1933+
store.setKeyEntry("hmacKey", hmacKey,
1934+
storePass.toCharArray(), null);
1935+
1936+
/* Verify all three keys work together */
1937+
keyOut = store.getKey("aesKey1", storePass.toCharArray());
1938+
assertTrue(Arrays.equals(aesKey1.getEncoded(),
1939+
keyOut.getEncoded()));
1940+
keyOut = store.getKey("aesKey2", storePass.toCharArray());
1941+
assertTrue(Arrays.equals(aesKey2.getEncoded(),
1942+
keyOut.getEncoded()));
1943+
keyOut = store.getKey("hmacKey", storePass.toCharArray());
1944+
assertTrue(Arrays.equals(hmacKey.getEncoded(),
1945+
keyOut.getEncoded()));
1946+
1947+
} finally {
1948+
/* Reset iteration count back to original value */
1949+
if (origIterationCount != null) {
1950+
Security.setProperty("wolfjce.wks.iterationCount",
1951+
origIterationCount);
1952+
}
1953+
else {
1954+
Security.setProperty("wolfjce.wks.iterationCount", "10000");
1955+
}
1956+
}
1957+
}
1958+
1959+
/*
1960+
* Test that PrivateKey entries can be stored with one iteration count,
1961+
* then retrieved successfully after the iteration count changes.
1962+
* This is a regression test to ensure PrivateKey continues to work
1963+
* correctly alongside the SecretKey fix.
1964+
*/
1965+
@Test
1966+
public void testPrivateKeyWithIterationCountChange()
1967+
throws KeyStoreException, IOException, FileNotFoundException,
1968+
NoSuchProviderException, NoSuchAlgorithmException,
1969+
CertificateException, InvalidKeySpecException,
1970+
UnrecoverableKeyException {
1971+
1972+
KeyStore store = null;
1973+
PrivateKey keyOut = null;
1974+
Certificate[] chainOut = null;
1975+
ByteArrayOutputStream baos = null;
1976+
ByteArrayInputStream bais = null;
1977+
String origIterationCount =
1978+
Security.getProperty("wolfjce.wks.iterationCount");
1979+
1980+
try {
1981+
/* Create KeyStore and load empty */
1982+
store = KeyStore.getInstance(storeType, storeProvider);
1983+
store.load(null, storePass.toCharArray());
1984+
1985+
/* Set iteration count to 20000 */
1986+
Security.setProperty("wolfjce.wks.iterationCount", "20000");
1987+
1988+
/* Store RSA PrivateKey with cert chain at 20000 iterations */
1989+
store.setKeyEntry("serverRsa", serverKeyRsa,
1990+
storePass.toCharArray(), rsaServerChain);
1991+
assertEquals(1, store.size());
1992+
assertTrue(store.isKeyEntry("serverRsa"));
1993+
1994+
/* Store KeyStore to byte array */
1995+
baos = new ByteArrayOutputStream();
1996+
store.store(baos, storePass.toCharArray());
1997+
1998+
/* Change iteration count to 15000 (LOWER) */
1999+
Security.setProperty("wolfjce.wks.iterationCount", "15000");
2000+
2001+
/* Load KeyStore from byte array */
2002+
bais = new ByteArrayInputStream(baos.toByteArray());
2003+
store = KeyStore.getInstance(storeType, storeProvider);
2004+
store.load(bais, storePass.toCharArray());
2005+
2006+
/* Retrieve and verify key works with lower iteration count */
2007+
keyOut = (PrivateKey)store.getKey("serverRsa",
2008+
storePass.toCharArray());
2009+
assertNotNull(keyOut);
2010+
assertTrue(keyOut instanceof PrivateKey);
2011+
assertTrue(Arrays.equals(serverKeyRsa.getEncoded(),
2012+
keyOut.getEncoded()));
2013+
chainOut = store.getCertificateChain("serverRsa");
2014+
assertNotNull(chainOut);
2015+
assertTrue(Arrays.equals(rsaServerChain, chainOut));
2016+
2017+
/* Add ECC PrivateKey with lower iteration count (15000) */
2018+
store.setKeyEntry("serverEcc", serverKeyEcc,
2019+
storePass.toCharArray(), eccServerChain);
2020+
2021+
/* Store KeyStore again */
2022+
baos = new ByteArrayOutputStream();
2023+
store.store(baos, storePass.toCharArray());
2024+
2025+
/* Change iteration count to 30000 (HIGHER than both) */
2026+
Security.setProperty("wolfjce.wks.iterationCount", "30000");
2027+
2028+
/* Load KeyStore from byte array */
2029+
bais = new ByteArrayInputStream(baos.toByteArray());
2030+
store = KeyStore.getInstance(storeType, storeProvider);
2031+
store.load(bais, storePass.toCharArray());
2032+
2033+
/* Verify both keys work with higher iteration count set */
2034+
keyOut = (PrivateKey)store.getKey("serverRsa",
2035+
storePass.toCharArray());
2036+
assertNotNull(keyOut);
2037+
assertTrue(Arrays.equals(serverKeyRsa.getEncoded(),
2038+
keyOut.getEncoded()));
2039+
chainOut = store.getCertificateChain("serverRsa");
2040+
assertTrue(Arrays.equals(rsaServerChain, chainOut));
2041+
2042+
keyOut = (PrivateKey)store.getKey("serverEcc",
2043+
storePass.toCharArray());
2044+
assertNotNull(keyOut);
2045+
assertTrue(Arrays.equals(serverKeyEcc.getEncoded(),
2046+
keyOut.getEncoded()));
2047+
chainOut = store.getCertificateChain("serverEcc");
2048+
assertTrue(Arrays.equals(eccServerChain, chainOut));
2049+
2050+
} finally {
2051+
/* Reset iteration count back to original value */
2052+
if (origIterationCount != null) {
2053+
Security.setProperty("wolfjce.wks.iterationCount",
2054+
origIterationCount);
2055+
}
2056+
else {
2057+
Security.setProperty("wolfjce.wks.iterationCount", "10000");
2058+
}
2059+
}
2060+
}
2061+
2062+
/*
2063+
* Test that a KeyStore can contain PrivateKey, SecretKey, and
2064+
* Certificate entries encrypted with different iteration counts,
2065+
* and all can be retrieved correctly regardless of what the current
2066+
* iteration count property is set to.
2067+
*/
2068+
@Test
2069+
public void testMixedIterationCountsInSameKeyStore()
2070+
throws KeyStoreException, IOException, FileNotFoundException,
2071+
NoSuchProviderException, NoSuchAlgorithmException,
2072+
CertificateException, InvalidKeySpecException,
2073+
UnrecoverableKeyException {
2074+
2075+
KeyStore store = null;
2076+
KeyGenerator kg = null;
2077+
SecretKey aesKey1 = null;
2078+
SecretKey aesKey2 = null;
2079+
SecretKey hmacKey = null;
2080+
Key keyOut = null;
2081+
PrivateKey privKeyOut = null;
2082+
Certificate[] chainOut = null;
2083+
Certificate certOut = null;
2084+
ByteArrayOutputStream baos = null;
2085+
ByteArrayInputStream bais = null;
2086+
String origIterationCount =
2087+
Security.getProperty("wolfjce.wks.iterationCount");
2088+
2089+
try {
2090+
/* Create KeyStore and load empty */
2091+
store = KeyStore.getInstance(storeType, storeProvider);
2092+
store.load(null, storePass.toCharArray());
2093+
2094+
/* Set iteration count to 12000 and add first entries */
2095+
Security.setProperty("wolfjce.wks.iterationCount", "12000");
2096+
2097+
kg = KeyGenerator.getInstance("AES");
2098+
kg.init(256, rand);
2099+
aesKey1 = kg.generateKey();
2100+
store.setKeyEntry("aesKey12k", aesKey1,
2101+
storePass.toCharArray(), null);
2102+
store.setKeyEntry("rsaKey12k", serverKeyRsa,
2103+
storePass.toCharArray(), rsaServerChain);
2104+
2105+
/* Change to 18000 and add more entries */
2106+
Security.setProperty("wolfjce.wks.iterationCount", "18000");
2107+
2108+
kg = KeyGenerator.getInstance("HmacSHA256");
2109+
kg.init(256, rand);
2110+
hmacKey = kg.generateKey();
2111+
store.setKeyEntry("hmacKey18k", hmacKey,
2112+
storePass.toCharArray(), null);
2113+
store.setKeyEntry("eccKey18k", serverKeyEcc,
2114+
storePass.toCharArray(), eccServerChain);
2115+
2116+
/* Change to 25000 and add more entries */
2117+
Security.setProperty("wolfjce.wks.iterationCount", "25000");
2118+
2119+
kg = KeyGenerator.getInstance("AES");
2120+
kg.init(128, rand);
2121+
aesKey2 = kg.generateKey();
2122+
store.setKeyEntry("aesKey25k", aesKey2,
2123+
storePass.toCharArray(), null);
2124+
store.setCertificateEntry("clientCertRsa", clientCertRsa);
2125+
2126+
/* Store KeyStore to byte array with 25000 iteration count */
2127+
baos = new ByteArrayOutputStream();
2128+
store.store(baos, storePass.toCharArray());
2129+
2130+
/* Change iteration count to something completely different */
2131+
Security.setProperty("wolfjce.wks.iterationCount", "35000");
2132+
2133+
/* Load KeyStore */
2134+
bais = new ByteArrayInputStream(baos.toByteArray());
2135+
store = KeyStore.getInstance(storeType, storeProvider);
2136+
store.load(bais, storePass.toCharArray());
2137+
2138+
/* Verify all entries can be retrieved correctly with 35000 set */
2139+
assertEquals(6, store.size());
2140+
2141+
/* Verify 12000-iteration entries */
2142+
keyOut = store.getKey("aesKey12k", storePass.toCharArray());
2143+
assertNotNull(keyOut);
2144+
assertTrue(Arrays.equals(aesKey1.getEncoded(),
2145+
keyOut.getEncoded()));
2146+
2147+
privKeyOut = (PrivateKey)store.getKey("rsaKey12k",
2148+
storePass.toCharArray());
2149+
assertNotNull(privKeyOut);
2150+
assertTrue(Arrays.equals(serverKeyRsa.getEncoded(),
2151+
privKeyOut.getEncoded()));
2152+
chainOut = store.getCertificateChain("rsaKey12k");
2153+
assertTrue(Arrays.equals(rsaServerChain, chainOut));
2154+
2155+
/* Verify 18000-iteration entries */
2156+
keyOut = store.getKey("hmacKey18k", storePass.toCharArray());
2157+
assertNotNull(keyOut);
2158+
assertTrue(Arrays.equals(hmacKey.getEncoded(),
2159+
keyOut.getEncoded()));
2160+
2161+
privKeyOut = (PrivateKey)store.getKey("eccKey18k",
2162+
storePass.toCharArray());
2163+
assertNotNull(privKeyOut);
2164+
assertTrue(Arrays.equals(serverKeyEcc.getEncoded(),
2165+
privKeyOut.getEncoded()));
2166+
chainOut = store.getCertificateChain("eccKey18k");
2167+
assertTrue(Arrays.equals(eccServerChain, chainOut));
2168+
2169+
/* Verify 25000-iteration entries */
2170+
keyOut = store.getKey("aesKey25k", storePass.toCharArray());
2171+
assertNotNull(keyOut);
2172+
assertTrue(Arrays.equals(aesKey2.getEncoded(),
2173+
keyOut.getEncoded()));
2174+
2175+
certOut = store.getCertificate("clientCertRsa");
2176+
assertNotNull(certOut);
2177+
assertEquals(clientCertRsa, certOut);
2178+
2179+
/* Change iteration count again and verify still works */
2180+
Security.setProperty("wolfjce.wks.iterationCount", "11000");
2181+
2182+
/* Re-store and reload with new iteration count */
2183+
baos = new ByteArrayOutputStream();
2184+
store.store(baos, storePass.toCharArray());
2185+
bais = new ByteArrayInputStream(baos.toByteArray());
2186+
store = KeyStore.getInstance(storeType, storeProvider);
2187+
store.load(bais, storePass.toCharArray());
2188+
2189+
/* Verify all original entries still work */
2190+
keyOut = store.getKey("aesKey12k", storePass.toCharArray());
2191+
assertTrue(Arrays.equals(aesKey1.getEncoded(),
2192+
keyOut.getEncoded()));
2193+
keyOut = store.getKey("hmacKey18k", storePass.toCharArray());
2194+
assertTrue(Arrays.equals(hmacKey.getEncoded(),
2195+
keyOut.getEncoded()));
2196+
keyOut = store.getKey("aesKey25k", storePass.toCharArray());
2197+
assertTrue(Arrays.equals(aesKey2.getEncoded(),
2198+
keyOut.getEncoded()));
2199+
2200+
} finally {
2201+
/* Reset iteration count back to original value */
2202+
if (origIterationCount != null) {
2203+
Security.setProperty("wolfjce.wks.iterationCount",
2204+
origIterationCount);
2205+
}
2206+
else {
2207+
Security.setProperty("wolfjce.wks.iterationCount", "10000");
2208+
}
2209+
}
2210+
}
18352211
}
18362212

0 commit comments

Comments
 (0)