jwt2 0.1.0-alpha.0

JSON Web Tokens done right... hopefully.
Documentation
//! # RSA-based algorithms using PKCS1-v1_5 ([`RS256`], [`RS384`], [`RS512`])

use crate::{
    Algorithm, Header, JwsSigner, JwsVerifier, RecommendHeaderParams, SigningAlgorithm,
    ValidateHeaderParams,
};
use base64ct::LineEnding;
use rsa::pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey};
use rsa::pkcs1v15::{Signature, SigningKey, VerifyingKey};
use rsa::pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey};
use sha2::{Sha256, Sha384, Sha512};
use signature::{Keypair, SignatureEncoding};

pub struct RSAVerifierConfig {}

macro_rules! impl_rs {
    (
        $(#[$main_attrs:meta])*
        main: $main_ident:ident,
        $(#[$public_attrs:meta])*
        public: $public_ident:ident,
        hash: $hash_ty:ty
    ) => {
        $(#[$main_attrs])*
        pub type $main_ident = GenericRsaImpl<SigningKey<$hash_ty>>;
        $(#[$public_attrs])*
        pub type $public_ident = GenericRsaImpl<VerifyingKey<$hash_ty>>;

        impl $main_ident {
            // TODO: Documentation
            /// Create an instance of this algorithm from a signing key.
            pub fn from(key: SigningKey<$hash_ty>) -> Self {
                Self {
                    key
                }
            }

            pub fn parse_pkcs1_pem(key: &str) -> rsa::pkcs1::Result<Self> {
                SigningKey::from_pkcs1_pem(key).map(Self::from)
            }
            pub fn parse_pkcs8_pem(key: &str) -> rsa::pkcs8::Result<Self> {
                SigningKey::from_pkcs8_pem(key).map(Self::from)
            }

            // NOTE(tecc): Maybe more direct dependencies so we can avoid these absurdly long type names?
            pub fn encode_as_pkcs8_pem(&self) -> rsa::pkcs8::Result<rsa::pkcs8::der::zeroize::Zeroizing<String>> {
                EncodePrivateKey::to_pkcs8_pem(&self.key, LineEnding::default())
            }

            /// Generates a new key.
            #[cfg(feature = "rand")]
            #[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
            pub fn new_rand<R>(rng: &mut R, bits: usize) -> rsa::Result<Self> where R: rand_core::CryptoRngCore {
                Ok(Self {
                    key: SigningKey::random(rng, bits)?
                })
            }

            /// Creates a corresponding verifying-only instance from `self`.
            pub fn public(&self) -> $public_ident {
                // This function is only duplicated for the convenience of the consumer.
                // In reality they have no implementation differences.
                $public_ident::from(self.key.verifying_key())
            }
        }
        impl $public_ident {
            /// Create an instance of this algorithm from
            pub fn from(key: VerifyingKey<$hash_ty>) -> Self {
                Self {
                    key
                }
            }
            pub fn parse_pkcs1_pem(key: &str) -> rsa::pkcs1::Result<Self> {
                VerifyingKey::from_pkcs1_pem(key).map(Self::from)
            }
            pub fn parse_pkcs8_pem(key: &str) -> rsa::pkcs8::spki::Result<Self> {
                VerifyingKey::<$hash_ty>::from_public_key_pem(key).map(Self::from)
            }

            pub fn encode_as_pkcs8_pem(&self) -> rsa::pkcs8::spki::Result<String> {
                EncodePublicKey::to_public_key_pem(&self.key, LineEnding::default())
            }
        }
        impl Algo for $main_ident {
            // Whilst this maybe *shouldn't* be done, it's the shortest solution.
            const ALGORITHM: SigningAlgorithm = SigningAlgorithm::$main_ident;
        }
        impl Algo for $public_ident {
            const ALGORITHM: SigningAlgorithm = SigningAlgorithm::$main_ident;
        }
    };
}

impl_rs!(
    /// RSASSA-PKCS1-v1_5 using SHA2-256.
    ///
    /// If you only need verifying capabilities, try [`RS256Public`].
    main: RS256,
    public: RS256Public,
    hash: Sha256
);
impl_rs!(
    /// RSASSA-PKCS1-v1_5 using SHA2-384.
    ///
    /// If you only need verifying capabilities, try [`RS384Public`].
    main: RS384,
    public: RS384Public,
    hash: Sha384
);
impl_rs!(
    /// RSASSA-PKCS1-v1_5 using SHA2-512.
    ///
    /// If you only need verifying capabilities, try [`RS512Public`].
    main: RS512,
    /// RSASSA-PKCS1-v1_5 using SHA2-512.
    ///
    /// If you only need verifying capabilities, try [`RS512Public`].
    public: RS512Public,
    hash: Sha512
);

/// RSA algorithm.
pub struct GenericRsaImpl<Key> {
    key: Key,
}
impl<Key> GenericRsaImpl<Key> {
    pub fn get_key(&self) -> &Key {
        &self.key
    }
}

trait Algo {
    const ALGORITHM: SigningAlgorithm;
}

impl<Key> RecommendHeaderParams for GenericRsaImpl<Key>
where
    Self: Algo,
{
    fn alg(&self) -> Algorithm {
        Algorithm::Signing(Self::ALGORITHM)
    }
}
impl<Key> JwsSigner for GenericRsaImpl<Key>
where
    Key: signature::Signer<Signature>,
    Self: Algo,
{
    fn sign(&self, data: &[u8]) -> Vec<u8> {
        self.key.sign(data).to_bytes().into_vec()
    }
}

impl<Key> ValidateHeaderParams for GenericRsaImpl<Key>
where
    Self: Algo,
{
    fn validate_header(&self, header: &Header) -> bool {
        header.algorithm == Self::ALGORITHM
    }
}
impl<Key> JwsVerifier for GenericRsaImpl<Key>
where
    Key: signature::Verifier<Signature>,
    Self: Algo,
{
    fn verify_signature(&self, data: &[u8], signature: &[u8]) -> bool {
        let Ok(signature) = Signature::try_from(signature) else {
            return false;
        };
        self.key.verify(data, &signature).is_ok()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::repr;
    use rsa::pkcs1v15::{SigningKey, VerifyingKey};
    use rsa::pkcs8::{DecodePrivateKey, DecodePublicKey};
    use sha2::Sha256;

    macro_rules! test_rsa {
        (
            $private_ty:path = $private_key:expr,
            $public_ty:path = $public_key:expr =>
            data: $data:expr,
            sig: $signature:expr
        ) => {{
            {
                let data: &[u8] = { $data }.as_ref();
                let signature =
                    repr::decode_bytes_from_base64url($signature).expect("invalid signature");

                let private_instance = <$private_ty>::from($private_key);

                let created_signature = private_instance.sign(data);
                // eprintln!("{}", repr::encode_bytes_as_base64url(&created_signature));

                let public_instance = <$public_ty>::from($public_key);
                assert!(
                    public_instance.verify_signature(data, &signature),
                    "Precalculated signature does not match"
                );
                assert!(
                    public_instance.verify_signature(data, &created_signature),
                    "Precalculated signature does not match"
                );
            }
        }};
    }

    #[test]
    fn rs256() {
        let jwtio_private_key: SigningKey<Sha256> =
            SigningKey::from_pkcs8_pem(JWTIO_PRIVATE_KEY_RS256)
                .expect("Could not decode signing key");
        let jwtio_public_key: VerifyingKey<Sha256> =
            VerifyingKey::from_public_key_pem(JWTIO_PUBLIC_KEY_RS256)
                .expect("Could not decode verifying key");

        test_rsa!(
            RS256 = jwtio_private_key.clone(),
            RS256Public = jwtio_public_key.clone() =>
            data: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0",
            sig: "NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2\
            t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0a\
            SHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2wo\
            YIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ"
        )
    }

    #[test]
    fn rs384() {
        let jwtio_private_key: SigningKey<Sha384> =
            SigningKey::from_pkcs8_pem(JWTIO_PRIVATE_KEY_RS384)
                .expect("Could not decode signing key");
        let jwtio_public_key: VerifyingKey<Sha384> =
            VerifyingKey::from_public_key_pem(JWTIO_PUBLIC_KEY_RS384)
                .expect("Could not decode verifying key");

        test_rsa!(
            RS384 = jwtio_private_key.clone(),
            RS384Public = jwtio_public_key.clone() =>
            data: "eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0",
            sig: "o1hC1xYbJolSyh0-bOY230w22zEQSk5TiBfc-OCvtpI2JtYlW-23-8B48NpATozzMHn0j3rE0xVUldxShz\
            y0xeJ7vYAccVXu2Gs9rnTVqouc-UZu_wJHkZiKBL67j8_61L6SXswzPAQu4kVDwAefGf5hyYBUM-80vYZwWPEpLI\
            8K4yCBsF6I9N1yQaZAJmkMp_Iw371Menae4Mp4JusvBJS-s6LrmG2QbiZaFaxVJiW8KlUkWyUCns8-qFl5OMeYlg\
            GFsyvvSHvXCzQrsEXqyCdS4tQJd73ayYA4SPtCb9clz76N1zE5WsV4Z0BYrxeb77oA7jJhh994RAPzCG0hmQ"
        )
    }

    #[test]
    fn rs512() {
        let jwtio_private_key: SigningKey<Sha512> =
            SigningKey::from_pkcs8_pem(JWTIO_PRIVATE_KEY_RS512)
                .expect("Could not decode signing key");
        let jwtio_public_key: VerifyingKey<Sha512> =
            VerifyingKey::from_public_key_pem(JWTIO_PUBLIC_KEY_RS512)
                .expect("Could not decode verifying key");

        test_rsa!(
            RS512 = jwtio_private_key.clone(),
            RS512Public = jwtio_public_key.clone() =>
            data: "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0",
            sig: "jYW04zLDHfR1v7xdrW3lCGZrMIsVe0vWCfVkN2DRns2c3MN-mcp_-RE6TN9umSBYoNV-mnb31wFf8iun3f\
            B6aDS6m_OXAiURVEKrPFNGlR38JSHUtsFzqTOj-wFrJZN4RwvZnNGSMvK3wzzUriZqmiNLsG8lktlEn6KA4kYVaM\
            61_NpmPHWAjGExWv7cjHYupcjMSmR8uMTwN5UuAwgW6FRstCJEfoxwb0WKiyoaSlDuIiHZJ0cyGhhEmmAPiCwtPA\
            wGeaL1yZMcp0p82cpTQ5Qb-7CtRov3N4DcOHgWYk6LomPR5j5cCkePAz87duqyzSMpCB0mCOuE3CU2VMtGeQ"
        )
    }

    const JWTIO_PRIVATE_KEY_RS256: &str = "-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj
MzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu
NMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ
qgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg
p2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR
ZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi
VuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV
laAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8
sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H
mQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY
dgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cw
ta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQ
DM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2T
N0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t
0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPv
t8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDU
AhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk
48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISL
DY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnK
xt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEA
mNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh
2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfz
et6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhr
VBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicD
TQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc
dn/RsYEONbwQSjIfMPkvxF+8HQ==
-----END PRIVATE KEY-----";
    const JWTIO_PUBLIC_KEY_RS256: &str = "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo
4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u
+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh
kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ
0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg
cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc
mwIDAQAB
-----END PUBLIC KEY-----";

    const JWTIO_PRIVATE_KEY_RS384: &str = "-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj
MzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu
NMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ
qgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg
p2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR
ZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi
VuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV
laAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8
sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H
mQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY
dgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cw
ta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQ
DM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2T
N0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t
0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPv
t8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDU
AhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk
48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISL
DY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnK
xt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEA
mNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh
2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfz
et6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhr
VBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicD
TQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc
dn/RsYEONbwQSjIfMPkvxF+8HQ==
-----END PRIVATE KEY-----";
    const JWTIO_PUBLIC_KEY_RS384: &str = "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo
4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u
+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh
kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ
0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg
cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc
mwIDAQAB
-----END PUBLIC KEY-----";

    const JWTIO_PRIVATE_KEY_RS512: &str = "-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj
MzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu
NMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ
qgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg
p2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR
ZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi
VuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV
laAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8
sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H
mQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY
dgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cw
ta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQ
DM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2T
N0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t
0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPv
t8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDU
AhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk
48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISL
DY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnK
xt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEA
mNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh
2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfz
et6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhr
VBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicD
TQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc
dn/RsYEONbwQSjIfMPkvxF+8HQ==
-----END PRIVATE KEY-----";
    const JWTIO_PUBLIC_KEY_RS512: &str = "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo
4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u
+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh
kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ
0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg
cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc
mwIDAQAB
-----END PUBLIC KEY-----";
}