Skip to main content

jsonwebtoken/
jwk.rs

1#![allow(missing_docs)]
2//! This crate contains types only for working JWK and JWK Sets
3//! This is only meant to be used to deal with public JWK, not generate ones.
4//! Most of the code in this file is taken from <https://github.com/lawliet89/biscuit> but
5//! tweaked to remove the private bits as it's not the goal for this crate currently.
6
7use std::{fmt, str::FromStr};
8
9use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
10
11use crate::crypto::CryptoProvider;
12use crate::serialization::b64_encode;
13use crate::{
14    Algorithm, EncodingKey,
15    errors::{self, Error, ErrorKind},
16};
17
18/// The intended usage of the public `KeyType`. This enum is serialized `untagged`
19#[derive(Clone, Debug, Eq, PartialEq, Hash)]
20pub enum PublicKeyUse {
21    /// Indicates a public key is meant for signature verification
22    Signature,
23    /// Indicates a public key is meant for encryption
24    Encryption,
25    /// Other usage
26    Other(String),
27}
28
29impl Serialize for PublicKeyUse {
30    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
31    where
32        S: Serializer,
33    {
34        let string = match self {
35            PublicKeyUse::Signature => "sig",
36            PublicKeyUse::Encryption => "enc",
37            PublicKeyUse::Other(other) => other,
38        };
39
40        serializer.serialize_str(string)
41    }
42}
43
44impl<'de> Deserialize<'de> for PublicKeyUse {
45    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
46    where
47        D: Deserializer<'de>,
48    {
49        struct PublicKeyUseVisitor;
50        impl de::Visitor<'_> for PublicKeyUseVisitor {
51            type Value = PublicKeyUse;
52
53            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
54                write!(formatter, "a string")
55            }
56
57            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
58            where
59                E: de::Error,
60            {
61                Ok(match v {
62                    "sig" => PublicKeyUse::Signature,
63                    "enc" => PublicKeyUse::Encryption,
64                    other => PublicKeyUse::Other(other.to_string()),
65                })
66            }
67        }
68
69        deserializer.deserialize_string(PublicKeyUseVisitor)
70    }
71}
72
73/// Operations that the key is intended to be used for. This enum is serialized `untagged`
74#[derive(Clone, Debug, Eq, PartialEq, Hash)]
75pub enum KeyOperations {
76    /// Computer digital signature or MAC
77    Sign,
78    /// Verify digital signature or MAC
79    Verify,
80    /// Encrypt content
81    Encrypt,
82    /// Decrypt content and validate decryption, if applicable
83    Decrypt,
84    /// Encrypt key
85    WrapKey,
86    /// Decrypt key and validate decryption, if applicable
87    UnwrapKey,
88    /// Derive key
89    DeriveKey,
90    /// Derive bits not to be used as a key
91    DeriveBits,
92    /// Other operation
93    Other(String),
94}
95
96impl Serialize for KeyOperations {
97    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
98    where
99        S: Serializer,
100    {
101        let string = match self {
102            KeyOperations::Sign => "sign",
103            KeyOperations::Verify => "verify",
104            KeyOperations::Encrypt => "encrypt",
105            KeyOperations::Decrypt => "decrypt",
106            KeyOperations::WrapKey => "wrapKey",
107            KeyOperations::UnwrapKey => "unwrapKey",
108            KeyOperations::DeriveKey => "deriveKey",
109            KeyOperations::DeriveBits => "deriveBits",
110            KeyOperations::Other(other) => other,
111        };
112
113        serializer.serialize_str(string)
114    }
115}
116
117impl<'de> Deserialize<'de> for KeyOperations {
118    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
119    where
120        D: Deserializer<'de>,
121    {
122        struct KeyOperationsVisitor;
123        impl de::Visitor<'_> for KeyOperationsVisitor {
124            type Value = KeyOperations;
125
126            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
127                write!(formatter, "a string")
128            }
129
130            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
131            where
132                E: de::Error,
133            {
134                Ok(match v {
135                    "sign" => KeyOperations::Sign,
136                    "verify" => KeyOperations::Verify,
137                    "encrypt" => KeyOperations::Encrypt,
138                    "decrypt" => KeyOperations::Decrypt,
139                    "wrapKey" => KeyOperations::WrapKey,
140                    "unwrapKey" => KeyOperations::UnwrapKey,
141                    "deriveKey" => KeyOperations::DeriveKey,
142                    "deriveBits" => KeyOperations::DeriveBits,
143                    other => KeyOperations::Other(other.to_string()),
144                })
145            }
146        }
147
148        deserializer.deserialize_string(KeyOperationsVisitor)
149    }
150}
151
152/// The algorithms of the keys
153#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
154#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)]
155pub enum KeyAlgorithm {
156    /// HMAC using SHA-256
157    HS256,
158    /// HMAC using SHA-384
159    HS384,
160    /// HMAC using SHA-512
161    HS512,
162
163    /// ECDSA using SHA-256
164    ES256,
165    /// ECDSA using SHA-384
166    ES384,
167
168    /// RSASSA-PKCS1-v1_5 using SHA-256
169    RS256,
170    /// RSASSA-PKCS1-v1_5 using SHA-384
171    RS384,
172    /// RSASSA-PKCS1-v1_5 using SHA-512
173    RS512,
174
175    /// RSASSA-PSS using SHA-256
176    PS256,
177    /// RSASSA-PSS using SHA-384
178    PS384,
179    /// RSASSA-PSS using SHA-512
180    PS512,
181
182    /// Edwards-curve Digital Signature Algorithm (EdDSA)
183    EdDSA,
184
185    /// RSAES-PKCS1-V1_5
186    RSA1_5,
187
188    /// RSAES-OAEP using SHA-1
189    #[serde(rename = "RSA-OAEP")]
190    RSA_OAEP,
191
192    /// RSAES-OAEP-256 using SHA-2
193    #[serde(rename = "RSA-OAEP-256")]
194    RSA_OAEP_256,
195
196    /// Catch-All for when the key algorithm can not be determined or is not supported
197    #[serde(other)]
198    UNKNOWN_ALGORITHM,
199}
200
201impl FromStr for KeyAlgorithm {
202    type Err = Error;
203    fn from_str(s: &str) -> errors::Result<Self> {
204        match s {
205            "HS256" => Ok(KeyAlgorithm::HS256),
206            "HS384" => Ok(KeyAlgorithm::HS384),
207            "HS512" => Ok(KeyAlgorithm::HS512),
208            "ES256" => Ok(KeyAlgorithm::ES256),
209            "ES384" => Ok(KeyAlgorithm::ES384),
210            "RS256" => Ok(KeyAlgorithm::RS256),
211            "RS384" => Ok(KeyAlgorithm::RS384),
212            "PS256" => Ok(KeyAlgorithm::PS256),
213            "PS384" => Ok(KeyAlgorithm::PS384),
214            "PS512" => Ok(KeyAlgorithm::PS512),
215            "RS512" => Ok(KeyAlgorithm::RS512),
216            "EdDSA" => Ok(KeyAlgorithm::EdDSA),
217            "RSA1_5" => Ok(KeyAlgorithm::RSA1_5),
218            "RSA-OAEP" => Ok(KeyAlgorithm::RSA_OAEP),
219            "RSA-OAEP-256" => Ok(KeyAlgorithm::RSA_OAEP_256),
220            _ => Err(ErrorKind::InvalidAlgorithmName.into()),
221        }
222    }
223}
224
225impl fmt::Display for KeyAlgorithm {
226    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
227        write!(f, "{:?}", self)
228    }
229}
230
231impl KeyAlgorithm {
232    fn to_algorithm(self) -> errors::Result<Algorithm> {
233        Algorithm::from_str(self.to_string().as_str())
234    }
235}
236
237/// Common JWK parameters
238#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Default, Hash)]
239pub struct CommonParameters {
240    /// The intended use of the public key. Should not be specified with `key_operations`.
241    /// See sections 4.2 and 4.3 of [RFC7517](https://tools.ietf.org/html/rfc7517).
242    #[serde(rename = "use", skip_serializing_if = "Option::is_none", default)]
243    pub public_key_use: Option<PublicKeyUse>,
244
245    /// The "key_ops" (key operations) parameter identifies the operation(s)
246    /// for which the key is intended to be used.  The "key_ops" parameter is
247    /// intended for use cases in which public, private, or symmetric keys
248    /// may be present.
249    /// Should not be specified with `public_key_use`.
250    /// See sections 4.2 and 4.3 of [RFC7517](https://tools.ietf.org/html/rfc7517).
251    #[serde(rename = "key_ops", skip_serializing_if = "Option::is_none", default)]
252    pub key_operations: Option<Vec<KeyOperations>>,
253
254    /// The algorithm keys intended for use with the key.
255    #[serde(rename = "alg", skip_serializing_if = "Option::is_none", default)]
256    pub key_algorithm: Option<KeyAlgorithm>,
257
258    /// The case sensitive Key ID for the key
259    #[serde(rename = "kid", skip_serializing_if = "Option::is_none", default)]
260    pub key_id: Option<String>,
261
262    /// X.509 Public key certificate URL. This is currently not implemented (correctly).
263    ///
264    /// Serialized to `x5u`.
265    #[serde(rename = "x5u", skip_serializing_if = "Option::is_none")]
266    pub x509_url: Option<String>,
267
268    /// X.509 public key certificate chain. This is currently not implemented (correctly).
269    ///
270    /// Serialized to `x5c`.
271    #[serde(rename = "x5c", skip_serializing_if = "Option::is_none")]
272    pub x509_chain: Option<Vec<String>>,
273
274    /// X.509 Certificate SHA1 thumbprint. This is currently not implemented (correctly).
275    ///
276    /// Serialized to `x5t`.
277    #[serde(rename = "x5t", skip_serializing_if = "Option::is_none")]
278    pub x509_sha1_fingerprint: Option<String>,
279
280    /// X.509 Certificate SHA256 thumbprint. This is currently not implemented (correctly).
281    ///
282    /// Serialized to `x5t#S256`.
283    #[serde(rename = "x5t#S256", skip_serializing_if = "Option::is_none")]
284    pub x509_sha256_fingerprint: Option<String>,
285}
286
287/// Key type value for an Elliptic Curve Key.
288/// This single value enum is a workaround for Rust not supporting associated constants.
289#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Hash)]
290pub enum EllipticCurveKeyType {
291    /// Key type value for an Elliptic Curve Key.
292    #[default]
293    EC,
294}
295
296/// Type of cryptographic curve used by a key. This is defined in
297/// [RFC 7518 #7.6](https://tools.ietf.org/html/rfc7518#section-7.6)
298#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Hash)]
299pub enum EllipticCurve {
300    /// P-256 curve
301    #[serde(rename = "P-256")]
302    #[default]
303    P256,
304    /// P-384 curve
305    #[serde(rename = "P-384")]
306    P384,
307    /// P-521 curve -- unsupported by `ring`.
308    #[serde(rename = "P-521")]
309    P521,
310    /// Ed25519 curve
311    #[serde(rename = "Ed25519")]
312    Ed25519,
313}
314
315/// Parameters for an Elliptic Curve Key
316#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default, Hash)]
317pub struct EllipticCurveKeyParameters {
318    /// Key type value for an Elliptic Curve Key.
319    #[serde(rename = "kty")]
320    pub key_type: EllipticCurveKeyType,
321    /// The "crv" (curve) parameter identifies the cryptographic curve used
322    /// with the key.
323    #[serde(rename = "crv")]
324    pub curve: EllipticCurve,
325    /// The "x" (x coordinate) parameter contains the x coordinate for the
326    /// Elliptic Curve point.
327    pub x: String,
328    /// The "y" (y coordinate) parameter contains the y coordinate for the
329    /// Elliptic Curve point.
330    pub y: String,
331}
332
333/// Key type value for an RSA Key.
334/// This single value enum is a workaround for Rust not supporting associated constants.
335#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Hash)]
336pub enum RSAKeyType {
337    /// Key type value for an RSA Key.
338    #[default]
339    RSA,
340}
341
342/// Parameters for a RSA Key
343#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default, Hash)]
344pub struct RSAKeyParameters {
345    /// Key type value for a RSA Key
346    #[serde(rename = "kty")]
347    pub key_type: RSAKeyType,
348
349    /// The "n" (modulus) parameter contains the modulus value for the RSA
350    /// public key.
351    pub n: String,
352
353    /// The "e" (exponent) parameter contains the exponent value for the RSA
354    /// public key.
355    pub e: String,
356}
357
358/// Key type value for an Octet symmetric key.
359/// This single value enum is a workaround for Rust not supporting associated constants.
360#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Hash)]
361pub enum OctetKeyType {
362    /// Key type value for an Octet symmetric key.
363    #[serde(rename = "oct")]
364    #[default]
365    Octet,
366}
367
368/// Parameters for an Octet Key
369#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default, Hash)]
370pub struct OctetKeyParameters {
371    /// Key type value for an Octet Key
372    #[serde(rename = "kty")]
373    pub key_type: OctetKeyType,
374    /// The octet key value
375    #[serde(rename = "k")]
376    pub value: String,
377}
378
379/// Key type value for an Octet Key Pair.
380/// This single value enum is a workaround for Rust not supporting associated constants.
381#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Hash)]
382pub enum OctetKeyPairType {
383    /// Key type value for an Octet Key Pair.
384    #[serde(rename = "OKP")]
385    #[default]
386    OctetKeyPair,
387}
388
389/// Parameters for an Octet Key Pair
390#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default, Hash)]
391pub struct OctetKeyPairParameters {
392    /// Key type value for an Octet Key Pair
393    #[serde(rename = "kty")]
394    pub key_type: OctetKeyPairType,
395    /// The "crv" (curve) parameter identifies the cryptographic curve used
396    /// with the key.
397    #[serde(rename = "crv")]
398    pub curve: EllipticCurve,
399    /// The "x" parameter contains the base64 encoded public key
400    pub x: String,
401}
402
403/// Algorithm specific parameters
404#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
405#[serde(untagged)]
406pub enum AlgorithmParameters {
407    EllipticCurve(EllipticCurveKeyParameters),
408    RSA(RSAKeyParameters),
409    OctetKey(OctetKeyParameters),
410    OctetKeyPair(OctetKeyPairParameters),
411}
412
413/// The function to use to hash the intermediate thumbprint data.
414#[derive(Debug, Clone, Eq, PartialEq)]
415pub enum ThumbprintHash {
416    SHA256,
417    SHA384,
418    SHA512,
419}
420
421#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
422pub struct Jwk {
423    #[serde(flatten)]
424    pub common: CommonParameters,
425    /// Key algorithm specific parameters
426    #[serde(flatten)]
427    pub algorithm: AlgorithmParameters,
428}
429
430impl Jwk {
431    /// Find whether the Algorithm is implemented and supported
432    pub fn is_supported(&self) -> bool {
433        match self.common.key_algorithm {
434            Some(alg) => alg.to_algorithm().is_ok(),
435            _ => false,
436        }
437    }
438    pub fn from_encoding_key(key: &EncodingKey, alg: Algorithm) -> crate::errors::Result<Self> {
439        Ok(Self {
440            common: CommonParameters {
441                key_algorithm: Some(match alg {
442                    Algorithm::HS256 => KeyAlgorithm::HS256,
443                    Algorithm::HS384 => KeyAlgorithm::HS384,
444                    Algorithm::HS512 => KeyAlgorithm::HS512,
445                    Algorithm::ES256 => KeyAlgorithm::ES256,
446                    Algorithm::ES384 => KeyAlgorithm::ES384,
447                    Algorithm::RS256 => KeyAlgorithm::RS256,
448                    Algorithm::RS384 => KeyAlgorithm::RS384,
449                    Algorithm::RS512 => KeyAlgorithm::RS512,
450                    Algorithm::PS256 => KeyAlgorithm::PS256,
451                    Algorithm::PS384 => KeyAlgorithm::PS384,
452                    Algorithm::PS512 => KeyAlgorithm::PS512,
453                    Algorithm::EdDSA => KeyAlgorithm::EdDSA,
454                }),
455                ..Default::default()
456            },
457            algorithm: match key.family() {
458                crate::algorithms::AlgorithmFamily::Hmac => {
459                    AlgorithmParameters::OctetKey(OctetKeyParameters {
460                        key_type: OctetKeyType::Octet,
461                        value: b64_encode(key.inner()),
462                    })
463                }
464                crate::algorithms::AlgorithmFamily::Rsa => {
465                    let (n, e) = (CryptoProvider::get_default()
466                        .jwk_utils
467                        .extract_rsa_public_key_components)(
468                        key.inner()
469                    )?;
470                    AlgorithmParameters::RSA(RSAKeyParameters {
471                        key_type: RSAKeyType::RSA,
472                        n: b64_encode(n),
473                        e: b64_encode(e),
474                    })
475                }
476                crate::algorithms::AlgorithmFamily::Ec => {
477                    let (curve, x, y) = (CryptoProvider::get_default()
478                        .jwk_utils
479                        .extract_ec_public_key_coordinates)(
480                        key.inner(), alg
481                    )?;
482                    AlgorithmParameters::EllipticCurve(EllipticCurveKeyParameters {
483                        key_type: EllipticCurveKeyType::EC,
484                        curve,
485                        x: b64_encode(x),
486                        y: b64_encode(y),
487                    })
488                }
489                crate::algorithms::AlgorithmFamily::Ed => {
490                    unimplemented!();
491                }
492            },
493        })
494    }
495
496    /// Compute the thumbprint of the JWK.
497    ///
498    /// Per [RFC-7638](https://datatracker.ietf.org/doc/html/rfc7638)
499    pub fn thumbprint(&self, hash_function: ThumbprintHash) -> String {
500        let pre = match &self.algorithm {
501            AlgorithmParameters::EllipticCurve(a) => match a.curve {
502                EllipticCurve::P256 | EllipticCurve::P384 | EllipticCurve::P521 => {
503                    format!(
504                        r#"{{"crv":{},"kty":{},"x":"{}","y":"{}"}}"#,
505                        serde_json::to_string(&a.curve).unwrap(),
506                        serde_json::to_string(&a.key_type).unwrap(),
507                        a.x,
508                        a.y,
509                    )
510                }
511                EllipticCurve::Ed25519 => panic!("EllipticCurve can't contain this curve type"),
512            },
513            AlgorithmParameters::RSA(a) => {
514                format!(
515                    r#"{{"e":"{}","kty":{},"n":"{}"}}"#,
516                    a.e,
517                    serde_json::to_string(&a.key_type).unwrap(),
518                    a.n,
519                )
520            }
521            AlgorithmParameters::OctetKey(a) => {
522                format!(
523                    r#"{{"k":"{}","kty":{}}}"#,
524                    a.value,
525                    serde_json::to_string(&a.key_type).unwrap()
526                )
527            }
528            AlgorithmParameters::OctetKeyPair(a) => match a.curve {
529                EllipticCurve::P256 | EllipticCurve::P384 | EllipticCurve::P521 => {
530                    panic!("OctetKeyPair can't contain this curve type")
531                }
532                EllipticCurve::Ed25519 => {
533                    format!(
534                        r#"{{crv:{},"kty":{},"x":"{}"}}"#,
535                        serde_json::to_string(&a.curve).unwrap(),
536                        serde_json::to_string(&a.key_type).unwrap(),
537                        a.x,
538                    )
539                }
540            },
541        };
542
543        b64_encode((CryptoProvider::get_default().jwk_utils.compute_digest)(
544            pre.as_bytes(),
545            hash_function,
546        ))
547    }
548}
549
550/// A JWK set
551#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
552pub struct JwkSet {
553    pub keys: Vec<Jwk>,
554}
555
556impl JwkSet {
557    /// Find the key in the set that matches the given key id, if any.
558    pub fn find(&self, kid: &str) -> Option<&Jwk> {
559        self.keys
560            .iter()
561            .find(|jwk| jwk.common.key_id.is_some() && jwk.common.key_id.as_ref().unwrap() == kid)
562    }
563}
564
565#[cfg(test)]
566mod tests {
567    use serde_json::json;
568    use wasm_bindgen_test::wasm_bindgen_test;
569
570    use crate::Algorithm;
571    use crate::jwk::{
572        AlgorithmParameters, Jwk, JwkSet, KeyAlgorithm, OctetKeyType, RSAKeyParameters,
573        ThumbprintHash,
574    };
575    use crate::serialization::b64_encode;
576
577    #[test]
578    #[wasm_bindgen_test]
579    fn check_hs256() {
580        let key = b64_encode("abcdefghijklmnopqrstuvwxyz012345");
581        let jwks_json = json!({
582            "keys": [
583                {
584                    "kty": "oct",
585                    "alg": "HS256",
586                    "kid": "abc123",
587                    "k": key
588                }
589            ]
590        });
591
592        let set: JwkSet = serde_json::from_value(jwks_json).expect("Failed HS256 check");
593        assert_eq!(set.keys.len(), 1);
594        let key = &set.keys[0];
595        assert_eq!(key.common.key_id, Some("abc123".to_string()));
596        let algorithm = key.common.key_algorithm.unwrap().to_algorithm().unwrap();
597        assert_eq!(algorithm, Algorithm::HS256);
598
599        match &key.algorithm {
600            AlgorithmParameters::OctetKey(key) => {
601                assert_eq!(key.key_type, OctetKeyType::Octet);
602                assert_eq!(key.value, key.value)
603            }
604            _ => panic!("Unexpected key algorithm"),
605        }
606    }
607
608    #[test]
609    fn deserialize_unknown_key_algorithm() {
610        let key_alg_json = json!("");
611        let key_alg_result: KeyAlgorithm =
612            serde_json::from_value(key_alg_json).expect("Could not deserialize json");
613        assert_eq!(key_alg_result, KeyAlgorithm::UNKNOWN_ALGORITHM);
614    }
615
616    #[test]
617    #[wasm_bindgen_test]
618    fn check_thumbprint() {
619        let tp = Jwk {
620            common: crate::jwk::CommonParameters { key_id: Some("2011-04-29".to_string()), ..Default::default() },
621            algorithm: AlgorithmParameters::RSA(RSAKeyParameters {
622                key_type: crate::jwk::RSAKeyType::RSA,
623                n: "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw".to_string(),
624                e: "AQAB".to_string(),
625            }),
626        }
627            .thumbprint(ThumbprintHash::SHA256);
628        assert_eq!(tp.as_str(), "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs");
629    }
630}