4242 * AdvancedTlsX509KeyManager is an {@code X509ExtendedKeyManager} that allows users to configure
4343 * advanced TLS features, such as private key and certificate chain reloading.
4444 *
45- * <p>The key material alias increments on every credential load (e.g. {@code "key-1"},
46- * {@code "key-2"}, ...), ensuring the same alias always maps to the same key material. This is
47- * required by Netty's {@code OpenSslCachingX509KeyManagerFactory} to correctly cache key
48- * material and create a new cache entry on cert reload .
45+ * <p>The alias increments on every credential load (e.g. {@code "key-1"}, {@code "key-2"}, ...) ,
46+ * so the same alias always maps to the same key material. The previous alias is retained for one
47+ * rotation to allow in-progress handshakes to complete. This is required by Netty's
48+ * {@code OpenSslCachingX509KeyManagerFactory} to cache key material across cert reloads .
4949 *
5050 * <p>When using {@code SslProvider.OPENSSL}, wrap this key manager in Netty's
5151 * {@code OpenSslCachingX509KeyManagerFactory} to avoid per-handshake key material encoding
@@ -61,44 +61,39 @@ public final class AdvancedTlsX509KeyManager extends X509ExtendedKeyManager {
6161 static final String ALIAS_PREFIX = "key-" ;
6262
6363 private final AtomicInteger revision = new AtomicInteger (0 );
64- private final int revisionWarningThreshold ;
65- // The credential information to be sent to peers to prove our identity .
66- private volatile KeyInfo keyInfo ;
64+ // Snapshot of current and previous KeyInfo; previous is retained for in-progress handshakes
65+ // after one rotation .
66+ private volatile KeyInfoSnapshot snapshot = new KeyInfoSnapshot ( null , null ) ;
6767
68- public AdvancedTlsX509KeyManager () {
69- // Netty's default OpenSslCachingX509KeyManagerFactory maxCachedEntries.
70- this (1024 );
71- }
72-
73- /**
74- * Creates a key manager with a custom revision warning threshold.
75- * @param revisionWarningThreshold the number of credential loads after which a warning is logged.
76- * Only relevant when using {@code SslProvider.OPENSSL} with
77- * {@code OpenSslCachingX509KeyManagerFactory}.
78- */
79- public AdvancedTlsX509KeyManager (int revisionWarningThreshold ) {
80- this .revisionWarningThreshold = revisionWarningThreshold ;
81- }
68+ public AdvancedTlsX509KeyManager () {}
8269
8370 private String alias () {
84- KeyInfo info = this .keyInfo ;
85- if (info == null ) {
86- return null ;
87- }
88- return info .alias ;
71+ KeyInfo curr = this .snapshot .current ;
72+ return curr != null ? curr .alias : null ;
8973 }
9074
9175 @ Override
9276 public PrivateKey getPrivateKey (String alias ) {
93- KeyInfo info = this .keyInfo ;
94- return info != null && info .alias .equals (alias ) ? info .key : null ;
77+ KeyInfoSnapshot snap = this .snapshot ;
78+ if (snap .current != null && snap .current .alias .equals (alias )) {
79+ return snap .current .key ;
80+ }
81+ if (snap .previous != null && snap .previous .alias .equals (alias )) {
82+ return snap .previous .key ;
83+ }
84+ return null ;
9585 }
9686
9787 @ Override
9888 public X509Certificate [] getCertificateChain (String alias ) {
99- KeyInfo info = this .keyInfo ;
100- return info != null && info .alias .equals (alias )
101- ? Arrays .copyOf (info .certs , info .certs .length ) : null ;
89+ KeyInfoSnapshot snap = this .snapshot ;
90+ if (snap .current != null && snap .current .alias .equals (alias )) {
91+ return Arrays .copyOf (snap .current .certs , snap .current .certs .length );
92+ }
93+ if (snap .previous != null && snap .previous .alias .equals (alias )) {
94+ return Arrays .copyOf (snap .previous .certs , snap .previous .certs .length );
95+ }
96+ return null ;
10297 }
10398
10499 @ Override
@@ -155,15 +150,9 @@ public void updateIdentityCredentials(PrivateKey key, X509Certificate[] certs) {
155150 * @param key the private key that is going to be used
156151 */
157152 public void updateIdentityCredentials (X509Certificate [] certs , PrivateKey key ) {
158- // When using SslProvider.OPENSSL with OpenSslCachingX509KeyManagerFactory, its cache stops
159- // accepting new aliases once maxCachedEntries is reached (default: 1024). Beyond this,
160- // handshakes still succeed but per-handshake re-encoding overhead resumes.
161- if (revision .get () >= revisionWarningThreshold ) {
162- log .warning ("AdvancedTlsX509KeyManager: revision counter has reached "
163- + revisionWarningThreshold );
164- }
165- this .keyInfo = new KeyInfo (checkNotNull (certs , "certs" ), checkNotNull (key , "key" ),
153+ KeyInfo newInfo = new KeyInfo (checkNotNull (certs , "certs" ), checkNotNull (key , "key" ),
166154 ALIAS_PREFIX + revision .incrementAndGet ());
155+ this .snapshot = new KeyInfoSnapshot (newInfo , this .snapshot .current );
167156 }
168157
169158 /**
@@ -262,7 +251,6 @@ public Closeable updateIdentityCredentialsFromFile(File keyFile, File certFile,
262251 }
263252
264253 private static class KeyInfo {
265- // The private key and the cert chain we will use to send to peers to prove our identity.
266254 final X509Certificate [] certs ;
267255 final PrivateKey key ;
268256 final String alias ;
@@ -274,6 +262,16 @@ public KeyInfo(X509Certificate[] certs, PrivateKey key, String alias) {
274262 }
275263 }
276264
265+ private static class KeyInfoSnapshot {
266+ final KeyInfo current ;
267+ final KeyInfo previous ;
268+
269+ KeyInfoSnapshot (KeyInfo current , KeyInfo previous ) {
270+ this .current = current ;
271+ this .previous = previous ;
272+ }
273+ }
274+
277275 private class LoadFilePathExecution implements Runnable {
278276 File keyFile ;
279277 File certFile ;
0 commit comments