├── .fmf └── version ├── LICENSE ├── README.md └── certgen ├── Makefile ├── lib.sh ├── main.fmf └── runtest.sh /.fmf/version: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Bash library for generating arbitrary certificates using the openssl command line tools. 2 | 3 | Usable also as a [beakerlib](https://git.fedorahosted.org/cgit/beakerlib.git) library. 4 | 5 | See certgen/runtest.sh for usage examples. 6 | -------------------------------------------------------------------------------- /certgen/Makefile: -------------------------------------------------------------------------------- 1 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2 | # 3 | # Makefile of /CoreOS/openssl/Library/certgen 4 | # Description: Library for creating X.509 certificates for any use 5 | # Author: Hubert Kario 6 | # 7 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | # 9 | # Copyright (c) 2014 Red Hat, Inc. 10 | # 11 | # This copyrighted material is made available to anyone wishing 12 | # to use, modify, copy, or redistribute it subject to the terms 13 | # and conditions of the GNU General Public License version 2. 14 | # 15 | # This program is distributed in the hope that it will be 16 | # useful, but WITHOUT ANY WARRANTY; without even the implied 17 | # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 18 | # PURPOSE. See the GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public 21 | # License along with this program; if not, write to the Free 22 | # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 23 | # Boston, MA 02110-1301, USA. 24 | # 25 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 26 | 27 | export TEST=/CoreOS/openssl/Library/certgen 28 | export TESTVERSION=2.0 29 | 30 | BUILT_FILES= 31 | 32 | FILES=$(METADATA) runtest.sh Makefile lib.sh 33 | 34 | .PHONY: all install download clean 35 | 36 | run: $(FILES) build 37 | ./runtest.sh 38 | 39 | build: $(BUILT_FILES) 40 | test -x runtest.sh || chmod a+x runtest.sh 41 | 42 | clean: 43 | rm -f *~ $(BUILT_FILES) 44 | 45 | 46 | include /usr/share/rhts/lib/rhts-make.include 47 | 48 | $(METADATA): Makefile 49 | @echo "Owner: Hubert Kario " > $(METADATA) 50 | @echo "Name: $(TEST)" >> $(METADATA) 51 | @echo "TestVersion: $(TESTVERSION)" >> $(METADATA) 52 | @echo "Path: $(TEST_DIR)" >> $(METADATA) 53 | @echo "Description: Library for creating X.509 certificates for any use" >> $(METADATA) 54 | @echo "Type: Library" >> $(METADATA) 55 | @echo "TestTime: 45m" >> $(METADATA) 56 | @echo "RunFor: openssl" >> $(METADATA) 57 | @echo "Requires: openssl oqsprovider" >> $(METADATA) 58 | @echo "Provides: library(openssl/certgen)" >> $(METADATA) 59 | @echo "RhtsRequires: openssl" >> $(METADATA) 60 | @echo "Priority: Normal" >> $(METADATA) 61 | @echo "License: GPLv2" >> $(METADATA) 62 | @echo "Confidential: no" >> $(METADATA) 63 | @echo "Destructive: no" >> $(METADATA) 64 | 65 | rhts-lint $(METADATA) 66 | -------------------------------------------------------------------------------- /certgen/lib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # vim: dict+=/usr/share/beakerlib/dictionary.vim cpt=.,w,b,u,t,i,k 3 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4 | # 5 | # lib.sh of /CoreOS/openssl/Library/certgen 6 | # Description: Library for creating X.509 certificates for any use 7 | # Author: Hubert Kario 8 | # 9 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 10 | # 11 | # Copyright (c) 2014 Red Hat, Inc. 12 | # 13 | # This copyrighted material is made available to anyone wishing 14 | # to use, modify, copy, or redistribute it subject to the terms 15 | # and conditions of the GNU General Public License version 2. 16 | # 17 | # This program is distributed in the hope that it will be 18 | # useful, but WITHOUT ANY WARRANTY; without even the implied 19 | # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 20 | # PURPOSE. See the GNU General Public License for more details. 21 | # 22 | # You should have received a copy of the GNU General Public 23 | # License along with this program; if not, write to the Free 24 | # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 25 | # Boston, MA 02110-1301, USA. 26 | # 27 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 28 | # library-prefix = x509 29 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 30 | 31 | true <<'=cut' 32 | =pod 33 | 34 | =head1 NAME 35 | 36 | openssl/certgen - Library for creating X.509 certificates for any use 37 | 38 | =head1 DESCRIPTION 39 | 40 | This is a library aimed at making X.509 certificate creation simple without 41 | sacrificing advanced functionality. 42 | 43 | Typical use cases won't require any additional options and even complex 44 | PKI structure for TLS can be created with just few commands. 45 | 46 | Note that it assumes that all generated keys and certificates can be used as 47 | CAs (even if they have extensions that specifically forbid it). Because of 48 | that, every single key pair is placed in a separate directory named after its 49 | alias. 50 | 51 | This library uses I for option parsing, as such the order of options 52 | to functions is not significant unless noted. 53 | 54 | =cut 55 | 56 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 57 | # Variables 58 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 59 | 60 | true <<'=cut' 61 | =pod 62 | 63 | =head1 VARIABLES 64 | 65 | Below is the list of global variables. If they are already defined in 66 | environment when the library is loaded, they will NOT be overwritten. 67 | 68 | =over 69 | 70 | =item B 71 | 72 | Name of the configuration file for CA operation and CSR generation. 73 | F by default. 74 | 75 | =item B 76 | 77 | Name of the file with information about all the previously generated 78 | certificates. F by default. 79 | 80 | =item B 81 | 82 | Name of the file with next available serial number. F by default. 83 | 84 | =item B 85 | 86 | Name of file in which certificates will be placed. F by default 87 | 88 | =item B 89 | 90 | Name of file in which certificate revocation list will be placed. F by 91 | default 92 | 93 | =item B 94 | 95 | Name of the file with next available CRL number. F by default. 96 | 97 | =item B 98 | 99 | Name of the file with certificate signing request. F by default. 100 | 101 | =item B 102 | 103 | Name of the file where certificates encoded in DER format will be placed. 104 | F by default. Note that those files are generated on demand only by 105 | B function. 106 | 107 | =item B 108 | 109 | Name of the file where private keys encoded in DER format will be placed. 110 | F by default. Note that those file are generated on demand only by 111 | B function. 112 | 113 | =item B 114 | 115 | The first CRL number that will be used when generating a CRL. 116 | Used when the CA generates a CRL. Must be a valid, nonegative hex number. 117 | C<01> by default. 118 | 119 | =item B 120 | 121 | The first serial number that will be assigned to the certificate. 122 | Used when the CA self signes its certificate or when intermediate CA signes 123 | it first certificate. Must be a valid, nonegative hex number. 124 | C<01> by default. 125 | 126 | =item B 127 | 128 | Formatting required by the I tool for generating certificates. 129 | For RHEL6 and later it should be set to C<+%Y%m%d%H%M%SZ>. 130 | For RHEL5 it should be set to C<+%y%m%d%H%M%SZ>. 131 | 132 | Defaults to version supported by locally installed OpenSSL 133 | 134 | =item B 135 | Name of the file where private keys in PKCS#8 format will be placed. 136 | F by default. Note that those files are generated on demand 137 | only by B function. 138 | 139 | =item B 140 | Name of the file where private keys in PKCS#8 DER format will be placed. 141 | F by default. Note that those files are generated on demand only 142 | by B function. 143 | 144 | =item B 145 | 146 | Name of the file where certificates and keys in PKCS#12 format will be placed. 147 | F by default. Not that those files are generated on demand only 148 | by B and B functions. 149 | 150 | =item B 151 | 152 | Name of file with private and public key. F by default 153 | 154 | =item B 155 | 156 | Path to the openssl tool used as the backend. F by default. 157 | 158 | =back 159 | 160 | Note that changing the values of above variables between running different 161 | functions may cause the library to misbehave. 162 | 163 | =cut 164 | 165 | x509PKEY=${x509PKEY:-key.pem} 166 | x509DERKEY=${x509DERKEY:-key.key} 167 | x509CERT=${x509CERT:-cert.pem} 168 | x509CRL=${x509CRL:-crl.pem} 169 | x509CRLNUMBER=${x509CRLNUMBER:-crlnumber} 170 | x509DERCERT=${x509DERCERT:-cert.crt} 171 | x509PKCS8KEY=${x509PKCS8KEY:-pkcs8.pem} 172 | x509PKCS8DERKEY=${x509PKCS8DERKEY:-pkcs8.key} 173 | x509PKCS12=${x509PKCS12:-bundle.p12} 174 | x509CSR=${x509CSR:-request.csr} 175 | x509CACNF=${x509CACNF:-ca.cnf} 176 | x509CAINDEX=${x509CAINDEX:-index.txt} 177 | x509CASERIAL=${x509CASERIAL:-serial} 178 | x509FIRSTCRLNUMBER=${x509FIRSTCRLNUMBER:-01} 179 | x509FIRSTSERIAL=${x509FIRSTSERIAL:-01} 180 | x509OPENSSL=${x509OPENSSL:-openssl} 181 | if ${x509OPENSSL} version | grep -q '0[.]9[.].'; then 182 | x509FORMAT=${x509FORMAT:-+%y%m%d%H%M%SZ} 183 | else 184 | x509FORMAT=${x509FORMAT:-+%Y%m%d%H%M%SZ} 185 | fi 186 | 187 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 188 | # Internal Functions 189 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 190 | 191 | __INTERNAL_x509GenConfig() { 192 | 193 | # variable that has the DN broken up by items, most significant first 194 | declare -a dn 195 | # hash used to sign the certificate 196 | if ${x509OPENSSL} version | grep -q '0[.]9[.]7'; then 197 | local md="sha1" 198 | else 199 | local md="sha256" 200 | fi 201 | # current time in seconds from UNIX epoch 202 | local now=$(env date '+%s') 203 | # date before which the certificate is not valid 204 | local notBefore="" 205 | # date after which the certificate is not valid 206 | local notAfter="" 207 | # Basic Key Usage to set 208 | local basicKeyUsage="" 209 | # Basic Constraints to set 210 | local basicConstraints="" 211 | # CRL distribution points URL 212 | local crlDistributionPoints="" 213 | # value of the Subject Key Identifier extension 214 | local subjectKeyIdentifier="" 215 | # whatever to generate Authority Key Identifier extension 216 | local authorityKeyIdentifier="" 217 | # variable that has the Subject Alternative Name split by lines 218 | declare -a subjectAltName 219 | # variable to set when the Subject Alternative Name is to be marked critical 220 | local subjectAltNameCritical="" 221 | # variable to store Authority Info Access (OCSP responder and CA file loc.) 222 | declare -a authorityInfoAccess 223 | # value of the Extended Key Usage extension 224 | local extendedKeyUsage="" 225 | # list of all the arbitrary X509v3 extensions 226 | declare -a x509v3Extension 227 | 228 | # 229 | # parse options 230 | # 231 | 232 | local TEMP=$(getopt -o t: -l dn: -l md: -l notBefore: -l notAfter: \ 233 | -l basicKeyUsage: \ 234 | -l basicConstraints: \ 235 | -l crlDistributionPoints: \ 236 | -l subjectKeyIdentifier:: \ 237 | -l authorityKeyIdentifier: \ 238 | -l subjectAltName: \ 239 | -l subjectAltNameCritical \ 240 | -l authorityInfoAccess: \ 241 | -l extendedKeyUsage: \ 242 | -l x509v3Extension: \ 243 | -n x509GenConfig -- "$@") 244 | if [ $? -ne 0 ]; then 245 | echo "x509GenConfig: can't parse options" >&2 246 | return 1 247 | fi 248 | 249 | eval set -- "$TEMP" 250 | 251 | while true ; do 252 | case "$1" in 253 | --dn) dn=("${dn[@]}" "$2"); shift 2 254 | ;; 255 | --md) md="$2"; shift 2 256 | ;; 257 | --notBefore) notBefore="$2"; shift 2 258 | ;; 259 | --notAfter) notAfter="$2"; shift 2 260 | ;; 261 | --basicKeyUsage) basicKeyUsage="$2"; shift 2 262 | ;; 263 | --basicConstraints) basicConstraints="$2"; shift 2 264 | ;; 265 | --crlDistributionPoints) crlDistributionPoints="$2"; shift 2 266 | ;; 267 | --subjectKeyIdentifier) 268 | case "$2" in 269 | '') 270 | subjectKeyIdentifier="hash" 271 | ;; 272 | *) 273 | subjectKeyIdentifier="$2" 274 | ;; 275 | esac 276 | shift 2 277 | ;; 278 | --authorityKeyIdentifier) authorityKeyIdentifier="$2"; shift 2 279 | ;; 280 | --subjectAltName) subjectAltName=("${subjectAltName[@]}" "$2"); shift 2 281 | ;; 282 | --authorityInfoAccess) authorityInfoAccess=("${authorityInfoAccess[@]}" "$2"); shift 2 283 | ;; 284 | --extendedKeyUsage) extendedKeyUsage="$2"; shift 2 285 | ;; 286 | --x509v3Extension) x509v3Extension="$2"; shift 2 287 | ;; 288 | --subjectAltNameCritical) subjectAltNameCritical="critical,"; 289 | shift 1 290 | ;; 291 | --) shift 1 292 | break 293 | ;; 294 | *) echo "x509GenConfig: Unknown option: \"$1\"" >&2 295 | return 1 296 | esac 297 | done 298 | 299 | local kAlias="$1" 300 | 301 | # 302 | # sanity check 303 | # 304 | 305 | if [ ! -e "$kAlias" ]; then 306 | echo "x509GenConfig: to gen config, the directory must be present" >&2 307 | return 1 308 | fi 309 | if [ ${#dn[@]} -lt 1 ]; then 310 | echo "x509GenConfig: at least one element in DN must be present" >&2 311 | return 1 312 | fi 313 | 314 | # 315 | # process options 316 | # 317 | 318 | if [ -z "$notBefore" ]; then 319 | notBefore="now" 320 | fi 321 | notBefore=$(env date -d "$notBefore" -u $x509FORMAT) 322 | if [ $? -ne 0 ]; then 323 | echo "x509GenConfig: notBefore date value is invalid" >&2 324 | return 1 325 | fi 326 | 327 | if [ -z "$notAfter" ]; then 328 | notAfter="1 year" 329 | fi 330 | notAfter=$(env date -d "$notAfter" -u $x509FORMAT) 331 | if [ $? -ne 0 ]; then 332 | echo "x509GenConfig: notAfter date value is invalid" >&2 333 | return 1 334 | fi 335 | 336 | # 337 | # for Ed25519 we can't specify a hash as it is built-in 338 | # 339 | if ${x509OPENSSL} pkey -in "$kAlias/$x509PKEY" -noout -text 2> /dev/null | grep -qE '^(ED25519|ED448)'; then 340 | md="null" 341 | fi 342 | 343 | # in openssl 1.1.1 the oid was renamed to uppercase and the 344 | # lower case stopped working, so fix it 345 | if ! ${x509OPENSSL} version | grep -Eq '0[.]9[.]|1[.]0[.]'; then 346 | extendedKeyUsage="${extendedKeyUsage/ocspSigning/OCSPSigning}" 347 | fi 348 | 349 | # 350 | # generate config 351 | # 352 | 353 | touch "$kAlias/$x509CAINDEX" 354 | echo "unique_subject = no" >> "$kAlias/$x509CAINDEX.attr" 355 | if [ ! -e $kAlias/$x509CASERIAL ]; then 356 | echo $x509FIRSTSERIAL > $kAlias/$x509CASERIAL 357 | fi 358 | 359 | if [ ! -e $kAlias/$x509CRLNUMBER ]; then 360 | echo $x509FIRSTCRLNUMBER > $kAlias/$x509CRLNUMBER 361 | fi 362 | 363 | # OpenSSL 1.1.0 (? 1.1.1 definitely has) has the OID definition 364 | if ${x509OPENSSL} version | grep -Eq '0[.]9[.]|1[.]0[.]'; then 365 | cat > "$kAlias/$x509CACNF" < "$kAlias/$x509CACNF" 375 | fi 376 | cat >> "$kAlias/$x509CACNF" <> "$kAlias/$x509CACNF" 413 | done 414 | 415 | cat >> "$kAlias/$x509CACNF" <> "$kAlias/$x509CACNF" 422 | fi 423 | 424 | if [[ ! -z $crlDistributionPoints ]]; then 425 | echo "crlDistributionPoints =URI:$crlDistributionPoints" >> "$kAlias/$x509CACNF" 426 | fi 427 | 428 | if [[ ! -z $basicKeyUsage ]]; then 429 | echo "keyUsage =$basicKeyUsage" >> "$kAlias/$x509CACNF" 430 | fi 431 | 432 | if [[ ! -z $extendedKeyUsage ]]; then 433 | echo "extendedKeyUsage =$extendedKeyUsage" >> "$kAlias/$x509CACNF" 434 | fi 435 | 436 | # OpenSSL 3.0 and later requires an explicit "none" to not include SPKI 437 | # but earlier versions (0.9.8, 1.0.x, 1.1.x) don't accept "none" as argument 438 | if [[ -z $subjectKeyIdentifier ]] && ! $x509OPENSSL version | grep -qE '0[.]9[.]|1[.][01][.]'; then 439 | echo "subjectKeyIdentifier=none" >> "$kAlias/$x509CACNF" 440 | fi 441 | if [[ ! -z $subjectKeyIdentifier ]]; then 442 | echo "subjectKeyIdentifier=$subjectKeyIdentifier" >> "$kAlias/$x509CACNF" 443 | fi 444 | 445 | # OpenSSL 3.0 and later requires an explicit "none" to not include AKI 446 | # but earlier versions (0.9.8, 1.0.x, 1.1.x) don't accept "none" as argument 447 | if [[ -z $authorityKeyIdentifier ]] && ! $x509OPENSSL version | grep -qE '0[.]9[.]|1[.][01][.]'; then 448 | echo "authorityKeyIdentifier=none" >> "$kAlias/$x509CACNF" 449 | fi 450 | if [[ ! -z $authorityKeyIdentifier ]]; then 451 | echo "authorityKeyIdentifier=$authorityKeyIdentifier" >> "$kAlias/$x509CACNF" 452 | fi 453 | 454 | if [[ ${#subjectAltName[@]} -ne 0 ]]; then 455 | echo "subjectAltName =${subjectAltNameCritical} @alt_name" \ 456 | >> "$kAlias/$x509CACNF" 457 | fi 458 | 459 | if [[ ${#authorityInfoAccess[@]} -ne 0 ]]; then 460 | local aia_val="" 461 | local separator="" 462 | local aia 463 | for aia in "${authorityInfoAccess[@]}"; do 464 | aia_val="${aia_val}${separator}${aia}" 465 | separator="," 466 | done 467 | echo "authorityInfoAccess = $aia_val" >> "$kAlias/$x509CACNF" 468 | fi 469 | 470 | local ext 471 | for ext in "${x509v3Extension[@]}"; do 472 | echo "$ext" >> "$kAlias/$x509CACNF" 473 | done 474 | 475 | # subject alternative name section 476 | 477 | if [[ ${#subjectAltName[@]} -ne 0 ]]; then 478 | echo "" >> "$kAlias/$x509CACNF" 479 | echo "[ alt_name ]" >> "$kAlias/$x509CACNF" 480 | 481 | local name 482 | for name in "${subjectAltName[@]}"; do 483 | echo "$name" >> "$kAlias/$x509CACNF" 484 | done 485 | fi 486 | 487 | } 488 | 489 | # Converts object (to be name constrained) to syntax understood 490 | # by openssl.cnf 491 | __INTERNAL_x509NameToConstraint() { 492 | local name="$1" 493 | local result="" 494 | if echo "$name" | grep -q '[A-Z]\+:.\+'; then 495 | result="$name" 496 | elif echo "$name" | grep -q '^([0-9]{1,3}\.){3}[0-9]{1,3}$'; then 497 | result="IP:$name/255.255.255.255" 498 | else 499 | result="DNS:$name" 500 | fi 501 | echo $result 502 | } 503 | 504 | # Converts array of objects (to be name constrained) to specification 505 | # for openssl.cnf 506 | __INTERNAL_x509NamesToNCs() { 507 | local -a names=("${!1}") 508 | local keyword=$2 509 | local -a constraints 510 | local result="" 511 | local name 512 | for name in "${names[@]}"; do 513 | local constraint=$(__INTERNAL_x509NameToConstraint "$name") 514 | constraints=("${constraints[@]}" "$keyword;$constraint") 515 | done 516 | oldIFS="$IFS" 517 | IFS=, 518 | result="${constraints[*]}" 519 | IFS="$oldIFS" 520 | echo "$result" 521 | } 522 | 523 | 524 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 525 | # Functions 526 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 527 | 528 | true <<'=cut' 529 | =pod 530 | 531 | =head1 FUNCTIONS 532 | 533 | =head2 x509KeyGen() 534 | 535 | Generate new key pair using given algorithm and key size. 536 | By default it generates RSA key of the smallest size aceptable in FIPS mode 537 | (currently 2048 bit). 538 | 539 | =over 4 540 | 541 | B 542 | [B<-t> I] 543 | [B<-s> I] 544 | [B<--params> I] 545 | [B<--conservative>] 546 | [B<--anti-conservative>] 547 | [B<--gen-opts> I] 548 | I 549 | 550 | =back 551 | 552 | =over 553 | 554 | =item B<-t> I 555 | 556 | Type of key pair to generate. Acceptable values are I and I. In 557 | case the script is running on RHEL 6.5, RHEL 7.0, Fedora 19 or later, I 558 | is also supported. For I, OpenSSL 1.1.1 is required. 559 | 560 | I by default. 561 | 562 | =item B<-s> I 563 | 564 | Size of the used key for RSA and DSA. Name of the elliptic curve in case 565 | of ECDSA key. 566 | 567 | By default 2048 bit in case of RSA and DSA and C in case of 568 | ECDSA. 569 | 570 | Other valid names for ECDSA curves can be acquired by running 571 | 572 | openssl ecparam -list_curves 573 | 574 | =item B<--params> I 575 | 576 | Reuse DSA parameters from another certificate (usually the CA that will later 577 | sign the certificate). 578 | 579 | =item B<--conservative> 580 | 581 | Because some implementations incorrectly infer the strength of DSA keys from 582 | the public key value instead of the prime P, they will fail to process 583 | parameters of size smaller than the 1024, 2048 or 3072 bit defined in the 584 | standard. 585 | 586 | With this option both the PQG parameters and the public key value will be 587 | regenerated until the most significant bit for all of the 4 values is set. 588 | 589 | Note that this is just a workaround for RHBZ#1238279 and RHBZ#1238290, and 590 | should not be used by default. 591 | 592 | =item B<--anti-conservative> 593 | 594 | Generate a set of parameters that will fail if the implementation checks the 595 | size of PQG DSA parameters incorrecty - the G parameter won't have its MSB set. 596 | 597 | This is sort-of reverse of --conservative, the default behaviour is to generate 598 | a set of paramters randomly. 599 | 600 | =item B<--gen-opts> I 601 | 602 | Set additional key generation options for non rsa, dsa and ec key generation. 603 | 604 | Example options include I for RSA-PSS keygen. 605 | 606 | =item I 607 | 608 | Name of directory in which the generated key pair will be placed. 609 | The file with key will be named F if the variable I was 610 | not changed. If the directory does not exist it will be created. Please don't 611 | put any files in it as they may be overwritten by running functions. 612 | 613 | =back 614 | 615 | Returns 0 if the key generation was successful. Non zero otherwise. 616 | 617 | =cut 618 | 619 | x509KeyGen() { 620 | 621 | # type of key to generate 622 | local kType="RSA" 623 | # size of key to generate 624 | local kSize="" 625 | # name of key to generate 626 | local kAlias 627 | # name of key alias with parameters to reuse 628 | local paramAlias="" 629 | # name of file with DSA parameters 630 | local dsaParams="" 631 | # whether to gen "safer" parameters 632 | local conservative="False" 633 | # whether to gen unsafe paramters that are known to break interoperability 634 | local incompatible="False" 635 | # additional options for keygen 636 | local genpkeyOpts 637 | declare -a genpkeyOpts 638 | genpkeyOpts=() 639 | 640 | # 641 | # parse options 642 | # 643 | 644 | local TEMP=$(getopt -o t:s: -l params: \ 645 | -l conservative \ 646 | -l anti-conservative \ 647 | -l gen-opts: \ 648 | -n x509KeyGen -- "$@") 649 | if [ $? -ne 0 ]; then 650 | echo "x509KeyGen: can't parse options" >&2 651 | return 1 652 | fi 653 | 654 | eval set -- "$TEMP" 655 | 656 | while true ; do 657 | case "$1" in 658 | -t) kType="$2"; shift 2 659 | ;; 660 | -s) kSize="$2"; shift 2 661 | ;; 662 | --params) paramAlias="$2"; shift 2 663 | ;; 664 | --conservative) conservative="True"; shift 1 665 | ;; 666 | --anti-conservative) incompatible="True"; shift 1 667 | ;; 668 | --gen-opts) genpkeyOpts=("${genpkeyOpts[@]}" \ 669 | "-pkeyopt" "$2") 670 | shift 2 671 | ;; 672 | --) shift 1 673 | break 674 | ;; 675 | *) echo "x509KeyGen: Unknown option: '$1'" >&2 676 | return 1 677 | esac 678 | done 679 | 680 | kAlias="$1" 681 | 682 | # 683 | # sanity check options 684 | # 685 | 686 | #upper case and lower case 687 | kType=$(tr '[:lower:]' '[:upper:]' <<< ${kType}) 688 | kSize=$(tr '[:upper:]' '[:lower:]' <<< ${kSize}) 689 | 690 | if [[ -z $kType ]]; then 691 | echo "x509KeyGen: Key type can't be empty" >&2 692 | return 1 693 | fi 694 | if [[ $kType != "RSA" ]] && [[ $kType != "DSA" ]] \ 695 | && [[ $kType != "ECDSA" ]] && [[ $kType != "RSA-PSS" ]] \ 696 | && [[ $kType != "ED25519" ]] && [[ $kType != "ED448" ]] \ 697 | && ! [[ $kType =~ MLDSA ]] && ! [[ $kType =~ SLHDSA ]] \ 698 | && ! [[ $kType =~ SPHINCS ]] ; then 699 | 700 | echo "x509KeyGen: Unknown key type: $kType" >&2 701 | return 1 702 | fi 703 | if [[ -z $kSize ]]; then 704 | if [[ $kType == "ECDSA" ]]; then 705 | kSize="prime256v1" 706 | else 707 | kSize="2048" 708 | fi 709 | fi 710 | 711 | if [[ $conservative == "True" && $incompatible == "True" ]]; then 712 | echo "x509KeyGen: can't do conservative and anti-conservative at once" >&2 713 | return 1 714 | fi 715 | 716 | if [[ -z $kAlias ]]; then 717 | echo "x509KeyGen: No certificate alias specified" >&2 718 | return 1 719 | fi 720 | 721 | # 722 | # Generate the key 723 | # 724 | 725 | mkdir -p "$kAlias" 726 | 727 | if [[ $kType == "ECDSA" ]]; then 728 | ${x509OPENSSL} ecparam -genkey -name "$kSize" -out "$kAlias/$x509PKEY" 729 | if [ $? -ne 0 ]; then 730 | echo "x509KeyGen: Key generation failed" >&2 731 | return 1 732 | fi 733 | elif [[ $kType == "DSA" ]]; then 734 | if [[ -z $paramAlias ]]; then 735 | while true; do 736 | rm -f "$kAlias/dsa_params.pem" 737 | ${x509OPENSSL} dsaparam -out "$kAlias/dsa_params.pem" "$kSize" 738 | if [ $? -ne 0 ]; then 739 | echo "x509KeyGen: Parameter generation failed" >&2 740 | return 1 741 | fi 742 | if [[ $conservative == "False" && $incompatible == "False" ]]; then 743 | break 744 | fi 745 | if [[ $conservative == "True" ]] && 746 | ${x509OPENSSL} dsaparam -noout -text -in "$kAlias/dsa_params.pem" | \ 747 | grep -iA1 'G:' | tail -n 1 | grep -E '^[[:space:]]*00:'; then 748 | break 749 | fi 750 | if [[ $incompatible == "True" ]] && 751 | ${x509OPENSSL} dsaparam -noout -text -in "$kAlias/dsa_params.pem" |\ 752 | grep -iA1 'G:' | tail -n 1 | grep -E '^[[:space:]]*[1-3]'; then 753 | break 754 | fi 755 | done 756 | dsaParams="$kAlias/dsa_params.pem" 757 | else 758 | dsaParams="$paramAlias/dsa_params.pem" 759 | fi 760 | 761 | while true; do 762 | ${x509OPENSSL} gendsa -out "$kAlias/$x509PKEY" "$dsaParams" 763 | if [ $? -ne 0 ]; then 764 | echo "x509KeyGen: Key generation failed" >&2 765 | return 1 766 | fi 767 | if [[ $conservative == "False" && $incompatible == "False" ]]; then 768 | break 769 | fi 770 | local prime_chars 771 | local pub_chars 772 | prime_chars="$(${x509OPENSSL} dsa -noout -text -in "$kAlias/$x509PKEY" | \ 773 | grep -iA 100 '^P:' | grep -iB 100 '^Q:' | wc -c)" 774 | pub_chars="$(${x509OPENSSL} dsa -noout -text -in "$kAlias/$x509PKEY" | \ 775 | grep -iA 100 'pub:' | grep -iB 100 '^P:' | wc -c)" 776 | # make sure that MSB is set 777 | # and that the public value is large enough 778 | if [[ $conservative == "True" ]] && 779 | ${x509OPENSSL} dsa -noout -text -in "$kAlias/$x509PKEY" | \ 780 | grep -A1 'pub:' | tail -n 1 | grep -E '^[[:space:]]*00:' && 781 | [[ $pub_chars == $prime_chars ]]; then 782 | break 783 | fi 784 | if [[ $incompatible == "True" ]] && 785 | ${x509OPENSSL} dsa -noout -text -in "$kAlias/$x509PKEY" | \ 786 | grep -A1 'pub:' | tail -n 1 | grep -E '^[[:space:]]*[1-3]'; then 787 | break 788 | fi 789 | done 790 | elif [[ $kType == "RSA" ]]; then 791 | ${x509OPENSSL} genrsa -out "$kAlias/$x509PKEY" "$kSize" 792 | if [ $? -ne 0 ]; then 793 | echo "x509KeyGen: Key generation failed" >&2 794 | return 1 795 | fi 796 | else # RSA-PSS, DH, GOST2001 797 | local options 798 | declare -a options 799 | options=("-out" "$kAlias/$x509PKEY") 800 | options=("${options[@]}" "-algorithm" "$kType") 801 | if [[ $kType == "RSA-PSS" ]]; then 802 | options=("${options[@]}" "-pkeyopt" "rsa_keygen_bits:$kSize") 803 | fi 804 | if [[ ${#genpkeyOpts[@]} -gt 0 ]]; then 805 | options=("${options[@]}" "${genpkeyOpts[@]}") 806 | fi 807 | ${x509OPENSSL} genpkey "${options[@]}" 808 | if [ $? -ne 0 ]; then 809 | echo "x509KeyGen: Key generation failed" >&2 810 | return 1 811 | fi 812 | fi 813 | 814 | } 815 | 816 | true <<'=cut' 817 | =pod 818 | 819 | =head2 x509SelfSign() 820 | 821 | Create a self signed certificate for a given alias. 822 | 823 | =over 4 824 | 825 | B 826 | [B<--basicKeyUsage> I] 827 | [B<--bcCritical>] 828 | [B<--bcPathLen> I] 829 | [B<--caFalse>] 830 | [B<--caTrue>] 831 | [B<--CN> I] 832 | [B<--DN> I] 833 | [B<--ncPermit> I] 834 | [B<--ncExclude> I] 835 | [B<--ncNotCritical>] 836 | [B<--md> I] 837 | [B<--padding> I] 838 | [B<--pssSaltLen> I] 839 | [B<--pssMgf1Md> I] 840 | [B<--noAuthKeyId>] 841 | [B<--noBasicConstraints>] 842 | [B<--noSubjKeyId>] 843 | [B<--subjectKeyIdentifier> I<[IDENTIFIER]>] 844 | [B<--notAfter> I] 845 | [B<--notBefore> I] 846 | [B<-t> I] 847 | [B<-v> I] 848 | I 849 | 850 | =back 851 | 852 | =over 853 | 854 | =item B<--basicKeyUsage> I 855 | 856 | Specified the value of X.509 version 3 Basic Key Usage extension. 857 | 858 | See B section for avaliable values for I. 859 | In case the value should be marked critical, prepend the values with 860 | C. 861 | 862 | Default value for role C is C. 863 | For role C is 864 | C. 865 | For role C is C. 866 | 867 | =item B<--bcCritical> 868 | 869 | Sets the C flag for Basic Constraints extension. 870 | 871 | =item B<--bcPathLen> I 872 | 873 | Sets the maximum path len for certificate chain to I. 874 | 875 | Undefined (unbounded) by default. 876 | 877 | =item B<--caFalse> 878 | 879 | Sets the Basic Constraints flag for CA to false. Note that this unsets the 880 | default criticality flag for Basic Constraints. To restore it, use 881 | B<--bcCritical>. 882 | 883 | =item B<--caTrue> 884 | 885 | Sets the Basic Constraints flag for CA to true. Note that this unsets 886 | the flag for criticality of Basic Constraints. To restore it, use 887 | B<--bcCritical>. 888 | 889 | This is the default for C role together with B<--bcCritical> 890 | 891 | =item B<--CN> I 892 | 893 | Specifies the common name (CN) for distinguished name (DN) in the certificate. 894 | This applies for both the subject name and issuer name in the generated 895 | certificate. 896 | 897 | If no B<--DN>'s are specified, C will be used for I and 898 | C for I. I role will not get a common name but 899 | its DN will be set to C. 900 | 901 | =item B<--DN> I 902 | 903 | Specifies parts of distinguished name (DN) of the generated certificate. 904 | The order will be the same as will appear in the certificate. 905 | If the B<--CN> option is also specified then I will be placed last. 906 | 907 | The I is comprised of two values with C<=> in the middle. 908 | For example: C, C or C. 909 | 910 | Note that existence of no particular element is enforced but the DN I 911 | have at least one element. If none is specified, the defaults from B<--CN> 912 | option will be used. 913 | 914 | Note that the case in DN elements B significant. 915 | 916 | TODO: Insert list of known DN parts 917 | 918 | =over 919 | 920 | =item I | I 921 | 922 | Human readable name 923 | 924 | =item I | I 925 | 926 | Name of company department 927 | 928 | =item I | I 929 | 930 | Name of organisation or company 931 | 932 | =item I | I 933 | 934 | Two letter code of country 935 | 936 | =item I 937 | 938 | RFC822 address 939 | 940 | =item I 941 | 942 | City name 943 | 944 | =item I 945 | 946 | State or province name hosting the HQ. 947 | 948 | =back 949 | 950 | =item B<--ncPermit> I 951 | 952 | Adds HOST to x509v3 nameConstraint as permitted (see RFC 5820). 953 | 954 | HOST can be a hostname (google.com), IP address (8.8.8.8), 955 | or something supported directly by openssl (IP:192.168.0.0/255.255.0.0, 956 | DNS:google.com - see man x509v3_config for details). 957 | 958 | =item B<--ncExclude> I 959 | 960 | Adds HOST to x509v3 nameConstraint as excluded (see RFC 5820). 961 | See B<--ncPermit> for details. 962 | 963 | =item B<--ncNotCritical> 964 | 965 | Marks nameConstraints as NOT critical, which is against RFC 5820. 966 | Default is critical. 967 | 968 | =item B<--md> I 969 | 970 | Sets the cryptographic hash (message digest) for signing certificates. 971 | 972 | Note that some combinations of key types and digest algorithms are unsupported. 973 | For example, you can't sign using ECDSA and MD5. 974 | 975 | SHA256 by default, will be updated to weakeast hash recommended by NIST or 976 | generally thought to be secure. SHA1 in case the openssl version installed 977 | doesn't support SHA256. 978 | 979 | =item B<--padding> I 980 | 981 | Set the specified RSA padding type for the certificate signature. Acceptable 982 | values are B (the default, for PKCS#1 v1.5 padding with DigestInfo), 983 | B (for X 9.31 padding) and B (for RSASSA-PSS padding). 984 | 985 | =item B<--pssSaltLen> I 986 | 987 | Set the length of used salt (in bytes) that will be used to create the 988 | signature. Special values are: B<-1> for setting the salt size to the size 989 | of used message digest, B<-2> for automatically determining the size of 990 | the salt and B<-3> for using the maximum possible salt size. 991 | 992 | =item B<--noAuthKeyId> 993 | 994 | Do not set the Authority Key Identifier extension in the certificate. 995 | 996 | =item B<--noBasicConstraints> 997 | 998 | Remove Basic Constraints extension from the certificate completely. 999 | Note that in PKIX certificate validation, V3 certificate with no Basic 1000 | Constraints will I be considered to be a CA. 1001 | 1002 | =item B<--noSubjKeyId> 1003 | 1004 | Do not set the Subject Key Identifier extension in the certificate. 1005 | Implies B<--noAuthKeyId>. 1006 | 1007 | =item B<--subjectKeyIdentifier> I<[IDENTIFIER]> 1008 | 1009 | Set the Subject Key Identifier extension in the certificate to the given value, 1010 | if passed. If no value is passed, default to using the SHA-1 hash of the BIT 1011 | STRING subjectPublicKey, i.e., OpenSSL's default. If provided, the value must be 1012 | a hex string (possibly with : separating bytes) to use as the 1013 | subjectKeyIdentifier. 1014 | 1015 | =item B<--notAfter> I 1016 | 1017 | Sets the date after which the certificate won't be valid. 1018 | Uses date(1) for conversion so values like "1 year" (from now), "2 years ago", 1019 | "3 months", "4 weeks ago", "2 days ago", etc. work just as well as values 1020 | like "201001011235Z". 1021 | Use C> to verify if it represent the date you want. 1022 | 1023 | By default C<10 years> for I role, C<1 year> for all others. 1024 | 1025 | =item B<--notBefore> I 1026 | 1027 | Sets the date since which the certificate is valid. Uses date(1) for conversion 1028 | so values like "1 year" (from now), "2 years ago", "3 months", "4 weeks ago", 1029 | "2 days ago", etc. work just as well as values like "201001011235Z". 1030 | Use C> to verify if it represents the date you want. 1031 | 1032 | By default C<5 years ago> for I role, C for all others. 1033 | 1034 | =item B<-t> I 1035 | 1036 | Sets the general type of certificate: C, C, C or 1037 | C. 1038 | In case there are no additional options, this also sets correct values 1039 | for basic key usage and extended key usage for given role. 1040 | The special value of C removes use of basic key usage and extended key 1041 | usage extensions. 1042 | 1043 | Note that while the names indicate "web", they actually apply for all servers 1044 | and clients that use TLS or SSL and in case of C also for S/MIME. 1045 | 1046 | C by default. 1047 | 1048 | =item B<-v> I 1049 | 1050 | Version of the certificate to create, accepted versions are C<1> and C<3>. 1051 | Unfortunately, creating version C<1> certificate with extensions is impossible 1052 | with current openssl so the script detects that and returns error. 1053 | 1054 | Version C<3> by default. 1055 | 1056 | =item I 1057 | 1058 | Name of directory in which the generated certificate will be placed 1059 | and where the private key used for signing is located. 1060 | The certificate will be placed in file named F if I 1061 | variable was not changed. 1062 | 1063 | =back 1064 | 1065 | Returns 0 if signing was successfull, non zero otherwise. 1066 | 1067 | =cut 1068 | 1069 | x509SelfSign() { 1070 | # name of key to process 1071 | local kAlias 1072 | # version of cert to generate 1073 | local certV=3 1074 | # role for certificate 1075 | local certRole="CA" 1076 | # common name of certificate 1077 | local certCN 1078 | # parts of DN (array) 1079 | declare -a certDN 1080 | # date since which the cert is valid 1081 | local notBefore="" 1082 | # date until which the cert is valid 1083 | local notAfter="" 1084 | # value for Basic Key Usage Extension 1085 | local basicKeyUsage="" 1086 | # set the value for CA bit for Basic Constraints 1087 | local basicConstraints="" 1088 | # set the length for pathlen in Basic Constraints 1089 | local bcPathLen="" 1090 | # set the criticality flag for Basic Constraints 1091 | local bcCritical="" 1092 | # permitted names as per RFC-5280 1093 | local -a namesPermitted 1094 | # excluded names as per RFC-5280 1095 | local -a namesExcluded 1096 | # flag set when name constraints are not to be marked critical 1097 | local ncCritical="critical," 1098 | # set the message digest algorithm used for signing 1099 | local certMD="" 1100 | # set the padding mode used for signature 1101 | local sigPad="" 1102 | # set the length of salt used in RSA-PSS signatures 1103 | local pssSaltLen="" 1104 | # flag set when the Authority Key Identifier is not supposed to be 1105 | # added to certificate 1106 | local noAuthKeyId="" 1107 | # flag set when the Subject Key Identifier is not supposed to be added 1108 | # to certificate 1109 | local noSubjKeyId="" 1110 | # Explicit value to use as the Subject Key Identifier, if provided 1111 | local subjectKeyIdentifier="" 1112 | 1113 | # 1114 | # parse options 1115 | # 1116 | 1117 | local TEMP=$(getopt -o t:v: -l CN: -l DN: -l notAfter: -l notBefore: \ 1118 | -l basicKeyUsage: \ 1119 | -l caTrue \ 1120 | -l caFalse \ 1121 | -l noBasicConstraints \ 1122 | -l ncPermit: \ 1123 | -l ncExclude: \ 1124 | -l ncNotCritical \ 1125 | -l bcPathLen: \ 1126 | -l bcCritical \ 1127 | -l noAuthKeyId \ 1128 | -l noSubjKeyId \ 1129 | -l subjectKeyIdentifier:: \ 1130 | -l md: \ 1131 | -l padding: \ 1132 | -l pssSaltLen: \ 1133 | -n x509SelfSign -- "$@") 1134 | if [ $? -ne 0 ]; then 1135 | echo "X509SelfSign: can't parse options" >&2 1136 | return 1 1137 | fi 1138 | 1139 | eval set -- "$TEMP" 1140 | 1141 | while true ; do 1142 | case "$1" in 1143 | -t) certRole="$2"; shift 2 1144 | ;; 1145 | -v) certV="$2"; shift 2 1146 | ;; 1147 | --CN) certCN="$2"; shift 2 1148 | ;; 1149 | --DN) certDN=("${certDN[@]}" "$2"); shift 2 1150 | ;; 1151 | --notAfter) notAfter="$2"; shift 2 1152 | ;; 1153 | --notBefore) notBefore="$2"; shift 2 1154 | ;; 1155 | --basicKeyUsage) basicKeyUsage="$2"; shift 2 1156 | ;; 1157 | --caTrue) basicConstraints="CA:TRUE"; shift 1 1158 | ;; 1159 | --caFalse) basicConstraints="CA:FALSE"; shift 1 1160 | ;; 1161 | --noBasicConstraints) basicConstraints="undefined"; shift 1 1162 | ;; 1163 | --bcPathLen) bcPathLen="$2"; shift 2 1164 | ;; 1165 | --bcCritical) bcCritical="true"; shift 1 1166 | ;; 1167 | --ncPermit) namesPermitted=("${namesPermitted[@]}" "$2"); shift 2 1168 | ;; 1169 | --ncExclude) namesExcluded=("${namesExcluded[@]}" "$2"); shift 2 1170 | ;; 1171 | --ncNotCritical) ncCritical=""; shift 1 1172 | ;; 1173 | --md) certMD="$2"; shift 2 1174 | ;; 1175 | --padding) sigPad="$2"; shift 2 1176 | ;; 1177 | --pssSaltLen) pssSaltLen="$2"; shift 2 1178 | ;; 1179 | --noAuthKeyId) noAuthKeyId="true"; shift 1 1180 | ;; 1181 | --noSubjKeyId) noSubjKeyId="true"; shift 1 1182 | ;; 1183 | --subjectKeyIdentifier) subjectKeyIdentifier="$2"; shift 2 1184 | ;; 1185 | --) shift 1 1186 | break 1187 | ;; 1188 | *) echo "x509SelfSign: Unknown option: '$1'" >&2 1189 | return 1 1190 | esac 1191 | done 1192 | 1193 | kAlias="$1" 1194 | 1195 | # 1196 | # sanity check options 1197 | # 1198 | 1199 | if [ ! -d "$kAlias" ] || [ ! -e "$kAlias/$x509PKEY" ]; then 1200 | echo "x509SelfSign: private key '$kAlias' has not yet been generated"\ 1201 | >&2 1202 | return 1 1203 | fi 1204 | 1205 | if [[ "$sigPad" && "$sigPad" != "pss" && "$pssSaltLen" ]] \ 1206 | || [[ -z "$sigPad" && "$pssSaltLen" ]]; then 1207 | 1208 | echo "x509SelfSign: pssSaltLen is only applicable to pss padding" >&2 1209 | return 1 1210 | fi 1211 | 1212 | certRole=$(tr '[:upper:]' '[:lower:]' <<< ${certRole}) 1213 | if [[ $certRole != "ca" ]] && [[ $certRole != "webserver" ]] \ 1214 | && [[ $certRole != "webclient" ]] && [[ $certRole != "none" ]]; then 1215 | 1216 | echo "x509SelfSign: Unknown role: '$certRole'" >&2 1217 | return 1 1218 | fi 1219 | 1220 | if [[ $certV != 1 ]] && [[ $certV != 3 ]]; then 1221 | echo "x509SelfSign: Certificate version must be 1 or 3" >&2 1222 | return 1 1223 | fi 1224 | 1225 | if [[ $certV == 1 ]]; then 1226 | if [[ ! -z $basicKeyUsage ]]; then 1227 | echo "x509SelfSign: Can't create version 1 certificate with "\ 1228 | "extensions" >&2 1229 | return 1 1230 | fi 1231 | fi 1232 | 1233 | if [ ! -z "$certCN" ]; then 1234 | certDN=("${certDN[@]}" "CN = $certCN") 1235 | fi 1236 | 1237 | if [ ${#certDN[@]} -eq 0 ]; then 1238 | case $certRole in 1239 | ca) certDN=("${certDN[@]}" "O = Example CA") 1240 | ;; 1241 | webserver) certDN=("${certDN[@]}" "CN = localhost") 1242 | ;; 1243 | webclient) certDN=("${certDN[@]}" "CN = John Smith") 1244 | ;; 1245 | none) certDN=("${certDN[@]}" "O = Unknown use cert") 1246 | ;; 1247 | *) echo "x509SelfSign: Unknown cert role: $certRole" >&2 1248 | return 1 1249 | ;; 1250 | esac 1251 | fi 1252 | 1253 | if [[ -z $notAfter ]] && [[ $certRole == "ca" ]]; then 1254 | notAfter="10 years" 1255 | fi # default of "1 year" is in config generator 1256 | 1257 | if [[ -z $notBefore ]] && [[ $certRole == "ca" ]]; then 1258 | notBefore="5 years ago" 1259 | fi # dafault of "now" is in config generator 1260 | 1261 | if [[ ! -z $bcPathLen ]]; then 1262 | if [[ $basicConstraints == "undefined" ]] || 1263 | [[ $basicConstraints == "CA:FALSE" ]]; then 1264 | echo "x509SelfSign: Path len can be specified only with caTrue "\ 1265 | "option" >&2 1266 | return 1 1267 | fi 1268 | if [[ $certRole != "ca" ]] && [[ -z $basicConstraints ]]; then 1269 | echo "x509SelfSign: Only ca role uses CA:TRUE constraint, use "\ 1270 | "--caTrue to override" >&2 1271 | return 1; 1272 | fi 1273 | fi 1274 | 1275 | if [[ -z $basicConstraints ]]; then 1276 | case $certRole in 1277 | ca) basicConstraints="CA:TRUE" 1278 | bcCritical="true" 1279 | ;; 1280 | *) basicConstraints="CA:FALSE" 1281 | bcCritical="true" 1282 | ;; 1283 | esac 1284 | fi 1285 | 1286 | local basicConstraintsOption="" 1287 | if [[ $bcCritical == "true" ]]; then 1288 | basicConstraintsOption="critical, " 1289 | fi 1290 | if [[ $basicConstraints == "undefined" ]]; then 1291 | basicConstraintsOption="" 1292 | else 1293 | basicConstraintsOption="${basicConstraintsOption}${basicConstraints}" 1294 | if [[ ! -z $bcPathLen ]]; then 1295 | basicConstraintsOption="${basicConstraintsOption}, pathlen: ${bcPathLen}" 1296 | fi 1297 | fi 1298 | 1299 | if [[ -z $basicKeyUsage ]]; then 1300 | case $certRole in 1301 | ca) basicKeyUsage="critical, keyCertSign, cRLSign" 1302 | ;; 1303 | webserver) basicKeyUsage="critical, digitalSignature, " 1304 | basicKeyUsage="${basicKeyUsage}keyEncipherment, keyAgreement" 1305 | ;; 1306 | webclient) basicKeyUsage="digitalSignature, keyEncipherment" 1307 | ;; 1308 | none) 1309 | ;; 1310 | *) echo "x509SelfSign: Unknown cert role: $certRole" >&2 1311 | return 1 1312 | ;; 1313 | esac 1314 | fi 1315 | 1316 | if [[ $noSubjKeyId == "true" ]]; then 1317 | noAuthKeyId="true" 1318 | fi 1319 | 1320 | if [[ $noSubjKeyId == "true" ]] && [ "$subjectKeyIdentifier" != "" ]; then 1321 | echo "x509SelfSign: --noSubjKeyId conflicts with --subjectKeyIdentifier" >&2 1322 | return 1 1323 | fi 1324 | 1325 | # 1326 | # prepare configuration file for signing 1327 | # 1328 | 1329 | declare -a parameters 1330 | local option 1331 | for option in "${certDN[@]}"; do 1332 | parameters=("${parameters[@]}" "--dn=$option") 1333 | done 1334 | 1335 | if [[ ! -z $notAfter ]]; then 1336 | parameters=("${parameters[@]}" "--notAfter=$notAfter") 1337 | fi 1338 | if [[ ! -z $notBefore ]]; then 1339 | parameters=("${parameters[@]}" "--notBefore=$notBefore") 1340 | fi 1341 | 1342 | if [[ ! -z $basicConstraintsOption ]]; then 1343 | parameters=("${parameters[@]}" "--basicConstraints=$basicConstraintsOption") 1344 | fi 1345 | 1346 | if [[ ! -z $basicKeyUsage ]]; then 1347 | parameters=("${parameters[@]}" "--basicKeyUsage=$basicKeyUsage") 1348 | fi 1349 | 1350 | local nameConstraints="$(__INTERNAL_x509NamesToNCs namesPermitted[@] permitted)" 1351 | local joinedNamesExcluded="$(__INTERNAL_x509NamesToNCs namesExcluded[@] excluded)" 1352 | if [[ ! -z $nameConstraints && ! -z $joinedNamesExcluded ]]; then 1353 | nameConstraints="${nameConstraints},${joinedNamesExcluded}" 1354 | else 1355 | nameConstraints="${nameConstraints}${joinedNamesExcluded}" 1356 | fi 1357 | if [[ ! -z $nameConstraints ]]; then 1358 | parameters=("${parameters[@]}" "--x509v3Extension=nameConstraints=$ncCritical$nameConstraints") 1359 | fi 1360 | 1361 | if [[ -n $certMD ]]; then 1362 | parameters=("${parameters[@]}" "--md=$certMD") 1363 | fi 1364 | 1365 | if [[ $noSubjKeyId != "true" ]]; then 1366 | if [ "$subjectKeyIdentifier" != "" ]; then 1367 | parameters=("${parameters[@]}" "--subjectKeyIdentifier=$subjectKeyIdentifier") 1368 | else 1369 | parameters=("${parameters[@]}" "--subjectKeyIdentifier") 1370 | fi 1371 | fi 1372 | 1373 | __INTERNAL_x509GenConfig "${parameters[@]}" "$kAlias" 1374 | if [ $? -ne 0 ]; then 1375 | return 1 1376 | fi 1377 | 1378 | # 1379 | # create self signed certificate 1380 | # 1381 | declare -a options=() 1382 | if [[ ! -z $sigPad ]]; then 1383 | options=("${options[@]}" "-sigopt" "rsa_padding_mode:$sigPad") 1384 | fi 1385 | if [[ ! -z $pssSaltLen ]]; then 1386 | options=("${options[@]}" "-sigopt" "rsa_pss_saltlen:$pssSaltLen") 1387 | fi 1388 | 1389 | # because we want to have full control over certificate fields 1390 | # (like notBefore and notAfter) we have to create the certificate twice 1391 | 1392 | # create dummy self signed certificate 1393 | ${x509OPENSSL} req -x509 -new -key $kAlias/$x509PKEY \ 1394 | -out $kAlias/temp-$x509CERT \ 1395 | -batch -config $kAlias/$x509CACNF "${options[@]}" 1396 | if [ $? -ne 0 ]; then 1397 | echo "x509SelfSign: temporary certificate generation failed" >&2 1398 | return 1 1399 | fi 1400 | 1401 | # create CSR for signing by the dummy certificate 1402 | ${x509OPENSSL} x509 -x509toreq -signkey $kAlias/$x509PKEY \ 1403 | -out $kAlias/$x509CSR \ 1404 | -in $kAlias/temp-$x509CERT 1405 | if [ $? -ne 0 ]; then 1406 | echo "x509SelfSign: certificate signing request failed" >&2 1407 | return 1 1408 | fi 1409 | 1410 | declare -a caOptions 1411 | caOptions=("${caOptions[@]}" "-preserveDN") 1412 | if [[ $certV == "3" ]]; then 1413 | caOptions=("${caOptions[@]}" "-extensions" "v3_ext") 1414 | fi 1415 | if [[ ! -z "$sigPad" ]]; then 1416 | caOptions=("${caOptions[@]}" "-sigopt" "rsa_padding_mode:$sigPad") 1417 | fi 1418 | if [[ ! -z "$pssSaltLen" ]]; then 1419 | caOptions=("${caOptions[@]}" "-sigopt" "rsa_pss_saltlen:$pssSaltLen") 1420 | fi 1421 | # the serial number must be the same, so reset index and serial number 1422 | rm -f "$kAlias/$x509CAINDEX" "$kAlias/$x509CASERIAL" 1423 | touch "$kAlias/$x509CAINDEX" 1424 | echo 01 > "$kAlias/$x509CASERIAL" 1425 | 1426 | # sign the certificate using the full CA functionality to get proper 1427 | # key id and subject key identifier 1428 | ${x509OPENSSL} ca -config $kAlias/$x509CACNF -batch \ 1429 | -keyfile $kAlias/$x509PKEY \ 1430 | -cert $kAlias/temp-$x509CERT -in $kAlias/$x509CSR \ 1431 | -out $kAlias/$x509CERT "${caOptions[@]}" 1432 | if [ $? -ne 0 ]; then 1433 | echo "x509SelfSign: signing the certificate failed" >&2 1434 | return 1 1435 | fi 1436 | 1437 | mv -f "$kAlias/$x509CERT" "$kAlias/temp-$x509CERT" 1438 | 1439 | # now we have a certificate with proper serial number, it's just missing 1440 | # Authority Key Identifier that references it, so we sign itself for the 1441 | # third time 1442 | if [[ $noAuthKeyId != "true" ]]; then 1443 | parameters=("${parameters[@]}" "--authorityKeyIdentifier=keyid,issuer") 1444 | fi 1445 | # the serial number must be the same, so reset index and serial number 1446 | rm -f "$kAlias/$x509CAINDEX" "$kAlias/$x509CASERIAL" 1447 | __INTERNAL_x509GenConfig "${parameters[@]}" "$kAlias" 1448 | if [ $? -ne 0 ]; then 1449 | return 1 1450 | fi 1451 | 1452 | ${x509OPENSSL} ca -config $kAlias/$x509CACNF -batch \ 1453 | -keyfile $kAlias/$x509PKEY \ 1454 | -cert $kAlias/temp-$x509CERT -in $kAlias/$x509CSR \ 1455 | -out $kAlias/$x509CERT "${caOptions[@]}" 1456 | 1457 | if [ $? -ne 0 ]; then 1458 | echo "x509SelfSign: signing the certificate failed" >&2 1459 | return 1 1460 | fi 1461 | } 1462 | 1463 | true <<'=cut' 1464 | =pod 1465 | 1466 | =head2 x509KeyCopy() 1467 | 1468 | Create a new key by copying the key material from a different certificate/key. 1469 | 1470 | =over 4 1471 | 1472 | B 1473 | B<-t> I 1474 | I 1475 | 1476 | =back 1477 | 1478 | Uses the key from I to create a directory I with the same key. 1479 | 1480 | Returns non zero if I exists or I doesn't exist or doesn't 1481 | contain private key. 1482 | 1483 | =cut 1484 | 1485 | x509KeyCopy() { 1486 | 1487 | # destination of copy 1488 | local newKey="" 1489 | # source of key 1490 | local kAlias="" 1491 | 1492 | local TEMP=$(getopt -o t: -n x509KeyCopy -- "$@") 1493 | if [ $? -ne 0 ]; then 1494 | echo "X509KeyCopy: Can't parse options" >&2 1495 | return 1 1496 | fi 1497 | 1498 | eval set -- "$TEMP" 1499 | 1500 | while true ; do 1501 | case "$1" in 1502 | -t) newKey="$2"; shift 2 1503 | ;; 1504 | --) shift 1 1505 | break 1506 | ;; 1507 | *) echo "x509KeyCopy: Unknown option: $1" >&2 1508 | return 1 1509 | esac 1510 | done 1511 | 1512 | kAlias="$1" 1513 | 1514 | if [ ! -e "$kAlias/$x509PKEY" ]; then 1515 | echo "x509KeyCopy: Source invalid" >&2 1516 | return 1 1517 | fi 1518 | 1519 | if [ -e "$newKey" ]; then 1520 | echo "x509KeyCopy: Destination exists" >&2 1521 | return 1 1522 | fi 1523 | 1524 | mkdir "$newKey" 1525 | if [ $? -ne 0 ]; then 1526 | echo "x509KeyCopy: Can't create directory for new key" >&2 1527 | return 1 1528 | fi 1529 | 1530 | cp "$kAlias/$x509PKEY" "$newKey" 1531 | if [ $? -ne 0 ]; then 1532 | echo "x509KeyCopy: Can't copy key" >&2 1533 | return 1 1534 | fi 1535 | 1536 | return 0 1537 | } 1538 | 1539 | true <<'=cut' 1540 | =pod 1541 | 1542 | =head2 x509CertSign() 1543 | 1544 | Create a certificate signed by a given alias. 1545 | 1546 | =over 4 1547 | 1548 | B 1549 | [B<--basicKeyUsage> I] 1550 | [B<--bcCritical>] 1551 | [B<--bcPathLen> I] 1552 | [B<--caFalse>] 1553 | [B<--caTrue>] 1554 | [B<--DN> I] 1555 | [B<--extendedKeyUsage> I] 1556 | [B<--ncPermit> I] 1557 | [B<--ncExclude> I] 1558 | [B<--ncNotCritical>] 1559 | [B<--md> I] 1560 | [B<--padding> I] 1561 | [B<--pssSaltLen> I] 1562 | [B<--noBasicConstraints>] 1563 | [B<--notAfter> I] 1564 | [B<--notBefore> I] 1565 | [B<--ocspNoCheck>[=I]] 1566 | [B<--ocspResponderURI> I] 1567 | [B<--subjectAltName> I] 1568 | [B<--subjectAltNameCritical>] 1569 | [B<--noAuthKeyId>] 1570 | [B<--noSubjKeyId>] 1571 | [B<-t> I] 1572 | [B<-v> I] 1573 | B<--CA> I 1574 | I 1575 | 1576 | =back 1577 | 1578 | =over 1579 | 1580 | =item B<--basicKeyUsage> I 1581 | 1582 | Specify the settings for basic key usage extension. 1583 | See B section for list of available keywords. 1584 | 1585 | Default "critical, keyCertSign, cRLSign" for C role, 1586 | "critical, digitalSignature, keyEncipherment, keyAgreement" for C 1587 | role and "digitalSignature, keyEncipherment" for C role. 1588 | 1589 | =item B<--bcCritical> 1590 | 1591 | Sets the C flag for Basic Constraints extension. 1592 | See B section to see what it means. 1593 | 1594 | =item B<--bcPathLen> I 1595 | 1596 | Sets the maximum path len for certificate chain to I. 1597 | 1598 | Undefined (unbounded) by default. 1599 | 1600 | =item B<--CA> I 1601 | 1602 | Name the key and certificate used for signing the new certificate. 1603 | 1604 | The CA specified by I must have its key generated and certificate 1605 | present (either through self signing or through previous certificate 1606 | signing operation). 1607 | 1608 | =item B<--caFalse> 1609 | 1610 | Sets the Basic Constraints flag for CA to false. Note that his unsets the 1611 | default criticality flag for Basic Constraints. To restore it, use 1612 | B<--bcCritical>. 1613 | 1614 | This is the default for C and C roles. 1615 | 1616 | =item B<--caTrue> 1617 | 1618 | Sets the Basic Constraints flag for CA to true. Note that this unsets 1619 | the default flag for criticality of Basic Constraints. To restore it, use 1620 | B<--bcCritical>. 1621 | 1622 | This is the default for C role. 1623 | 1624 | =item B<--crlDistributionPoints> I 1625 | 1626 | Add the CRL Distributions Points extension that specifies the location of the 1627 | Certificate Revocation List. The URI must be specified with protocol. 1628 | 1629 | For example: 1630 | 1631 | http://crl.example.com/ 1632 | 1633 | =item B<--DN> I 1634 | 1635 | Specifies parts of distinguished name (DN) of the generated certificate. 1636 | The order in which they are provided will be used for certificate generation. 1637 | 1638 | See the same option description for I for available I 1639 | options. 1640 | 1641 | By default C for C role, C 1642 | for C role and C for C role. 1643 | 1644 | =item B<--extendedKeyUsage> I 1645 | 1646 | Add the Extended Key Usage extension to the certificate. I is a comma 1647 | separated list of key usages. Both literal OIDs and names can be used. 1648 | 1649 | Define as empty string to remove the default value. Prepend C before 1650 | usage names to mark the extension as critical. 1651 | 1652 | Valid names are: 1653 | 1654 | =over 1655 | 1656 | =item I 1657 | 1658 | SSL/TLS Server authentication 1659 | 1660 | =item I 1661 | 1662 | SSL/TLS Client authentication 1663 | 1664 | =item I 1665 | 1666 | Executable code signing 1667 | 1668 | =item I 1669 | 1670 | Signing and encrypting S/MIME messages. 1671 | 1672 | =item I 1673 | 1674 | Signing of trusted timestamps (required for Time Stamping Authority), 1675 | many implementations require this use to be only one and marked as critical 1676 | for the TSA to be considered valid. 1677 | 1678 | =item I 1679 | 1680 | Microsoft Individual Code Signing (authnticode) 1681 | 1682 | =item I 1683 | 1684 | Microsoft Commercial Code Signing (authenticode) 1685 | 1686 | =item I 1687 | 1688 | Microsoft Trust List signing 1689 | 1690 | =item I 1691 | 1692 | Microsoft Server Gated Cryptography 1693 | 1694 | =item I 1695 | 1696 | Microsoft Encrypted File System 1697 | 1698 | =item I 1699 | 1700 | Netscape Server Gated Crypto 1701 | 1702 | =item I 1703 | 1704 | Allow the server to sign OCSP responses, also known as id_kp_OCSPSigning. 1705 | 1706 | =item I 1707 | 1708 | Certificate can be used as the End System in IPsec 1709 | 1710 | =item I 1711 | 1712 | Certificate can be used in IPsec tunnels. 1713 | 1714 | =item I 1715 | 1716 | Certificate can be used by user (client). 1717 | 1718 | =item I 1719 | 1720 | Certificate can be used as a Data Validation and Certification Server (a trusted 1721 | third party). 1722 | 1723 | =back 1724 | 1725 | By default undefined for C role, I for C role and 1726 | I for C. 1727 | 1728 | =item B<--ncPermit> I 1729 | 1730 | Adds HOST to x509v3 nameConstraint as permitted (see RFC 5820). 1731 | 1732 | HOST can be a hostname (google.com), IP address (8.8.8.8), 1733 | or something supported directly by openssl (IP:192.168.0.0/255.255.0.0, 1734 | DNS:google.com - see man x509v3_config for details). 1735 | 1736 | =item B<--ncExclude> I 1737 | 1738 | Adds HOST to x509v3 nameConstraint as excluded (see RFC 5820). 1739 | See B<--ncPermit> for details. 1740 | 1741 | =item B<--ncNotCritical> 1742 | 1743 | Marks nameConstraints as NOT critical, which is against RFC 5820. 1744 | Default is critical. 1745 | 1746 | =item B<--md> I 1747 | 1748 | Sets the cryptographic hash (message digest) for signing certificates. 1749 | 1750 | Note that some combinations of key types and digest algorithms are unsupported. 1751 | For example, you can't sign using ECDSA and MD5. 1752 | 1753 | SHA256 by default, will be updated to weakeast hash recommended by NIST or 1754 | generally thought to be secure. 1755 | 1756 | =item B<--padding> I 1757 | 1758 | Set the specified RSA padding type for the certificate signature. Acceptable 1759 | values are B (the default, for PKCS#1 v1.5 padding with DigestInfo), 1760 | B (for X 9.31 padding) and B (for RSASSA-PSS padding). 1761 | 1762 | =item B<--pssSaltLen> I 1763 | 1764 | Set the length of used salt (in bytes) that will be used to create the 1765 | signature. Special values are: B<-1> for setting the salt size to the size 1766 | of used message digest, B<-2> for automatically determining the size of 1767 | the salt and B<-3> for using the maximum possible salt size. 1768 | 1769 | =item B<--pssMgf1Md> I 1770 | 1771 | Set the hash used for the MGF1 inside RSA-PSS signatures. 1772 | By default it's the same value that is used for B<--md> option. 1773 | 1774 | =item B<--noAuthKeyId> 1775 | 1776 | Do not add the Authority Key Identifier extension to generated certificates. 1777 | 1778 | =item B<--noBasicConstraints> 1779 | 1780 | Remove Basic Constraints extension from the certificate completely. 1781 | Note that in PKIX certificate validation, V3 certificate with no Basic 1782 | Constraints will I be considered to be a CA. 1783 | 1784 | =item B<--noSubjKeyId> 1785 | 1786 | Do not add the Subject Key Identifier extension to generated certificates. 1787 | 1788 | =item B<--notAfter> I 1789 | 1790 | Sets the date after which the certificate won't be valid. 1791 | Uses date(1) for conversion so values like "1 year" (from now), "2 years ago", 1792 | "3 months", "4 weeks ago", "2 days ago", etc. work just as well as values 1793 | like "201001011235Z". 1794 | Use C> to verify if it represent the date you want. 1795 | 1796 | By default C<10 years> for I role, C<1 year> for all others. 1797 | 1798 | =item B<--notBefore> I 1799 | 1800 | Sets the date since which the certificate is valid. Uses date(1) for conversion 1801 | so values like "1 year" (from now), "2 years ago", "3 months", "4 weeks ago", 1802 | "2 days ago", etc. work just as well as values like "201001011235Z". 1803 | Use C> to verify if it represents the date you want. 1804 | 1805 | By default C<5 years ago> for I role, C for all others. 1806 | 1807 | =item B<--ocspNoCheck>[=I] 1808 | 1809 | Add the OCSP No Check extension to certificate, also known as 1810 | id-pkix-ocsp-nocheck. 1811 | 1812 | I is the optional argument that, if provided (with any value, though 1813 | C is recommended), will mark the extension as critical. 1814 | 1815 | =item B<--ocspResponderURI> I 1816 | 1817 | Add Authority Info Access extension that specifies location of the OCSP 1818 | responder fo this certificate. The URI must be specified with protocol. 1819 | 1820 | For example: 1821 | 1822 | http://ocsp.example.com/ 1823 | 1824 | =item B<--subjectAltName> I 1825 | 1826 | Specify the Subject Alternative Name extension items to add. The format is 1827 | similar to the B, first the literal added, then equals sign (=) and 1828 | finally the value added. 1829 | 1830 | The literals supported are: 1831 | 1832 | =over 1833 | 1834 | =item I 1835 | 1836 | Email address in the form: 1837 | 1838 | username@domainname 1839 | 1840 | =item I 1841 | 1842 | Full Uniform Resource Identifier, with protocol, host name and location. 1843 | 1844 | =item I 1845 | 1846 | DNS host name 1847 | 1848 | =item I 1849 | 1850 | An IP Address, both IPv4 and IPv6 is supported 1851 | 1852 | =back 1853 | 1854 | Note that if you want multiple literals of the same type, you need to specify 1855 | the order in which they will be placed by appending position after a dot: 1856 | 1857 | DNS.1=example.com 1858 | DNS.2=www.example.com 1859 | 1860 | =item B<--subjectAltNameCritical> 1861 | 1862 | Mark the Subject Alternative Name as critical. 1863 | 1864 | =item B<-t> I 1865 | 1866 | Sets the general type of certificate: C, C, C or 1867 | C. 1868 | In case there are no additional options, this also sets correct values 1869 | for basic key usage and extended key usage for given role. 1870 | For case of C the default is to not add basic key usage or extended 1871 | key usage extensions to the certificate. 1872 | 1873 | Note that while the names indicate "web", they actually apply for all servers 1874 | and clients that use TLS or SSL and in case of C also for S/MIME. 1875 | 1876 | C by default. 1877 | 1878 | =item B<-v> I 1879 | 1880 | Version of the certificate to create, accepted versions are C<1> and C<3>. 1881 | Unfortunately, creating version C<1> certificate with extensions is impossible 1882 | with current openssl so the script detects that and returns error. 1883 | 1884 | Version C<3> by default. 1885 | 1886 | =item I 1887 | 1888 | Location of the private key for signing. 1889 | 1890 | Note that the private key must have been already generated. 1891 | 1892 | =back 1893 | 1894 | Return 0 if signing was successfull, non zero otherwise. 1895 | 1896 | =cut 1897 | 1898 | x509CertSign() { 1899 | # alias of the key to be signed 1900 | local kAlias 1901 | # alias of the CA key and cert to be used for signing 1902 | local caAlias 1903 | # X.509 certificate version (1 or 3) 1904 | local certV="3" 1905 | # role of certificate 1906 | local certRole="webserver" 1907 | # CRL distribution URL 1908 | local crlDistributionPoints="" 1909 | # date since which the cert is valid 1910 | # default is in config generator (now) 1911 | local notBefore="" 1912 | # date until which the cert is valid 1913 | # default is in config generator (1 year) 1914 | local notAfter="" 1915 | # set the value for CA bit for Basic Constraints 1916 | local basicConstraints="" 1917 | # set the length for pathlen in Basic Constraints 1918 | local bcPathLen="" 1919 | # set the criticality flag for Basic Constraints 1920 | local bcCritical="" 1921 | # permitted names as per RFC-5280 1922 | local -a namesPermitted 1923 | # excluded names as per RFC-5280 1924 | local -a namesExcluded 1925 | # flag set when name constraints are not to be marked critical 1926 | local ncCritical="critical," 1927 | # set the message digest used for signing the certificate 1928 | # default is in config generator (sha256) 1929 | local certMD="" 1930 | # set the RSA signature padding mode 1931 | local sigPad="" 1932 | # set the length of the salt used with RSA-PSS signatures 1933 | local pssSaltLen="" 1934 | # set the mgf1 message digest for RSA-PSS signatures 1935 | local pssMgf1Md="" 1936 | # sets the Basic Key Usage 1937 | local basicKeyUsage="" 1938 | # distinguished name of the signed certificate 1939 | declare -a certDN 1940 | # Subject Alternative Name of the signed certificate 1941 | declare -a subjectAltName 1942 | # flag set when Subject Alternative Name is to be marked critical 1943 | local subjectAltNameCritical="" 1944 | # location of OCSP responder for the CA that issued this certificate 1945 | local ocspResponderURI="" 1946 | # value for the Extended Key Usage extension 1947 | local extendedKeyUsage="" 1948 | # flag to set the ocsp nocheck extension 1949 | local ocspNoCheck="" 1950 | # flag to remove Authority Key Identifier extension from certificate 1951 | local noAuthKeyId="" 1952 | # flag to remove Subject Key Identifier extension from certificate 1953 | local noSubjKeyId="" 1954 | 1955 | # 1956 | # parse options 1957 | # 1958 | 1959 | local TEMP=$(getopt -o v:t: -l CA: \ 1960 | -l DN: \ 1961 | -l notAfter: \ 1962 | -l notBefore: \ 1963 | -l caTrue \ 1964 | -l caFalse \ 1965 | -l crlDistributionPoints: \ 1966 | -l noBasicConstraints \ 1967 | -l bcPathLen: \ 1968 | -l bcCritical \ 1969 | -l ncPermit: \ 1970 | -l ncExclude: \ 1971 | -l ncNotCritical \ 1972 | -l basicKeyUsage: \ 1973 | -l md: \ 1974 | -l padding: \ 1975 | -l pssSaltLen: \ 1976 | -l pssMgf1Md: \ 1977 | -l subjectAltName: \ 1978 | -l subjectAltNameCritical \ 1979 | -l ocspResponderURI: \ 1980 | -l extendedKeyUsage: \ 1981 | -l ocspNoCheck:: \ 1982 | -l noAuthKeyId \ 1983 | -l noSubjKeyId \ 1984 | -n x509CertSign -- "$@") 1985 | if [ $? -ne 0 ]; then 1986 | echo "x509CertSign: can't parse options" >&2 1987 | return 1 1988 | fi 1989 | 1990 | eval set -- "$TEMP" 1991 | 1992 | while true ; do 1993 | case "$1" in 1994 | -v) certV="$2"; shift 2 1995 | ;; 1996 | -t) certRole="$2"; shift 2 1997 | ;; 1998 | --CA) caAlias="$2"; shift 2 1999 | ;; 2000 | --DN) certDN=("${certDN[@]}" "$2"); shift 2 2001 | ;; 2002 | --notAfter) notAfter="$2"; shift 2 2003 | ;; 2004 | --notBefore) notBefore="$2"; shift 2 2005 | ;; 2006 | --caTrue) basicConstraints="CA:TRUE"; shift 1 2007 | ;; 2008 | --caFalse) basicConstraints="CA:FALSE"; shift 1 2009 | ;; 2010 | --crlDistributionPoints) crlDistributionPoints="$2"; shift 2 2011 | ;; 2012 | --noBasicConstraints) basicConstraints="undefined"; shift 1 2013 | ;; 2014 | --bcPathLen) bcPathLen="$2"; shift 2 2015 | ;; 2016 | --bcCritical) bcCritical="true"; shift 1 2017 | ;; 2018 | --basicKeyUsage) basicKeyUsage="$2"; shift 2 2019 | ;; 2020 | --ncPermit) namesPermitted=("${namesPermitted[@]}" "$2"); shift 2 2021 | ;; 2022 | --ncExclude) namesExcluded=("${namesExcluded[@]}" "$2"); shift 2 2023 | ;; 2024 | --ncNotCritical) ncCritical=""; shift 1 2025 | ;; 2026 | --md) certMD="$2"; shift 2 2027 | ;; 2028 | --padding) sigPad="$2"; shift 2 2029 | ;; 2030 | --pssSaltLen) pssSaltLen="$2"; shift 2 2031 | ;; 2032 | --pssMgf1Md) pssMgf1Md="$2"; shift 2 2033 | ;; 2034 | --subjectAltName) subjectAltName=("${subjectAltName[@]}" "$2"); shift 2 2035 | ;; 2036 | --subjectAltNameCritical) subjectAltNameCritical="true"; shift 1 2037 | ;; 2038 | --ocspResponderURI) ocspResponderURI="$2"; shift 2 2039 | ;; 2040 | --extendedKeyUsage) extendedKeyUsage="$2"; shift 2 2041 | ;; 2042 | --ocspNoCheck) if [[ -z $2 ]]; then 2043 | ocspNoCheck="true" 2044 | else 2045 | ocspNoCheck="critical" 2046 | fi 2047 | shift 2 2048 | ;; 2049 | --noAuthKeyId) noAuthKeyId="true"; shift 1 2050 | ;; 2051 | --noSubjKeyId) noSubjKeyId="true"; shift 1 2052 | ;; 2053 | --) shift 1 2054 | break 2055 | ;; 2056 | *) echo "x509CertSign: Unknown option: $1" >&2 2057 | return 1 2058 | esac 2059 | done 2060 | 2061 | kAlias="$1" 2062 | 2063 | # 2064 | # sanity check options 2065 | # 2066 | 2067 | if [ ! -e "$kAlias/$x509PKEY" ]; then 2068 | echo "x509CertSign: Private key to be signed does not exist" >&2 2069 | return 1 2070 | fi 2071 | 2072 | if [ ! -e "$caAlias/$x509PKEY" ]; then 2073 | echo "x509CertSign: CA private key does not exist" >&2 2074 | return 1 2075 | fi 2076 | 2077 | if [ ! -e "$caAlias/$x509CERT" ]; then 2078 | echo "x509CertSign: CA certificate does not exist" >&2 2079 | return 1 2080 | fi 2081 | 2082 | if [[ $certV != "1" ]] && [[ $certV != "3" ]]; then 2083 | echo "x509CertSign: Only version 1 and 3 certificates are supported" \ 2084 | >&2 2085 | return 1 2086 | fi 2087 | 2088 | certRole=$(tr '[:upper:]' '[:lower:]' <<< ${certRole}) 2089 | if [[ $certRole != "ca" ]] && [[ $certRole != "webserver" ]] \ 2090 | && [[ $certRole != "webclient" ]] && [[ $certRole != "none" ]]; then 2091 | 2092 | echo "x509CertSign: Unknown role: '$certRole'" >&2 2093 | return 1 2094 | fi 2095 | 2096 | if [ ${#certDN[@]} -eq 0 ]; then 2097 | case $certRole in 2098 | ca) certDN=("${certDN[@]}" "O = Example intermediate CA") 2099 | ;; 2100 | webserver) certDN=("${certDN[@]}" "CN = localhost") 2101 | ;; 2102 | webclient) certDN=("${certDN[@]}" "CN = John Smith") 2103 | ;; 2104 | none) certDN=("${certDN[@]}" "O = No role cert") 2105 | ;; 2106 | *) echo "x509CertSign: Unknown cert role: $certRole" >&2 2107 | return 1 2108 | ;; 2109 | esac 2110 | fi 2111 | 2112 | if [[ "$sigPad" && "$sigPad" != "pss" && "$pssSaltLen" ]] \ 2113 | || [[ -z "$sigPad" && "$pssSaltLen" ]]; then 2114 | 2115 | echo "x509CertSign: pssSaltLen is only applicable to pss padding" >&2 2116 | return 1 2117 | fi 2118 | if [[ "$sigPad" && "$sigPad" != "pss" && "$pssMgf1Md" ]] \ 2119 | || [[ -z "$sigPad" && "$pssMgf1Md" ]]; then 2120 | 2121 | echo "x509CertSign: pssMgf1Md is only applicable to pss padding" >&2 2122 | return 1 2123 | fi 2124 | 2125 | if [[ -z $notAfter ]] && [[ $certRole == "ca" ]]; then 2126 | notAfter="10 years" 2127 | fi # default of "1 year" for other roles is in config generator 2128 | 2129 | if [[ -z $notBefore ]] && [[ $certRole == "ca" ]]; then 2130 | notBefore="5 years ago" 2131 | fi # default of "now" for other roles is in config generator 2132 | 2133 | if [[ ! -z $bcPathLen ]]; then 2134 | if [[ $basicConstraints == "undefined" ]] || 2135 | [[ $basicConstraints == "CA:FALSE" ]]; then 2136 | echo "x509SelfSign: Path len can be specified only with caTrue "\ 2137 | "option" >&2 2138 | return 1 2139 | fi 2140 | if [[ $certRole != "ca" ]] && [[ -z $basicConstraints ]]; then 2141 | echo "x509SelfSign: Only ca role uses CA:TRUE constraint, use "\ 2142 | "--caTrue to override" >&2 2143 | return 1; 2144 | fi 2145 | fi 2146 | 2147 | if [[ -z $basicConstraints ]]; then 2148 | case $certRole in 2149 | ca) basicConstraints="CA:TRUE" 2150 | bcCritical="true" 2151 | ;; 2152 | # for other usages, the recommendation is to not define it at 2153 | # all 2154 | esac 2155 | fi 2156 | 2157 | local basicConstraintsOption="" 2158 | if [[ $bcCritical == "true" ]]; then 2159 | basicConstraintsOption="critical, " 2160 | fi 2161 | if [[ $basicConstraints == "undefined" ]]; then 2162 | basicConstraintsOption="" 2163 | else 2164 | basicConstraintsOption="${basicConstraintsOption}${basicConstraints}" 2165 | if [[ ! -z $bcPathLen ]]; then 2166 | basicConstraintsOption="${basicConstraintsOption}, pathlen: ${bcPathLen}" 2167 | fi 2168 | fi 2169 | 2170 | if [[ -z $basicKeyUsage ]]; then 2171 | case $certRole in 2172 | ca) basicKeyUsage="critical, keyCertSign, cRLSign" 2173 | ;; 2174 | webserver) basicKeyUsage="critical, digitalSignature, " 2175 | basicKeyUsage="${basicKeyUsage}keyEncipherment, keyAgreement" 2176 | ;; 2177 | webclient) basicKeyUsage="digitalSignature, keyEncipherment" 2178 | ;; 2179 | none) 2180 | ;; 2181 | *) echo "x509SelfSign: Unknown cert role: $certRole" >&2 2182 | return 1 2183 | ;; 2184 | esac 2185 | fi 2186 | 2187 | if [[ -z $extendedKeyUsage ]]; then 2188 | case $certRole in 2189 | webserver) extendedKeyUsage="serverAuth" 2190 | ;; 2191 | webclient) extendedKeyUsage="clientAuth,emailProtection" 2192 | ;; 2193 | esac 2194 | fi 2195 | 2196 | # 2197 | # prepare configuration file for signing 2198 | # 2199 | 2200 | declare -a parameters 2201 | local option 2202 | for option in "${certDN[@]}"; do 2203 | parameters=("${parameters[@]}" "--dn=$option") 2204 | done 2205 | 2206 | if [[ ! -z $notAfter ]]; then 2207 | parameters=("${parameters[@]}" "--notAfter=$notAfter") 2208 | fi 2209 | if [[ ! -z $notBefore ]]; then 2210 | parameters=("${parameters[@]}" "--notBefore=$notBefore") 2211 | fi 2212 | 2213 | if [[ ! -z $basicConstraintsOption ]]; then 2214 | parameters=("${parameters[@]}" "--basicConstraints=$basicConstraintsOption") 2215 | fi 2216 | 2217 | if [[ ! -z $basicKeyUsage ]]; then 2218 | parameters=("${parameters[@]}" "--basicKeyUsage=$basicKeyUsage") 2219 | fi 2220 | 2221 | local nameConstraints="$(__INTERNAL_x509NamesToNCs namesPermitted[@] permitted)" 2222 | local joinedNamesExcluded="$(__INTERNAL_x509NamesToNCs namesExcluded[@] excluded)" 2223 | if [[ ! -z $nameConstraints && ! -z $joinedNamesExcluded ]]; then 2224 | nameConstraints="${nameConstraints},${joinedNamesExcluded}" 2225 | else 2226 | nameConstraints="${nameConstraints}${joinedNamesExcluded}" 2227 | fi 2228 | if [[ ! -z $nameConstraints ]]; then 2229 | parameters=("${parameters[@]}" "--x509v3Extension=nameConstraints=$ncCritical$nameConstraints") 2230 | fi 2231 | 2232 | if [[ -n $certMD ]]; then 2233 | parameters=("${parameters[@]}" "--md=$certMD") 2234 | fi 2235 | 2236 | if [[ ! -z $crlDistributionPoints ]]; then 2237 | parameters=("${parameters[@]}" "--crlDistributionPoints=${crlDistributionPoints}") 2238 | fi 2239 | 2240 | local name 2241 | for name in "${subjectAltName[@]}"; do 2242 | parameters=("${parameters[@]}" "--subjectAltName=$name") 2243 | done 2244 | 2245 | if [[ $subjectAltNameCritical == "true" ]]; then 2246 | parameters=("${parameters[@]}" "--subjectAltNameCritical") 2247 | fi 2248 | 2249 | if [[ ! -z $ocspResponderURI ]]; then 2250 | parameters=("${parameters[@]}" "--authorityInfoAccess=OCSP;URI:${ocspResponderURI}") 2251 | fi 2252 | 2253 | if [[ ! -z $extendedKeyUsage ]]; then 2254 | parameters=("${parameters[@]}" "--extendedKeyUsage=$extendedKeyUsage") 2255 | fi 2256 | 2257 | # DER:05:00 is a DER encoding of NULL (empty) 2258 | if [[ $ocspNoCheck == "true" ]]; then 2259 | parameters=("${parameters[@]}" "--x509v3Extension=noCheck=DER:05:00") 2260 | fi 2261 | if [[ $ocspNoCheck == "critical" ]]; then 2262 | parameters=("${parameters[@]}" "--x509v3Extension=noCheck=critical,DER:05:00") 2263 | fi 2264 | 2265 | if [[ $noSubjKeyId != "true" ]]; then 2266 | parameters=("${parameters[@]}" "--subjectKeyIdentifier") 2267 | fi 2268 | 2269 | if [[ $noAuthKeyId != "true" ]]; then 2270 | parameters=("${parameters[@]}" "--authorityKeyIdentifier=keyid,issuer") 2271 | fi 2272 | 2273 | __INTERNAL_x509GenConfig "${parameters[@]}" "$caAlias" 2274 | if [ $? -ne 0 ]; then 2275 | return 1 2276 | fi 2277 | 2278 | # 2279 | # create the certificate 2280 | # 2281 | 2282 | ${x509OPENSSL} req -new -batch -key "$kAlias/$x509PKEY" \ 2283 | -out "$kAlias/$x509CSR" \ 2284 | -config "$caAlias/$x509CACNF" 2285 | if [ $? -ne 0 ]; then 2286 | echo "x509CertSign: Certificate Signing Request generation failed" >&2 2287 | return 1 2288 | fi 2289 | 2290 | declare -a caOptions 2291 | caOptions=("${caOptions[@]}" "-preserveDN") 2292 | if [[ $certV == "3" ]]; then 2293 | caOptions=("${caOptions[@]}" "-extensions" "v3_ext") 2294 | fi 2295 | if [[ ! -z $sigPad ]]; then 2296 | caOptions=("${caOptions[@]}" "-sigopt" "rsa_padding_mode:$sigPad") 2297 | fi 2298 | if [[ ! -z $pssSaltLen ]]; then 2299 | caOptions=("${caOptions[@]}" "-sigopt" "rsa_pss_saltlen:$pssSaltLen") 2300 | fi 2301 | if [[ ! -z $pssMgf1Md ]]; then 2302 | caOptions=("${caOptions[@]}" "-sigopt" "rsa_mgf1_md:$pssMgf1Md") 2303 | fi 2304 | 2305 | ${x509OPENSSL} ca -config "$caAlias/$x509CACNF" -batch \ 2306 | -keyfile "$caAlias/$x509PKEY" \ 2307 | -cert "$caAlias/$x509CERT" \ 2308 | -in "$kAlias/$x509CSR" \ 2309 | -out "$kAlias/$x509CERT" \ 2310 | "${caOptions[@]}" 2311 | if [ $? -ne 0 ]; then 2312 | echo "x509CertSign: Signing of the certificate failed" >&2 2313 | return 1 2314 | fi 2315 | } 2316 | 2317 | true <<'=cut' 2318 | =pod 2319 | 2320 | =head2 x509Key() 2321 | 2322 | Return the key associated with given alias. 2323 | 2324 | =over 4 2325 | 2326 | B 2327 | I 2328 | [B<--der>] 2329 | [B<--pkcs12>] 2330 | [B<--with-cert>] 2331 | [B<--pkcs8>] 2332 | 2333 | =back 2334 | 2335 | The function returns on standard output the relative path of the file 2336 | that contains the PEM formatted (SSLeay or PKCS#8), unencrypted 2337 | private key file. 2338 | 2339 | To be used for simple variable substitution on command line, e.g.: 2340 | 2341 | openssl rsa -in $(x509Key ca) -noout -text 2342 | 2343 | Note that the function doesn't check if the private file was actually 2344 | generated or that conversion to DER format was successful. 2345 | 2346 | =over 2347 | 2348 | =item I 2349 | 2350 | Name of the key to return key file for 2351 | 2352 | =item B<--der> 2353 | 2354 | Convert a copy of the private key to DER format and output the location of the 2355 | DER encoded file (binary, not base64). 2356 | 2357 | =item B<--pkcs12> 2358 | 2359 | Convert a copy of the private key to PKCS#12 format and output the location 2360 | of PKCS#12 encoded file. The key will be encrypted with null password and 2361 | PKCS#5 v2 PBE/PBKDF and DES-EDE3-CBC cipher (strongest supported by NSS) 2362 | when using OpenSSL before 3.0 and with AES when using 3.0 (in practice 2363 | it uses the default parameters in OpenSSL 3.0). 2364 | Its friendly name will be set to alias. 2365 | 2366 | Note that the export does cache the last exported file, so if you exported the 2367 | key or certificate to PKCS#12 format before, you will have to `rm` the previous 2368 | file first. 2369 | 2370 | =item B<--with-cert> 2371 | When exporting to the PKCS#12 format, include the certificate too. 2372 | 2373 | =item B<--password> I 2374 | When exporting to the PKCS#12 format, use the I as the password 2375 | of the file. Uses empty string by default 2376 | 2377 | =item B<--pkcs8> 2378 | 2379 | Convert a copy of the private key to PKCS#8 format and output the location 2380 | of PKCS#8 encoded file. Can be combined with B<--der>. 2381 | 2382 | Note that the export does cache the last exported file, so if you exported the 2383 | key or certificate to PKCS#8 format before, you will have to `rm` the previous 2384 | file first. 2385 | 2386 | =back 2387 | 2388 | =cut 2389 | 2390 | x509Key() { 2391 | 2392 | # generate DER file? 2393 | local der="false" 2394 | # generate PKCS#12 file? 2395 | local pkcs12="false" 2396 | # generate PKCS#8 file? 2397 | local pkcs8="false" 2398 | # include certificate in PKCS#12 file? 2399 | local withCert="false" 2400 | # name of the key to return 2401 | local kAlias 2402 | # password to use for PKCS#12 export 2403 | local password='' 2404 | 2405 | local TEMP=$(getopt -o h -l der -l pkcs12 -l with-cert -l pkcs8,password:\ 2406 | -n x509Key -- "$@") 2407 | if [ $? -ne 0 ]; then 2408 | echo "x509Key: can't parse options" >&2 2409 | return 1 2410 | fi 2411 | 2412 | eval set -- "$TEMP" 2413 | 2414 | while true ; do 2415 | case "$1" in 2416 | --der) der="true"; shift 1 2417 | ;; 2418 | --pkcs12) pkcs12="true"; shift 1 2419 | ;; 2420 | --with-cert) withCert="true"; shift 1 2421 | ;; 2422 | --pkcs8) pkcs8="true"; shift 1 2423 | ;; 2424 | --password) password="$2"; shift 2 2425 | ;; 2426 | --) shift 1 2427 | break 2428 | ;; 2429 | esac 2430 | done 2431 | 2432 | kAlias="$1" 2433 | 2434 | if [[ $der == "true" ]] && [[ $pkcs12 == "true" ]]; then 2435 | echo "Can't export PKCS#12 and DER together" >&2 2436 | return 1 2437 | fi 2438 | 2439 | if [[ $pkcs12 == "true" ]] && [[ $pkcs8 == "true" ]]; then 2440 | echo "Can't export PKCS#12 and PKCS#8 together" >&2 2441 | return 1 2442 | fi 2443 | 2444 | if [[ $der == "true" ]]; then 2445 | if [[ $pkcs8 == "true" ]]; then 2446 | if [[ ! -e $kAlias/$x509PKCS8DERKEY ]]; then 2447 | ${x509OPENSSL} pkcs8 -topk8 -in "$kAlias/$x509PKEY" -nocrypt \ 2448 | -outform DER -out "$kAlias/$x509PKCS8DERKEY" 2449 | fi 2450 | echo "$kAlias/$x509PKCS8DERKEY" 2451 | else 2452 | if [[ ! -e $kAlias/$x509DERKEY ]]; then 2453 | # openssl 0.9.8 doesn't have pkey subcommand, simulate it with 2454 | # rsa and dsa subcommands, ec subcommand is not supported there 2455 | if ${x509OPENSSL} version | grep -q '0[.]9[.].'; then 2456 | if [[ -e "$kAlias/dsa_params.pem" ]]; then 2457 | ${x509OPENSSL} dsa -in "$kAlias/$x509PKEY" \ 2458 | -outform DER -out "$kAlias/$x509DERKEY" 2459 | elif grep -q 'BEGIN RSA PRIVATE KEY' "$kAlias/$x509PKEY" \ 2460 | || grep -q 'BEGIN PRIVATE KEY' "$kAlias/$x509PKEY"; then 2461 | ${x509OPENSSL} rsa -in "$kAlias/$x509PKEY" \ 2462 | -outform DER -out "$kAlias/$x509DERKEY" 2463 | else 2464 | echo "Private key in unknown format" >&2 2465 | return 1 2466 | fi 2467 | 2468 | else 2469 | ${x509OPENSSL} pkey -in "$kAlias/$x509PKEY" -outform DER \ 2470 | -out "$kAlias/$x509DERKEY" 2471 | fi 2472 | fi 2473 | echo "$kAlias/$x509DERKEY" 2474 | fi 2475 | elif [[ $pkcs12 == "true" ]]; then 2476 | if [[ ! -e $kAlias/$x509PKCS12 ]]; then 2477 | local -a options 2478 | options=(-export -out "$kAlias/$x509PKCS12" 2479 | -passout "pass:$password" 2480 | -inkey "$kAlias/$x509PKEY" -name "$kAlias") 2481 | # NSS doesn't support MACs other than MD5 and SHA1, or encryption 2482 | # stronger than 3DES, see RHBZ#1220573 2483 | # old OpenSSL doesn't support setting MAC at all 2484 | if ${x509OPENSSL} version | grep -q '0[.]9[.].'; then 2485 | options=(${options[@]} -keypbe PBE-SHA1-3DES) 2486 | elif ${x509OPENSSL} version | grep -q '1[.][01][.]'; then 2487 | options=(${options[@]} -keypbe DES-EDE3-CBC -macalg SHA1) 2488 | # else # OpenSSL 3.0+ 2489 | # use default (should be AES) 2490 | fi 2491 | 2492 | if [[ $withCert == "true" ]]; then 2493 | options=("${options[@]}" -in "$kAlias/$x509CERT" 2494 | -caname "$kAlias") 2495 | 2496 | # old OpenSSL versions don't support no encryption on certs 2497 | # use the weakest suppported by current (2015) FIPS 2498 | if ${x509OPENSSL} version | grep -q '0[.]9[.]7'; then 2499 | options=(${options[@]} -certpbe PBE-SHA1-3DES) 2500 | else 2501 | options=(${options[@]} -certpbe NONE) 2502 | fi 2503 | else 2504 | if ${x509OPENSSL} version | grep -q '0[.]9[.]7'; then 2505 | echo "Export without certificate unsupported with this version of OpenSSL, try --with-cert" >&2 2506 | return 1 2507 | fi 2508 | options=("${options[@]}" -nocerts) 2509 | fi 2510 | ${x509OPENSSL} pkcs12 ${options[@]} 2511 | if [[ $? -ne 0 ]]; then 2512 | echo "Key export failed" >&2 2513 | return 1 2514 | fi 2515 | fi 2516 | echo "$kAlias/$x509PKCS12" 2517 | elif [[ $pkcs8 == "true" ]]; then 2518 | if [[ ! -e $kAlias/$x509PKCS8KEY ]]; then 2519 | ${x509OPENSSL} pkcs8 -topk8 -in "$kAlias/$x509PKEY" -nocrypt \ 2520 | -out "$kAlias/$x509PKCS8KEY" 2521 | fi 2522 | echo "$kAlias/$x509PKCS8KEY" 2523 | else 2524 | echo "$kAlias/$x509PKEY" 2525 | fi 2526 | } 2527 | 2528 | true <<'=cut' 2529 | =pod 2530 | 2531 | =head2 x509Cert() 2532 | 2533 | Return the certificate associated with given alias. 2534 | 2535 | =over 4 2536 | 2537 | B 2538 | I 2539 | [B<--der>] 2540 | [B<--pkcs12>] 2541 | 2542 | =back 2543 | 2544 | The function returns on standard output the relative path of the file 2545 | that contains the PEM formatted X.509 certificate associated with provided 2546 | alias. 2547 | 2548 | To be used for simple variable substitution on command line, e.g.: 2549 | 2550 | openssl x509 -in $(x509Cert ca) -noout -text 2551 | 2552 | Note that the function doesn't check if the certificate was actually signed 2553 | before. 2554 | 2555 | Note that the DER format and PKCS#12 format files are cached, as such, if you 2556 | regenerated certificate you will have to remove them to get the new cert in 2557 | those formats. 2558 | 2559 | =over 2560 | 2561 | =item I 2562 | 2563 | Name of the certificate-key pair to return the certificate for. 2564 | 2565 | =item B<--der> 2566 | 2567 | Convert a copy of the certificate to the DER format and print on standard 2568 | output the file name of the copy. 2569 | 2570 | =item B<--pkcs12> 2571 | 2572 | Convert a copy of the certificate to the PKCS#12 format and print on standard 2573 | output the file name of the copy. Friendly name of the certificate in PKCS#12 2574 | file is set to the alias. 2575 | 2576 | =item B<--password> I 2577 | When exporting to the PKCS#12 format, use the I as the password 2578 | of the file. Uses empty string by default 2579 | 2580 | =back 2581 | 2582 | =cut 2583 | 2584 | x509Cert() { 2585 | 2586 | # generate DER file? 2587 | local der="false" 2588 | # generate PKCS12 file? 2589 | local pkcs12="false" 2590 | # password to use (empty by default) 2591 | local password="" 2592 | # name of the key to return 2593 | local kAlias 2594 | 2595 | local TEMP=$(getopt -o h -l der -l pkcs12 -l password:\ 2596 | -n x509Cert -- "$@") 2597 | if [ $? -ne 0 ]; then 2598 | echo "x509Cert: can't parse options" >&2 2599 | return 1 2600 | fi 2601 | 2602 | eval set -- "$TEMP" 2603 | 2604 | while true ; do 2605 | case "$1" in 2606 | --der) der="true"; shift 1 2607 | ;; 2608 | --pkcs12) pkcs12="true"; shift 1 2609 | ;; 2610 | --password) password="$2"; shift 2 2611 | ;; 2612 | --) shift 1 2613 | break 2614 | ;; 2615 | esac 2616 | done 2617 | 2618 | if [ $der == "true" ] && [ $pkcs12 == "true" ]; then 2619 | echo "Can't export DER and PKCS12 files at the same time!" >&2 2620 | return 1 2621 | fi 2622 | 2623 | kAlias="$1" 2624 | 2625 | if [[ $der == "true" ]]; then 2626 | if [[ ! -e $kAlias/$x509DERCERT ]]; then 2627 | ${x509OPENSSL} x509 -in "$kAlias/$x509CERT" -outform DER \ 2628 | -out "$kAlias/$x509DERCERT" 2629 | if [ $? -ne 0 ]; then 2630 | echo "File conversion failed" >&2 2631 | return 1 2632 | fi 2633 | fi 2634 | echo "$kAlias/$x509DERCERT" 2635 | elif [[ $pkcs12 == "true" ]]; then 2636 | if [[ ! -e $kAlias/$x509PKCS12 ]]; then 2637 | local -a options 2638 | options=(-export -out "$kAlias/$x509PKCS12" 2639 | -in "$kAlias/$x509CERT" -caname "$kAlias" 2640 | -nokeys -passout "pass:$password") 2641 | 2642 | # Old OpenSSL versions don't support lack of encryption on 2643 | # certificate, use the weakest supported by current (2015) FIPS 2644 | if ${x509OPENSSL} version | grep -q '0[.]9[.]7'; then 2645 | options=(${options[@]} -certpbe PBE-SHA1-3DES) 2646 | else 2647 | options=(${options[@]} -certpbe NONE) 2648 | fi 2649 | 2650 | # note that NSS doesn't support MACs other that MD5 and SHA1 2651 | # see RHBZ#1220573 2652 | # older version of openssl don't support setting MAC at all 2653 | if ${x509OPENSSL} version | grep -q '0[.]9[.].'; then 2654 | : 2655 | elif ${x509OPENSSL} version | grep -q '1[.][01][.]'; then 2656 | options=(${options[@]} -macalg SHA1) 2657 | fi 2658 | 2659 | local ret 2660 | ${x509OPENSSL} pkcs12 "${options[@]}" 2661 | ret=$? 2662 | 2663 | # old openssl has broken return codes... 2664 | if ! ${x509OPENSSL} version | grep -q '0[.]9[.]7'; then 2665 | if [ $ret -ne 0 ]; then 2666 | echo "File conversion failed" >&2 2667 | return 1 2668 | fi 2669 | fi 2670 | fi 2671 | echo "$kAlias/$x509PKCS12" 2672 | else 2673 | echo "$kAlias/$x509CERT" 2674 | fi 2675 | } 2676 | 2677 | true <<'=cut' 2678 | =pod 2679 | 2680 | =head2 x509DumpCert() 2681 | 2682 | Output text version of certificate to standard output 2683 | 2684 | =over 4 2685 | 2686 | B 2687 | I 2688 | 2689 | =back 2690 | 2691 | Used as a shorthand for C: 2692 | 2693 | openssl x509 -in $(x509Cert alias) -noout -text 2694 | 2695 | =over 2696 | 2697 | =item I 2698 | 2699 | Specify the name of the certificate to dump 2700 | 2701 | =back 2702 | 2703 | =cut 2704 | 2705 | x509DumpCert(){ 2706 | 2707 | ${x509OPENSSL} x509 -in $(x509Cert "$1") -noout -text 2708 | } 2709 | 2710 | true <<'=cut' 2711 | =pod 2712 | 2713 | =head2 x509RmAlias() 2714 | 2715 | Remove private key, certificate and settings related to given alias. 2716 | 2717 | =over 4 2718 | 2719 | B 2720 | I 2721 | 2722 | =back 2723 | 2724 | =over 2725 | 2726 | =item I 2727 | 2728 | Name of the private key to remove 2729 | 2730 | =back 2731 | 2732 | =cut 2733 | 2734 | x509RmAlias() { 2735 | 2736 | if [[ -e $1/$x509PKEY ]]; then 2737 | rm -rf "$1" 2738 | return $? 2739 | else 2740 | echo "Alias does not refer to certgen library directory" >&2 2741 | return 1 2742 | fi 2743 | } 2744 | 2745 | true <<'=cut' 2746 | =pod 2747 | 2748 | =head2 x509Revoke() 2749 | 2750 | Revoke a certificate. 2751 | 2752 | =over 4 2753 | 2754 | B 2755 | B<--CA> I 2756 | [B<--crlReason> I] 2757 | [B<--crlCompromiseTime> I] 2758 | [B<--crlCACompromiseTime> I] 2759 | I 2760 | 2761 | =back 2762 | 2763 | =over 2764 | 2765 | =item B<--CA> I 2766 | 2767 | The CA alias which issued the certificate to be revoked. 2768 | 2769 | The specified CA must have issued the certificate to be revoked. 2770 | 2771 | =back 2772 | 2773 | =item B<--crlReason> I 2774 | 2775 | The reason for the certificate to be revoked. 2776 | 2777 | Acceptable values are B, B, B, 2778 | B, B, B, 2779 | B, and . The matching of reason is case 2780 | insensitive. Setting any revocation reason will make the CRL v2. 2781 | 2782 | In practice removeFromCRL is not particularly useful because it is only used in 2783 | delta CRLs which are not currently implemented. 2784 | 2785 | =back 2786 | 2787 | =item B<--crlCompromiseTime> I