Problem: no root certificates generated for some non-english locales.
In this message, first, I describe the problem, then the solution. Many
users may have the bug, so a test is necessary. This test is described
below.
I am most greatful to Ken Moffat for reading a first draft and giving
important suggestions that changed the text in many places. Other than
technical input and corrections to my poor English: "... unsecure
communication ? That definitely needs to be noted. ... publicizing the
issue sooner rather than later will be the right thing to do". So I will.
First, I give my locale:
$ echo $LANG
pt_BR.utf8
I believe this is not just my problem. It seems to be a problem with
mkcacerts.sh, the script for generating cacerts for OpenJDK. In my logs,
I could find the problem since version 1.7.0.9. I have installed many
versions, the last one being 1.7.0.40 (this one has been installed with
IcedTea 2.4.0 and 2.4.1). Or the problem is with the other ones
involved, OpenSSL and OpenJDK.
Or not any of them individually, but a combination of what one expects
and the other one gives (input/output).
Perhaps it is not a critical bug, but it has to be reported, as it
involves security, as noted above by Ken. I think the the support list
should be informed, due to the nature of the problem.
This problem is caused by the following instruction in mkcacerts.sh:
echo yes | "${KEYTOOL}" -import -alias `basename "${cert}"` -keystore \
"${OUTFILE}" -storepass 'changeit' -file "${tempfile}"
I have just downloaded OpenJDK-1.7.0.9 and tested it.
<http://anduin.linuxfromscratch.org/files/BLFS/OpenJDK-1.7.0.9/OpenJDK-1.7.0.9-i686-bin.tar.xz>
The script mkcacerts.sh fails to generate the certificates, for my
locale ince 1.7.0.9. Simply put, *not even an empty cacerts* is
generated, null.
To demonstrate, Just untar OpenJDK-1.7.0.* in a temporary directory, cd
OpenJDK-1.7.0.*, copy the script into here, and issue:
sh mkcacerts.sh -d "/etc/ssl/certs/" -k "bin/keytool" -s
"/usr/bin/openssl" \
-o "../"
Result (for my locale) is no cacerts at ../ at all:
$ ls -l ..
total 4
drwxr-xr-x 9 fernando fernando 4096 Jul 19 19:43 OpenJDK-1.7.0.9-i686-bin
$
I just checked if by any chance it was placed elsewhere, but only one
from the tarball, dated from Oct 21 2012.
In the logs, I find the error:
$ xzless
../../mkcacerts.sh-pt_BR.utf8-1.7.0.9-i686-bin-blfs-anduin-2013.07.19.log.xz
...
/etc/ssl/certs/cfa1c2ee.pem
Proprietário: CN=Buypass Class 2 CA 1, O=Buypass AS-983163327, C=NO
...
Fingerprints do certificado:
...
Extensões:
#1: ObjectId: 2.5.29.19 Criticality=true
...
Confiar neste certificado? [não]: Resposta errada; tente novamente
Confiar neste certificado? [não]: erro de keytool:
java.lang.NullPointerException
...
This is, I think, the problem: "não" instead of "no" and "sim", instead
of "yes".
Solution? Use LC_ALL=C to run the script or, inside the script, write
echo yes | LC_ALL=C "${KEYTOOL}" -import -alias `basename "${cert}"` \
-keystore "${OUTFILE}" -storepass 'changeit' -file "${tempfile}"
This is what I think is easier, because only one place in the OpenJDK
cacerts patch needs to be modified.
This finishes the presentation of the problem.
Now, starting to describe the solution, so that anyone can correct an
eventually faulty installation. Also, if you have updated the system's
root certificates, after the installation of OJDK, the procedure
described can be used to updated the OJDK root certificates. Perhaps
instructions below for that could be included in the page "Certificate
Authority Certificates".
Having demontrated the problem is not only for the present version, from
here on, consider just present version in the book.
Test if the root certificates have been installed. If you obtain a
result with last line:
"Your keystore contains 0 entries"
then you need to install them, you have none. The number of entries has
to be non-zero, obviously, if the certificates are installed. To make
the test:
$ cd opt/OpenJDK-1.7.0.40
$ env LC_ALL=C bin/keytool -list -keystore jre/lib/security/cacerts
Enter keystore password:
***************** WARNING WARNING WARNING *****************
* The integrity of the information stored in your keystore *
* has NOT been verified! In order to verify its integrity, *
* you must provide your keystore password. *
***************** WARNING WARNING WARNING *****************
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 0 entries
$
At "Enter keystore password:", just hit enter, if the password has not
yet been
defined. Meaning of the message is obvious.
If you have this problem, after the procedure below, the root
certificates will
be installed.
After correctly installed, the result can be tested with the same command
above, in which case, all certificates and repective properties will be
listed,
or, to simplify the output, use grep:
$ cd opt/OpenJDK-1.7.0.40
$ env LC_ALL=C bin/keytool -list -keystore jre/lib/security/cacerts \
| grep entries
Enter keystore password:
***************** WARNING WARNING WARNING *****************
* The integrity of the information stored in your keystore *
* has NOT been verified! In order to verify its integrity, *
* you must provide your keystore password. *
***************** WARNING WARNING WARNING *****************
Your keystore contains 157 entries
$
(Again, just hit enter at the password prompt.)
PROCEDURE TO INSTALL OR UPDATE THE ROOT CERTIFICATES FOR OpenJDK
1. How To Obtain A Good Patch For A Fresh Build
2. Generating The Certificate File
3. Manual Installation Of Certificates
4. How To Obtain A Good Script
5. Difference Between Original And New Scripts
6. Difference Between Original And New Patches
Items 1. to 3. have explanations. The others are more like apendixes.
1. How To Obtain A Good Patch For A Fresh Build
First, correct the patch to run the most iportant command in locale C:
sed 's/echo yes | /echo yes | env LC_ALL=C /' \
icedtea-2.4.1-add_cacerts-1.patch > icedtea-2.4.1-add_cacerts-2.patch
As described earlier, this will make the reply expected by keytool to be
yes/no, instead of sim/não. No certificate was accepted with the old
patch, because it was expecting "sim" and received "yes".
2. Generating The Certificate File
Further below, find a good mkcacerts.sh script.
#(by DJ and BD, small modification as above by FO).
Change to OpenJDK-1.7.0.40 directory. If it is in /opt, then everything
must be run as root. Make sure you do not have a file name certificates
at ../, as it will be overwritten.
sh mkcacerts.sh -d "/etc/ssl/certs/" -k "bin/keytool" \
-s "/usr/bin/openssl" -o "../certificates"
If you have my locale installed, you can test the original script, to
find that it probably fails using the procedure above, with the command
env LC_ALL=pt_BR.utf8 sh mkcacerts.sh ...
On the contrary, if you have a locale with problem, as mine, and have an
original script as in the cacerts patch of BLFS, correct certificates
can be obtained, alternatively, using:
env LC_ALL=C sh mkcacerts.sh ...
3. Manual Installation Of Certificates
It is better to backup, first:
mv -vi jre/lib/security/cacerts{,-back}
Now, certificates can be properly installed.
cp -v ../certificates jre/lib/security/cacerts
chown -c 0:0 jre/lib/security/cacerts
chmod -c 0644 jre/lib/security/cacerts
4. How To Obtain A Good Script
$ cat > mkcacerts.sh << "EOF" &&
#!/bin/sh
# Simple script to extract x509 certificates and create a JRE cacerts file.
function get_args()
{
if test -z "${1}" ; then
showhelp
exit 1
fi
while test -n "${1}" ; do
case "${1}" in
-f | --cafile)
check_arg $1 $2
CAFILE="${2}"
shift 2
;;
-d | --cadir)
check_arg $1 $2
CADIR="${2}"
shift 2
;;
-o | --outfile)
check_arg $1 $2
OUTFILE="${2}"
shift 2
;;
-k | --keytool)
check_arg $1 $2
KEYTOOL="${2}"
shift 2
;;
-s | --openssl)
check_arg $1 $2
OPENSSL="${2}"
shift 2
;;
-h | --help)
showhelp
exit 0
;;
*)
showhelp
exit 1
;;
esac
done
}
function check_arg()
{
echo "${2}" | grep -v "^-" > /dev/null
if [ -z "$?" -o ! -n "$2" ]; then
echo "Error: $1 requires a valid argument."
exit 1
fi
}
# The date binary is not reliable on 32bit systems for dates after 2038
function 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}"
}
function showhelp()
{
echo "`basename ${0}` creates a valid cacerts file for use with
IcedTea."
echo ""
echo " -f --cafile The path to a file containing
PEM formated CA"
echo " certificates. May not be used
with -d/--cadir."
echo " -d --cadir The path to a diectory of PEM
formatted CA"
echo " certificates. May not be used
with -f/--cafile."
echo " -o --outfile The path to the output file."
echo ""
echo " -k --keytool The path to the java keytool
utility."
echo ""
echo " -s --openssl The path to the openssl utility."
echo ""
echo " -h --help Show this help message and exit."
echo ""
echo ""
}
# Initialize empty variables so that the shell does not polute the script
CAFILE=""
CADIR=""
OUTFILE=""
OPENSSL=""
KEYTOOL=""
certdate=""
date=""
today=$( date +%Y%m%d )
# Process command line arguments
get_args ${@}
# Handle common errors
if test "${CAFILE}x" == "x" -a "${CADIR}x" == "x" ; then
echo "ERROR! You must provide an x509 certificate store!"
echo "\'$(basename ${0}) --help\' for more info."
echo ""
exit 1
fi
if test "${CAFILE}x" != "x" -a "${CADIR}x" != "x" ; then
echo "ERROR! You cannot provide two x509 certificate stores!"
echo "\'$(basename ${0}) --help\' for more info."
echo ""
exit 1
fi
if test "${KEYTOOL}x" == "x" ; then
echo "ERROR! You must provide a valid keytool program!"
echo "\'$(basename ${0}) --help\' for more info."
echo ""
exit 1
fi
if test "${OPENSSL}x" == "x" ; then
echo "ERROR! You must provide a valid path to openssl!"
echo "\'$(basename ${0}) --help\' for more info."
echo ""
exit 1
fi
if test "${OUTFILE}x" == "x" ; then
echo "ERROR! You must provide a valid output file!"
echo "\'$(basename ${0}) --help\' for more info."
echo ""
exit 1
fi
# Get on with the work
# If using a CAFILE, split it into individual files in a temp directory
if test "${CAFILE}x" != "x" ; then
TEMPDIR=`mktemp -d`
CADIR="${TEMPDIR}"
# Get a list of staring lines for each cert
CERTLIST=`grep -n "^-----BEGIN" "${CAFILE}" | cut -d ":" -f 1`
# Get a list of ending lines for each cert
ENDCERTLIST=`grep -n "^-----END" "${CAFILE}" | cut -d ":" -f 1`
# Start a loop
for certbegin in `echo "${CERTLIST}"` ; do
for certend in `echo "${ENDCERTLIST}"` ; do
if test "${certend}" -gt "${certbegin}"; then
break
fi
done
sed -n "${certbegin},${certend}p" "${CAFILE}" >
"${CADIR}/${certbegin}.pem"
keyhash=`${OPENSSL} x509 -noout -in "${CADIR}/${certbegin}.pem"
-hash`
echo "Generated PEM file with hash: ${keyhash}."
done
fi
# Write the output file
for cert in `find "${CADIR}" -type f -name "*.pem" -o -name "*.crt"`
do
# Make sure the certificate date is valid...
date=$( ${OPENSSL} x509 -enddate -in "${cert}" -noout | sed
's/^notAfter=//' )
mydate "${date}"
if test "${certdate}" -lt "${today}" ; then
echo "${cert} expired on ${certdate}! Skipping..."
unset date certdate
continue
fi
unset date certdate
ls "${cert}"
tempfile=`mktemp`
certbegin=`grep -n "^-----BEGIN" "${cert}" | cut -d ":" -f 1`
certend=`grep -n "^-----END" "${cert}" | cut -d ":" -f 1`
sed -n "${certbegin},${certend}p" "${cert}" > "${tempfile}"
echo yes | env LC_ALL=C "${KEYTOOL}" -import -alias `basename
"${cert}"` -keystore \
"${OUTFILE}" -storepass 'changeit' -file "${tempfile}"
rm "${tempfile}"
done
if test "${TEMPDIR}x" != "x" ; then
rm -rf "${TEMPDIR}"
fi
exit 0
EOF
chmod -v 0755 mkcacerts.sh
5. Difference Between Original And New Scripts
$ diff -Naur mkcacerts-blfs-1.0.7.40-2.4.1.sh
mkcacerts-new-1.0.7.40-2.4.1.sh
--- mkcacerts-blfs-1.0.7.40-2.4.1.sh 2013-07-19 07:46:41.904926806 -0300
+++ mkcacerts-new-1.0.7.40-2.4.1.sh 2013-07-19 09:19:42.517727563 -0300
@@ -196,7 +196,7 @@
certbegin=`grep -n "^-----BEGIN" "${cert}" | cut -d ":" -f 1`
certend=`grep -n "^-----END" "${cert}" | cut -d ":" -f 1`
sed -n "${certbegin},${certend}p" "${cert}" > "${tempfile}"
- echo yes | "${KEYTOOL}" -import -alias `basename "${cert}"` -keystore \
+ echo yes | env LC_ALL=C "${KEYTOOL}" -import -alias `basename
"${cert}"` -keystore \
"${OUTFILE}" -storepass 'changeit' -file "${tempfile}"
rm "${tempfile}"
done
6. Difference Between Original And New Patches
$ diff -Naur icedtea-2.4.1-add_cacerts-1.patch
icedtea-2.4.1-add_cacerts-2.patch
--- icedtea-2.4.1-add_cacerts-1.patch 2013-07-18 05:20:05.000000000 -0300
+++ icedtea-2.4.1-add_cacerts-2.patch 2013-07-19 10:42:51.238359104 -0300
@@ -539,7 +539,7 @@
+ certbegin=`grep -n "^-----BEGIN" "${cert}" | cut -d ":" -f 1`
+ certend=`grep -n "^-----END" "${cert}" | cut -d ":" -f 1`
+ sed -n "${certbegin},${certend}p" "${cert}" > "${tempfile}"
-+ echo yes | "${KEYTOOL}" -import -alias `basename "${cert}"`
-keystore \
++ echo yes | env LC_ALL=C "${KEYTOOL}" -import -alias `basename
"${cert}"` -keystore \
+ "${OUTFILE}" -storepass 'changeit' -file "${tempfile}"
+ rm "${tempfile}"
+done
--
[]s,
Fernando
--
http://linuxfromscratch.org/mailman/listinfo/blfs-dev
FAQ: http://www.linuxfromscratch.org/blfs/faq.html
Unsubscribe: See the above information page