<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE sect1 PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
   "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
  <!ENTITY % general-entities SYSTEM "../../general.ent">
  %general-entities;

  <!ENTITY certhost              "http://mxr.mozilla.org">
  <!ENTITY certdir               "/mozilla/source/security/nss/lib/ckfw/builtins">
  <!ENTITY ca-bundle-download    "&sources-anduin-other-http;/certdata.txt">
  <!ENTITY ca-bundle-size        "1.2 MB">
  <!ENTITY cacerts-buildsize     "1.2 MB">
  <!ENTITY cacerts-time          "less than 0.1 SBU">
]>

<sect1 id="cacerts" xreflabel="Certificate Authority Certificates">
  <?dbhtml filename="cacerts.html"?>

  <sect1info>
    <othername>$LastChangedBy: bdubbs $</othername>
    <date>$Date: 2015-02-20 15:02:49 -0500 (Fri, 20 Feb 2015) $</date>
  </sect1info>

  <title>Certificate Authority Certificates</title>

  <para>The Public Key Inrastructure is used for many security issues in a
  Linux system.  In order for a certificate to be trusted, it must be signed by
  a trusted agent called a Certificate Authority (CA).  The certificates loaded
  by this section are from the list on the Mozilla version control system and
  formats it into a form used by <xref linkend='openssl'/>.  The certificates
  can also be used by other applications either directly of indirectly through
  <application>openssl</application>.</para>

  &lfs77_checked;

  <indexterm zone="cacerts">
    <primary sortas="a-cacerts">Certificate Authority Certificates</primary>
  </indexterm>

  <sect2 role="package">
    <title>Introduction to Certificate Authorities</title>

   <bridgehead renderas="sect3">Package Information</bridgehead>
    <itemizedlist spacing="compact">
      <listitem>
        <para>CA Certificate Download: <ulink url="&ca-bundle-download;"/></para>
      </listitem>
      <listitem>
        <para>CA Bundle size: &ca-bundle-size;</para>
      </listitem>
      <listitem>
        <para>Estimated disk space required: &cacerts-buildsize;</para>
      </listitem>
      <listitem>
        <para>Estimated build time: &cacerts-time;</para>
      </listitem>
    </itemizedlist>

    <note><para>The certfile.txt file above is actually retrieved from <ulink
    url="https://hg.mozilla.org/releases/mozilla-release/file/default/security/nss/lib/ckfw/builtins/certdata.txt"/>.
    It is really an HTML file, but the text file can be retrieved indirectly
    from the HTML file.  The Download URL above automates that process and also
    adds a line where the date can be extracted as a revision number by the
    scripts below.</para></note>

    <bridgehead renderas="sect3">Certificate Authority Certificates Dependencies</bridgehead>

    <bridgehead renderas="sect4">Required</bridgehead>
    <para role="required"><xref linkend="openssl"/></para>

    <bridgehead renderas="sect4">Recommended</bridgehead>
    <para role="recommended"><xref linkend="wget"/></para>

    <para condition="html" role="usernotes">User Notes:
    <ulink url='&blfs-wiki;/cacerts'/></para>
  </sect2>

  <sect2 role="installation">
    <title>Installation of Certificate Authority Certificates</title>

    <para>First create a script to reformat a certificate into a
    form needed by <application>openssl</application>.  As the <systemitem
    class="username">root</systemitem> user:</para>

<screen role="root"><userinput>cat > /usr/bin/make-cert.pl &lt;&lt; "EOF"
<literal>#!/usr/bin/perl -w

# Used to generate PEM encoded files from Mozilla certdata.txt.
# Run as ./make-cert.pl > certificate.crt
#
# Parts of this script courtesy of RedHat (mkcabundle.pl)
#
# This script modified for use with single file data (tempfile.cer) extracted
# from certdata.txt, taken from the latest version in the Mozilla NSS source.
# mozilla/security/nss/lib/ckfw/builtins/certdata.txt
#
# Authors: DJ Lucas
#          Bruce Dubbs
#
# Version 20120211

my $certdata = './tempfile.cer';

open( IN, "cat $certdata|" )
    || die "could not open $certdata";

my $incert = 0;

while ( &lt;IN&gt; )
{
    if ( /^CKA_VALUE MULTILINE_OCTAL/ )
    {
        $incert = 1;
        open( OUT, "|openssl x509 -text -inform DER -fingerprint" )
            || die "could not pipe to openssl x509";
    }

    elsif ( /^END/ &amp;&amp; $incert )
    {
        close( OUT );
        $incert = 0;
        print "\n\n";
    }

    elsif ($incert)
    {
        my @bs = split( /\\/ );
        foreach my $b (@bs)
        {
            chomp $b;
            printf( OUT "%c", oct($b) ) unless $b eq '';
        }
    }
}</literal>
EOF

