如何将CA证书捆绑包拆分为单独的文件?

时间:2014-05-14 02:00:32

标签: linux ssl openssl

我正在使用OpenSSL,需要一个理智的默认CA列表。我使用Mozilla的受信任CA列表bundled by cURL。但是,我需要拆分此CA证书捆绑包,因为OpenSSL documentation says

  

如果CApath不为NULL,则它指向包含PEM格式的CA证书的目录。每个文件都包含一个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

但指定包含ca-bundle.crt文件的目录不起作用:

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

我认为这是因为我的文件夹并不符合文档要求的内容(即包含PEM格式的CA证书的目录,每个文件包含一个证书,由哈希值命名)。我的目录只有一组证书。

如何拆分我的证书捆绑以遵守OpenSSL的请求,即每个证书都在一个单独的文件中?如果也可以进行散列,则可以获得奖励积分(如果需要的话,如果所有证书都在单个文件中,我可以编写一个脚本来自己完成)。

5 个答案:

答案 0 :(得分:15)

您可以将awk这样的捆绑包拆分到适当的目录中:

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

然后,通过运行OpenSSL附带的c_rehash实用程序来创建OpenSSL想要的链接:

c_rehash .

注意:使用&#39; gawk&#39;在非Linux平台上 - 如上所述依赖于GNU特定功能。

答案 1 :(得分:8)

只是提供另一种选择;面对同样的问题,我最终得到了csplit:

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

答案 2 :(得分:1)

以下Ruby脚本会将捆绑包(其中包含一个或多个证书)拆分为以散列命名的文件 - 在大多数情况下都是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

答案 3 :(得分:0)

如果您想从多证书PEM中获取单个证书,请尝试:

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

source

答案 4 :(得分:0)

这是Perl中的我(代码太多,但是我喜欢奇闻趣事编程):

#!/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  = $1;
         $trusted    = $2;
         $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       = $1;
         my $trustedCheck = $2;
         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";
}