Как разделить пакет сертификатов CA на отдельные файлы?

Я работаю с OpenSSL и мне нужен нормальный список CAs по умолчанию. Я использую список доверенных CAs Mozilla, как в комплекте с cURL. Однако мне нужно разделить этот пакет сертификатов CA, потому что документация OpenSSL говорит:

если CApath не NULL, он указывает на каталог, содержащий сертификаты CA в формате PEM. Каждый файл содержит один сертификат CA. Файлы просматриваются хэш-значением имени субъекта CA, которое, следовательно, должно быть доступный.

например, используя ca-bundle.crt файл напрямую работает нормально:

openssl-1.0.1g> ./apps/openssl s_client -connect www.google.com:443 -CAfile /home/user/certs/ca-bundle.crt
...
    Verify return code: 0 (ok)
---
DONE

но указание каталога, содержащего не работает:

openssl-1.0.1g> ./apps/openssl s_client -connect www.google.com:443 -CApath /opt/aspera/certs
    Verify return code: 20 (unable to get local issuer certificate)
---
DONE

Я предполагаю, что это потому, что моя папка не соответствует тому, что запрашивает документация (а именно, каталог, содержащий сертификаты CA в формате PEM, с каждым файлом, содержащим один сертификат, названный хэш-значением). В моем каталоге есть только один пакет сертификатов.

как может Я разделил свой пакет сертификатов, чтобы придерживаться запроса OpenSSL о том, чтобы каждый сертификат был в отдельном файле? Бонусные баллы, если хеширование можно сделать тоже (хотя при необходимости я мог бы написать сценарий, чтобы сделать это сам, если все сертификаты находятся в отдельных файлах).

5 ответов


вы можете разделить пакет с awk, вот так, в соответствующем каталоге:

awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert." c ".pem"}' < ca-bundle.pem 

затем создайте ссылки OpenSSL, запустив c_rehash утилита, которая поставляется с OpenSSL:

c_rehash .

Примечание: используйте "gawk" на не linux-платформах - как указано выше, зависит от конкретной функции GNU.


просто чтобы дать альтернативу; столкнувшись с той же проблемой, я закончил с csplit:

csplit -k -f bar foo.pem '/END CERTIFICATE/+1' {10}

следующий Ruby-скрипт разделит пакет (с одним или несколькими сертификатами в нем) на файлы с именем после хэшей -- side-stepping c_rehash шаг в большинстве случаев.

использовать cd в нужный каталог (например,/etc/ssl/certs/) и запустите скрипт с путем к вашему пакету сертификатов в качестве единственного аргумента. Например: ruby /tmp/split-certificates.rb ca-root-nss.crt.

#!/usr/bin/env ruby

require 'openssl'

blob = IO.binread(ARGV[0]) # Read the entire file at once

DELIMITER = "\n-----END CERTIFICATE-----\n"
blobs = blob.split(DELIMITER)

blobs.each do |blob|
    blob.strip!
    blob += DELIMITER # Does not break DER
    begin
        cert = OpenSSL::X509::Certificate.new blob
    rescue
        puts "Skipping what seems like junk"
        next
    end
    begin
        # XXX Need to handle clashes, suffix other than 0
        filename=sprintf("%x.0", cert.subject.hash)
        File.open(filename,
            File::WRONLY|File::CREAT|File::EXCL) do |f|
            f.write(blob)
        end
    rescue Errno::EEXIST
        puts "#{filename} already exists, skipping"
    end
end

Если вы хотите получить один сертификат из PEM с несколькими сертификатами, попробуйте:

$ awk '/subject.*CN=host.domain.com/,/END CERTIFICATE/' INPUT.PEM

источник


вот мой в Perl (так много кода, но мне нравится программирование gonzo):

#!/usr/bin/perl -w                                                                                                                                                                                                                           

# -------
# Split "certificate bundles" like those found in /etc/pki/tls/certs into
# individual files and append the X509 cleartext description to each file.
#
# The file to split is given on the command line or piped via STDIN.
#
# Files are simply created in the current directory!
#
# Created files are named "certificate.XX" or "trusted-certificate.XX",
# with XX an index value.
#
# If a file with the same name as the output file already exists, it is not 
# overwritten. Instead a new name with a higher index is tried.
#
# This works for bundles of both trusted and non-trusted certificates.
#
# See http://tygerclan.net/?q=node/49 for another program of this kind, 
# which sets the name of the split-off files in function of the subject
# -------

my @lines = <> or die "Could not slurp: $!";

my $state = "outside"; # reader state machine state
my $count = 0;         # index of the certificate file we create
my $fh;                # file handle of the certificate file we create
my $fn;                # file name of the certificate file we create
my $trusted;           # either undef or "TRUSTED" depend on type of certificate

for my $line (@lines) {
   chomp $line;
   if ($state eq "outside") {
      if ($line =~ /^(-----BEGIN (TRUSTED )?CERTIFICATE-----)\s*$/) {         
         my $marker  = ;
         $trusted    = ;
         $state      = "inside";
         my $created = 0;
         my $prefix  = "";
         if ($trusted) {
            $prefix = "trusted-"
         }
         while (!$created) {
            $fn = "${prefix}certificate.$count"; 
            $count++;
            if (-f $fn) {
               # print STDERR "File '$fn' exists; increasing version number to $count\n";
            } 
            else {
               print STDERR "Certificate data goes to file '$fn'\n";
               open($fh,">$fn") || die "Could not create file '$fn': $!\n";
               $created = 1;
               print $fh "$marker\n"
            }
         }
      }
      else {
         print STDERR "Skipping line '$line'\n"
      }
   }
   else {
      if ($line =~ /^(-----END (TRUSTED )?CERTIFICATE-----)\s*$/) {
         my $marker       = ;
         my $trustedCheck = ;
         if (!((($trusted && $trustedCheck) || (!$trusted && !$trustedCheck)))) {
            die "Trusted flag difference detected\n"
         }
         $state = "outside";
         print $fh "$marker\n";
         print STDERR "Closing file '$fn'\n";
         close $fh;
         # Append x509 cleartext output by calling openssl tool
         `openssl x509 -noout -text -in '$fn' >> '$fn'`;
         if ($? != 0) { 
            die "Could not run 'openssl x509' command: $!\n";
         }
      } 
      else {
         print $fh "$line\n"
      }
   }
}

if ($state eq "inside") {
   die "Last certificate was not properly terminated\n";
}