haste_server/auth_n/certificates/
mod.rs1use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
2use haste_config::{Config, ConfigType, get_config};
3use haste_fhir_model::r4::generated::terminology::IssueType;
4use haste_fhir_operation_error::OperationOutcomeError;
5use rand::rngs::OsRng;
6use rsa::{
7 RsaPrivateKey, RsaPublicKey,
8 pkcs1::{DecodeRsaPrivateKey, EncodeRsaPrivateKey, EncodeRsaPublicKey},
9 pkcs8::LineEnding,
10 traits::PublicKeyParts,
11};
12use serde::{Deserialize, Serialize};
13use sha1::{Digest, Sha1};
14use std::{path::Path, sync::LazyLock};
15
16use crate::ServerEnvironmentVariables;
17
18static PRIVATE_KEY_FILENAME: &str = "private_key.pem";
19static PUBLIC_KEY_FILENAME: &str = "public_key.pem";
20
21pub fn create_certifications(
22 config: &dyn Config<ServerEnvironmentVariables>,
23) -> Result<(), OperationOutcomeError> {
24 let certificate_dir = config
25 .get(ServerEnvironmentVariables::CertificationDir)
26 .unwrap();
27 let dir: &Path = Path::new(&certificate_dir);
28
29 let mut rng = OsRng;
30 let bits = 2048;
31
32 let private_key_file = dir.join(PRIVATE_KEY_FILENAME);
33 let public_key_file = dir.join(PUBLIC_KEY_FILENAME);
34
35 if !private_key_file.exists() {
37 let priv_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
38 let pub_key = RsaPublicKey::from(&priv_key);
39 std::fs::create_dir_all(certificate_dir).unwrap();
40 std::fs::write(
41 private_key_file,
42 priv_key.to_pkcs1_pem(LineEnding::default()).unwrap(),
43 )
44 .map_err(|e| OperationOutcomeError::fatal(IssueType::Exception(None), e.to_string()))?;
45
46 std::fs::write(
47 public_key_file,
48 pub_key.to_pkcs1_pem(LineEnding::default()).unwrap(),
49 )
50 .map_err(|e| OperationOutcomeError::fatal(IssueType::Exception(None), e.to_string()))?;
51 }
52
53 Ok(())
54}
55
56#[derive(Serialize, Deserialize, Debug)]
57pub enum JSONWebKeyAlgorithm {
58 RS256,
59}
60
61#[derive(Serialize, Deserialize, Debug)]
62pub enum JSONWebKeyType {
63 RSA,
64}
65
66#[derive(Serialize, Deserialize, Debug)]
67pub struct JSONWebKey {
68 kid: String,
69
70 alg: JSONWebKeyAlgorithm,
71 kty: JSONWebKeyType,
72 e: String,
74 n: String,
75 x5t: Option<String>,
76}
77
78#[derive(Serialize, Deserialize, Debug)]
79pub struct JSONWebKeySet {
80 keys: Vec<JSONWebKey>,
81}
82
83pub static JWK_SET: LazyLock<JSONWebKeySet> = LazyLock::new(|| {
84 let config = get_config(ConfigType::Environment);
85 let certificate_dir = config
86 .get(ServerEnvironmentVariables::CertificationDir)
87 .unwrap();
88 let cert_dir: &Path = Path::new(&certificate_dir);
89 let rsa_private = RsaPrivateKey::from_pkcs1_pem(
90 &std::fs::read_to_string(&cert_dir.join(PRIVATE_KEY_FILENAME)).unwrap(),
91 )
92 .unwrap();
93 let rsa_public_key = rsa_private.to_public_key();
94
95 let mut hasher = Sha1::new();
96 hasher.update(rsa_public_key.to_pkcs1_der().unwrap().as_bytes());
97 let x5t = hasher.finalize();
98
99 let rsa_public = JSONWebKey {
100 kid: URL_SAFE_NO_PAD.encode(&x5t),
101 alg: JSONWebKeyAlgorithm::RS256,
102 kty: JSONWebKeyType::RSA,
103 e: URL_SAFE_NO_PAD.encode(&rsa_public_key.e().clone().to_bytes_be()),
104 n: URL_SAFE_NO_PAD.encode(&rsa_public_key.n().clone().to_bytes_be()),
105 x5t: Some(URL_SAFE_NO_PAD.encode(&x5t)),
106 };
107
108 JSONWebKeySet {
109 keys: vec![rsa_public],
110 }
111});
112
113#[allow(unused)]
114static DECODING_KEY: LazyLock<jsonwebtoken::DecodingKey> = LazyLock::new(|| {
116 let config = get_config(ConfigType::Environment);
118 let certificate_dir = config
119 .get(ServerEnvironmentVariables::CertificationDir)
120 .unwrap();
121 let cert_dir: &Path = Path::new(&certificate_dir);
122 jsonwebtoken::DecodingKey::from_rsa_pem(
123 &std::fs::read(cert_dir.join(PUBLIC_KEY_FILENAME)).unwrap(),
124 )
125 .unwrap()
126});
127
128static ENCODING_KEY: LazyLock<jsonwebtoken::EncodingKey> = LazyLock::new(|| {
129 let config = get_config(ConfigType::Environment);
131 let certificate_dir = config
132 .get(ServerEnvironmentVariables::CertificationDir)
133 .unwrap();
134 let cert_dir: &Path = Path::new(&certificate_dir);
135 jsonwebtoken::EncodingKey::from_rsa_pem(
136 &std::fs::read(cert_dir.join(PRIVATE_KEY_FILENAME)).unwrap(),
137 )
138 .unwrap()
139});
140
141#[allow(unused)]
142pub fn decoding_key() -> &'static jsonwebtoken::DecodingKey {
143 &*DECODING_KEY
144}
145
146pub fn encoding_key() -> &'static jsonwebtoken::EncodingKey {
147 &*ENCODING_KEY
148}