1919class JwkService {
2020
2121 public const PEM_SIG_KEY_SETTINGS_KEY = 'pemSignatureKey ' ;
22- public const PEM_SIG_KEY_EXPIRES_AT_SETTINGS_KEY = 'pemSignatureKeyExpiresAt ' ;
23- public const PEM_SIG_KEY_EXPIRES_IN_SECONDS = 60 * 60 ;
22+ public const PEM_SIG_KEY_CREATED_AT_SETTINGS_KEY = 'pemSignatureKeyCreatedAt ' ;
23+ public const PEM_SIG_KEY_EXPIRES_AFTER_SECONDS = 60 * 60 ;
2424 public const PEM_SIG_KEY_ALGORITHM = 'ES384 ' ;
2525 public const PEM_SIG_KEY_CURVE = 'P-384 ' ;
2626
2727 public const PEM_ENC_KEY_SETTINGS_KEY = 'pemEncryptionKey ' ;
28- public const PEM_ENC_KEY_EXPIRES_AT_SETTINGS_KEY = 'pemEncryptionKeyExpiresAt ' ;
29- public const PEM_ENC_KEY_EXPIRES_IN_SECONDS = 60 * 60 ;
28+ public const PEM_ENC_KEY_CREATED_AT_SETTINGS_KEY = 'pemEncryptionKeyCreatedAt ' ;
29+ public const PEM_ENC_KEY_EXPIRES_AFTER_SECONDS = 60 * 60 ;
3030 public const PEM_ENC_KEY_ALGORITHM = 'ECDH-ES+A192KW ' ;
3131 public const PEM_ENC_KEY_CURVE = 'P-384 ' ;
32+ // we store the expired encryption key and can use it for one extra hour after a new one has been generated
33+ public const PEM_EXPIRED_ENC_KEY_SETTINGS_KEY = 'pemExpiredEncryptionKey ' ;
34+ public const PEM_EXPIRED_ENC_KEY_CREATED_AT_SETTINGS_KEY = 'pemExpiredEncryptionKeyCreatedAt ' ;
3235
3336 public function __construct (
3437 private IAppConfig $ appConfig ,
@@ -44,13 +47,15 @@ public function __construct(
4447 */
4548 public function getMyPemSignatureKey (bool $ refresh = true ): string {
4649 $ pemSignatureKey = $ this ->appConfig ->getAppValueString (self ::PEM_SIG_KEY_SETTINGS_KEY , lazy: true );
47- $ pemSignatureKeyExpiresAt = $ this ->appConfig ->getAppValueInt (self ::PEM_SIG_KEY_EXPIRES_AT_SETTINGS_KEY , lazy: true );
50+ $ pemSignatureKeyCreatedAt = $ this ->appConfig ->getAppValueInt (self ::PEM_SIG_KEY_CREATED_AT_SETTINGS_KEY , lazy: true );
4851
49- if ($ pemSignatureKey === '' || $ pemSignatureKeyExpiresAt === 0 || ($ refresh && time () > $ pemSignatureKeyExpiresAt )) {
52+ if ($ pemSignatureKey === ''
53+ || $ pemSignatureKeyCreatedAt === 0
54+ || ($ refresh && (time () > $ pemSignatureKeyCreatedAt + self ::PEM_SIG_KEY_EXPIRES_AFTER_SECONDS ))) {
5055 $ pemSignatureKey = $ this ->generatePemPrivateKey ();
5156 // store the key
5257 $ this ->appConfig ->setAppValueString (self ::PEM_SIG_KEY_SETTINGS_KEY , $ pemSignatureKey , lazy: true );
53- $ this ->appConfig ->setAppValueInt (self ::PEM_SIG_KEY_EXPIRES_AT_SETTINGS_KEY , time () + self :: PEM_SIG_KEY_EXPIRES_IN_SECONDS , lazy: true );
58+ $ this ->appConfig ->setAppValueInt (self ::PEM_SIG_KEY_CREATED_AT_SETTINGS_KEY , time (), lazy: true );
5459 }
5560 return $ pemSignatureKey ;
5661 }
@@ -64,13 +69,24 @@ public function getMyPemSignatureKey(bool $refresh = true): string {
6469 */
6570 public function getMyEncryptionKey (bool $ refresh = true ): string {
6671 $ pemEncryptionKey = $ this ->appConfig ->getAppValueString (self ::PEM_ENC_KEY_SETTINGS_KEY , lazy: true );
67- $ pemEncryptionKeyExpiresAt = $ this ->appConfig ->getAppValueInt (self ::PEM_ENC_KEY_EXPIRES_AT_SETTINGS_KEY , lazy: true );
68-
69- if ($ pemEncryptionKey === '' || $ pemEncryptionKeyExpiresAt === 0 || ($ refresh && time () > $ pemEncryptionKeyExpiresAt )) {
72+ $ pemEncryptionKeyCreatedAt = $ this ->appConfig ->getAppValueInt (self ::PEM_ENC_KEY_CREATED_AT_SETTINGS_KEY , lazy: true );
73+
74+ if ($ pemEncryptionKey === ''
75+ || $ pemEncryptionKeyCreatedAt === 0
76+ || ($ refresh && (time () > $ pemEncryptionKeyCreatedAt + self ::PEM_ENC_KEY_EXPIRES_AFTER_SECONDS ))
77+ ) {
78+ // if we have an old expired key, keep it for one hour
79+ if ($ pemEncryptionKey !== ''
80+ && $ pemEncryptionKeyCreatedAt !== 0
81+ && (time () > $ pemEncryptionKeyCreatedAt + self ::PEM_ENC_KEY_EXPIRES_AFTER_SECONDS )) {
82+ $ this ->appConfig ->setAppValueString (self ::PEM_EXPIRED_ENC_KEY_SETTINGS_KEY , $ pemEncryptionKey , lazy: true );
83+ $ this ->appConfig ->setAppValueInt (self ::PEM_EXPIRED_ENC_KEY_CREATED_AT_SETTINGS_KEY , $ pemEncryptionKeyCreatedAt , lazy: true );
84+ }
85+ // generate a new key
7086 $ pemEncryptionKey = $ this ->generatePemPrivateKey ();
7187 // store the key
7288 $ this ->appConfig ->setAppValueString (self ::PEM_ENC_KEY_SETTINGS_KEY , $ pemEncryptionKey , lazy: true );
73- $ this ->appConfig ->setAppValueInt (self ::PEM_ENC_KEY_EXPIRES_AT_SETTINGS_KEY , time () + self :: PEM_ENC_KEY_EXPIRES_IN_SECONDS , lazy: true );
89+ $ this ->appConfig ->setAppValueInt (self ::PEM_ENC_KEY_CREATED_AT_SETTINGS_KEY , time (), lazy: true );
7490 }
7591 return $ pemEncryptionKey ;
7692 }
@@ -122,11 +138,14 @@ public function getJwks(): array {
122138 }
123139
124140 public function getJwkFromSslKey (array $ sslKeyDetails , bool $ isEncryptionKey = false , bool $ includePrivateKey = false ): array {
125- $ pemPrivateKeyExpiresAt = $ this ->appConfig ->getAppValueInt (self ::PEM_SIG_KEY_EXPIRES_AT_SETTINGS_KEY , lazy: true );
141+ $ pemPrivateKeyCreatedAt = $ this ->appConfig ->getAppValueInt (
142+ $ isEncryptionKey ? self ::PEM_ENC_KEY_CREATED_AT_SETTINGS_KEY : self ::PEM_SIG_KEY_CREATED_AT_SETTINGS_KEY ,
143+ lazy: true ,
144+ );
126145 $ jwk = [
127146 'kty ' => 'EC ' ,
128147 'use ' => $ isEncryptionKey ? 'enc ' : 'sig ' ,
129- 'kid ' => ($ isEncryptionKey ? 'enc ' : 'sig ' ) . '_key_ ' . $ pemPrivateKeyExpiresAt ,
148+ 'kid ' => ($ isEncryptionKey ? 'enc ' : 'sig ' ) . '_key_ ' . $ pemPrivateKeyCreatedAt ,
130149 'crv ' => $ isEncryptionKey ? self ::PEM_ENC_KEY_CURVE : self ::PEM_SIG_KEY_CURVE ,
131150 'x ' => \rtrim (\strtr (\base64_encode ($ sslKeyDetails ['ec ' ]['x ' ]), '+/ ' , '-_ ' ), '= ' ),
132151 'y ' => \rtrim (\strtr (\base64_encode ($ sslKeyDetails ['ec ' ]['y ' ]), '+/ ' , '-_ ' ), '= ' ),
@@ -155,7 +174,7 @@ public function generateClientAssertion(Provider $provider, string $discoveryIss
155174 // we refresh (if needed) here to make sure we use a key that will be served to the IdP in a few seconds
156175 $ myPemPrivateKey = $ this ->getMyPemSignatureKey ();
157176 $ sslPrivateKey = openssl_pkey_get_private ($ myPemPrivateKey );
158- $ pemPrivateKeyExpiresAt = $ this ->appConfig ->getAppValueInt (self ::PEM_SIG_KEY_EXPIRES_AT_SETTINGS_KEY , lazy: true );
177+ $ pemSignatureKeyCreatedAt = $ this ->appConfig ->getAppValueInt (self ::PEM_SIG_KEY_CREATED_AT_SETTINGS_KEY , lazy: true );
159178
160179 $ payload = [
161180 'sub ' => $ provider ->getClientId (),
@@ -170,7 +189,7 @@ public function generateClientAssertion(Provider $provider, string $discoveryIss
170189 $ payload ['code ' ] = $ code ;
171190 }
172191
173- return $ this ->createJwt ($ payload , $ sslPrivateKey , 'sig_key_ ' . $ pemPrivateKeyExpiresAt , self ::PEM_SIG_KEY_ALGORITHM );
192+ return $ this ->createJwt ($ payload , $ sslPrivateKey , 'sig_key_ ' . $ pemSignatureKeyCreatedAt , self ::PEM_SIG_KEY_ALGORITHM );
174193 }
175194
176195 public function debug (): array {
@@ -180,8 +199,8 @@ public function debug(): array {
180199 $ pubKeyPem = $ pubKey ['key ' ];
181200
182201 $ payload = ['lll ' => 'aaa ' ];
183- $ pemPrivateKeyExpiresAt = $ this ->appConfig ->getAppValueInt (self ::PEM_SIG_KEY_EXPIRES_AT_SETTINGS_KEY , lazy: true );
184- $ signedJwtToken = $ this ->createJwt ($ payload , $ sslPrivateKey , 'sig_key_ ' . $ pemPrivateKeyExpiresAt , self ::PEM_SIG_KEY_ALGORITHM );
202+ $ pemSignatureKeyCreatedAt = $ this ->appConfig ->getAppValueInt (self ::PEM_SIG_KEY_CREATED_AT_SETTINGS_KEY , lazy: true );
203+ $ signedJwtToken = $ this ->createJwt ($ payload , $ sslPrivateKey , 'sig_key_ ' . $ pemSignatureKeyCreatedAt , self ::PEM_SIG_KEY_ALGORITHM );
185204
186205 // check content of JWT
187206 $ rawJwks = ['keys ' => [$ this ->getJwkFromSslKey ($ pubKey )]];
0 commit comments