forked from srvrco/getssl
-
Notifications
You must be signed in to change notification settings - Fork 3
/
getssl
executable file
·2203 lines (2013 loc) · 84.3 KB
/
getssl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env bash
# ---------------------------------------------------------------------------
# getssl - Obtain SM2 SSL certificates from the ca.gmssl.org ACME server
# 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 at <http://www.gnu.org/licenses/> for
# more details.
# For usage, run "getssl -h" or see https://github.com/lianran/GmSSL-WebCA-Client
# Revision history:
# 2016-01-08 Created (v0.1)
# 2016-01-11 type correction and upload to github (v0.2)
# 2016-01-11 added import of any existing cert on -c option (v0.3)
# 2016-01-12 corrected formatting of imported certificate (v0.4)
# 2016-01-12 corrected error on removal of token in some instances (v0.5)
# 2016-01-18 corrected issue with removing tmp if run as root with the -c option (v0.6)
# 2016-01-18 added option to upload a single PEN file ( used by cpanel) (v0.7)
# 2016-01-23 added dns challenge option (v0.8)
# 2016-01-24 create the ACL directory if it does not exist. (v0.9) - dstosberg
# 2016-01-26 correcting a couple of small bugs and allow curl to follow redirects (v0.10)
# 2016-01-27 add a very basic openssl.cnf file if it doesn't exist and tidy code slightly (v0.11)
# 2016-01-28 Typo corrections, quoted file variables and fix bug on DNS_DEL_COMMAND (v0.12)
# 2016-01-28 changed DNS checks to use nslookup and allow hyphen in domain names (v0.13)
# 2016-01-29 Fix ssh-reload-command, extra waiting for DNS-challenge,
# 2016-01-29 add error_exit and cleanup help message (v0.14)
# 2016-01-29 added -a|--all option to renew all configured certificates (v0.15)
# 2016-01-29 added option for elliptic curve keys (v0.16)
# 2016-01-29 added server-type option to use and check cert validity from website (v0.17)
# 2016-01-30 added --quiet option for running in cron (v0.18)
# 2016-01-31 removed usage of xxd to make script more compatible across versions (v0.19)
# 2016-01-31 removed usage of base64 to make script more compatible across platforms (v0.20)
# 2016-01-31 added option to safe a full chain certificate (v0.21)
# 2016-02-01 commented code and added option for copying concatenated certs to file (v0.22)
# 2016-02-01 re-arrange flow for DNS-challenge, to reduce time taken (v0.23)
# 2016-02-04 added options for other server types (ldaps, or any port) and check_remote (v0.24)
# 2016-02-04 added short sleep following service restart before checking certs (v0.25)
# 2016-02-12 fix challenge token location when directory doesn't exist (v0.26)
# 2016-02-17 fix sed -E issue, and reduce length of renew check to 365 days for older systems (v0.27)
# 2016-04-05 Ensure DNS cleanup on error exit. (0.28) - pecigonzalo
# 2016-04-15 Remove NS Lookup of A record when using dns validation (0.29) - pecigonzalo
# 2016-04-17 Improving the wording in a couple of comments and info statements. (0.30)
# 2016-05-04 Improve check for if DNS_DEL_COMMAND is blank. (0.31)
# 2016-05-06 Setting umask to 077 for security of private keys etc. (0.32)
# 2016-05-20 update to reflect changes in staging ACME server json (0.33)
# 2016-05-20 tidying up checking of json following AMCE changes. (0.34)
# 2016-05-21 added AUTH_DNS_SERVER to getssl.cfg as optional definition of authoritative DNS server (0.35)
# 2016-05-21 added DNS_WAIT to getssl.cfg as (default = 10 seconds as before) (0.36)
# 2016-05-21 added PUBLIC_DNS_SERVER option, for forcing use of an external DNS server (0.37)
# 2016-05-28 added FTP method of uploading tokens to remote server (blocked for certs as not secure) (0.38)
# 2016-05-28 added FTP method into the default config notes. (0.39)
# 2016-05-30 Add sftp with password to copy files (0.40)
# 2016-05-30 Add version check to see if there is a more recent version of getssl (0.41)
# 2016-05-30 Add [-u|--upgrade] option to automatically upgrade getssl (0.42)
# 2016-05-30 Added backup when auto-upgrading (0.43)
# 2016-05-30 Improvements to auto-upgrade (0.44)
# 2016-05-31 Improved comments - no structural changes
# 2016-05-31 After running for nearly 6 months, final testing prior to a 1.00 stable version. (0.90)
# 2016-06-01 Reorder functions alphabetically as part of code tidy. (0.91)
# 2016-06-03 Version 1.0 of code for release (1.00)
# 2016-06-09 bugfix of issue 44, and add success statement (ignoring quiet flag) (1.01)
# 2016-06-13 test return status of DNS_ADD_COMMAND and error_exit if a problem (hadleyrich) (1.02)
# 2016-06-13 bugfix of issue 45, problem with SERVER_TYPE when it's just a port number (1.03)
# 2016-06-13 bugfix issue 47 - DNS_DEL_COMMAND cleanup was run when not required. (1.04)
# 2016-06-15 add error checking on RELOAD_CMD (1.05)
# 2016-06-20 updated sed and date functions to run on MAC OS X (1.06)
# 2016-06-20 added CHALLENGE_CHECK_TYPE variable to allow checks direct on https rather than http (1.07)
# 2016-06-21 updated grep functions to run on MAC OS X (1.08)
# 2016-06-11 updated to enable running on windows with cygwin (1.09)
# 2016-07-02 Corrections to work with older slackware issue #56 (1.10)
# 2016-07-02 Updating help info re ACL in config file (1.11)
# 2016-07-04 adding DOMAIN_STORAGE as a variable to solve for issue #59 (1.12)
# 2016-07-05 updated order to better handle non-standard DOMAIN_STORAGE location (1.13)
# 2016-07-06 added additional comments about SANS in example template (1.14)
# 2016-07-07 check for duplicate domains in domain / SANS (1.15)
# 2016-07-08 modified to be used on older bash for issue #64 (1.16)
# 2016-07-11 added -w to -a option and comments in domain template (1.17)
# 2016-07-18 remove / regenerate csr when generating new private domain key (1.18)
# 2016-07-21 add output of combined private key and domain cert (1.19)
# 2016-07-21 updated typo (1.20)
# 2016-07-22 corrected issue in nslookup debug option - issue #74 (1.21)
# 2016-07-26 add more server-types based on openssl s_client (1.22)
# 2016-08-01 updated agreement for letsencrypt (1.23)
# 2016-08-02 updated agreement for letsencrypt to update automatically (1.24)
# 2016-08-03 improve messages on test of certificate installation (1.25)
# 2016-08-04 remove carriage return from agreement - issue #80 (1.26)
# 2016-08-04 set permissions for token folders - issue #81 (1.27)
# 2016-08-07 allow default chained file creation - issue #85 (1.28)
# 2016-08-07 use copy rather than move when archiving certs - issue #86 (1.29)
# 2016-08-07 enable use of a single ACL for all checks (if USE_SINGLE_ACL="true" (1.30)
# 2016-08-23 check for already validated domains (issue #93) - (1.31)
# 2016-08-23 updated already validated domains (1.32)
# 2016-08-23 included better force_renew and template for USE_SINGLE_ACL (1.33)
# 2016-08-23 enable insecure certificate on https token check #94 (1.34)
# 2016-08-23 export OPENSSL_CONF so it's used by all openssl commands (1.35)
# 2016-08-25 updated defaults for ACME agreement (1.36)
# 2016-09-04 correct issue #101 when some domains already validated (1.37)
# 2016-09-12 Checks if which is installed (1.38)
# 2016-09-13 Don't check for updates, if -U parameter has been given (1.39)
# 2016-09-17 Improved error messages from invalid certs (1.40)
# 2016-09-19 remove update check on recursive calls when using -a (1.41)
# 2016-09-21 changed shebang for portability (1.42)
# 2016-09-21 Included option to Deactivate an Authorization (1.43)
# 2016-09-22 retry on 500 error from ACME server (1.44)
# 2016-09-22 added additional checks and retry on 500 error from ACME server (1.45)
# 2016-09-24 merged in IPv6 support (1.46)
# 2016-09-27 added additional debug info issue #119 (1.47)
# 2016-09-27 removed IPv6 switch in favour of checking both IPv4 and IPv6 (1.48)
# 2016-09-28 Add -Q, or --mute, switch to mute notifications about successfully upgrading getssl (1.49)
# 2016-09-30 improved portability to work natively on FreeBSD, Slackware and OSX (1.50)
# 2016-09-30 comment out PRIVATE_KEY_ALG from the domain template Issue #125 (1.51)
# 2016-10-03 check remote certificate for right domain before saving to local (1.52)
# 2016-10-04 allow existing CSR with domain name in subject (1.53)
# 2016-10-05 improved the check for CSR with domain in subject (1.54)
# 2016-10-06 prints update info on what was included in latest updates (1.55)
# 2016-10-06 when using -a flag, ignore folders in working directory which aren't domains (1.56)
# 2016-10-12 alllow multiple tokens in DNS challenge (1.57)
# 2016-10-14 added CHECK_ALL_AUTH_DNS option to check all DNS servres, not just one primary server (1.58)
# 2016-10-14 added archive of chain and private key for each cert, and purge old archives (1.59)
# 2016-10-17 updated info comment on failed cert due to rate limits. (1.60)
# 2016-10-17 fix error messages when using 1.0.1e-fips (1.61)
# 2016-10-20 set secure permissions when generating account key (1.62)
# 2016-10-20 set permsissions to 700 for getssl script during upgrade (1.63)
# 2016-10-20 add option to revoke a certificate (1.64)
# 2016-10-21 set revocation server default to acme-v01.api.letsencrypt.org (1.65)
# 2016-10-21 bug fix for revocation on different servers. (1.66)
# 2016-10-22 Tidy up archive code for certificates and reduce permissions for security
# 2016-10-22 Add EC signing for secp384r1 and secp521r1 (the latter not yet supported by Let's Encrypt
# 2016-10-22 Add option to create a new private key for every cert (REUSE_PRIVATE_KEY="true" by default)
# 2016-10-22 Combine EC signing, Private key reuse and archive permissions (1.67)
# 2016-10-25 added CHECK_REMOTE_WAIT option ( to pause before final remote check)
# 2016-10-25 Added EC account key support ( prime256v1, secp384r1 ) (1.68)
# 2016-10-25 Ignore DNS_EXTRA_WAIT if all domains already validated (issue #146) (1.69)
# 2016-10-25 Add option for dual ESA / EDSA certs (1.70)
# 2016-10-25 bug fix Issue #141 challenge error 400 (1.71)
# 2016-10-26 check content of key files, not just recreate if missing.
# 2016-10-26 Improvements on portability (1.72)
# 2016-10-26 Date formatting for busybox (1.73)
# 2016-10-27 bug fix - issue #157 not recognising EC keys on some versions of openssl (1.74)
# 2016-10-31 generate EC account keys and tidy code.
# 2016-10-31 fix warning message if cert doesn't exist (1.75)
# 2016-10-31 remove only specified DNS token #161 (1.76)
# 2016-11-03 Reduce long lines, and remove echo from update (1.77)
# 2016-11-05 added TOKEN_USER_ID (to set ownership of token files )
# 2016-11-05 updated style to work with latest shellcheck (1.78)
# 2016-11-07 style updates
# 2016-11-07 bug fix DOMAIN_PEM_LOCATION starting with ./ #167
# 2016-11-08 Fix for openssl 1.1.0 #166 (1.79)
# 2016-11-08 Add and comment optional sshuserid for ssh ACL (1.80)
# 2016-11-09 Add SKIP_HTTP_TOKEN_CHECK option (Issue #170) (1.81)
# 2016-11-13 bug fix DOMAIN_KEY_CERT generation (1.82)
# 2016-11-17 add PREVENT_NON_INTERACTIVE_RENEWAL option (1.83)
# 2016-12-03 add HTTP_TOKEN_CHECK_WAIT option (1.84)
# 2016-12-03 bugfix CSR renewal when no SANS and when using MINGW (1.85)
# 2016-12-16 create CSR_SUBJECT variable - Issue #193
# 2016-12-16 added fullchain to archive (1.86)
# 2016-12-16 updated DOMAIN_PEM_LOCATION when using DUAL_RSA_ECDSA (1.87)
# 2016-12-19 allow user to ignore permission preservation with nfsv3 shares (1.88)
# 2016-12-19 bug fix for CA (1.89)
# 2016-12-19 included IGNORE_DIRECTORY_DOMAIN option (1.90)
# 2016-12-22 allow copying files to multiple locations (1.91)
# 2016-12-22 bug fix for copying tokens to multiple locations (1.92)
# 2016-12-23 tidy code - place default variables in alphabetical order.
# 2016-12-27 update checks to work with openssl in FIPS mode (1.93)
# 2016-12-28 fix leftover tmpfiles in upgrade routine (1.94)
# 2016-12-28 tidied up upgrade tmpfile handling (1.95)
# 2017-01-01 update comments
# 2017-01-01 create stable release 2.0 (2.00)
# 2017-01-02 Added option to limit amount of old versions to keep (2.01)
# 2017-01-03 Created check_config function to list all obvious config issues (2.02)
# 2017-01-10 force renew if FORCE_RENEWAL file exists (2.03)
# 2017-01-12 added drill, dig or host as alternatives to nslookup (2.04)
# 2017-01-18 bugfix issue #227 - error deleting csr if doesn't exist
# 2017-01-18 issue #228 check private key and account key are different (2.05)
# 2017-01-21 issue #231 mingw bugfix and typos in debug messages (2.06)
# 2017-01-29 issue #232 use neutral locale for date formatting (2.07)
# 2017-01-30 issue #243 compatibility with bash 3.0 (2.08)
# 2017-01-30 issue #243 additional compatibility with bash 3.0 (2.09)
# 2017-02-18 add OCSP Must-Staple to the domain csr generation (2.10)
# ----------------------------------------------------------------------------------------
PROGNAME=${0##*/}
VERSION="2.10"
# defaults
ACCOUNT_KEY_LENGTH=4096
ACCOUNT_KEY_TYPE="rsa"
CA="ca.gmssl.org:4000"
CA_CERT_LOCATION=""
CHALLENGE_CHECK_TYPE="http"
CHECK_ALL_AUTH_DNS="false"
CHECK_REMOTE="true"
CHECK_REMOTE_WAIT=0
CODE_LOCATION="https://raw.githubusercontent.com/lianran/GmSSL-WebCA-Client/master/getssl"
CSR_SUBJECT="/"
DEACTIVATE_AUTH="false"
DEFAULT_REVOKE_CA="https://acme-v01.api.letsencrypt.org"
DNS_EXTRA_WAIT=""
DNS_WAIT=10
DOMAIN_KEY_LENGTH=4096
DUAL_RSA_ECDSA="true"
GETSSL_IGNORE_CP_PRESERVE="false"
HTTP_TOKEN_CHECK_WAIT=0
IGNORE_DIRECTORY_DOMAIN="false"
ORIG_UMASK=$(umask)
PREVIOUSLY_VALIDATED="true"
PRIVATE_KEY_ALG="sm2p256v1"
PUBLIC_DNS_SERVER=""
RELOAD_CMD=""
RENEW_ALLOW="30"
REUSE_PRIVATE_KEY="true"
SERVER_TYPE="https"
SKIP_HTTP_TOKEN_CHECK="false"
SSLCONF="$(openssl version -d 2>/dev/null| cut -d\" -f2)/openssl.cnf"
OCSP_MUST_STAPLE="false"
TEMP_UPGRADE_FILE=""
TOKEN_USER_ID=""
USE_SINGLE_ACL="false"
VALIDATE_VIA_DNS=""
WORKING_DIR=~/.getssl
_CHECK_ALL=0
_CREATE_CONFIG=0
_FORCE_RENEW=0
_KEEP_VERSIONS=""
_MUTE=0
_QUIET=0
_RECREATE_CSR=0
_REVOKE=0
_UPGRADE=0
_UPGRADE_CHECK=1
_USE_DEBUG=0
config_errors="false"
LANG=C
# store copy of original command in case of upgrading script and re-running
ORIGCMD="$0 $*"
# Define all functions (in alphabetical order)
cert_archive() { # Archive certificate file by copying files to dated archive dir.
debug "creating an achive copy of current new certs"
date_time=$(date +%Y_%m_%d_%H_%M)
mkdir -p "${DOMAIN_DIR}/archive/${date_time}"
umask 077
cp "$CERT_FILE" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.crt"
cp "$DOMAIN_DIR/${DOMAIN}.csr" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.csr"
cp "$DOMAIN_DIR/${DOMAIN}.key" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.key"
cp "$CA_CERT" "${DOMAIN_DIR}/archive/${date_time}/chain.crt"
cat "$CERT_FILE" "$CA_CERT" > "${DOMAIN_DIR}/archive/${date_time}/fullchain.crt"
if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then
cp "${CERT_FILE::-4}.ec.crt" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.ec.crt"
cp "$DOMAIN_DIR/${DOMAIN}.ec.csr" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.ec.csr"
cp "$DOMAIN_DIR/${DOMAIN}.ec.key" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.ec.key"
cp "${CA_CERT::-4}.ec.crt" "${DOMAIN_DIR}/archive/${date_time}/chain.ec.crt"
cat "${CERT_FILE::-4}.ec.crt" "${CA_CERT::-4}.ec.crt" > "${DOMAIN_DIR}/archive/${date_time}/fullchain.ec.crt"
fi
umask "$ORIG_UMASK"
debug "purging old GetSSL archives"
purge_archive "$DOMAIN_DIR"
}
check_challenge_completion() { # checks with the ACME server if our challenge is OK
uri=$1
domain=$2
keyauthorization=$3
debug "sending request to ACME server saying we're ready for challenge"
send_signed_request "$uri" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}"
# check response from our request to perform challenge
if [[ ! -z "$code" ]] && [[ ! "$code" == '202' ]] ; then
error_exit "$domain:Challenge error: $code"
fi
# loop "forever" to keep checking for a response from the ACME server.
while true ; do
debug "checking"
if ! get_cr "$uri" ; then
error_exit "$domain:Verify error:$code"
fi
status=$(json_get "$response" status)
# If ACME response is valid, then break out of loop
if [[ "$status" == "valid" ]] ; then
info "Verified $domain"
break;
fi
# if ACME response is that their check gave an invalid response, error exit
if [[ "$status" == "invalid" ]] ; then
err_detail=$(json_get "$response" detail)
error_exit "$domain:Verify error:$err_detail"
fi
# if ACME response is pending ( they haven't completed checks yet) then wait and try again.
if [[ "$status" == "pending" ]] ; then
info "Pending"
else
error_exit "$domain:Verify error:$response"
fi
debug "sleep 5 secs before testing verify again"
sleep 5
done
if [[ "$DEACTIVATE_AUTH" == "true" ]]; then
deactivate_url=$(echo "$responseHeaders" | grep "^Link" | awk -F"[<>]" '{print $2}')
deactivate_url_list="$deactivate_url_list $deactivate_url"
debug "adding url to deactivate list - $deactivate_url"
fi
}
check_config() { # check the config files for all obvious errors
debug "checking config"
# check keys
case "$ACCOUNT_KEY_TYPE" in
rsa|prime256v1|secp384r1|secp521r1)
debug "checked ACCOUNT_KEY_TYPE " ;;
*)
info "${DOMAIN}: invalid ACCOUNT_KEY_TYPE - $ACCOUNT_KEY_TYPE"
config_errors=true ;;
esac
if [[ "$ACCOUNT_KEY" == "$DOMAIN_DIR/${DOMAIN}.key" ]]; then
info "${DOMAIN}: ACCOUNT_KEY and domain key ( $DOMAIN_DIR/${DOMAIN}.key ) must be different"
config_errors=true
fi
case "$PRIVATE_KEY_ALG" in
rsa|prime256v1|secp384r1|secp521r1|sm2p256v1)
debug "checked PRIVATE_KEY_ALG " ;;
*)
info "${DOMAIN}: invalid PRIVATE_KEY_ALG - $PRIVATE_KEY_ALG"
config_errors=true ;;
esac
if [[ "$DUAL_RSA_ECDSA" == "true" ]] && [[ "$PRIVATE_KEY_ALG" == "rsa" ]]; then
info "${DOMAIN}: PRIVATE_KEY_ALG not set to an EC type and DUAL_RSA_ECDSA=\"true\""
config_errors=true
fi
# get all domains
if [[ "$IGNORE_DIRECTORY_DOMAIN" == "true" ]]; then
alldomains=${SANS//,/ }
else
alldomains=$(echo "$DOMAIN,$SANS" | sed "s/,/ /g")
fi
if [[ -z "$alldomains" ]]; then
info "${DOMAIN}: no domains specified"
config_errors=true
fi
if [[ $VALIDATE_VIA_DNS == "true" ]]; then # using dns-01 challenge
if [[ -z "$DNS_ADD_COMMAND" ]]; then
info "${DOMAIN}: DNS_ADD_COMMAND not defined (whilst VALIDATE_VIA_DNS=\"true\")"
config_errors=true
fi
if [[ -z "$DNS_DEL_COMMAND" ]]; then
info "${DOMAIN}: DNS_DEL_COMMAND not defined (whilst VALIDATE_VIA_DNS=\"true\")"
config_errors=true
fi
fi
dn=0
tmplist=$(mktemp)
for d in $alldomains; do # loop over domains (dn is domain number)
debug "checking domain $d"
if [[ "$(grep "^${d}$" "$tmplist")" = "$d" ]]; then
info "${DOMAIN}: $d appears to be duplicated in domain, SAN list"
config_errors=true
else
echo "$d" >> "$tmplist"
fi
if [[ "$USE_SINGLE_ACL" == "true" ]]; then
DOMAIN_ACL="${ACL[0]}"
else
DOMAIN_ACL="${ACL[$dn]}"
fi
if [[ $VALIDATE_VIA_DNS != "true" ]]; then # using http-01 challenge
if [[ -z "${DOMAIN_ACL}" ]]; then
info "${DOMAIN}: ACL location not specified for domain $d in $DOMAIN_DIR/getssl.cfg"
config_errors=true
fi
# check domain exist
if [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then
if [[ "$($DNS_CHECK_FUNC "${d}" SOA|grep -c "^${d}")" -ge 1 ]]; then
debug "found IP for ${d}"
else
info "${DOMAIN}: DNS lookup failed for ${d}"
config_errors=true
fi
elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then
if [[ "$($DNS_CHECK_FUNC "${d}" |grep -c "^${d}")" -ge 1 ]]; then
debug "found IP for ${d}"
else
info "${DOMAIN}: DNS lookup failed for ${d}"
config_errors=true
fi
elif [[ "$(nslookup -query=AAAA "${d}"|grep -c "^${d}.*has AAAA address")" -ge 1 ]]; then
debug "found IPv6 record for ${d}"
elif [[ "$(nslookup "${d}"| grep -c ^Name)" -ge 1 ]]; then
debug "found IPv4 record for ${d}"
else
info "${DOMAIN}: DNS lookup failed for $d"
config_errors=true
fi
fi # end using http-01 challenge
((dn++))
done
# tidy up
rm -f "$tmplist"
if [[ "$config_errors" == "true" ]]; then
error_exit "${DOMAIN}: exiting due to config errors"
fi
debug "${DOMAIN}: check_config completed - all OK"
}
check_getssl_upgrade() { # check if a more recent version of code is available available
TEMP_UPGRADE_FILE="$(mktemp)"
curl --silent "$CODE_LOCATION" --output "$TEMP_UPGRADE_FILE"
errcode=$?
if [[ $errcode -eq 60 ]]; then
error_exit "curl needs updating, your version does not support SNI (multiple SSL domains on a single IP)"
elif [[ $errcode -gt 0 ]]; then
error_exit "curl error : $errcode"
fi
latestversion=$(awk -F '"' '$1 == "VERSION=" {print $2}' "$TEMP_UPGRADE_FILE")
latestvdec=$(echo "$latestversion"| tr -d '.')
localvdec=$(echo "$VERSION"| tr -d '.' )
debug "current code is version ${VERSION}"
debug "Most recent version is ${latestversion}"
# use a default of 0 for cases where the latest code has not been obtained.
if [[ "${latestvdec:-0}" -gt "$localvdec" ]]; then
if [[ ${_UPGRADE} -eq 1 ]]; then
install "$0" "${0}.v${VERSION}"
install -m 700 "$TEMP_UPGRADE_FILE" "$0"
if [[ ${_MUTE} -eq 0 ]]; then
echo "Updated getssl from v${VERSION} to v${latestversion}"
echo "these update notification can be turned off using the -Q option"
echo ""
echo "Updates are;"
awk "/\(${VERSION}\)$/ {s=1} s; /\(${latestversion}\)$/ {s=0}" "$TEMP_UPGRADE_FILE" | awk '{if(NR>1)print}'
echo ""
fi
if [[ -n "$_KEEP_VERSIONS" ]] && [[ "$_KEEP_VERSIONS" =~ ^[0-9]+$ ]]; then
# Obtain all locally stored old versions in getssl_versions
declare -a getssl_versions
shopt -s nullglob
for getssl_version in $0.v*; do
getssl_versions[${#getssl_versions[@]}]="$getssl_version"
done
shopt -u nullglob
# Explicitly sort the getssl_versions array to make sure
shopt -s -o noglob
IFS=$'\n' getssl_versions=($(sort <<< "${getssl_versions[*]}"))
shopt -u -o noglob
# Remove entries until given amount of old versions to keep is reached
while [[ ${#getssl_versions[@]} -gt $_KEEP_VERSIONS ]]; do
debug "removing old version ${getssl_versions[0]}"
rm "${getssl_versions[0]}"
getssl_versions=("${getssl_versions[@]:1}")
done
fi
eval "$ORIGCMD"
graceful_exit
else
info ""
info "A more recent version (v${latestversion}) of getssl is available, please update"
info "the easiest way is to use the -u or --upgrade flag"
info ""
fi
fi
}
clean_up() { # Perform pre-exit housekeeping
umask "$ORIG_UMASK"
if [[ $VALIDATE_VIA_DNS == "true" ]]; then
# Tidy up DNS entries if things failed part way though.
shopt -s nullglob
for dnsfile in $TEMP_DIR/dns_verify/*; do
# shellcheck source=/dev/null
. "$dnsfile"
debug "attempting to clean up DNS entry for $d"
eval "$DNS_DEL_COMMAND" "$d" "$auth_key"
done
shopt -u nullglob
fi
if [[ ! -z "$DOMAIN_DIR" ]]; then
rm -rf "${TEMP_DIR:?}"
fi
if [[ ! -z "$TEMP_UPGRADE_FILE" ]] && [[ -f "$TEMP_UPGRADE_FILE" ]]; then
rm -f "$TEMP_UPGRADE_FILE"
fi
}
copy_file_to_location() { # copies a file, using scp, sftp or ftp if required.
cert=$1 # descriptive name, just used for display
from=$2 # current file location
to=$3 # location to move file to.
IFS=\; read -r -a copy_locations <<<"$3"
for to in "${copy_locations[@]}"; do
info "copying $cert to $to"
debug "copying from $from to $to"
if [[ "${to:0:4}" == "ssh:" ]] ; then
debug "using scp scp -q $from ${to:4}"
if ! scp -q "$from" "${to:4}" >/dev/null 2>&1 ; then
error_exit "problem copying file to the server using scp.
scp $from ${to:4}"
fi
debug "userid $TOKEN_USER_ID"
if [[ "$cert" == "challenge token" ]] && [[ ! -z "$TOKEN_USER_ID" ]]; then
servername=$(echo "$to" | awk -F":" '{print $2}')
tofile=$(echo "$to" | awk -F":" '{print $3}')
debug "servername $servername"
debug "file $tofile"
# shellcheck disable=SC2029
ssh "$servername" "chown $TOKEN_USER_ID $tofile"
fi
elif [[ "${to:0:4}" == "ftp:" ]] ; then
if [[ "$cert" != "challenge token" ]] ; then
error_exit "ftp is not a sercure method for copying certificates or keys"
fi
debug "using ftp to copy the file from $from"
ftpuser=$(echo "$to"| awk -F: '{print $2}')
ftppass=$(echo "$to"| awk -F: '{print $3}')
ftphost=$(echo "$to"| awk -F: '{print $4}')
ftplocn=$(echo "$to"| awk -F: '{print $5}')
ftpdirn=$(dirname "$ftplocn")
ftpfile=$(basename "$ftplocn")
fromdir=$(dirname "$from")
fromfile=$(basename "$from")
debug "ftp user=$ftpuser - pass=$ftppass - host=$ftphost dir=$ftpdirn file=$ftpfile"
debug "from dir=$fromdir file=$fromfile"
ftp -n <<- _EOF
open $ftphost
user $ftpuser $ftppass
cd $ftpdirn
lcd $fromdir
put $fromfile
_EOF
elif [[ "${to:0:5}" == "sftp:" ]] ; then
debug "using sftp to copy the file from $from"
ftpuser=$(echo "$to"| awk -F: '{print $2}')
ftppass=$(echo "$to"| awk -F: '{print $3}')
ftphost=$(echo "$to"| awk -F: '{print $4}')
ftplocn=$(echo "$to"| awk -F: '{print $5}')
ftpdirn=$(dirname "$ftplocn")
ftpfile=$(basename "$ftplocn")
fromdir=$(dirname "$from")
fromfile=$(basename "$from")
debug "sftp user=$ftpuser - pass=$ftppass - host=$ftphost dir=$ftpdirn file=$ftpfile"
debug "from dir=$fromdir file=$fromfile"
sshpass -p "$ftppass" sftp "$ftpuser@$ftphost" <<- _EOF
cd $ftpdirn
lcd $fromdir
put $fromfile
_EOF
else
if ! mkdir -p "$(dirname "$to")" ; then
error_exit "cannot create ACL directory $(basename "$to")"
fi
if [[ "$GETSSL_IGNORE_CP_PRESERVE" == "true" ]]; then
if ! cp "$from" "$to" ; then
error_exit "cannot copy $from to $to"
fi
else
if ! cp -p "$from" "$to" ; then
error_exit "cannot copy $from to $to"
fi
fi
if [[ "$cert" == "challenge token" ]] && [[ ! -z "$TOKEN_USER_ID" ]]; then
chown "$TOKEN_USER_ID" "$to"
fi
fi
debug "copied $from to $to"
done
}
create_csr() { # create a csr using a given key (if it doesn't already exist)
csr_file=$1
csr_key=$2
key_type=$3
# check if domain csr exists - if not then create it
if [[ -s "$csr_file" ]]; then
debug "domain csr exists at - $csr_file"
# check all domains in config are in csr
if [[ "$IGNORE_DIRECTORY_DOMAIN" == "true" ]]; then
alldomains=$(echo "$SANS" | sed -e 's/ //g; s/,$//; y/,/\n/' | sort -u)
else
alldomains=$(echo "$DOMAIN,$SANS" | sed -e 's/ //g; s/,$//; y/,/\n/' | sort -u)
fi
domains_in_csr=$(gmssl req -text -noout -in "$csr_file" \
| sed -n -e 's/^ *Subject: .* CN=\([A-Za-z0-9.-]*\).*$/\1/p; /^ *DNS:.../ { s/ *DNS://g; y/,/\n/; p; }' \
| sort -u)
for d in $alldomains; do
if [[ "$(echo "${domains_in_csr}"| grep "^${d}$")" != "${d}" ]]; then
info "existing csr at $csr_file does not contain ${d} - re-create-csr"\
".... $(echo "${domains_in_csr}"| grep "^${d}$")"
_RECREATE_CSR=1
fi
done
# check all domains in csr are in config
if [[ "$alldomains" != "$domains_in_csr" ]]; then
info "existing csr at $csr_file does not have the same domains as the config - re-create-csr"
_RECREATE_CSR=1
fi
fi
# end of ... check if domain csr exists - if not then create it
# if CSR does not exist, or flag set to recreate, then create csr
if [[ ! -s "$csr_file" ]] || [[ "$_RECREATE_CSR" == "1" ]]; then
info "creating domain csr - $csr_file"
# create a temporary config file, for portability.
tmp_conf=$(mktemp)
cat "$SSLCONF" > "$tmp_conf"
printf "[SAN]\n%s" "$SANLIST" >> "$tmp_conf"
# add OCSP Must-Staple to the domain csr
# if gmssl version >= 1.3.0 one can also use "tlsfeature = status_request"
if [[ "$OCSP_MUST_STAPLE" == "true" ]]; then
printf "\n1.3.6.1.5.5.7.1.24 = DER:30:03:02:01:05" >> "$tmp_conf"
fi
case "$key_type" in
sm2p256v1)
#echo "use the gmssl to generate a csr and use the absolute path for config"
#echo "so you should cp the san.cnf and /demoCA to the current directory"
#pwd
#echo $tmp_conf
#gmssl req -new -sm3 -key "$csr_key" -subj /C=CN/ST=HB/L=SJZ/O=CCIT/OU=CCIT/CN=${DOMAIN}/emailAddress=fym0121@163.com -reqexts SAN -config "$tmp_conf" > "$csr_file";;
gmssl req -new -sm3 -key "$csr_key" -subj "$CSR_SUBJECT" -reqexts SAN -config "$tmp_conf" > "$csr_file";;
#gmssl req -new -sm3 -key "$csr_key" -config san.cnf -subj "$CSR_SUBJECT" > "$csr_file";;
*)
gmssl req -new -sha256 -key "$csr_key" -subj "$CSR_SUBJECT" -reqexts SAN -config "$tmp_conf" > "$csr_file";;
esac
rm -f "$tmp_conf"
fi
}
create_key() { # create a domain key (if it doesn't already exist)
key_type=$1 # domain key type
key_loc=$2 # domain key location
key_len=$3 # domain key length - for rsa keys.
# check if key exists, if not then create it.
if [[ -s "$key_loc" ]]; then
debug "domain key exists at $key_loc - skipping generation"
# ideally need to check validity of domain key
else
umask 077
info "creating key - $key_loc"
case "$key_type" in
rsa)
gmssl genrsa "$key_len" > "$key_loc";;
prime256v1|secp384r1|secp521r1)
gmssl ecparam -genkey -name "$key_type" > "$key_loc";;
sm2p256v1)
echo "generate a sm2p256v1 key using gmssl"
#gmssl ecparam -genkey -name "$key_type" > "$key_loc";;
gmssl ecparam -genkey -name prime256v1 > "$key_loc";;
*)
error_exit "unknown private key algorithm type $key_loc";;
esac
umask "$ORIG_UMASK"
# remove csr on generation of new domain key
if [[ -e "${key_loc::-4}.csr" ]]; then
rm -f "${key_loc::-4}.csr"
fi
fi
}
date_epoc() { # convert the date into epoch time
if [[ "$os" == "bsd" ]]; then
date -j -f "%b %d %T %Y %Z" "$1" +%s
elif [[ "$os" == "mac" ]]; then
date -j -f "%b %d %T %Y %Z" "$1" +%s
elif [[ "$os" == "busybox" ]]; then
de_ld=$(echo "$1" | awk '{print $1 $2 $3 $4}')
date -D "%b %d %T %Y" -d "$de_ld" +%s
else
date -d "$1" +%s
fi
}
date_fmt() { # format date from epoc time to YYYY-MM-DD
if [[ "$os" == "bsd" ]]; then #uses older style date function.
date -j -f "%s" "$1" +%F
elif [[ "$os" == "mac" ]]; then # MAC OSX uses older BSD style date.
date -j -f "%s" "$1" +%F
else
date -d "@$1" +%F
fi
}
date_renew() { # calculates the renewal time in epoch
date_now_s=$( date +%s )
echo "$((date_now_s + RENEW_ALLOW*24*60*60))"
}
debug() { # write out debug info if the debug flag has been set
if [[ ${_USE_DEBUG} -eq 1 ]]; then
echo " "
echo "$@"
fi
}
error_exit() { # give error message on error exit
echo -e "${PROGNAME}: ${1:-"Unknown Error"}" >&2
clean_up
exit 1
}
get_auth_dns() { # get the authoritative dns server for a domain (sets primary_ns )
gad_d="$1" # domain name
gad_s="$PUBLIC_DNS_SERVER" # start with PUBLIC_DNS_SERVER
if [[ "$os" == "cygwin" ]]; then
all_auth_dns_servers=$(nslookup -type=soa "${d}" ${PUBLIC_DNS_SERVER} 2>/dev/null \
| grep "primary name server" \
| awk '{print $NF}')
if [[ -z "$all_auth_dns_servers" ]]; then
error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config"
fi
primary_ns="$all_auth_dns_servers"
return
fi
if [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then
if [[ -z "$gad_s" ]]; then #checking for CNAMEs
res=$($DNS_CHECK_FUNC CNAME "$gad_d"| grep "^$gad_d")
else
res=$($DNS_CHECK_FUNC CNAME "$gad_d" "@$gad_s"| grep "^$gad_d")
fi
if [[ ! -z "$res" ]]; then # domain is a CNAME so get main domain
gad_d=$(echo "$res"| awk '{print $5}' |sed 's/\.$//g')
fi
if [[ -z "$gad_s" ]]; then #checking for CNAMEs
res=$($DNS_CHECK_FUNC NS "$gad_d"| grep "^$gad_d")
else
res=$($DNS_CHECK_FUNC NS "$gad_d" "@$gad_s"| grep "^$gad_d")
fi
if [[ -z "$res" ]]; then
error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config"
else
all_auth_dns_servers=$(echo "$res" | awk '$4 ~ "NS" {print $5}' | sed 's/\.$//g'|tr '\n' ' ')
fi
if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then
primary_ns="$all_auth_dns_servers"
else
primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}')
fi
return
fi
if [[ "$DNS_CHECK_FUNC" == "host" ]]; then
if [[ -z "$gad_s" ]]; then
res=$($DNS_CHECK_FUNC -t NS "$gad_d"| grep "name server")
else
res=$($DNS_CHECK_FUNC -t NS "$gad_d" "$gad_s"| grep "name server")
fi
if [[ -z "$res" ]]; then
error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config"
else
all_auth_dns_servers=$(echo "$res" | awk '{print $4}' | sed 's/\.$//g'|tr '\n' ' ')
fi
if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then
primary_ns="$all_auth_dns_servers"
else
primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}')
fi
return
fi
res=$(nslookup -debug=1 -type=soa -type=ns "$gad_d" ${gad_s})
if [[ "$(echo "$res" | grep -c "Non-authoritative")" -gt 0 ]]; then
# this is a Non-authoritative server, need to check for an authoritative one.
gad_s=$(echo "$res" | awk '$2 ~ "nameserver" {print $4; exit }' |sed 's/\.$//g')
if [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then
# if domain name doesn't exist, then find auth servers for next level up
gad_s=$(echo "$res" | awk '$1 ~ "origin" {print $3; exit }')
gad_d=$(echo "$res" | awk '$1 ~ "->" {print $2; exit}')
fi
fi
if [[ -z "$gad_s" ]]; then
res=$(nslookup -debug=1 -type=soa -type=ns "$gad_d")
else
res=$(nslookup -debug=1 -type=soa -type=ns "$gad_d" "${gad_s}")
fi
if [[ "$(echo "$res" | grep -c "canonical name")" -gt 0 ]]; then
gad_d=$(echo "$res" | awk ' $2 ~ "canonical" {print $5; exit }' |sed 's/\.$//g')
elif [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then
gad_s=$(echo "$res" | awk ' $1 ~ "origin" {print $3; exit }')
gad_d=$(echo "$res"| awk '$1 ~ "->" {print $2; exit}')
fi
all_auth_dns_servers=$(nslookup -type=soa -type=ns "$gad_d" "$gad_s" \
| awk ' $2 ~ "nameserver" {print $4}' \
| sed 's/\.$//g'| tr '\n' ' ')
if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then
primary_ns="$all_auth_dns_servers"
else
primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}')
fi
}
get_certificate() { # get certificate for csr, if all domains validated.
gc_csr=$1 # the csr file
gc_certfile=$2 # The filename for the certificate
gc_cafile=$3 # The filename for the CA certificate
der=$(gmssl req -in "$gc_csr" -outform DER | urlbase64)
debug "der $der"
send_signed_request "$URL_new_cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64"
# convert certificate information into correct format and save to file.
CertData=$(awk ' $1 ~ "^Location" {print $2}' "$CURL_HEADER" |tr -d '\r')
debug "certdata location = $CertData"
if [[ "$CertData" ]] ; then
echo -----BEGIN CERTIFICATE----- > "$gc_certfile"
curl --silent "$CertData" | gmssl base64 -e >> "$gc_certfile"
echo -----END CERTIFICATE----- >> "$gc_certfile"
info "Certificate saved in $CERT_FILE"
fi
# If certificate wasn't a valid certificate, error exit.
if [[ -z "$CertData" ]] ; then
response2=$(echo "$response" | fold -w64 |gmssl base64 -d)
debug "response was $response"
error_exit "Sign failed: $(echo "$response2" | grep "detail")"
fi
# get a copy of the CA certificate.
IssuerData=$(grep -i '^Link' "$CURL_HEADER" \
| cut -d " " -f 2\
| cut -d ';' -f 1 \
| sed 's/<//g' \
| sed 's/>//g')
if [[ "$IssuerData" ]] ; then
echo -----BEGIN CERTIFICATE----- > "$gc_cafile"
curl --silent "$IssuerData" | gmssl base64 -e >> "$gc_cafile"
echo -----END CERTIFICATE----- >> "$gc_cafile"
info "The intermediate CA cert is in $gc_cafile"
fi
}
get_cr() { # get curl response
url="$1"
debug url "$url"
response=$(curl --silent "$url")
ret=$?
debug response "$response"
code=$(json_get "$response" status)
debug code "$code"
debug "get_cr return code $ret"
return $ret
}
get_os() { # function to get the current Operating System
uname_res=$(uname -s)
if [[ $(date -h 2>&1 | grep -ic busybox) -gt 0 ]]; then
os="busybox"
elif [[ ${uname_res} == "Linux" ]]; then
os="linux"
elif [[ ${uname_res} == "FreeBSD" ]]; then
os="bsd"
elif [[ ${uname_res} == "Darwin" ]]; then
os="mac"
elif [[ ${uname_res:0:6} == "CYGWIN" ]]; then
os="cygwin"
elif [[ ${uname_res:0:5} == "MINGW" ]]; then
os="mingw"
else
os="unknown"
fi
debug "detected os type = $os"
}
get_signing_params() { # get signing parameters from key
skey=$1
info $skey
if gmssl rsa -in "${skey}" -noout 2>/dev/null ; then # RSA key
pub_exp64=$(gmssl rsa -in "${skey}" -noout -text \
| grep publicExponent \
| grep -oE "0x[a-f0-9]+" \
| cut -d'x' -f2 \
| hex2bin \
| urlbase64)
pub_mod64=$(gmssl rsa -in "${skey}" -noout -modulus \
| cut -d'=' -f2 \
| hex2bin \
| urlbase64)
jwk='{"e":"'"${pub_exp64}"'","kty":"RSA","n":"'"${pub_mod64}"'"}'
jwkalg="RS256"
signalg="sha256"
elif gmssl ec -in "${skey}" -noout 2>/dev/null ; then # Elliptic curve key.
echo "!!!! ec"
crv="$(gmssl ec -in "$skey" -noout -text 2>/dev/null | awk '$2 ~ "CURVE:" {print $3}')"
if [[ -z "$crv" ]]; then
gsp_keytype="$(gmssl ec -in "$skey" -noout -text 2>/dev/null \
| grep "^ASN1 OID:" \
| awk '{print $3}')"
case "$gsp_keytype" in
prime256v1) crv="P-256" ;;
secp384r1) crv="P-384" ;;
secp521r1) crv="P-521" ;;
*) error_exit "invalid curve algorithm type $gsp_keytype";;
esac
fi
case "$crv" in
P-256) jwkalg="ES256" ; signalg="sha256" ;;
P-384) jwkalg="ES384" ; signalg="sha384" ;;
P-521) jwkalg="ES512" ; signalg="sha512" ;;
*) error_exit "invalid curve algorithm type $crv";;
esac
pubtext="$(gmssl ec -in "$skey" -noout -text 2>/dev/null \
| awk '/^pub:/{p=1;next}/^ASN1 OID:/{p=0}p' \
| tr -d ": \n\r")"
mid=$(( (${#pubtext} -2) / 2 + 2 ))
debug "pubtext = $pubtext"
x64=$(echo "$pubtext" | cut -b 3-$mid | hex2bin | urlbase64)
y64=$(echo "$pubtext" | cut -b $((mid+1))-${#pubtext} | hex2bin | urlbase64)
jwk='{"crv":"'"$crv"'","kty":"EC","x":"'"$x64"'","y":"'"$y64"'"}'
debug "jwk $jwk"
else
error_exit "Invalid key file"
fi
thumbprint="$(printf "%s" "$jwk" | gmssl dgst -sha256 -binary | urlbase64)"
debug "jwk alg = $jwkalg"
debug "jwk = $jwk"
debug "thumbprint $thumbprint"
}
graceful_exit() { # normal exit function.
clean_up
exit
}
help_message() { # print out the help message
cat <<- _EOF_
$PROGNAME ver. $VERSION
Obtain SSL certificates from the letsencrypt.org ACME server
$(usage)
Options:
-a, --all Check all certificates
-d, --debug Outputs debug information
-c, --create Create default config files
-f, --force Force renewal of cert (overrides expiry checks)
-h, --help Display this help message and exit
-q, --quiet Quiet mode (only outputs on error, success of new cert, or getssl was upgraded)
-Q, --mute Like -q, but mutes notification about successful upgrade
-r, --revoke "cert" "key" [CA_server] Revoke a certificate (the cert and key are required)
-u, --upgrade Upgrade getssl if a more recent version is available
-k, --keep "#" Maximum amount of old getssl versions to keep when upgrading
-U, --nocheck Do not check if a more recent version is available
-w working_dir "Working directory"
_EOF_
}
hex2bin() { # Remove spaces, add leading zero, escape as hex string ensuring no trailing new line char
# printf -- "$(cat | os_esed -e 's/[[:space:]]//g' -e 's/^(.(.{2})*)$/0\1/' -e 's/(.{2})/\\x\1/g')"
echo -e -n "$(cat | os_esed -e 's/[[:space:]]//g' -e 's/^(.(.{2})*)$/0\1/' -e 's/(.{2})/\\x\1/g')"
}
info() { # write out info as long as the quiet flag has not been set.
if [[ ${_QUIET} -eq 0 ]]; then
echo "$@"
fi
}
json_get() { # get the value corresponding to $2 in the JSON passed as $1.
# remove newlines, so it's a single chunk of JSON
json_data=$( echo "$1" | tr '\n' ' ')
# if $3 is defined, this is the section which the item is in.
if [[ ! -z "$3" ]]; then
jg_section=$(echo "$json_data" | awk -F"[}]" '{for(i=1;i<=NF;i++){if($i~/\"'"${3}"'\"/){print $i}}}')
if [[ "$2" == "uri" ]]; then
jg_subsect=$(echo "$jg_section" | awk -F"[,]" '{for(i=1;i<=NF;i++){if($i~/\"'"${2}"'\"/){print $(i)}}}')
jg_result=$(echo "$jg_subsect" | awk -F'"' '{print $4}')
else
jg_result=$(echo "$jg_section" | awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/\"'"${2}"'\"/){print $(i+1)}}}')
fi
else
jg_result=$(echo "$json_data" |awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/\"'"${2}"'\"/){print $(i+1)}}}')
fi
# check number of quotes