From 28363db2873a3c2f1609b72df49c8fd55a77bea9 Mon Sep 17 00:00:00 2001 From: eemhu <125959687+eemhu@users.noreply.github.com> Date: Mon, 27 May 2024 11:29:29 +0300 Subject: [PATCH] Add Cache for KeyStore implementation (#13) * initial caching version * change cache to verifyKey * add refreshAfterWrite option * fix typo * remove throws from method declaration in (Caching)KeyStoreAccessTest and replace with assertDoesNotThrow --- .../keystore/CachingKeyStoreAccess.java | 111 ++++++++++++++++++ .../jai_02/keystore/KeyStoreAccess.java | 2 +- .../jai_02/keystore/UserNameAndPassword.java | 64 ++++++++++ .../tests/CachingKeyStoreAccessTest.java | 107 +++++++++++++++++ .../jai_02/tests/KeyStoreAccessTest.java | 35 +++--- 5 files changed, 302 insertions(+), 17 deletions(-) create mode 100644 src/main/java/com/teragrep/jai_02/keystore/CachingKeyStoreAccess.java create mode 100644 src/main/java/com/teragrep/jai_02/keystore/UserNameAndPassword.java create mode 100644 src/test/java/com/teragrep/jai_02/tests/CachingKeyStoreAccessTest.java diff --git a/src/main/java/com/teragrep/jai_02/keystore/CachingKeyStoreAccess.java b/src/main/java/com/teragrep/jai_02/keystore/CachingKeyStoreAccess.java new file mode 100644 index 0000000..2c06508 --- /dev/null +++ b/src/main/java/com/teragrep/jai_02/keystore/CachingKeyStoreAccess.java @@ -0,0 +1,111 @@ +/* + * Java Authentication Info jai_02 + * Copyright (C) 2021 Suomen Kanuuna Oy + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * + * Additional permission under GNU Affero General Public License version 3 + * section 7 + * + * If you modify this Program, or any covered work, by linking or combining it + * with other code, such other code is not for that reason alone subject to any + * of the requirements of the GNU Affero GPL version 3 as long as this Program + * is the same Program as licensed from Suomen Kanuuna Oy without any additional + * modifications. + * + * Supplemented terms under GNU Affero General Public License version 3 + * section 7 + * + * Origin of the software must be attributed to Suomen Kanuuna Oy. Any modified + * versions must be marked as "Modified version of" The Program. + * + * Names of the licensors and authors may not be used for publicity purposes. + * + * No rights are granted for use of trade names, trademarks, or service marks + * which are in The Program if any. + * + * Licensee must indemnify licensors and authors for any liability that these + * contractual assumptions impose on licensors and authors. + * + * To the extent this program is licensed as part of the Commercial versions of + * Teragrep, the applicable Commercial License may apply to this file if you as + * a licensee so wish it. + */ +package com.teragrep.jai_02.keystore; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.*; +import java.security.cert.CertificateException; +import java.security.spec.InvalidKeySpecException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +/** + * Provides access to the KeyStore, such as loading, saving + * and deleting entries. Keeps track of the username->alias mapping + * via the UserToAliasMapping object. + */ +public class CachingKeyStoreAccess { + private final KeyStoreAccess keyStoreAccess; + + // cache + private final LoadingCache loadingCache; + + public CachingKeyStoreAccess(final KeyStoreAccess keyStoreAccess, final long secs) { + this.keyStoreAccess = keyStoreAccess; + + CacheLoader cacheLoader = new CacheLoader() { + + @Override + public Boolean load(UserNameAndPassword pe) throws Exception { + return keyStoreAccess.verifyKey(pe.username(), pe.password()); + } + }; + + this.loadingCache = CacheBuilder.newBuilder().refreshAfterWrite(secs, TimeUnit.SECONDS).build(cacheLoader); + + } + + public PasswordEntry loadKey(final String username) throws UnrecoverableEntryException, KeyStoreException, InvalidKeyException { + return keyStoreAccess.loadKey(username); + } + + public void saveKey(final String username, final char[] password) throws KeyStoreException { + keyStoreAccess.saveKey(username, password); + } + + public boolean verifyKey(final String username, final char[] password) throws ExecutionException { + return loadingCache.get(new UserNameAndPassword(username, password)); + } + + public int deleteKey(final String usernameToRemove) throws KeyStoreException, IOException { + return keyStoreAccess.deleteKey(usernameToRemove); + } + + public boolean checkForExistingAlias(final String usernameToCheck) throws KeyStoreException { + return keyStoreAccess.checkForExistingAlias(usernameToCheck); + } +} diff --git a/src/main/java/com/teragrep/jai_02/keystore/KeyStoreAccess.java b/src/main/java/com/teragrep/jai_02/keystore/KeyStoreAccess.java index 156ef39..97fa63d 100644 --- a/src/main/java/com/teragrep/jai_02/keystore/KeyStoreAccess.java +++ b/src/main/java/com/teragrep/jai_02/keystore/KeyStoreAccess.java @@ -164,7 +164,7 @@ public int deleteKey(final String usernameToRemove) throws KeyStoreException, IO return aliasesToRemove.size(); } - private boolean checkForExistingAlias(final String usernameToCheck) throws KeyStoreException { + public boolean checkForExistingAlias(final String usernameToCheck) throws KeyStoreException { boolean exists = false; final Enumeration aliases = keyStore.aliases(); diff --git a/src/main/java/com/teragrep/jai_02/keystore/UserNameAndPassword.java b/src/main/java/com/teragrep/jai_02/keystore/UserNameAndPassword.java new file mode 100644 index 0000000..1e9e2ce --- /dev/null +++ b/src/main/java/com/teragrep/jai_02/keystore/UserNameAndPassword.java @@ -0,0 +1,64 @@ +/* + * Java Authentication Info jai_02 + * Copyright (C) 2021 Suomen Kanuuna Oy + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * + * Additional permission under GNU Affero General Public License version 3 + * section 7 + * + * If you modify this Program, or any covered work, by linking or combining it + * with other code, such other code is not for that reason alone subject to any + * of the requirements of the GNU Affero GPL version 3 as long as this Program + * is the same Program as licensed from Suomen Kanuuna Oy without any additional + * modifications. + * + * Supplemented terms under GNU Affero General Public License version 3 + * section 7 + * + * Origin of the software must be attributed to Suomen Kanuuna Oy. Any modified + * versions must be marked as "Modified version of" The Program. + * + * Names of the licensors and authors may not be used for publicity purposes. + * + * No rights are granted for use of trade names, trademarks, or service marks + * which are in The Program if any. + * + * Licensee must indemnify licensors and authors for any liability that these + * contractual assumptions impose on licensors and authors. + * + * To the extent this program is licensed as part of the Commercial versions of + * Teragrep, the applicable Commercial License may apply to this file if you as + * a licensee so wish it. + */ +package com.teragrep.jai_02.keystore; + +public class UserNameAndPassword { + private final String username; + private final char[] password; + + public UserNameAndPassword(String username, char[] password) { + this.username = username; + this.password = password; + } + + public String username() { + return username; + } + + public char[] password() { + return password; + } +} diff --git a/src/test/java/com/teragrep/jai_02/tests/CachingKeyStoreAccessTest.java b/src/test/java/com/teragrep/jai_02/tests/CachingKeyStoreAccessTest.java new file mode 100644 index 0000000..fcb8711 --- /dev/null +++ b/src/test/java/com/teragrep/jai_02/tests/CachingKeyStoreAccessTest.java @@ -0,0 +1,107 @@ +/* + * Java Authentication Info jai_02 + * Copyright (C) 2021 Suomen Kanuuna Oy + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * + * Additional permission under GNU Affero General Public License version 3 + * section 7 + * + * If you modify this Program, or any covered work, by linking or combining it + * with other code, such other code is not for that reason alone subject to any + * of the requirements of the GNU Affero GPL version 3 as long as this Program + * is the same Program as licensed from Suomen Kanuuna Oy without any additional + * modifications. + * + * Supplemented terms under GNU Affero General Public License version 3 + * section 7 + * + * Origin of the software must be attributed to Suomen Kanuuna Oy. Any modified + * versions must be marked as "Modified version of" The Program. + * + * Names of the licensors and authors may not be used for publicity purposes. + * + * No rights are granted for use of trade names, trademarks, or service marks + * which are in The Program if any. + * + * Licensee must indemnify licensors and authors for any liability that these + * contractual assumptions impose on licensors and authors. + * + * To the extent this program is licensed as part of the Commercial versions of + * Teragrep, the applicable Commercial License may apply to this file if you as + * a licensee so wish it. + */ + +package com.teragrep.jai_02.tests; + +import com.teragrep.jai_02.keystore.CachingKeyStoreAccess; +import com.teragrep.jai_02.keystore.KeyStoreAccess; +import com.teragrep.jai_02.keystore.KeyStoreFactory; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.KeyStoreException; +import java.security.UnrecoverableEntryException; +import java.security.spec.InvalidKeySpecException; +import java.util.concurrent.ExecutionException; + +public class CachingKeyStoreAccessTest { + + private static String keyStorePath = "target/keystore.p12"; + private static String keyStorePassword = "changeit"; + private static String userName = "trusted-12"; + private static String userPassWord = "XOsAqIhmKUTwWMjWwDaYmVgR8sl_l70H1oDPBw9z2yY"; + + private static CachingKeyStoreAccess cksa; + @BeforeAll + public static void prepare() { + Assertions.assertDoesNotThrow(() -> { + cksa = new CachingKeyStoreAccess( + new KeyStoreAccess( + new KeyStoreFactory(keyStorePath, keyStorePassword.toCharArray()).build(), + keyStorePath, keyStorePassword.toCharArray()), 10L); + + cksa.deleteKey(userName); + }); + } + + public void save() { + Assertions.assertDoesNotThrow(() -> { + cksa.saveKey( + userName, + userPassWord.toCharArray()); + }); + } + + public void verify() { + Assertions.assertDoesNotThrow(() -> { + boolean authOk = cksa.verifyKey( + userName, + userPassWord.toCharArray()); + + Assertions.assertTrue(authOk); + }); + } + + @Test + public void saveAndVerifyTest() { + save(); + verify(); + } +} + diff --git a/src/test/java/com/teragrep/jai_02/tests/KeyStoreAccessTest.java b/src/test/java/com/teragrep/jai_02/tests/KeyStoreAccessTest.java index 6c46347..2e8a611 100644 --- a/src/test/java/com/teragrep/jai_02/tests/KeyStoreAccessTest.java +++ b/src/test/java/com/teragrep/jai_02/tests/KeyStoreAccessTest.java @@ -67,30 +67,33 @@ public class KeyStoreAccessTest { private static KeyStoreAccess ksa; @BeforeAll - public static void prepare() throws KeyStoreException, IOException { - ksa = new KeyStoreAccess(new KeyStoreFactory(keyStorePath, keyStorePassword.toCharArray()).build(), keyStorePath, keyStorePassword.toCharArray()); - ksa.deleteKey(userName); + public static void prepare() { + Assertions.assertDoesNotThrow(() -> { + ksa = new KeyStoreAccess(new KeyStoreFactory(keyStorePath, keyStorePassword.toCharArray()).build(), keyStorePath, keyStorePassword.toCharArray()); + ksa.deleteKey(userName); + }); } - public void save() throws KeyStoreException { - ksa.saveKey( - userName, - userPassWord.toCharArray()); + public void save() { + Assertions.assertDoesNotThrow(() -> { + ksa.saveKey( + userName, + userPassWord.toCharArray()); + }); } - public void verify() throws UnrecoverableEntryException, KeyStoreException, - InvalidKeySpecException, InvalidKeyException { + public void verify() { + Assertions.assertDoesNotThrow(() -> { + boolean authOk = ksa.verifyKey( + userName, + userPassWord.toCharArray()); - boolean authOk = ksa.verifyKey( - userName, - userPassWord.toCharArray()); - - Assertions.assertTrue(authOk); + Assertions.assertTrue(authOk); + }); } @Test - public void saveAndVerifyTest() throws KeyStoreException, - InvalidKeySpecException, UnrecoverableEntryException, InvalidKeyException { + public void saveAndVerifyTest() { save(); verify(); }