@@ -157,6 +157,7 @@ private static class Entry {
157
157
158
158
/** The decrypted secret data. See {@link #decrypt(char[])}. */
159
159
private final SetOnce <Map <String , Entry >> entries = new SetOnce <>();
160
+ private volatile boolean closed ;
160
161
161
162
private KeyStoreWrapper (int formatVersion , boolean hasPassword , byte [] dataBytes ) {
162
163
this .formatVersion = formatVersion ;
@@ -448,8 +449,8 @@ private void decryptLegacyEntries() throws GeneralSecurityException, IOException
448
449
}
449
450
450
451
/** 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 ();
453
454
454
455
SimpleFSDirectory directory = new SimpleFSDirectory (configDir );
455
456
// write to tmp file first, then overwrite
@@ -500,16 +501,22 @@ public void save(Path configDir, char[] password) throws Exception {
500
501
}
501
502
}
502
503
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
+ */
503
510
@ Override
504
511
public Set <String > getSettingNames () {
505
- assert isLoaded () ;
512
+ assert entries . get () != null : "Keystore is not loaded" ;
506
513
return entries .get ().keySet ();
507
514
}
508
515
509
516
// TODO: make settings accessible only to code that registered the setting
510
517
@ Override
511
- public SecureString getString (String setting ) {
512
- assert isLoaded ();
518
+ public synchronized SecureString getString (String setting ) {
519
+ ensureOpen ();
513
520
Entry entry = entries .get ().get (setting );
514
521
if (entry == null || entry .type != EntryType .STRING ) {
515
522
throw new IllegalArgumentException ("Secret setting " + setting + " is not a string" );
@@ -520,13 +527,12 @@ public SecureString getString(String setting) {
520
527
}
521
528
522
529
@ Override
523
- public InputStream getFile (String setting ) {
524
- assert isLoaded ();
530
+ public synchronized InputStream getFile (String setting ) {
531
+ ensureOpen ();
525
532
Entry entry = entries .get ().get (setting );
526
533
if (entry == null || entry .type != EntryType .FILE ) {
527
534
throw new IllegalArgumentException ("Secret setting " + setting + " is not a file" );
528
535
}
529
-
530
536
return new ByteArrayInputStream (entry .bytes );
531
537
}
532
538
@@ -543,8 +549,8 @@ public static void validateSettingName(String setting) {
543
549
}
544
550
545
551
/** Set a string setting. */
546
- void setString (String setting , char [] value ) {
547
- assert isLoaded ();
552
+ synchronized void setString (String setting , char [] value ) {
553
+ ensureOpen ();
548
554
validateSettingName (setting );
549
555
550
556
ByteBuffer byteBuffer = StandardCharsets .UTF_8 .encode (CharBuffer .wrap (value ));
@@ -556,8 +562,8 @@ void setString(String setting, char[] value) {
556
562
}
557
563
558
564
/** Set a file setting. */
559
- void setFile (String setting , byte [] bytes ) {
560
- assert isLoaded ();
565
+ synchronized void setFile (String setting , byte [] bytes ) {
566
+ ensureOpen ();
561
567
validateSettingName (setting );
562
568
563
569
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) {
568
574
569
575
/** Remove the given setting from the keystore. */
570
576
void remove (String setting ) {
571
- assert isLoaded ();
577
+ ensureOpen ();
572
578
Entry oldEntry = entries .get ().remove (setting );
573
579
if (oldEntry != null ) {
574
580
Arrays .fill (oldEntry .bytes , (byte )0 );
575
581
}
576
582
}
577
583
584
+ private void ensureOpen () {
585
+ if (closed ) {
586
+ throw new IllegalStateException ("Keystore is closed" );
587
+ }
588
+ assert isLoaded () : "Keystore is not loaded" ;
589
+ }
590
+
578
591
@ Override
579
- public void close () {
592
+ public synchronized void close () {
593
+ this .closed = true ;
580
594
for (Entry entry : entries .get ().values ()) {
581
595
Arrays .fill (entry .bytes , (byte )0 );
582
596
}
0 commit comments