@@ -157,6 +157,7 @@ private static class Entry {
157157
158158 /** The decrypted secret data. See {@link #decrypt(char[])}. */
159159 private final SetOnce <Map <String , Entry >> entries = new SetOnce <>();
160+ private volatile boolean closed ;
160161
161162 private KeyStoreWrapper (int formatVersion , boolean hasPassword , byte [] dataBytes ) {
162163 this .formatVersion = formatVersion ;
@@ -448,8 +449,8 @@ private void decryptLegacyEntries() throws GeneralSecurityException, IOException
448449 }
449450
450451 /** Write the keystore to the given config directory. */
451- public void save (Path configDir , char [] password ) throws Exception {
452- assert isLoaded ();
452+ public synchronized void save (Path configDir , char [] password ) throws Exception {
453+ ensureOpen ();
453454
454455 SimpleFSDirectory directory = new SimpleFSDirectory (configDir );
455456 // write to tmp file first, then overwrite
@@ -500,16 +501,22 @@ public void save(Path configDir, char[] password) throws Exception {
500501 }
501502 }
502503
504+ /**
505+ * It is possible to retrieve the setting names even if the keystore is closed.
506+ * This allows {@link SecureSetting} to correctly determine that a entry exists even though it cannot be read. Thus attempting to
507+ * read a secure setting after the keystore is closed will generate a "keystore is closed" exception rather than using the fallback
508+ * setting.
509+ */
503510 @ Override
504511 public Set <String > getSettingNames () {
505- assert isLoaded () ;
512+ assert entries . get () != null : "Keystore is not loaded" ;
506513 return entries .get ().keySet ();
507514 }
508515
509516 // TODO: make settings accessible only to code that registered the setting
510517 @ Override
511- public SecureString getString (String setting ) {
512- assert isLoaded ();
518+ public synchronized SecureString getString (String setting ) {
519+ ensureOpen ();
513520 Entry entry = entries .get ().get (setting );
514521 if (entry == null || entry .type != EntryType .STRING ) {
515522 throw new IllegalArgumentException ("Secret setting " + setting + " is not a string" );
@@ -520,13 +527,12 @@ public SecureString getString(String setting) {
520527 }
521528
522529 @ Override
523- public InputStream getFile (String setting ) {
524- assert isLoaded ();
530+ public synchronized InputStream getFile (String setting ) {
531+ ensureOpen ();
525532 Entry entry = entries .get ().get (setting );
526533 if (entry == null || entry .type != EntryType .FILE ) {
527534 throw new IllegalArgumentException ("Secret setting " + setting + " is not a file" );
528535 }
529-
530536 return new ByteArrayInputStream (entry .bytes );
531537 }
532538
@@ -543,8 +549,8 @@ public static void validateSettingName(String setting) {
543549 }
544550
545551 /** Set a string setting. */
546- void setString (String setting , char [] value ) {
547- assert isLoaded ();
552+ synchronized void setString (String setting , char [] value ) {
553+ ensureOpen ();
548554 validateSettingName (setting );
549555
550556 ByteBuffer byteBuffer = StandardCharsets .UTF_8 .encode (CharBuffer .wrap (value ));
@@ -556,8 +562,8 @@ void setString(String setting, char[] value) {
556562 }
557563
558564 /** Set a file setting. */
559- void setFile (String setting , byte [] bytes ) {
560- assert isLoaded ();
565+ synchronized void setFile (String setting , byte [] bytes ) {
566+ ensureOpen ();
561567 validateSettingName (setting );
562568
563569 Entry oldEntry = entries .get ().put (setting , new Entry (EntryType .FILE , Arrays .copyOf (bytes , bytes .length )));
@@ -568,15 +574,23 @@ void setFile(String setting, byte[] bytes) {
568574
569575 /** Remove the given setting from the keystore. */
570576 void remove (String setting ) {
571- assert isLoaded ();
577+ ensureOpen ();
572578 Entry oldEntry = entries .get ().remove (setting );
573579 if (oldEntry != null ) {
574580 Arrays .fill (oldEntry .bytes , (byte )0 );
575581 }
576582 }
577583
584+ private void ensureOpen () {
585+ if (closed ) {
586+ throw new IllegalStateException ("Keystore is closed" );
587+ }
588+ assert isLoaded () : "Keystore is not loaded" ;
589+ }
590+
578591 @ Override
579- public void close () {
592+ public synchronized void close () {
593+ this .closed = true ;
580594 for (Entry entry : entries .get ().values ()) {
581595 Arrays .fill (entry .bytes , (byte )0 );
582596 }
0 commit comments