You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-08-09 04:22:45 +03:00
Allow loading private keys from files
This commit is contained in:
@@ -253,6 +253,7 @@ impl ServerCommand {
|
|||||||
let key_store = config
|
let key_store = config
|
||||||
.oauth2
|
.oauth2
|
||||||
.key_store()
|
.key_store()
|
||||||
|
.await
|
||||||
.context("could not import keys from config")?;
|
.context("could not import keys from config")?;
|
||||||
// Wrap the key store in an Arc
|
// Wrap the key store in an Arc
|
||||||
let key_store = Arc::new(key_store);
|
let key_store = Arc::new(key_store);
|
||||||
|
@@ -12,6 +12,8 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use mas_jose::{JsonWebKeySet, StaticJwksStore, StaticKeystore};
|
use mas_jose::{JsonWebKeySet, StaticJwksStore, StaticKeystore};
|
||||||
@@ -24,7 +26,7 @@ use schemars::JsonSchema;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::skip_serializing_none;
|
use serde_with::skip_serializing_none;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::task;
|
use tokio::{fs::File, io::AsyncReadExt, task};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
@@ -37,10 +39,18 @@ pub enum KeyType {
|
|||||||
Ecdsa,
|
Ecdsa,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(JsonSchema, Serialize, Deserialize, Clone, Debug)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum KeyOrPath {
|
||||||
|
Key(String),
|
||||||
|
Path(PathBuf),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(JsonSchema, Serialize, Deserialize, Clone, Debug)]
|
#[derive(JsonSchema, Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct KeyConfig {
|
pub struct KeyConfig {
|
||||||
r#type: KeyType,
|
r#type: KeyType,
|
||||||
key: String,
|
#[serde(flatten)]
|
||||||
|
key: KeyOrPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(JsonSchema, Serialize, Deserialize, Clone, Debug)]
|
#[derive(JsonSchema, Serialize, Deserialize, Clone, Debug)]
|
||||||
@@ -140,20 +150,52 @@ impl OAuth2Config {
|
|||||||
.expect("could not build discovery url")
|
.expect("could not build discovery url")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn key_store(&self) -> anyhow::Result<StaticKeystore> {
|
pub async fn key_store(&self) -> anyhow::Result<StaticKeystore> {
|
||||||
let mut store = StaticKeystore::new();
|
let mut store = StaticKeystore::new();
|
||||||
|
|
||||||
for key in &self.keys {
|
for item in &self.keys {
|
||||||
match key.r#type {
|
// Read the key either embedded in the config file or on disk
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
let (key_as_bytes, key_as_str) = match &item.key {
|
||||||
|
KeyOrPath::Key(key) => (key.as_bytes(), Some(key.as_str())),
|
||||||
|
KeyOrPath::Path(path) => {
|
||||||
|
let mut file = File::open(path).await?;
|
||||||
|
file.read_to_end(&mut buf).await?;
|
||||||
|
|
||||||
|
(&buf[..], std::str::from_utf8(&buf).ok())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match item.r#type {
|
||||||
|
// TODO: errors are not well carried here
|
||||||
KeyType::Ecdsa => {
|
KeyType::Ecdsa => {
|
||||||
let key = p256::SecretKey::from_pkcs1_pem(&key.key)
|
// First try to read it as DER from the bytes
|
||||||
.or_else(|_| p256::SecretKey::from_pkcs8_pem(&key.key))
|
let mut key = p256::SecretKey::from_pkcs1_der(key_as_bytes)
|
||||||
.or_else(|_| p256::SecretKey::from_sec1_pem(&key.key))?;
|
.or_else(|_| p256::SecretKey::from_pkcs8_der(key_as_bytes))
|
||||||
|
.or_else(|_| p256::SecretKey::from_sec1_der(key_as_bytes));
|
||||||
|
|
||||||
|
// If the file was a valid string, try reading it as PEM
|
||||||
|
if let Some(key_as_str) = key_as_str {
|
||||||
|
key = key
|
||||||
|
.or_else(|_| p256::SecretKey::from_pkcs1_pem(key_as_str))
|
||||||
|
.or_else(|_| p256::SecretKey::from_pkcs8_pem(key_as_str))
|
||||||
|
.or_else(|_| p256::SecretKey::from_sec1_pem(key_as_str));
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = key?;
|
||||||
store.add_ecdsa_key(key.into())?;
|
store.add_ecdsa_key(key.into())?;
|
||||||
}
|
}
|
||||||
KeyType::Rsa => {
|
KeyType::Rsa => {
|
||||||
let key = rsa::RsaPrivateKey::from_pkcs1_pem(&key.key)
|
let mut key = rsa::RsaPrivateKey::from_pkcs1_der(key_as_bytes)
|
||||||
.or_else(|_| rsa::RsaPrivateKey::from_pkcs8_pem(&key.key))?;
|
.or_else(|_| rsa::RsaPrivateKey::from_pkcs8_der(key_as_bytes));
|
||||||
|
|
||||||
|
if let Some(key_as_str) = key_as_str {
|
||||||
|
key = key
|
||||||
|
.or_else(|_| rsa::RsaPrivateKey::from_pkcs1_pem(key_as_str))
|
||||||
|
.or_else(|_| rsa::RsaPrivateKey::from_pkcs8_pem(key_as_str));
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = key?;
|
||||||
store.add_rsa_key(key)?;
|
store.add_rsa_key(key)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,9 +228,11 @@ impl ConfigurationSection<'_> for OAuth2Config {
|
|||||||
.context("could not join blocking task")??;
|
.context("could not join blocking task")??;
|
||||||
let rsa_key = KeyConfig {
|
let rsa_key = KeyConfig {
|
||||||
r#type: KeyType::Rsa,
|
r#type: KeyType::Rsa,
|
||||||
key: rsa_key
|
key: KeyOrPath::Key(
|
||||||
.to_pkcs1_pem(pem_rfc7468::LineEnding::LF)?
|
rsa_key
|
||||||
.to_string(),
|
.to_pkcs1_pem(pem_rfc7468::LineEnding::LF)?
|
||||||
|
.to_string(),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
let span = tracing::info_span!("ecdsa");
|
let span = tracing::info_span!("ecdsa");
|
||||||
@@ -203,7 +247,7 @@ impl ConfigurationSection<'_> for OAuth2Config {
|
|||||||
.context("could not join blocking task")?;
|
.context("could not join blocking task")?;
|
||||||
let ecdsa_key = KeyConfig {
|
let ecdsa_key = KeyConfig {
|
||||||
r#type: KeyType::Ecdsa,
|
r#type: KeyType::Ecdsa,
|
||||||
key: ecdsa_key.to_pem(pem_rfc7468::LineEnding::LF)?.to_string(),
|
key: KeyOrPath::Key(ecdsa_key.to_pem(pem_rfc7468::LineEnding::LF)?.to_string()),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@@ -216,30 +260,34 @@ impl ConfigurationSection<'_> for OAuth2Config {
|
|||||||
fn test() -> Self {
|
fn test() -> Self {
|
||||||
let rsa_key = KeyConfig {
|
let rsa_key = KeyConfig {
|
||||||
r#type: KeyType::Rsa,
|
r#type: KeyType::Rsa,
|
||||||
key: indoc::indoc! {r#"
|
key: KeyOrPath::Key(
|
||||||
-----BEGIN PRIVATE KEY-----
|
indoc::indoc! {r#"
|
||||||
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAymS2RkeIZo7pUeEN
|
-----BEGIN PRIVATE KEY-----
|
||||||
QUGCG4GLJru5jzxomO9jiNr5D/oRcerhpQVc9aCpBfAAg4l4a1SmYdBzWqX0X5pU
|
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAymS2RkeIZo7pUeEN
|
||||||
scgTtQIDAQABAkEArNIMlrxUK4bSklkCcXtXdtdKE9vuWfGyOw0GyAB69fkEUBxh
|
QUGCG4GLJru5jzxomO9jiNr5D/oRcerhpQVc9aCpBfAAg4l4a1SmYdBzWqX0X5pU
|
||||||
3j65u+u3ZmW+bpMWHgp1FtdobE9nGwb2VBTWAQIhAOyU1jiUEkrwKK004+6b5QRE
|
scgTtQIDAQABAkEArNIMlrxUK4bSklkCcXtXdtdKE9vuWfGyOw0GyAB69fkEUBxh
|
||||||
vC9UI2vDWy5vioMNx5Y1AiEA2wGAJ6ETF8FF2Vd+kZlkKK7J0em9cl0gbJDsWIEw
|
3j65u+u3ZmW+bpMWHgp1FtdobE9nGwb2VBTWAQIhAOyU1jiUEkrwKK004+6b5QRE
|
||||||
N4ECIEyWYkMurD1WQdTQqnk0Po+DMOihdFYOiBYgRdbnPxWBAiEAmtd0xJAd7622
|
vC9UI2vDWy5vioMNx5Y1AiEA2wGAJ6ETF8FF2Vd+kZlkKK7J0em9cl0gbJDsWIEw
|
||||||
tPQniMnrBtiN2NxqFXHCev/8Gpc8gAECIBcaPcF59qVeRmYrfqzKBxFm7LmTwlAl
|
N4ECIEyWYkMurD1WQdTQqnk0Po+DMOihdFYOiBYgRdbnPxWBAiEAmtd0xJAd7622
|
||||||
Gh7BNzCeN+D6
|
tPQniMnrBtiN2NxqFXHCev/8Gpc8gAECIBcaPcF59qVeRmYrfqzKBxFm7LmTwlAl
|
||||||
-----END PRIVATE KEY-----
|
Gh7BNzCeN+D6
|
||||||
"#}
|
-----END PRIVATE KEY-----
|
||||||
.to_string(),
|
"#}
|
||||||
|
.to_string(),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
let ecdsa_key = KeyConfig {
|
let ecdsa_key = KeyConfig {
|
||||||
r#type: KeyType::Ecdsa,
|
r#type: KeyType::Ecdsa,
|
||||||
key: indoc::indoc! {r#"
|
key: KeyOrPath::Key(
|
||||||
-----BEGIN PRIVATE KEY-----
|
indoc::indoc! {r#"
|
||||||
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgqfn5mYO/5Qq/wOOiWgHA
|
-----BEGIN PRIVATE KEY-----
|
||||||
NaiDiepgUJ2GI5eq2V8D8nahRANCAARMK9aKUd/H28qaU+0qvS6bSJItzAge1VHn
|
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgqfn5mYO/5Qq/wOOiWgHA
|
||||||
OhBAAUVci1RpmUA+KdCL5sw9nadAEiONeiGr+28RYHZmlB9qXnjC
|
NaiDiepgUJ2GI5eq2V8D8nahRANCAARMK9aKUd/H28qaU+0qvS6bSJItzAge1VHn
|
||||||
-----END PRIVATE KEY-----
|
OhBAAUVci1RpmUA+KdCL5sw9nadAEiONeiGr+28RYHZmlB9qXnjC
|
||||||
"#}
|
-----END PRIVATE KEY-----
|
||||||
.to_string(),
|
"#}
|
||||||
|
.to_string(),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
Reference in New Issue
Block a user