]> Sergey Matveev's repositories - zeasypki.git/blob - zeasypki
Tiny README actualisation
[zeasypki.git] / zeasypki
1 #!/usr/bin/env zsh
2 # zeasypki -- easy PKI
3 # Copyright (C) 2022-2023 Sergey Matveev <stargrave@stargrave.org>
4
5 set -e
6
7 KEY_ENCRYPT_RECIPIENT=${KEY_ENCRYPT_RECIPIENT:-12AD32689C660D426967FD75CB8205632107AD8A}
8 COUNTRY=${COUNTRY:-RU}
9
10 path=(
11     ~/work/gogost/cmd/cer-selfsigned-example
12     ~/work/gogost/cmd/cer-dane-hash
13     $path
14 )
15
16 key_encrypt() {
17     gpg --encrypt --recipient $KEY_ENCRYPT_RECIPIENT
18 }
19
20 key_decrypt() {
21     gpg --decrypt
22 }
23
24 # ------------------------ >8 ------------------------
25
26 usage() {
27     cat >&2 <<EOF
28 Usage:
29   \$ $ZSH_ARGZERO:t ca [ecdsa|gost|eddsa] NAME -- new CA keypair
30   \$ $ZSH_ARGZERO:t list-ca                    -- list CA keypairs
31   \$ $ZSH_ARGZERO:t list                       -- list EE ones
32   \$ $ZSH_ARGZERO:t rem                        -- list certificate expirations
33   \$ $ZSH_ARGZERO:t new KEY                    -- new EE
34   \$ $ZSH_ARGZERO:t renew KEY                  -- renew EE
35   \$ $ZSH_ARGZERO:t dane KEY                   -- show DANE SHA256 hash
36   \$ $ZSH_ARGZERO:t encrypt KEY                -- encrypt private key
37   \$ $ZSH_ARGZERO:t keypair KEY                -- PEM-encoded full keypair
38 EOF
39     exit 1
40 }
41
42 zmodload -F zsh/files b:zf_mkdir
43 zmodload zsh/mapfile
44
45 key_get() {
46     [[ -s $1/key.pem ]] &&
47         REPLY=`< ${1}/key.pem` ||
48         REPLY=`key_decrypt < ${1}/key.pem.enc`
49 }
50
51 certtool_genkey() {
52     certtool --generate-privkey ${=1} --no-text
53 }
54
55 ca_new_xdsa() {
56     local keytype=$1
57     local domain=$2
58     local key=`mktemp`
59     local tmpl=`mktemp`
60     local cert=`mktemp`
61     trap "rm -f $key $tmpl $cert" HUP PIPE INT QUIT TERM EXIT
62     cat > $tmpl <<EOF
63 dn = "cn=$domain,c=$COUNTRY"
64 serial = 1
65 expiration_days = 3650
66 ca
67 cert_signing_key
68 EOF
69     certtool_genkey "$keytype" > $key
70     certtool \
71         --generate-self-signed \
72         --load-privkey $key \
73         --template $tmpl \
74         --outfile $cert
75     reply=(${mapfile[$key]} ${mapfile[$cert]})
76 }
77
78 ca_new_ecdsa() {
79     ca_new_xdsa "--key-type=ecdsa --bits 512" $1
80 }
81
82 ee_key_new_ecdsa() {
83     certtool_genkey "--key-type=ecdsa --bits 256"
84 }
85
86 ca_new_eddsa() {
87     ca_new_xdsa "--key-type=ed25519" $1
88 }
89
90 ee_key_new_eddsa() {
91     certtool_genkey "--key-type=ed25519"
92 }
93
94 ee_key_new_gost() {
95     cer-selfsigned-example -cn does-not-matter -ai 256A -only-key
96 }
97
98 ee_renew_xdsa() {
99     local algo=$1
100     local ca=$2
101     local domain=$3
102     local cakey=`mktemp`
103     local key=`mktemp`
104     local tmpl=`mktemp`
105     local cert=`mktemp`
106     trap "rm -f $cakey $key $tmpl $cert" HUP PIPE INT QUIT TERM EXIT
107     key_get ca/$algo/$ca
108     mapfile[$cakey]=$REPLY
109     key_get ee/$algo/$ca/$domain
110     mapfile[$key]=$REPLY
111     cat > $tmpl <<EOF
112 dn = "cn=$domain,c=RU"
113 expiration_days = 365
114 signing_key
115 dns_name = "$domain"
116 EOF
117     certtool \
118         --load-ca-certificate ca/$algo/$ca/cer.pem \
119         --load-ca-privkey $cakey \
120         --generate-certificate \
121         --load-privkey $key \
122         --template $tmpl
123 }
124
125 ee_renew_ecdsa() {
126     ee_renew_xdsa ecdsa "$1" "$2"
127 }
128
129 ee_renew_eddsa() {
130     ee_renew_xdsa eddsa "$1" "$2"
131 }
132
133 ee_renew_gost() {
134     local ca=$1
135     local domain=$2
136     local cakey=`mktemp`
137     local key=`mktemp`
138     local cert=`mktemp`
139     trap "rm -f $cakey $key $cert" HUP PIPE INT QUIT TERM EXIT
140     key_get ca/gost/$ca
141     mapfile[$cakey]=$REPLY
142     print >> $cakey
143     cat >> $cakey < ca/gost/$ca/cer.pem
144     key_get ee/gost/$ca/$domain
145     mapfile[$key]=$REPLY
146     cer-selfsigned-example \
147         -issue-with $cakey \
148         -reuse-key $key \
149         -cn $domain -country $COUNTRY -ai 256A
150 }
151
152 ca_new_gost() {
153     local domain=$1
154     local key=`mktemp`
155     local cert=`mktemp`
156     trap "rm -f $key $cert" HUP PIPE INT QUIT TERM EXIT
157     cer-selfsigned-example \
158         -ca \
159         -cn $domain \
160         -country $COUNTRY \
161         -serial 1 \
162         -ai 512C \
163         -out-key $key \
164         -out-cert $cert
165     reply=(${mapfile[$key]} ${mapfile[$cert]})
166 }
167
168 dane_ecdsa() {
169     certtool --key-id --hash=sha256
170 }
171
172 dane_eddsa() {
173     dane_ecdsa
174 }
175
176 dane_gost() {
177     cer-dane-hash
178 }
179
180 case $1 in
181 (ca)
182     [[ $# -eq 3 ]] || usage
183     algo=$2
184     domain=$3
185     dst=ca/$algo/$domain
186     zf_mkdir -p $dst
187     [[ -s $dst/key.pem ]] && {
188         print $dst/key.pem already exists >&2
189         exit 1
190     }
191     ca_new_${algo} $domain
192     _umask=`umask`
193     umask 077
194     mapfile[${dst}/key.pem]=${reply[1]}
195     umask $_umask
196     mapfile[${dst}/cer.pem]=${reply[2]}
197     print $dst
198     ;;
199 (encrypt)
200     [[ $# -eq 2 ]] || usage
201     key=$2/key.pem
202     [[ -s $key ]] || {
203         print no $key found >&2
204         exit 1
205     }
206     umask 077
207     key_encrypt < $key > $key.enc
208     rm $key
209     ;;
210 (new)
211     [[ $# -eq 2 ]] || usage
212     cols=(${(s:/:)2})
213     algo=${cols[2]}
214     ca=${cols[3]}
215     domain=${cols[4]}
216     dst=ee/$algo/$ca/$domain
217     [[ $dst = $2 ]]
218     zf_mkdir -p $dst
219     [[ -s $dst/key.pem ]] && {
220         print $dst/key.pem already exists >&2
221         exit 1
222     }
223     _umask=`umask`
224     umask 077
225     ee_key_new_${algo} > $dst/key.pem
226     umask $_umask
227     ee_renew_${algo} $ca $domain > $dst/cer.pem
228     ;;
229 (renew)
230     [[ $# -eq 2 ]] || usage
231     cols=(${(s:/:)2})
232     algo=${cols[2]}
233     ca=${cols[3]}
234     domain=${cols[4]}
235     ee_renew_${algo} $ca $domain > ee/$algo/$ca/$domain/cer.pem
236     ;;
237 (dane)
238     [[ $# -eq 2 ]] || usage
239     dane_${${(s:/:)2}[2]} < $2/cer.pem
240     ;;
241 (keypair)
242     [[ $# -eq 2 ]] || usage
243     key_get $2
244     print -- "$REPLY"
245     cat $2/cer.pem
246     ;;
247 (rem)
248     zmodload -F zsh/datetime b:strftime
249     export LC_ALL=C
250     for cer (**/cer.pem) {
251         certtool --certificate-info < $cer | while read line ; do
252             [[ $line =~ "^\s*Not After: .*" ]] && break
253         done
254         [[ $MATCH ]]
255         # Not After: Sat Jul 02 10:02:29 UTC 2022
256         cols=(${=MATCH})
257         strftime -s ts_ugly -r "%b %d %H:%M:%S UTC %Y" ${(j: :)cols[4,-1]}
258         strftime -s ts_good %F $ts_ugly
259         print REM $ts_good +30 MSG $cer
260     }
261     ;;
262 (list) print -C1 ee/*/*/*(/on) ;;
263 (list-ca) print -C1 ca/*/*(/on) ;;
264 (*) usage ;;
265 esac