#!/usr/bin/env zsh
# zeasypki -- easy PKI
-# Copyright (C) 2022 Sergey Matveev <stargrave@stargrave.org>
+# Copyright (C) 2022-2024 Sergey Matveev <stargrave@stargrave.org>
-set -e
+setopt ERR_EXIT PIPE_FAIL
-KEY_ENCRYPT_RECIPIENT=${KEY_ENCRYPT_RECIPIENT:-CF60E89A59231E76E2636422AE1A8109E49857EF}
COUNTRY=${COUNTRY:-RU}
-# Turn on PyGOST utilities
-path=(~/local/stow/py310/bin ~/work/pygost/pygost/asn1schemas $path)
-export -TU PYTHONPATH pythonpath
-pythonpath=(~/work/pygost ~/work/pyderasn)
+path=(
+ ~/work/gogost/cmd/cer-selfsigned-example
+ ~/work/gogost/cmd/cer-dane-hash
+ $path
+)
key_encrypt() {
- gpg --encrypt --recipient $KEY_ENCRYPT_RECIPIENT
+ age -R ~/.age/general.pub
}
key_decrypt() {
- gpg --decrypt
+ age -d -i ~/.age/general.age
}
# ------------------------ >8 ------------------------
usage() {
- >&2 <<EOF
+ cat >&2 <<EOF
Usage:
- \$ $ZSH_ARGZERO:t ca [ecdsa|gost] NAME -- new CA keypair
- \$ $ZSH_ARGZERO:t list-ca -- list CA keypairs
- \$ $ZSH_ARGZERO:t list -- list EE ones
- \$ $ZSH_ARGZERO:t rem -- list certificate expirations
- \$ $ZSH_ARGZERO:t new KEY -- new EE
- \$ $ZSH_ARGZERO:t renew KEY -- renew EE
- \$ $ZSH_ARGZERO:t dane KEY -- show DANE SHA256 hash
- \$ $ZSH_ARGZERO:t encrypt KEY -- encrypt private key
- \$ $ZSH_ARGZERO:t keypair KEY -- PEM-encoded full keypair
+ \$ $ZSH_ARGZERO:t ca [ecdsa|gost|eddsa] NAME -- new CA keypair
+ \$ $ZSH_ARGZERO:t list-ca -- list CA keypairs
+ \$ $ZSH_ARGZERO:t list -- list EE ones
+ \$ $ZSH_ARGZERO:t rem -- list certificate expirations
+ \$ $ZSH_ARGZERO:t new KEY -- new EE
+ \$ $ZSH_ARGZERO:t renew KEY -- renew EE
+ \$ $ZSH_ARGZERO:t dane KEY -- show DANE SHA256 hash
+ \$ $ZSH_ARGZERO:t encrypt KEY -- encrypt private key
+ \$ $ZSH_ARGZERO:t keypair KEY -- PEM-encoded full keypair
EOF
exit 1
}
zmodload zsh/mapfile
key_get() {
- [[ -s $1/key.pem ]] &&
- REPLY=`< ${1}/key.pem` ||
+ if [[ -s $1/key.pem ]] ; then
+ REPLY=`< ${1}/key.pem`
+ else
REPLY=`key_decrypt < ${1}/key.pem.enc`
+ fi
}
certtool_genkey() {
- certtool --generate-privkey --ecc --bits $1 --no-text
+ certtool --generate-privkey ${=1} --no-text
}
-ca_new_ecdsa() {
- local domain=$1
+ca_new_xdsa() {
+ local keytype=$1
+ local domain=$2
local key=`mktemp`
local tmpl=`mktemp`
local cert=`mktemp`
trap "rm -f $key $tmpl $cert" HUP PIPE INT QUIT TERM EXIT
- > $tmpl <<EOF
+ cat > $tmpl <<EOF
dn = "cn=$domain,c=$COUNTRY"
-serial = 1
expiration_days = 3650
ca
cert_signing_key
EOF
- certtool_genkey 512 > $key
+ certtool_genkey "$keytype" > $key
certtool \
--generate-self-signed \
--load-privkey $key \
reply=(${mapfile[$key]} ${mapfile[$cert]})
}
+ca_new_ecdsa() {
+ ca_new_xdsa "--key-type=ecdsa --bits 512" $1
+}
+
ee_key_new_ecdsa() {
- certtool_genkey 256
+ certtool_genkey "--key-type=ecdsa --bits 256"
+}
+
+ca_new_eddsa() {
+ ca_new_xdsa "--key-type=ed25519" $1
+}
+
+ee_key_new_eddsa() {
+ certtool_genkey "--key-type=ed25519"
}
ee_key_new_gost() {
- cert-selfsigned-example.py --cn does-not-matter --ai 256A --only-key
+ cer-selfsigned-example -cn does-not-matter -ai 256A -only-key
}
-ee_renew_ecdsa() {
- local ca=$1
- local domain=$2
+ee_renew_xdsa() {
+ local algo=$1
+ local ca=$2
+ local domain=$3
local cakey=`mktemp`
local key=`mktemp`
local tmpl=`mktemp`
local cert=`mktemp`
trap "rm -f $cakey $key $tmpl $cert" HUP PIPE INT QUIT TERM EXIT
- key_get ca/ecdsa/$ca
+ key_get ca/$algo/$ca
mapfile[$cakey]=$REPLY
- key_get ee/ecdsa/$ca/$domain
+ key_get ee/$algo/$ca/$domain
mapfile[$key]=$REPLY
- > $tmpl <<EOF
+ cat > $tmpl <<EOF
dn = "cn=$domain,c=RU"
expiration_days = 365
signing_key
dns_name = "$domain"
EOF
certtool \
- --load-ca-certificate ca/ecdsa/$ca/cer.pem \
+ --load-ca-certificate ca/$algo/$ca/cer.pem \
--load-ca-privkey $cakey \
--generate-certificate \
--load-privkey $key \
--template $tmpl
}
+ee_renew_ecdsa() {
+ ee_renew_xdsa ecdsa "$1" "$2"
+}
+
+ee_renew_eddsa() {
+ ee_renew_xdsa eddsa "$1" "$2"
+}
+
ee_renew_gost() {
local ca=$1
local domain=$2
trap "rm -f $cakey $key $cert" HUP PIPE INT QUIT TERM EXIT
key_get ca/gost/$ca
mapfile[$cakey]=$REPLY
- >> $cakey < ca/gost/$ca/cer.pem
+ print >> $cakey
+ cat >> $cakey < ca/gost/$ca/cer.pem
key_get ee/gost/$ca/$domain
mapfile[$key]=$REPLY
- cert-selfsigned-example.py \
- --issue-with $cakey \
- --reuse-key $key \
- --cn $domain --country $COUNTRY --ai 256A
+ cer-selfsigned-example \
+ -issue-with $cakey \
+ -reuse-key $key \
+ -cn $domain -country $COUNTRY -ai 256A
}
ca_new_gost() {
local key=`mktemp`
local cert=`mktemp`
trap "rm -f $key $cert" HUP PIPE INT QUIT TERM EXIT
- cert-selfsigned-example.py \
- --ca \
- --cn $domain \
- --country $COUNTRY \
- --serial 1 \
- --ai 512C \
- --out-key $key \
- --out-cert $cert
+ cer-selfsigned-example \
+ -ca \
+ -cn $domain \
+ -country $COUNTRY \
+ -ai 512C \
+ -out-key $key \
+ -out-cert $cert
reply=(${mapfile[$key]} ${mapfile[$cert]})
}
certtool --key-id --hash=sha256
}
+dane_eddsa() {
+ dane_ecdsa
+}
+
dane_gost() {
- cert-dane-hash.py
+ cer-dane-hash
}
case $1 in
domain=$3
dst=ca/$algo/$domain
zf_mkdir -p $dst
- [[ -s $dst/key.pem ]] && {
+ [[ ! -s $dst/key.pem ]] || {
print $dst/key.pem already exists >&2
exit 1
}
dst=ee/$algo/$ca/$domain
[[ $dst = $2 ]]
zf_mkdir -p $dst
- [[ -s $dst/key.pem ]] && {
+ [[ ! -s $dst/key.pem ]] || {
print $dst/key.pem already exists >&2
exit 1
}
export LC_ALL=C
for cer (**/cer.pem) {
certtool --certificate-info < $cer | while read line ; do
- [[ $line =~ "^\s*Not After: .*" ]] && break
+ [[ ! $line =~ "^Not After: .*" ]] || break
done
[[ $MATCH ]]
# Not After: Sat Jul 02 10:02:29 UTC 2022