-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: adjust chain and self signed certificate
- Loading branch information
Showing
10 changed files
with
317 additions
and
219 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// Copyright 2024 Tree xie. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
use super::Certificate; | ||
use crate::util; | ||
use once_cell::sync::Lazy; | ||
use pingora::tls::x509::X509; | ||
|
||
// https://letsencrypt.org/certificates/ | ||
const E5: &[u8] = include_bytes!("../assets/e5.pem"); | ||
const E6: &[u8] = include_bytes!("../assets/e6.pem"); | ||
const R10: &[u8] = include_bytes!("../assets/r10.pem"); | ||
const R11: &[u8] = include_bytes!("../assets/r11.pem"); | ||
|
||
fn parse_chain_certificate(data: &[u8]) -> Option<X509> { | ||
if let Ok(info) = Certificate::new( | ||
std::string::String::from_utf8_lossy(data).to_string(), | ||
"".to_string(), | ||
) { | ||
if info.not_after > util::now().as_secs() as i64 { | ||
return X509::from_pem(data).ok(); | ||
} | ||
} | ||
None | ||
} | ||
static E5_CERTIFICATE: Lazy<Option<X509>> = | ||
Lazy::new(|| parse_chain_certificate(E5)); | ||
static E6_CERTIFICATE: Lazy<Option<X509>> = | ||
Lazy::new(|| parse_chain_certificate(E6)); | ||
static R10_CERTIFICATE: Lazy<Option<X509>> = | ||
Lazy::new(|| parse_chain_certificate(R10)); | ||
static R11_CERTIFICATE: Lazy<Option<X509>> = | ||
Lazy::new(|| parse_chain_certificate(R11)); | ||
|
||
pub fn get_lets_encrypt_chain_certificate(cn: &str) -> Option<X509> { | ||
match cn { | ||
"E5" => E5_CERTIFICATE.clone(), | ||
"E6" => E6_CERTIFICATE.clone(), | ||
"R10" => R10_CERTIFICATE.clone(), | ||
"R11" => R11_CERTIFICATE.clone(), | ||
_ => None, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
// Copyright 2024 Tree xie. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
use crate::util; | ||
use serde::{Deserialize, Serialize}; | ||
use snafu::Snafu; | ||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; | ||
|
||
mod chain; | ||
mod self_signed; | ||
mod validity_checker; | ||
|
||
#[derive(Debug, Snafu)] | ||
pub enum Error { | ||
#[snafu(display("X509 error, category: {category}, {message}"))] | ||
X509 { category: String, message: String }, | ||
} | ||
|
||
type Result<T, E = Error> = std::result::Result<T, E>; | ||
|
||
fn parse_ip_addr(data: &[u8]) -> Result<IpAddr> { | ||
let addr = if data.len() == 4 { | ||
let arr: [u8; 4] = data.try_into().unwrap_or_default(); | ||
IpAddr::V4(Ipv4Addr::from(arr)) | ||
} else { | ||
let arr: [u8; 16] = data.try_into().unwrap_or_default(); | ||
IpAddr::V6(Ipv6Addr::from(arr)) | ||
}; | ||
Ok(addr) | ||
} | ||
|
||
#[derive(Debug, Deserialize, Serialize, Default, Clone)] | ||
pub struct Certificate { | ||
pub domains: Vec<String>, | ||
pub pem: Vec<u8>, | ||
pub key: Vec<u8>, | ||
pub acme: Option<String>, | ||
pub not_after: i64, | ||
pub not_before: i64, | ||
pub issuer: String, | ||
} | ||
impl Certificate { | ||
pub fn new(pem: String, key: String) -> Result<Certificate> { | ||
let pem_data = | ||
util::convert_certificate_bytes(&Some(pem)).unwrap_or_default(); | ||
let (_, p) = | ||
x509_parser::pem::parse_x509_pem(&pem_data).map_err(|e| { | ||
Error::X509 { | ||
category: "parse_x509_pem".to_string(), | ||
message: e.to_string(), | ||
} | ||
})?; | ||
let x509 = p.parse_x509().map_err(|e| Error::X509 { | ||
category: "parse_x509".to_string(), | ||
message: e.to_string(), | ||
})?; | ||
let mut dns_names = vec![]; | ||
if let Ok(Some(subject_alternative_name)) = | ||
x509.subject_alternative_name() | ||
{ | ||
for item in subject_alternative_name.value.general_names.iter() { | ||
match item { | ||
x509_parser::prelude::GeneralName::DNSName(name) => { | ||
dns_names.push(name.to_string()); | ||
}, | ||
x509_parser::prelude::GeneralName::IPAddress(data) => { | ||
if let Ok(addr) = parse_ip_addr(data) { | ||
dns_names.push(addr.to_string()); | ||
} | ||
}, | ||
_ => {}, | ||
}; | ||
} | ||
}; | ||
let validity = x509.validity(); | ||
Ok(Self { | ||
domains: dns_names, | ||
pem: pem_data, | ||
key: util::convert_certificate_bytes(&Some(key)) | ||
.unwrap_or_default(), | ||
not_after: validity.not_after.timestamp(), | ||
not_before: validity.not_before.timestamp(), | ||
issuer: x509.issuer.to_string(), | ||
..Default::default() | ||
}) | ||
} | ||
/// Get the common name of certificate issuer | ||
pub fn get_issuer_common_name(&self) -> String { | ||
let re = regex::Regex::new(r"CN=(?P<CN>[\S ]+?)($|,)").unwrap(); | ||
if let Some(caps) = re.captures(&self.issuer) { | ||
return caps["CN"].to_string(); | ||
} | ||
"".to_string() | ||
} | ||
/// Validate the cert is within the expiration date. | ||
pub fn valid(&self) -> bool { | ||
let ts = util::now().as_secs() as i64; | ||
self.not_after - ts > 2 * 24 * 3600 | ||
} | ||
/// Get the cert pem data. | ||
pub fn get_cert(&self) -> Vec<u8> { | ||
self.pem.clone() | ||
} | ||
/// Get the cert key data. | ||
pub fn get_key(&self) -> Vec<u8> { | ||
self.key.clone() | ||
} | ||
} | ||
|
||
pub use chain::get_lets_encrypt_chain_certificate; | ||
pub use self_signed::{ | ||
add_self_signed_certificate, get_self_signed_certificate, | ||
new_self_signed_certificate_validity_service, SelfSignedCertificate, | ||
}; | ||
pub use validity_checker::new_certificate_validity_service; |
Oops, something went wrong.