chmod +x /usr/bin/make-cert.pl</userinput></screen>

   <para>The following script creates the certificates and a bundle of all the
   certificates.  It creates a <filename class='directory'>./certs</filename>
   directory and <filename>./BLFS-ca-bundle-${VERSION}.crt</filename>.  Again
   create this script as the <systemitem class="username">root</systemitem>
   user:</para>

<screen role="root"><userinput>cat > /usr/bin/make-ca.sh &lt;&lt; "EOF"
<literal>#!/bin/sh
# Begin make-ca.sh
# Script to populate OpenSSL's CApath from a bundle of PEM formatted CAs
#
# The file certdata.txt must exist in the local directory
# Version number is obtained from the version of the data.
#
# Authors: DJ Lucas
#          Bruce Dubbs
#
# Version 20120211

certdata="certdata.txt"

if [ ! -r $certdata ]; then
  echo "$certdata must be in the local directory"
  exit 1
fi

REVISION=$(grep CVS_ID $certdata | cut -f4 -d'$')

if [ -z "${REVISION}" ]; then
  echo "$certfile has no 'Revision' in CVS_ID"
  exit 1
fi

VERSION=$(echo $REVISION | cut -f2 -d" ")

TEMPDIR=$(mktemp -d)
TRUSTATTRIBUTES="CKA_TRUST_SERVER_AUTH"
BUNDLE="BLFS-ca-bundle-${VERSION}.crt"
CONVERTSCRIPT="/usr/bin/make-cert.pl"
SSLDIR="/etc/ssl"

mkdir "${TEMPDIR}/certs"

# Get a list of starting lines for each cert
CERTBEGINLIST=$(grep -n "^# Certificate" "${certdata}" | cut -d ":" -f1)

# Get a list of ending lines for each cert
CERTENDLIST=`grep -n "^CKA_TRUST_STEP_UP_APPROVED" "${certdata}" | cut -d ":" -f 1`

# Start a loop
for certbegin in ${CERTBEGINLIST}; do
  for certend in ${CERTENDLIST}; do
    if test "${certend}" -gt "${certbegin}"; then
      break
    fi
  done

  # Dump to a temp file with the name of the file as the beginning line number
  sed -n "${certbegin},${certend}p" "${certdata}" > "${TEMPDIR}/certs/${certbegin}.tmp"
done

unset CERTBEGINLIST CERTDATA CERTENDLIST certbegin certend

