summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tedge/Cargo.toml3
-rw-r--r--tedge/src/certificate.rs85
2 files changed, 87 insertions, 1 deletions
diff --git a/tedge/Cargo.toml b/tedge/Cargo.toml
index ef4e879f..89608e4d 100644
--- a/tedge/Cargo.toml
+++ b/tedge/Cargo.toml
@@ -30,6 +30,7 @@ url = "2.2.0"
which = "4.0.2"
x509-parser = "0.9.0"
zeroize = "1.2.0"
+sha-1 = "0.9.4"
[dev-dependencies]
assert_cmd = "1.0.2"
@@ -37,6 +38,8 @@ assert_matches = "1.4.0"
mockito = "0.29.0"
pem = "0.8.2"
predicates = "1.0.6"
+crypto-hash = "0.3.4"
+base64 = "0.13.0"
[features]
integration-test = []
diff --git a/tedge/src/certificate.rs b/tedge/src/certificate.rs
index 941a1fa0..9bb50846 100644
--- a/tedge/src/certificate.rs
+++ b/tedge/src/certificate.rs
@@ -12,6 +12,7 @@ use rcgen::Certificate;
use rcgen::CertificateParams;
use rcgen::RcgenError;
use reqwest::{StatusCode, Url};
+use sha1::{Digest, Sha1};
use std::{
convert::TryFrom,
fs::{File, OpenOptions},
@@ -19,6 +20,7 @@ use std::{
path::{Path, PathBuf},
};
use structopt::StructOpt;
+use x509_parser::prelude::Pem;
#[derive(StructOpt, Debug)]
pub enum TEdgeCertOpt {
@@ -386,6 +388,7 @@ impl Command for CreateCertCmd {
Ok(())
}
}
+
impl Command for ShowCertCmd {
fn description(&self) -> String {
"show the device certificate".into()
@@ -508,11 +511,17 @@ impl ShowCertCmd {
"Valid up to: {}",
tbs_certificate.validity.not_after.to_rfc2822()
);
-
+ println!("Thumbprint: {}", show_thumbprint(&pem)?);
Ok(())
}
}
+fn show_thumbprint(pem: &Pem) -> Result<String, CertError> {
+ let bytes = Sha1::digest(&pem.contents).as_slice().to_vec();
+ let strs: Vec<String> = bytes.iter().map(|b| format!("{:02X}", b)).collect();
+ Ok(strs.concat())
+}
+
impl RemoveCertCmd {
fn remove_certificate(&self) -> Result<(), CertError> {
std::fs::remove_file(&self.cert_path).or_else(ok_if_not_found)?;
@@ -649,8 +658,13 @@ mod tests {
use super::*;
use assert_matches::assert_matches;
use std::fs::File;
+ use std::io::Cursor;
use tempfile::*;
+ extern crate base64;
+
+ use crypto_hash::{digest, Algorithm};
+
#[test]
fn basic_usage() {
let dir = tempdir().unwrap();
@@ -712,6 +726,75 @@ mod tests {
}
#[test]
+ fn check_certificate_thumbprint_b64_decode_sha1() {
+ let dir = tempdir().unwrap();
+ let cert_path = temp_file_path(&dir, "my-thumbprint-device-cert.pem");
+ let key_path = temp_file_path(&dir, "my-thumbprint-device-key.pem");
+ let id = "my-device-id";
+ let cmd = CreateCertCmd {
+ id: String::from(id),
+ cert_path: cert_path.clone(),
+ key_path: key_path.clone(),
+ };
+
+ //Create a certificate
+ assert!(cmd
+ .create_test_certificate(&CertConfig::default())
+ .err()
+ .is_none());
+
+ //Compute the thumbprint of the certificate
+ let pem = read_pem(&cert_path)
+ .map_err(|err| err.cert_context(&cert_path))
+ .unwrap();
+ let thumbprint_sha1 = show_thumbprint(&pem).unwrap();
+
+ //Compute the thumbprint of the certificate using openssl
+ let mut file = File::open(cert_path)
+ .map_err(|err| err.to_string())
+ .unwrap();
+ let pem_cont = file_content(&mut file);
+
+ //Remove new line and carriage return characters
+ let cert_cont = pem_cont.replace(&['\r', '\n'][..], "");
+
+ //Read the certificate contents, except the header and footer
+ let header_len = "-----BEGIN CERTIFICATE-----".len();
+ let footer_len = "-----END CERTIFICATE-----".len();
+
+ //just decode the key contents
+ let b64_bytes =
+ base64::decode(&cert_cont[header_len..cert_cont.len() - footer_len]).unwrap();
+ let result = digest(Algorithm::SHA1, b64_bytes.as_ref());
+ let thumbprint_crypto: Vec<String> = result.iter().map(|b| format!("{:02X}", b)).collect();
+
+ //compare the two thumbprints
+ assert_eq!(thumbprint_sha1, thumbprint_crypto.concat());
+ }
+
+ #[test]
+ fn check_thumbprint_static_certificate() {
+ let cert_content = r#"-----BEGIN CERTIFICATE-----
+MIIBlzCCAT2gAwIBAgIBKjAKBggqhkjOPQQDAjA7MQ8wDQYDVQQDDAZteS10YnIx
+EjAQBgNVBAoMCVRoaW4gRWRnZTEUMBIGA1UECwwLVGVzdCBEZXZpY2UwHhcNMjEw
+MzA5MTQxMDMwWhcNMjIwMzEwMTQxMDMwWjA7MQ8wDQYDVQQDDAZteS10YnIxEjAQ
+BgNVBAoMCVRoaW4gRWRnZTEUMBIGA1UECwwLVGVzdCBEZXZpY2UwWTATBgcqhkjO
+PQIBBggqhkjOPQMBBwNCAAR6DVDOQ9ey3TX4tD2V0zCYe8GtmUHekNZZX6P+lUXx
+886P/Kkyra0xCYKam2me2VzdLMc4X5cpRkybVa0XH/WCozIwMDAdBgNVHQ4EFgQU
+Iz8LzGgzHjqsvB+ppPsVa+xf2bYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQD
+AgNIADBFAiEAhMAATBcZqE3Li1TZCzDoweBxRw1WD6gaSAcrsIWuW94CIHuR5ZG7
+ozYxD+f5npF5kWWKcLIIo0wqvXg0GOLNfxTh
+-----END CERTIFICATE-----
+"#;
+ let expected_thumbprint = "860218AD0A996004449521E2713C28F67B5EA580";
+
+ let reader = Cursor::new(cert_content.as_bytes());
+ let (pem, _bytes_read) = Pem::read(reader).expect("Reading PEM failed");
+ let thumbprint = show_thumbprint(&pem).unwrap();
+ assert_eq!(thumbprint, expected_thumbprint);
+ }
+
+ #[test]
fn create_key_in_non_existent_directory() {
let dir = tempdir().unwrap();
let cert_path = temp_file_path(&dir, "my-device-cert.pem");