#!/bin/sh
#
# Converts NSS certdata objects text into a concatenated PEM bundle.
#
# Copyright (C) 2020 Patrick McDermott
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
set -eu
error()
{
local fmt="${1}"
shift 1
printf "Error: ${fmt}\n" "${@}" 1>&2
}
parse()
{
local txt_input="${1}"
local trust_type="${2}"
local pem_output="${3}"
shift 3
local attr=
local type=
local val=
local class=
local label=
local value=
local trust=
local trusted=
if [ x"${txt_input}" != x'-' ]; then
exec 0<"${txt_input}"
fi
if [ x"${pem_output}" != x'-' ]; then
exec 1>"${pem_output}"
fi
while read -r attr && [ x"${attr}" != x'BEGINDATA' ]; do :; done
while read -r attr type val; do
case "${attr}" in
'#'*)
continue
;;
'CKA_CLASS')
class="${val}"
;;
'CKA_LABEL')
val="${val#\"}"
val="${val%\"}"
label="$(printf '%s' "${val}" | \
tr -C 'A-Za-z0-9_' '_')"
;;
'CKA_VALUE')
if [ x"${type}" != x'MULTILINE_OCTAL' ]; then
error 'Unsupported value type'
return 1
fi
value=
while read -r val; do
case "${val}" in
'END')
break
;;
*[!0-7\\]*)
error "Invalid $(: \
)multiline $(: \
)octal value"
return 1
;;
*)
value="${value}${val}"
;;
esac
done
;;
"CKA_TRUST_${trust_type}")
trust="${val}"
;;
'')
if [ -z "${class}" ]; then
error 'Object missing class'
return 1
fi
if [ -z "${label}" ]; then
error 'Object missing label'
return 1
fi
if [ x"${class}" = x'CKO_CERTIFICATE' ]; then
if [ -z "${value}" ]; then
error 'Object missing value'
return 1
fi
eval "certificate_${label}=\${value}"
elif [ x"${class}" = x'CKO_NSS_TRUST' ]; then
if [ -z "${trust}" ]; then
error 'Object missing trust'
return 1
fi
if [ x"${trust}" = \
x'CKT_NSS_TRUSTED_DELEGATOR' ]
then
trusted="${trusted} ${label}"
fi
elif [ x"${class}" = \
x'CKO_NSS_BUILTIN_ROOT_LIST' ]
then
:
else
error 'Unknown class "%s"' "${val}"
return 1
fi
class=
label=
value=
trust=
esac
done <<-EOF
$(cat)
EOF
# Extra newline in input to trigger final empty line block in parser.
for label in ${trusted}; do
printf '%s\n' '-----BEGIN CERTIFICATE-----'
eval "printf \"\${certificate_${label}}\"" | base64 - | \
tr -d '\n' | fold -w 64 -
printf '\n%s\n' '-----END CERTIFICATE-----'
done
return 0
}
usage()
{
printf 'Usage: %s \n' "${0}"
printf 'Example: %s certdata.txt SERVER_AUTH certdata.pem\n' "${0}"
return 0
}
main()
{
local txt_input=
local trust_type=
local pem_output=
if [ ${#} -ne 3 ]; then
usage 1>&2
return 1
fi
txt_input="${1}"
trust_type="${2}"
pem_output="${3}"
shift 3
parse "${txt_input}" "${trust_type}" "${pem_output}" || return 1
return 0
}
main "${@}"