Fix OIDC: ECDSA private key support for SEC1 format.
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
use crate::{
|
||||
config::{build_ecdsa_pem, build_rsa_keypair},
|
||||
config::{EcKeyCurve, build_ecdsa_pem, build_rsa_keypair},
|
||||
manager::application::Resource,
|
||||
};
|
||||
use biscuit::{
|
||||
@@ -197,19 +197,22 @@ async fn parse_ecdsa_key(
|
||||
auth: &OidcProvider,
|
||||
oidc_signature_algorithm: SignatureAlgorithm,
|
||||
) -> Result<(Secret, AlgorithmParameters), String> {
|
||||
let (alg, curve) = match oidc_signature_algorithm {
|
||||
let (alg, curve, ec_curve) = match oidc_signature_algorithm {
|
||||
SignatureAlgorithm::ES256 => (
|
||||
&signature::ECDSA_P256_SHA256_FIXED_SIGNING,
|
||||
EllipticCurve::P256,
|
||||
EcKeyCurve::P256,
|
||||
),
|
||||
SignatureAlgorithm::ES384 => (
|
||||
&signature::ECDSA_P384_SHA384_FIXED_SIGNING,
|
||||
EllipticCurve::P384,
|
||||
EcKeyCurve::P384,
|
||||
),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let ecdsa_key_pair = build_ecdsa_pem(alg, auth.signature_key.secret().await?.as_ref())?;
|
||||
let ecdsa_key_pair =
|
||||
build_ecdsa_pem(alg, ec_curve, auth.signature_key.secret().await?.as_ref())?;
|
||||
let ecdsa_public_key = ecdsa_key_pair.public_key().as_ref();
|
||||
|
||||
let (x, y) = match oidc_signature_algorithm {
|
||||
|
||||
@@ -17,6 +17,7 @@ use crate::{
|
||||
use arc_swap::ArcSwap;
|
||||
use groupware::GroupwareConfig;
|
||||
use hyper::HeaderMap;
|
||||
use pkcs8::EncodePrivateKey;
|
||||
use ring::signature::{EcdsaKeyPair, RsaKeyPair};
|
||||
use store::registry::bootstrap::Bootstrap;
|
||||
use telemetry::Metrics;
|
||||
@@ -115,8 +116,15 @@ pub fn build_rsa_keypair(pem: &str) -> Result<RsaKeyPair, String> {
|
||||
Err("No RSA key found in PEM".to_string())
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum EcKeyCurve {
|
||||
P256,
|
||||
P384,
|
||||
}
|
||||
|
||||
pub fn build_ecdsa_pem(
|
||||
alg: &'static ring::signature::EcdsaSigningAlgorithm,
|
||||
curve: EcKeyCurve,
|
||||
pem: &str,
|
||||
) -> Result<EcdsaKeyPair, String> {
|
||||
for item in rustls_pemfile::read_all(&mut Cursor::new(pem)) {
|
||||
@@ -129,9 +137,104 @@ pub fn build_ecdsa_pem(
|
||||
)
|
||||
.map_err(|err| format!("Failed to parse PKCS8 ECDSA key: {err}"));
|
||||
}
|
||||
rustls_pemfile::Item::Sec1Key(key) => {
|
||||
let pkcs8 = curve.sec1_to_pkcs8(key.secret_sec1_der())?;
|
||||
return EcdsaKeyPair::from_pkcs8(
|
||||
alg,
|
||||
pkcs8.as_bytes(),
|
||||
&ring::rand::SystemRandom::new(),
|
||||
)
|
||||
.map_err(|err| format!("Failed to parse SEC1 ECDSA key: {err}"));
|
||||
}
|
||||
_ => continue, // Skip certificates, DH params, etc.
|
||||
}
|
||||
}
|
||||
|
||||
Err("No ECDSA key found in PEM".to_string())
|
||||
Err("No usable ECDSA private key found in PEM (expected PKCS8 or SEC1)".to_string())
|
||||
}
|
||||
|
||||
impl EcKeyCurve {
|
||||
fn sec1_to_pkcs8(self, der: &[u8]) -> Result<pkcs8::SecretDocument, String> {
|
||||
match self {
|
||||
EcKeyCurve::P256 => p256::SecretKey::from_sec1_der(der)
|
||||
.map_err(|err| format!("Failed to parse SEC1 ECDSA key: {err}"))?
|
||||
.to_pkcs8_der()
|
||||
.map_err(|err| format!("Failed to convert SEC1 ECDSA key to PKCS8: {err}")),
|
||||
EcKeyCurve::P384 => p384::SecretKey::from_sec1_der(der)
|
||||
.map_err(|err| format!("Failed to parse SEC1 ECDSA key: {err}"))?
|
||||
.to_pkcs8_der()
|
||||
.map_err(|err| format!("Failed to convert SEC1 ECDSA key to PKCS8: {err}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{EcKeyCurve, build_ecdsa_pem};
|
||||
use ring::signature::{ECDSA_P256_SHA256_FIXED_SIGNING, ECDSA_P384_SHA384_FIXED_SIGNING};
|
||||
|
||||
const P256_SEC1: &str = "-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIJ9a6n/cu7XaQez5ZX8z8jDFkkfsMB1P9Vbqzbaes2zOoAoGCCqGSM49
|
||||
AwEHoUQDQgAEPCbID7bo+8Nk1vIsTFhVKwRWvb9GWTzzwS75Dd8iZuFl23Twn6Sp
|
||||
V2ZO1FC0WyXxcVOMZN2sJFlCjtaQS+p5Zg==
|
||||
-----END EC PRIVATE KEY-----";
|
||||
|
||||
const P256_PKCS8: &str = "-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgn1rqf9y7tdpB7Pll
|
||||
fzPyMMWSR+wwHU/1VurNtp6zbM6hRANCAAQ8JsgPtuj7w2TW8ixMWFUrBFa9v0ZZ
|
||||
PPPBLvkN3yJm4WXbdPCfpKlXZk7UULRbJfFxU4xk3awkWUKO1pBL6nlm
|
||||
-----END PRIVATE KEY-----";
|
||||
|
||||
const P384_SEC1: &str = "-----BEGIN EC PRIVATE KEY-----
|
||||
MIGkAgEBBDAeecJf8ju/70Nf5nbI4DeRo/+Z3VWXUvB+GwuUczew7fyMbyc6B3EE
|
||||
BskOIqvqu6egBwYFK4EEACKhZANiAAQQjDW03Xn2h9ZmmCMRx+uRaLLfg4o2XITE
|
||||
pwACH9EY4IjTe9LNNp5CTjERd+RlpWxkYopmDS5Trzycz9sDxxSzzXmq90vomJqt
|
||||
fTnNHPFHuR2SAiwuzUf26rcPwa7DCWk=
|
||||
-----END EC PRIVATE KEY-----";
|
||||
|
||||
const P384_PKCS8: &str = "-----BEGIN PRIVATE KEY-----
|
||||
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDAeecJf8ju/70Nf5nbI
|
||||
4DeRo/+Z3VWXUvB+GwuUczew7fyMbyc6B3EEBskOIqvqu6ehZANiAAQQjDW03Xn2
|
||||
h9ZmmCMRx+uRaLLfg4o2XITEpwACH9EY4IjTe9LNNp5CTjERd+RlpWxkYopmDS5T
|
||||
rzycz9sDxxSzzXmq90vomJqtfTnNHPFHuR2SAiwuzUf26rcPwa7DCWk=
|
||||
-----END PRIVATE KEY-----";
|
||||
|
||||
#[test]
|
||||
fn ecdsa_pem_accepts_sec1_and_pkcs8() {
|
||||
build_ecdsa_pem(
|
||||
&ECDSA_P256_SHA256_FIXED_SIGNING,
|
||||
EcKeyCurve::P256,
|
||||
P256_SEC1,
|
||||
)
|
||||
.expect("P-256 SEC1 key should parse");
|
||||
build_ecdsa_pem(
|
||||
&ECDSA_P256_SHA256_FIXED_SIGNING,
|
||||
EcKeyCurve::P256,
|
||||
P256_PKCS8,
|
||||
)
|
||||
.expect("P-256 PKCS8 key should parse");
|
||||
build_ecdsa_pem(
|
||||
&ECDSA_P384_SHA384_FIXED_SIGNING,
|
||||
EcKeyCurve::P384,
|
||||
P384_SEC1,
|
||||
)
|
||||
.expect("P-384 SEC1 key should parse");
|
||||
build_ecdsa_pem(
|
||||
&ECDSA_P384_SHA384_FIXED_SIGNING,
|
||||
EcKeyCurve::P384,
|
||||
P384_PKCS8,
|
||||
)
|
||||
.expect("P-384 PKCS8 key should parse");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ecdsa_pem_rejects_keyless_pem() {
|
||||
let err = build_ecdsa_pem(
|
||||
&ECDSA_P256_SHA256_FIXED_SIGNING,
|
||||
EcKeyCurve::P256,
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB\n-----END CERTIFICATE-----",
|
||||
)
|
||||
.unwrap_err();
|
||||
assert!(err.contains("No usable ECDSA private key"), "{err}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user