@@ -4,31 +4,31 @@ use base64::Engine;
44use rsa:: traits:: PublicKeyParts ;
55use rsa:: RsaPublicKey ;
66
7- /// Generate a JWKS JSON response containing the proxy's RSA public key .
7+ /// Generate a JWKS JSON response containing one or more RSA public keys .
88///
99/// This is served at the JWKS URI referenced by the OIDC discovery document,
1010/// allowing relying parties (cloud providers) to verify JWTs signed by the proxy.
11- pub fn jwks_json ( public_key : & RsaPublicKey , kid : & str ) -> String {
11+ /// Multiple keys support key rotation.
12+ pub fn jwks_json ( keys : & [ ( & RsaPublicKey , & str ) ] ) -> String {
1213 let b64 = & base64:: engine:: general_purpose:: URL_SAFE_NO_PAD ;
1314
14- let n = public_key. n ( ) ;
15- let e = public_key. e ( ) ;
16-
17- let n_b64 = b64. encode ( n. to_bytes_be ( ) ) ;
18- let e_b64 = b64. encode ( e. to_bytes_be ( ) ) ;
19-
20- let jwks = serde_json:: json!( {
21- "keys" : [ {
22- "kty" : "RSA" ,
23- "alg" : "RS256" ,
24- "use" : "sig" ,
25- "kid" : kid,
26- "n" : n_b64,
27- "e" : e_b64,
28- } ]
29- } ) ;
30-
31- jwks. to_string ( )
15+ let jwk_entries: Vec < serde_json:: Value > = keys
16+ . iter ( )
17+ . map ( |( public_key, kid) | {
18+ let n_b64 = b64. encode ( public_key. n ( ) . to_bytes_be ( ) ) ;
19+ let e_b64 = b64. encode ( public_key. e ( ) . to_bytes_be ( ) ) ;
20+ serde_json:: json!( {
21+ "kty" : "RSA" ,
22+ "alg" : "RS256" ,
23+ "use" : "sig" ,
24+ "kid" : kid,
25+ "n" : n_b64,
26+ "e" : e_b64,
27+ } )
28+ } )
29+ . collect ( ) ;
30+
31+ serde_json:: json!( { "keys" : jwk_entries } ) . to_string ( )
3232}
3333
3434#[ cfg( test) ]
@@ -42,7 +42,7 @@ mod tests {
4242 let private_key = RsaPrivateKey :: new ( & mut rng, 2048 ) . unwrap ( ) ;
4343 let public_key: & RsaPublicKey = private_key. as_ref ( ) ;
4444
45- let json_str = jwks_json ( public_key, "my-kid" ) ;
45+ let json_str = jwks_json ( & [ ( public_key, "my-kid" ) ] ) ;
4646 let parsed: serde_json:: Value = serde_json:: from_str ( & json_str) . unwrap ( ) ;
4747
4848 let keys = parsed[ "keys" ] . as_array ( ) . unwrap ( ) ;
@@ -57,6 +57,23 @@ mod tests {
5757 assert ! ( key[ "e" ] . as_str( ) . unwrap( ) . len( ) > 0 ) ;
5858 }
5959
60+ #[ test]
61+ fn jwks_contains_multiple_keys ( ) {
62+ let mut rng = rand:: rngs:: OsRng ;
63+ let key1 = RsaPrivateKey :: new ( & mut rng, 2048 ) . unwrap ( ) ;
64+ let pub1: & RsaPublicKey = key1. as_ref ( ) ;
65+ let key2 = RsaPrivateKey :: new ( & mut rng, 2048 ) . unwrap ( ) ;
66+ let pub2: & RsaPublicKey = key2. as_ref ( ) ;
67+
68+ let json_str = jwks_json ( & [ ( pub1, "kid-1" ) , ( pub2, "kid-2" ) ] ) ;
69+ let parsed: serde_json:: Value = serde_json:: from_str ( & json_str) . unwrap ( ) ;
70+
71+ let keys = parsed[ "keys" ] . as_array ( ) . unwrap ( ) ;
72+ assert_eq ! ( keys. len( ) , 2 ) ;
73+ assert_eq ! ( keys[ 0 ] [ "kid" ] , "kid-1" ) ;
74+ assert_eq ! ( keys[ 1 ] [ "kid" ] , "kid-2" ) ;
75+ }
76+
6077 #[ test]
6178 fn jwks_roundtrips_through_rsa ( ) {
6279 use rsa:: BigUint ;
@@ -65,7 +82,7 @@ mod tests {
6582 let private_key = RsaPrivateKey :: new ( & mut rng, 2048 ) . unwrap ( ) ;
6683 let public_key: & RsaPublicKey = private_key. as_ref ( ) ;
6784
68- let json_str = jwks_json ( public_key, "k1" ) ;
85+ let json_str = jwks_json ( & [ ( public_key, "k1" ) ] ) ;
6986 let parsed: serde_json:: Value = serde_json:: from_str ( & json_str) . unwrap ( ) ;
7087
7188 let key = & parsed[ "keys" ] [ 0 ] ;
0 commit comments