151 lines
4.6 KiB
Plaintext
151 lines
4.6 KiB
Plaintext
|
#! /bin/sh
|
||
|
exec perl -x $0 "$@"
|
||
|
#! perl
|
||
|
|
||
|
# Copyright (c) 2015 DeNA Co., Ltd.
|
||
|
#
|
||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
# of this software and associated documentation files (the "Software"), to
|
||
|
# deal in the Software without restriction, including without limitation the
|
||
|
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||
|
# sell copies of the Software, and to permit persons to whom the Software is
|
||
|
# furnished to do so, subject to the following conditions:
|
||
|
#
|
||
|
# The above copyright notice and this permission notice shall be included in
|
||
|
# all copies or substantial portions of the Software.
|
||
|
#
|
||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||
|
# IN THE SOFTWARE.
|
||
|
|
||
|
use strict;
|
||
|
use warnings;
|
||
|
use File::Temp qw(tempdir);
|
||
|
use Getopt::Long;
|
||
|
|
||
|
# from sysexits.h
|
||
|
use constant EX_TEMPFAIL => 75;
|
||
|
|
||
|
my ($issuer_fn, $opt_help);
|
||
|
my $openssl_cmd = 'openssl';
|
||
|
|
||
|
GetOptions(
|
||
|
"issuer=s" => \$issuer_fn,
|
||
|
"openssl=s", => \$openssl_cmd,
|
||
|
help => \$opt_help,
|
||
|
) or exit(1);
|
||
|
if ($opt_help) {
|
||
|
print << "EOT";
|
||
|
Usage: $0 [<options>] <certificate-file>
|
||
|
|
||
|
Options:
|
||
|
--issuer <file> issuer certificate (if omitted, is extracted from the
|
||
|
certificate chain)
|
||
|
--openssl <cmd> openssl command to use (default: "openssl")
|
||
|
--help prints this help
|
||
|
|
||
|
The command issues an OCSP request for given server certificate, verifies the
|
||
|
response and prints the resulting DER.
|
||
|
|
||
|
The command exits 0 if successful, or 75 (EX_TEMPFAIL) on temporary error.
|
||
|
Other exit codes may be returned in case of hard errors.
|
||
|
|
||
|
EOT
|
||
|
exit(0);
|
||
|
}
|
||
|
|
||
|
die "no certificate file\n"
|
||
|
if @ARGV == 0;
|
||
|
my $cert_fn = shift @ARGV;
|
||
|
|
||
|
my $tempdir = tempdir(CLEANUP => 1);
|
||
|
|
||
|
my $openssl_version = run_openssl("version");
|
||
|
chomp $openssl_version;
|
||
|
print STDERR "fetch-ocsp-response (using $openssl_version)\n";
|
||
|
|
||
|
# obtain ocsp uri
|
||
|
my $ocsp_uri = run_openssl("x509 -in $cert_fn -noout -ocsp_uri");
|
||
|
chomp $ocsp_uri;
|
||
|
die "failed to extract ocsp URI from $cert_fn\n"
|
||
|
if $ocsp_uri !~ m{^https?://};
|
||
|
my($ocsp_host) = $ocsp_uri =~ m{^https?://([^/:]+)};
|
||
|
|
||
|
# save issuer certificate
|
||
|
if (! defined $issuer_fn) {
|
||
|
my $chain = read_file($cert_fn);
|
||
|
$chain =~ m{-----END CERTIFICATE-----.*?(-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----)}s
|
||
|
or die "--issuer option was not used, and failed to extract issuer certificate from the certificate\n";
|
||
|
$issuer_fn = "$tempdir/issuer.crt";
|
||
|
write_file($issuer_fn, "$1\n");
|
||
|
}
|
||
|
|
||
|
# obtain response (without verification)
|
||
|
print STDERR "sending OCSP request to $ocsp_uri\n";
|
||
|
my $resp = run_openssl(
|
||
|
"ocsp -issuer $issuer_fn -cert $cert_fn -url $ocsp_uri"
|
||
|
. ($openssl_version =~ /^OpenSSL 1\./is ? " -header Host $ocsp_host" : "")
|
||
|
. " -noverify -respout $tempdir/resp.der " . join(' ', @ARGV),
|
||
|
1,
|
||
|
);
|
||
|
print STDERR $resp;
|
||
|
|
||
|
# verify the response
|
||
|
print STDERR "verifying the response signature\n";
|
||
|
my $success;
|
||
|
for my $args (
|
||
|
# try from exotic options
|
||
|
"-VAfile $issuer_fn", # for comodo
|
||
|
"-partial_chain -trusted_first -CAfile $issuer_fn", # these options are only available in OpenSSL >= 1.0.2
|
||
|
"-CAfile $issuer_fn", # for OpenSSL <= 1.0.1
|
||
|
) {
|
||
|
if (system("$openssl_cmd ocsp -respin $tempdir/resp.der $args > $tempdir/verify.out 2>&1") == 0) {
|
||
|
print STDERR "verify OK (used: $args)\n";
|
||
|
$success = 1;
|
||
|
last;
|
||
|
}
|
||
|
}
|
||
|
if (! $success) {
|
||
|
print STDERR read_file("$tempdir/verify.out");
|
||
|
tempfail("failed to verify the response\n");
|
||
|
}
|
||
|
|
||
|
# success
|
||
|
print read_file("$tempdir/resp.der");
|
||
|
exit 0;
|
||
|
|
||
|
sub run_openssl {
|
||
|
my ($args, $tempfail) = @_;
|
||
|
open my $fh, "-|", "$openssl_cmd $args"
|
||
|
or die "failed to invoke $openssl_cmd:$!";
|
||
|
my $resp = do { local $/; <$fh> };
|
||
|
close $fh
|
||
|
or ($tempfail ? \&tempfail : \&die)->("OpenSSL exitted abnormally: $openssl_cmd $args:$!");
|
||
|
$resp;
|
||
|
}
|
||
|
|
||
|
sub read_file {
|
||
|
my $fn = shift;
|
||
|
open my $fh, "<", $fn
|
||
|
or die "failed to open file:$fn:$!";
|
||
|
local $/;
|
||
|
<$fh>;
|
||
|
}
|
||
|
|
||
|
sub write_file {
|
||
|
my ($fn, $data) = @_;
|
||
|
open my $fh, ">", $fn
|
||
|
or die "failed to open file:$fn:$!";
|
||
|
print $fh $data;
|
||
|
close $fh;
|
||
|
}
|
||
|
|
||
|
sub tempfail {
|
||
|
print STDERR @_;
|
||
|
exit EX_TEMPFAIL;
|
||
|
}
|