mkdir -p certs
rm -f certs/*      # Make sure the directory is clean

for tempfile in ${TEMPDIR}/certs/*.tmp; do
  # Make sure that the cert is trusted...
  grep "CKA_TRUST_SERVER_AUTH" "${tempfile}" | \
    egrep "TRUST_UNKNOWN|NOT_TRUSTED" > /dev/null

  if test "${?}" = "0"; then
    # Throw a meaningful error and remove the file
    cp "${tempfile}" tempfile.cer
    perl ${CONVERTSCRIPT} > tempfile.crt
    keyhash=$(openssl x509 -noout -in tempfile.crt -hash)
    echo "Certificate ${keyhash} is not trusted!  Removing..."
    rm -f tempfile.cer tempfile.crt "${tempfile}"
    continue
  fi

  # If execution made it to here in the loop, the temp cert is trusted
  # Find the cert data and generate a cert file for it

  cp "${tempfile}" tempfile.cer
  perl ${CONVERTSCRIPT} > tempfile.crt
  keyhash=$(openssl x509 -noout -in tempfile.crt -hash)
  mv tempfile.crt "certs/${keyhash}.pem"
  rm -f tempfile.cer "${tempfile}"
  echo "Created ${keyhash}.pem"
done

# Remove blacklisted files
# MD5 Collision Proof of Concept CA
if test -f certs/8f111d69.pem; then
  echo "Certificate 8f111d69 is not trusted!  Removing..."
  rm -f certs/8f111d69.pem
fi

# Finally, generate the bundle and clean up.
cat certs/*.pem >  ${BUNDLE}
rm -r "${TEMPDIR}"</literal>
EOF

chmod +x /usr/bin/make-ca.sh</userinput></screen>

   <para>Add a short script to remove expired certificates from a directory.
   Again create this script as the <systemitem
   class="username">root</systemitem> user:</para>

<screen role="root"><userinput>cat > /usr/bin/remove-expired-certs.sh &lt;&lt; "EOF"
<literal>#!/bin/sh
# Begin /usr/bin/remove-expired-certs.sh
#
# Version 20120211

# Make sure the date is parsed correctly on all systems
mydate()
{
  local y=$( echo $1 | cut -d" " -f4 )
  local M=$( echo $1 | cut -d" " -f1 )
  local d=$( echo $1 | cut -d" " -f2 )
  local m

  if [ ${d} -lt 10 ]; then d="0${d}"; fi

  case $M in
    Jan) m="01";;
    Feb) m="02";;
    Mar) m="03";;
    Apr) m="04";;
    May) m="05";;
    Jun) m="06";;
    Jul) m="07";;
    Aug) m="08";;
    Sep) m="09";;
    Oct) m="10";;
    Nov) m="11";;
    Dec) m="12";;
  esac

  certdate="${y}${m}${d}"
}

OPENSSL=/usr/bin/openssl
DIR=/etc/ssl/certs

if [ $# -gt 0 ]; then
  DIR="$1"
fi

certs=$( find ${DIR} -type f -name "*.pem" -o -name "*.crt" )
today=$( date +%Y%m%d )

for cert in $certs; do
  notafter=$( $OPENSSL x509 -enddate -in "${cert}" -noout )
  date=$( echo ${notafter} |  sed 's/^notAfter=//' )
  mydate "$date"

  if [ ${certdate} -lt ${today} ]; then
     echo "${cert} expired on ${certdate}! Removing..."
     rm -f "${cert}"
  fi
done</literal>
EOF

chmod +x /usr/bin/remove-expired-certs.sh</userinput></screen>

   <para>The following commands will fetch the certificates and convert them to
   the correct format.  If desired, a web browser may be used instead of
   <application>wget</application> but the file will need to be saved with the
   name <filename>certdata.txt</filename>.  These commands can be repeated as
   necessary to update the CA Certificates.</para>

   <screen><userinput>URL=&sources-anduin-other-http;/certdata.txt &amp;&amp;
rm -f certdata.txt &amp;&amp;
wget $URL          &amp;&amp;
make-ca.sh         &amp;&amp;
remove-expired-certs.sh certs &amp;&amp;
unset URL</userinput></screen>

   <para>Now, as the <systemitem class="username">root</systemitem> user:</para>

<screen role="root"><userinput>SSLDIR=/etc/ssl                                              &amp;&amp;
install -d ${SSLDIR}/certs                                   &amp;&amp;
cp -v certs/*.pem ${SSLDIR}/certs                            &amp;&amp;
c_rehash                                                     &amp;&amp;
install BLFS-ca-bundle*.crt ${SSLDIR}/ca-bundle.crt          &amp;&amp;
ln -sfv ../ca-bundle.crt ${SSLDIR}/certs/ca-certificates.crt &amp;&amp;
unset SSLDIR</userinput></screen>

   <para>Finally, clean up the current directory:</para>

<screen><userinput>rm -r certs BLFS-ca-bundle*</userinput></screen>

   <para>After installing or updating certificates, if OpenJDK is installed, 
   update the certificates for Java using the procedures at <xref linkend='ojdk-certs'/>.</para>


  </sect2>

  <sect2 role="content">
    <title>Contents</title>

    <segmentedlist>
      <segtitle>Installed Programs</segtitle>
      <segtitle>Installed Libraries</segtitle>
      <segtitle>Installed Directories</segtitle>

      <seglistitem>
        <seg>make-ca.sh, make-cert.pl and remove-expired-certs.sh</seg>
        <seg>None</seg>
        <seg>/etc/ssl/certs</seg>
      </seglistitem>
    </segmentedlist>

   <variablelist>
      <bridgehead renderas="sect3">Short Descriptions</bridgehead>
      <?dbfo list-presentation="list"?>
      <?dbhtml list-presentation="table"?>

      <varlistentry id="make-ca">
        <term><command>make-ca.sh</command></term>
        <listitem>
          <para>is a shell script that reformats
          the <filename>certdata.txt</filename> file for use by
          <application>openssl</application>.</para>
          <indexterm zone="cacerts make-ca">
            <primary sortas="b-make-ca">make-ca</primary>
          </indexterm>
        </listitem>
      </varlistentry>

      <varlistentry id="make-cert">
        <term><command>make-cert.pl</command></term>
        <listitem>
          <para>is a utility <application>perl</application> script that
          converts a single binary certificate (.der format) into .pem format.</para>
          <indexterm zone="cacerts make-cert">
            <primary sortas="b-make-cert">make-cert</primary>
          </indexterm>
        </listitem>
      </varlistentry>

      <varlistentry id="remove-expired-certs">
        <term><command>remove-expired-certs.sh</command></term>
        <listitem>
          <para>is a utility shell script that
          removes expired certificates from a directory.  The default
          directory is <filename class='directory'>/etc/ssl/certs</filename>.</para>
          <indexterm zone="cacerts remove-expired-certs">
            <primary sortas="b-remove-expired-certs">remove-expired-certs</primary>
          </indexterm>
        </listitem>
      </varlistentry>
   </variablelist>

  </sect2>
</sect1>
