[pve-devel] [RFC common 4/4] add Certificate helper
Fabian Grünbichler
f.gruenbichler at proxmox.com
Wed Apr 11 10:08:46 CEST 2018
general purpose certificate related helper functions
Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
---
src/Makefile | 1 +
src/PVE/Certificate.pm | 120 +++++++++++++++++++++++++++++++++++++++++++++++++
test/acme-test.pl | 8 ++--
3 files changed, 126 insertions(+), 3 deletions(-)
create mode 100644 src/PVE/Certificate.pm
diff --git a/src/Makefile b/src/Makefile
index 0754666..17a794a 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -11,6 +11,7 @@ LIB_SOURCES = \
ACME/Challenge.pm \
ACME/StandAlone.pm \
AtomicFile.pm \
+ Certificate.pm \
CLIHandler.pm \
CalendarEvent.pm \
CpuSet.pm \
diff --git a/src/PVE/Certificate.pm b/src/PVE/Certificate.pm
new file mode 100644
index 0000000..b97d182
--- /dev/null
+++ b/src/PVE/Certificate.pm
@@ -0,0 +1,120 @@
+package PVE::Certificate;
+
+use strict;
+use warnings;
+
+use Date::Parse;
+use Net::SSLeay;
+
+use PVE::Tools;
+
+# see RFC 7468
+my $b64_char_re = qr![0-9A-Za-z\+/]!;
+my $header_re = sub {
+ my ($label) = @_;
+ return qr!-----BEGIN\ $label-----(?:\s|\n)*!;
+};
+my $footer_re = sub {
+ my ($label) = @_;
+ return qr!-----END\ $label-----(?:\s|\n)*!;
+};
+my $pem_re = sub {
+ my ($label) = @_;
+
+ my $header = $header_re->($label);
+ my $footer = $footer_re->($label);
+
+ return qr{
+ $header
+ (?:(?:$b64_char_re)+\s*\n)*
+ (?:$b64_char_re)*(?:=\s*\n=|={0,2})?\s*\n
+ $footer
+ }x;
+};
+
+my $strip_text = sub {
+ my ($label, $content) = @_;
+
+ my $header = $header_re->($label);
+ $content =~ s/^.*?(?=$header)//s;
+ return $content;
+};
+
+sub check_pem {
+ my ($content, %opts) = @_;
+
+ my $label = $opts{label} // 'CERTIFICATE';
+ my $multiple = $opts{multiple};
+ my $noerr = $opts{noerr};
+
+ $content = $strip_text->($label, $content);
+
+ my $re = $pem_re->($label);
+
+ $re = qr/($re\n+)*$re/ if $multiple;
+
+ if ($content =~ /^$re$/) {
+ return $content;
+ } else {
+ return undef if $noerr;
+ die "not a valid PEM-formatted string.\n";
+ }
+}
+
+my $read_certificate = sub {
+ my ($cert_path) = @_;
+
+ die "'$cert_path' does not exist!\n" if ! -e $cert_path;
+
+ my $bio = Net::SSLeay::BIO_new_file($cert_path, 'r')
+ or die "unable to read '$cert_path' - $!\n";
+
+ my $cert = Net::SSLeay::PEM_read_bio_X509($bio);
+ if (!$cert) {
+ Net::SSLeay::BIO_free($bio);
+ die "unable to read certificate from '$cert_path'\n";
+ }
+
+ return $cert;
+};
+
+sub convert_asn1_to_epoch {
+ my ($asn1_time) = @_;
+
+ my $iso_time = Net::SSLeay::P_ASN1_TIME_get_isotime($asn1_time);
+ return Date::Parse::str2time($iso_time);
+}
+
+sub get_certificate_info {
+ my ($cert_path) = @_;
+
+ my $cert = $read_certificate->($cert_path);
+
+ my $info = {};
+ $info->{fingerprint} = Net::SSLeay::X509_get_fingerprint($cert, 'sha256');
+ $info->{subject} = Net::SSLeay::X509_NAME_oneline(Net::SSLeay::X509_get_subject_name($cert));
+ $info->{issuer} = Net::SSLeay::X509_NAME_oneline(Net::SSLeay::X509_get_issuer_name($cert));
+ $info->{notbefore} = convert_asn1_to_epoch(Net::SSLeay::X509_get_notBefore($cert));
+ $info->{notafter} = convert_asn1_to_epoch(Net::SSLeay::X509_get_notAfter($cert));
+ $info->{san} = [ Net::SSLeay::X509_get_subjectAltNames($cert) ];
+
+ Net::SSLeay::X509_free($cert);
+
+ return $info;
+};
+
+# Checks whether certificate expires before $timestamp (UNIX epoch)
+sub check_expiry {
+ my ($cert_path, $timestamp) = @_;
+
+ $timestamp //= time();
+
+ my $cert = $read_certificate->($cert_path);
+ my $not_after = convert_asn1_to_epoch(Net::SSLeay::X509_get_notAfter($cert));
+
+ Net::SSLeay::X509_free($cert);
+
+ return ($not_after < $timestamp) ? 1 : 0;
+};
+
+1;
diff --git a/test/acme-test.pl b/test/acme-test.pl
index d2d308a..729b711 100755
--- a/test/acme-test.pl
+++ b/test/acme-test.pl
@@ -37,6 +37,8 @@ my $tos_url = (defined($meta) && defined($meta->{termsOfService})) ? $meta->{ter
print "ToS: ", $tos_url ? $tos_url : '-', "\n";
print "OK\n\n";
+$tos_url = undef;
+
if (!$acme->{location}) {
print "Registering new account\n";
$acme->new_account($tos_url, contact => ['mailto:foo at bar.com']);
@@ -113,10 +115,10 @@ print "OK\n\n";
print "getting certificate\n";
my $cert = $acme->get_certificate($order);
-print Dumper($cert), "\n";
+PVE::Tools::file_set_contents('/tmp/cert.pem', $cert);
-print "revoking certificate\n";
-print Dumper($acme->revoke_certificate($cert)), "\n";
+#print "revoking certificate\n";
+#print Dumper($acme->revoke_certificate($cert)), "\n";
print "deactivating authorizations\n";
for my $auth_url (@{$order->{authorizations}}) {
--
2.14.2
More information about the pve-devel
mailing list