use kube::NoCertificateVerification;
use subjaltnames::{get_subj_alt_names, DNSNameRef, SubjAltName};
use der_parser;
use regex::Regex;
use rustls::{ClientConfig, ClientSession, Session};
use rustls::{Certificate, PrivateKey};
use rustls::sign::RSASigningKey;
use untrusted::{Input, Reader};
use std::error::Error;
use std::fs::File;
use std::net::{IpAddr, TcpStream};
use std::io::{self, BufReader, Read};
use std::path::Path;
use std::sync::Arc;
use std::time::Duration;
pub fn get_cert(path: &str) -> Option<Certificate> {
let inpath = Path::new(path);
let cert_file = File::open(inpath).unwrap();
if inpath.extension().unwrap() == "der" {
let mut cert_br = BufReader::new(cert_file);
let mut cert_buf = Vec::new();
cert_br.read_to_end(&mut cert_buf).unwrap();
Some(Certificate(cert_buf))
} else {
let mut pem_br = BufReader::new(cert_file);
let mut pem_buf = String::new();
match pem_br.read_to_string(&mut pem_buf) {
Ok(_) => get_cert_from_pem(pem_buf.as_str()),
Err(e) => {
println!("Error reading cert {}: {}", path, e);
None
}
}
}
}
pub fn get_private_key(path: &str) -> Option<PrivateKey> {
let key_file = File::open(path).unwrap();
let mut key_br = BufReader::new(key_file);
let mut key_buf = Vec::new();
key_br.read_to_end(&mut key_buf).unwrap();
let key = PrivateKey(key_buf.clone());
match RSASigningKey::new(&key) {
Ok(_) => Some(key), Err(_) => {
key_buf.retain(|&i| i != 0);
get_key_from_str(String::from_utf8(key_buf).unwrap().as_str())
}
}
}
fn get_body(s: &str) -> Option<String> {
lazy_static! {
static ref RE: Regex = Regex::new(r"(-----BEGIN .*-----\n)((?:(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)*\n)+)(-----END .*-----)").unwrap();
}
let remove_carriage = s.replace("\r", "");
match RE.captures(remove_carriage.as_str()) {
Some(caps) => caps.get(2).map(|m| {
let no_headers = m.as_str();
no_headers.replace("\n", "")
}),
None => None,
}
}
fn is_pkcs8(s: &str) -> bool {
lazy_static! {
static ref RE: Regex = Regex::new(r"-----BEGIN PRIVATE KEY-----").unwrap();
}
RE.is_match(s)
}
fn validate_private_key(key: PrivateKey) -> Option<PrivateKey> {
match RSASigningKey::new(&key) {
Ok(_) => Some(key),
Err(e) => {
println!("Private key data was invalid: {:?}", e);
None
}
}
}
pub fn get_cert_from_pem(pem: &str) -> Option<Certificate> {
match get_body(pem) {
Some(body) => match ::base64::decode(body.as_str()) {
Ok(der_vec) => Some(Certificate(der_vec)),
Err(e) => {
println!("Failed to decode cert: {}", e.description());
None
}
},
None => None,
}
}
static RSAOID: &'static [u64] = &[1, 2, 840, 113549, 1, 1, 1];
pub fn get_key_from_str(s: &str) -> Option<PrivateKey> {
let pkcs8 = is_pkcs8(s);
match get_body(s) {
Some(body) => {
match ::base64::decode(body.as_str()) {
Ok(der_vec) => {
if pkcs8 {
let der = der_parser::parse_der(der_vec.as_slice()).unwrap().1;
let mut di = der.ref_iter();
di.next(); let algo = di.next();
let bitso = di.next();
match (algo, bitso) {
(Some(alg), Some(bits)) => match alg.ref_iter().next() {
Some(oid) => {
if let der_parser::DerObjectContent::OID(ref v) = oid.content {
if v == &RSAOID {
if let der_parser::DerObjectContent::OctetString(
ref v,
) = bits.content
{
validate_private_key(PrivateKey(v.to_vec()))
} else {
println!("Bit string for private key is invalid");
None
}
} else {
println!("Invalid OID in pkcs8 key, cannot continue");
None
}
} else {
println!("Invalid OID in pkcs8 key, cannot continue");
None
}
}
None => {
println!("pkcs8 does not have an OID, cannot continue");
None
}
},
_ => {
println!("Invalid der data in pkcs8 private key, cannot continue");
None
}
}
} else {
validate_private_key(PrivateKey(der_vec))
}
}
Err(e) => {
println!("Failed to decode private key: {:?}", e);
None
}
}
}
None => None,
}
}
fn fetch_cert_for_ip(ip: &IpAddr, port: u16) -> Result<Vec<Certificate>, io::Error> {
let mut config = ClientConfig::new();
config.dangerous()
.set_certificate_verifier(Arc::new(NoCertificateVerification {}));
let ac = Arc::new(config);
let dns_name_str = format!("{}:{}", ip, port);
let dns_name_ref = DNSNameRef::get_webpki_ref(untrusted::Input::from(dns_name_str.as_bytes()));
let mut session = ClientSession::new(&ac, dns_name_ref);
let sock_addr = (*ip, port).into();
let mut sock = TcpStream::connect_timeout(&sock_addr, Duration::new(2, 0))?;
session.write_tls(&mut sock)?;
let rc = session.read_tls(&mut sock)?;
if rc == 0 {
return Err(io::Error::new(io::ErrorKind::WriteZero, "No data to read"));
}
let _processed = session.process_new_packets();
Ok(session.get_peer_certificates().unwrap_or(Vec::new()))
}
pub fn try_ip_to_name(target_ip: &IpAddr, port: u16) -> Option<String> {
fetch_cert_for_ip(target_ip, port)
.map(|certs| {
for cert in certs.iter() {
let mut reader = Reader::new(Input::from(cert.0.as_slice()));
let names = get_subj_alt_names(&mut reader);
let mut dns_name: Option<String> = None;
let mut found = false;
for san in names.into_iter() {
match san {
SubjAltName::DNSName(dns) => dns_name = Some(String::from(dns)),
SubjAltName::IPAddress(ip) => {
let ipaddr = IpAddr::from(ip);
if &ipaddr == target_ip {
found = true;
}
}
}
}
if found {
return dns_name;
}
}
None
})
.ok()
.and_then(|res| res)
}