├── .gitignore ├── .travis.yml ├── Notes.txt ├── README ├── README.md ├── mk-cert ├── mk-ovpn ├── mk-ovpn2 ├── mk-ss ├── testrun.sh └── whatssl /.gitignore: -------------------------------------------------------------------------------- 1 | certs 2 | demoCA 3 | 4 | # You don't need to see my identification. 5 | *.pem 6 | *.pfx 7 | *.crt 8 | *.cer 9 | *.key 10 | *.crl 11 | *.jks 12 | *.csr 13 | *.p7b 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # https://travis-ci.org/rdebath/x509scripts/builds 2 | language: generic 3 | 4 | dist: bionic 5 | 6 | os: 7 | - linux 8 | - osx 9 | 10 | script: 11 | bash testrun.sh 12 | -------------------------------------------------------------------------------- /Notes.txt: -------------------------------------------------------------------------------- 1 | One line openssl commands 2 | ========================= 3 | 4 | This is a collect of random notes about certificates. First there's 5 | small collection of openssl command lines that do various useful things 6 | with certificates and keys. Many of these need the default OPENSSL_CONF 7 | setup. Then there's a couple of JAVA "keytool" commands, a couple of SSL 8 | scanning websites, PEM file format stuff, OpenVPN cert notes and notes 9 | about the various fields in a certificate. 10 | 11 | 12 | # Create an openssl default CA certificate. 13 | openssl req -new -x509 -nodes -keyout ca.key -out ca.crt -subj /CN=localhost/ 14 | 15 | # Create an openssl self signed certificate. 16 | openssl req -new -x509 -nodes -keyout self.key -out self.crt -extensions usr_cert -subj /CN=localhost/ 17 | 18 | # Create a V3 certificate using a CSR and CA 19 | openssl req -new -nodes -keyout svr.key -subj /CN=localhost/ >svr.csr 20 | openssl x509 -req -out svr.crt -CA ca.crt -CAkey ca.key -CAcreateserial -extfile /usr/lib/ssl/openssl.cnf -extensions usr_cert plain.csr 24 | openssl x509 -req -out plain.crt -CA ca.crt -CAkey ca.key -CAcreateserial dhparam-2048.pem 28 | 29 | # Create a plain self signed certificate. 30 | openssl req -new -x509 -nodes -keyout plss.key -out plss.crt -subj /CN=localhost/ -newkey rsa:3072 -config <(echo [req];echo distinguished_name=req ) 31 | 32 | # Create a PFX file 33 | openssl pkcs12 -export -passout pass: -out svr.pfx -in svr.pem 34 | # or 35 | openssl pkcs12 -export -passout pass:123456 -out svr.pfx -name localhost -inkey svr.key -in svr.crt 36 | 37 | # Extract a PFX file 38 | openssl pkcs12 -out svr.pem -in svr.pfx -info -nodes -passin pass:123456 39 | 40 | # Convert a certificate from DER to PEM 41 | openssl x509 -in cert.der -inform DER -out cert.pem 42 | 43 | # Create SAN certificates 44 | openssl req -keyout sssan.key -out sssan.crt -new -x509 -nodes -newkey rsa -config <( echo "[req]" ; echo "distinguished_name = rdn" ; echo "prompt = no" ; echo "x509_extensions = x509v3" ; echo "[rdn]" ; echo "CN=$1" ; echo "[x509v3]" ; echo "subjectAltName=DNS:$1$(shift;echo "${*/#/,DNS:}")" ) 45 | 46 | # Okay ... I think that shows why I wrote the script :-) 47 | 48 | One Line JAVA keytool commands 49 | ============================== 50 | 51 | # Imports all three items (CA, cert & key) from PFX file. MUST have a 6 character+ password on the pfx. 52 | keytool -importkeystore -srckeystore pfx1.p12 -srcstoretype PKCS12 -deststoretype JKS -destkeystore javaKeyStore.jks -srcstorepass 123456 -deststorepass 123456 53 | 54 | # And back to pkcs12 55 | keytool -importkeystore -destkeystore pfx2.p12 -deststoretype PKCS12 -srcstoretype JKS -srckeystore javaKeyStore.jks -srcstorepass 123456 -deststorepass 123456 56 | 57 | # Import a certificate as a "trusted certificate" 58 | keytool -importcert -keystore x.jks -file x.ca.pem -storepass 123456 -noprompt 59 | 60 | # What's in the file 61 | keytool -list -keystore javaKeyStore.jks -storepass 123456 62 | 63 | -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 64 | 65 | SSL Scanners 66 | ============ 67 | 68 | https://www.ssllabs.com/ssltest 69 | 70 | http://wiki.cacert.org/SSLScanner 71 | 72 | $ nmap --script ssl-enum-ciphers -p 443 example.com 73 | 74 | -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 75 | 76 | PEM file order 77 | ============== 78 | 79 | https://www.digicert.com/ssl-support/pem-ssl-creation.htm 80 | 81 | --> Private key 82 | --> Matching certificate 83 | --> Certificate intermediates 84 | --> Certificate root 85 | 86 | openssl pkcs12 -in certs/testhost.plus-ca.pfx -out x.pem -nodes 87 | 88 | --> Matching certificate 89 | --> Other certificates 90 | --> Private key 91 | 92 | openssl req -new -x509 -nodes -keyout self.pem -out self.pem -extensions usr_cert -subj /CN=localhost/ 93 | 94 | --> Private key 95 | --> Matching certificate 96 | 97 | Result: 98 | ------- 99 | 100 | The location of the key in the file does not matter. 101 | The primary certificate must be the first certificate in the file. 102 | Each of the following certificates must sign the one previous to it. 103 | 104 | The root CA certificate MAY be in the file, however, ssllabs will give 105 | you a warning about this and the client is unlikely to use it for anything 106 | as there's no proof it should be trusted. If the client does use it it'll 107 | be an optimisation so it can find it's own copy of the key more quickly. 108 | 109 | -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 110 | 111 | Other OIDs / ASN.1 Objects 112 | ========================== 113 | 114 | 2.25.292783505130108047603316099750004376557 UUID OID 115 | 116 | 2.16.826 Country GB (United Kingdom) 117 | 118 | 1.2.826.0.1.9999999.any 119 | the 9999999 is an England/Wales company number. 120 | 121 | -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 122 | 123 | OpenVPN 124 | ======= 125 | 126 | Server can use: 127 | Normal CA certificates 128 | Self signed V1 certificates 129 | Self signed CA:FALSE certificates. 130 | 131 | Client needs a selector on the server certificate to stop it complaining 132 | nsCertType = server 133 | V3 SSL server limitation 134 | Common name selection 135 | Other Server subject parts selection 136 | 137 | Note: 138 | V3 extensions are designed to prevent certificates being used for 139 | tasks that have not been paid for. 140 | 141 | A local CA 142 | A Client certificate 143 | A Server certificate 144 | 145 | -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 146 | 147 | Max lengths for subject parts 148 | ============================= 149 | 150 | Standard/Normal items for the x509 subject 151 | Note: openssl doesn't check for duplicates ... 152 | commonName CN:64 UTF8 Multi 153 | countryName C:2 PrintableString 154 | localityName L:128 UTF8 Multi 155 | stateOrProvinceName ST:128 UTF8 Multi 156 | organizationName O:64 UTF8 Multi 157 | organizationalUnitName OU:64 UTF8 Multi 158 | emailAddress emailAddress:128 IA5String 159 | unstructuredName unstructuredName UTF8 160 | challengePassword challengePassword 161 | unstructuredAddress unstructuredAddress 162 | givenName GN:32768 UTF8 Multi 163 | surname SN:32768 Multi 164 | title title Multi 165 | initials initials:32768 Multi 166 | serialNumber serialNumber:64 PrintableString 167 | friendlyName friendlyName BMPString 168 | name name:32768 On windows 2.5.4.41 Multi 169 | dnQualifier dnQualifier PrintableString 170 | domainComponen DC IA5String Multi 171 | Microsoft CSP Name CSPName BMPString 172 | rfc822Mailbox mail Len=256? 173 | 174 | NB: 175 | BMPString is a string using limited Unicode "UCS-2LE". 176 | IA5String is just ASCII 177 | PrintableString is mostly identical to ASCII too. 178 | 179 | The dnQualifier is supposed to be used to distinguish names that might 180 | otherwise collide. 181 | 182 | Some of the many others allowed by openssl ... 183 | org ORG 184 | Domain domain 185 | X509 X509 186 | favouriteDrink favouriteDrink 187 | info info UTF8 188 | owner owner 189 | subjectAltName subjectAltName 190 | userId UID 191 | x500UniqueIdentifier x500UniqueIdentifier UTF8 192 | x509Crl x509Crl 193 | 194 | ..... 195 | generationQualifier 196 | 197 | 198 | -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 199 | #!/bin/bash 200 | 201 | D="${1}" 202 | [ -e "$D".pfx ] || :< "$D".pfx || exit 203 | # Windows PFX files can be base64 encoded, but openssl doesn't know that. 204 | { 205 | echo -----BEGIN PKCS12----- 206 | openssl base64 -e -in "$D".pfx 207 | echo -----END PKCS12----- 208 | } > "$D".b64.pfx 209 | 210 | -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 211 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Installing 2 | ========== 3 | 4 | The scripts do not need to be installed, but do prefer recent versions 5 | of Bash (4.1.0 or later) and OpenSSL (1.0.1 or later). You will get 6 | reduced functionality and/or unusable defaults on earlier versions. 7 | 8 | Tested and working on Linux, FreeBSD, NetBSD, MacOSX and Windows. 9 | 10 | To install on Windows you need MSYS2 or Cygwin. The portion of this used in 11 | Git for Windows is sufficient. Other Unix/Linux emulators may work too. 12 | 13 | whatssl bash script 14 | =================== 15 | 16 | Gives a description of the contents of any OpenSSL related certificate, 17 | key or parameter file. If the file contains multiple items they are 18 | all described. If an item is encrypted a tiny dictionary attack will 19 | be attempted. 20 | 21 | Note: It will not print any private part of a file, instead it will 22 | extract the related public key and describe the item as a "private key 23 | for" the displayed public part. 24 | 25 | mk-ovpn and mk-ovpn2 bash scripts 26 | ================================= 27 | 28 | The mk-ovpn script calls mk-cert to make certificates and keys for 29 | OpenVPN. The idea is to use the "singleuseca" mode to create a CA 30 | certificate specifically for every instance of OpenVPN (client and server) 31 | and only have the CA certificates that of the specific keys allowed to 32 | connect listed in the configuration file. The mk-ovpn2 script collects 33 | these CA certificates (and the user certificates and keys) does the 34 | necessary selections for generating each ovpn config file. 35 | 36 | At least one of the certificates must be marked as a server and a, 37 | possibly empty, pattern file is needed to prefix the client setups. 38 | 39 | mk-cert bash script 40 | =================== 41 | 42 | Usage: mk-cert [Common Name] [arguments] 43 | 44 | This script can create most styles of x509 certificate from the command 45 | line. All options can be configured from arguments and the standard 46 | OpenSSL configuration file is not used. The default is to prompt for a 47 | common name (plus OU and dnQualifier) and generate an ec:prime256r1 key 48 | and certificate which is sent to the standard output. 49 | 50 | Options: 51 | 52 | -cn=String 53 | -subj-cn=String 54 | -subj-o=String 55 | -subj-ou=String 56 | -subj-dc=String 57 | -subj-l=String 58 | -subj-st=String 59 | -subj-c=String 60 | -subj:oid=value 61 | 62 | Set a component of the certificate subject. Some items such as 63 | DC, OU and CN are allowed to be duplicated. OpenSSL will accept 64 | duplicates for any item. 65 | 66 | The long names are: 67 | "CN" Common name, put the domainname here. 68 | "O" Organization Name 69 | "OU" Organizational Unit Name 70 | "DC" Domain Component 71 | "L" Locality Name: American for "City", "Town", etc 72 | "ST" State or Province: "County" for rest of world. 73 | "C" ISO two alpha Country code or "XX" 74 | 75 | The "-cn=" option is taken as both a common name, a SAN and the 76 | official common name for generic use. The "-subj-cn=" option 77 | string will only be used in the subject. 78 | 79 | The OU is the most likely to be accepted as a place to store 80 | any unusual part of a name you may wish to include. 81 | 82 | Any arguments without a "-" to make them an option are assumed 83 | to be names too, the first one is a common name, like the "-cn" 84 | option. The rest are san names if they look like domain names 85 | or IP addresses and OU items if they don't. 86 | 87 | If a argument begins with a "/" and contains an "=" it is 88 | treated as an OpenSSL subject string. This conflicts with the 89 | other options for building a subject. 90 | 91 | The bare "-subj:" version copies the string "oid=value" to the 92 | end of the section of the configuration. This allows any of the 93 | possible name parts (surname, info, favouriteDrink, CSPName, etc) 94 | to be added in any order. If OpenSSL doesn't know a particular 95 | oid the numeric value can be used. 96 | 97 | If no subject is specified one will be prompted for by OpenSSL 98 | unless a SAN is requested in which case an error is generated 99 | unless "-no-prompt" is used. 100 | 101 | -days=7304 102 | How many days should the certificate run for? 103 | 104 | -days=2038-01-18 105 | What day should the certificate expire on? 106 | 107 | -alldays 108 | Make the certificate run until 9999-12-31 109 | 110 | -out=FileName 111 | Put the certificate, and any other items in a file. 112 | 113 | -addcert=FileName 114 | Filename contains a certificate, put it in the same location as 115 | the created certificate. (Used for intermediate CA certificates) 116 | 117 | -keyout=FileName 118 | Put the private key in a specific file not "-out=" or stdout. 119 | 120 | -crl 121 | Generate an empty CRL using the certificate used to sign. 122 | 123 | -crlout=FileName 124 | Generate an empty CRL using the certificate used to sign. 125 | 126 | -caout=FileName 127 | Put the certificate used to sign in a specific file. 128 | 129 | -ser=0123456789abcdef 130 | -serial=0123456789abcdef 131 | Set the certificate serial number (in hexadecimal). 132 | Various noise characters will be removed. 133 | 134 | -dnq 135 | Generate and add a dnQualifier item to the subject. 136 | This is the original way of making sure that the subject of 137 | a certificate is unique. It was only needed within an org 138 | as the other DN components were sufficient for identifing 139 | organisations. 140 | 141 | -dnq=... 142 | Add a specific dnQualifier item to the subject. 143 | 144 | -nodnq 145 | Do not add a dnQualifier item to any subject; even if told to. 146 | 147 | -no-prompt 148 | Prevent OpenSSL prompting for any part of the subject. 149 | 150 | -ed25519 151 | -ed448 152 | Generate an ed25519 or ed448 private key. 153 | 154 | -ec:prime256v1 155 | -ec:256 156 | -ec:curve:encoding 157 | -ec:curve:encoding:no_seed 158 | -ec:ed25519 159 | Use OpenSSL ecparam to generate an EC key. This includes ed25519, ed448 160 | and their X25519 and X448 counterparts. The numbers 256,384 and 521 are 161 | shorthands for the curves prime256v1, secp384r1 and secp521r1. 162 | 163 | -list-curves 164 | -show-curves 165 | List the EC curves that OpenSSL supports. 166 | 167 | -rsa 168 | -rsa=2048 169 | Generate RSA private keys. 170 | 171 | -dsa 172 | -dsa=1024 173 | Generate DSA private keys. 174 | NB: This also makes the default digest SHA1. 175 | 176 | -keyfile=FileName 177 | Don't generate a key, use the one in FileName. If you're 178 | generating a "single use ca" the second throwaway key (for the 179 | CA) will be generated to be the same type as the key in this 180 | file, if possible. 181 | Note that if it's a PFX file it must have a ".pfx" extension. 182 | 183 | -sign=FileName 184 | Use the key and certificate in FileName to sign the certificate. 185 | Note that if it's a PFX file it must have a ".pfx" extension. 186 | 187 | -csrin=FileName 188 | Use the CSR from FileName instead of generating a new key. 189 | This cannot be used to create a self-signed certificate. 190 | 191 | -pubin=FileName 192 | Like -csrin this will use the public key from the item in FileName 193 | instead of generating a new key. The file can be any recognised 194 | file that contains a public key, not just a CSR. But if it's 195 | a CSR or a certificate it's common name will also be used if 196 | nothing else is specified. 197 | 198 | -csr 199 | Create a certificate signing request not a certificate 200 | Must use -keyout= option too. 201 | 202 | -showcsr 203 | Dump any CSR we use to the output too. 204 | 205 | -singleuseca 206 | -suca 207 | Generate a new CA key for the CA certificate and to sign the standard 208 | certificate. Discard the CA key and send the CA certificate to the 209 | output. 210 | 211 | -v3singleuseca 212 | -v3suca 213 | Same but always make the CA a V3 certificate with the CA flag. 214 | 215 | -v1suca 216 | Same but always make the CA a V1 certificate. 217 | 218 | -casubj=... 219 | -caname=... 220 | Set the subject name for the single use CA, first is a CN or 221 | organisation, the rest are OU items. 222 | 223 | -pkcs12=FileName 224 | -pfx=FileName 225 | Create a "pfx" file with the certificate, private key and the 226 | CA certificate used to sign the certificate. The "-pkcs12" variant 227 | saves the file base64 encoded. 228 | Also changes the default key type to RSA. 229 | 230 | -pass:password 231 | -pass=OpenSSL:pass 232 | Set a password on the key. 233 | 234 | -cipher=name 235 | Select the cipher used for encrypting the key with a password. 236 | 237 | -dgst=sha1 238 | -ripemd160, -sha, -sha1, -sha224, -sha256, -sha384, -sha512, -whirlpool 239 | Select the digest used for signing. 240 | 241 | -openssl=/path/to/executable/openssl 242 | Use the specified copy of openssl, not the one in the PATH. 243 | 244 | -showconf 245 | Debugging option, show the config files generated for OpenSSL 246 | 247 | Version 3 certificate extensions. 248 | 249 | Note V3 extensions are used to limit the uses that a certificate 250 | can be put to. If you're not concerned with limiting use of the 251 | private key (ie the user is trusted) then these generally have 252 | no use. However, some validators insist on certain V3 extensions 253 | being present. 254 | 255 | The SAN extension is a minor exception to this as it allows multiple 256 | wildcard DNS names whereas browsers only accept one wildcard CN 257 | item in the subject. In addition some browsers are rejecting all 258 | certificates that don't have a SAN extension. 259 | 260 | -v3 261 | Make the certificate V3, don't add any extensions though. 262 | 263 | -v3san=SanName 264 | -san=SanName 265 | Add a SAN name. 266 | It may include the type prefix ie: DNS:www.example.com otherwise 267 | a dumb huristic is used to identify the type of the item. This 268 | option can be repeated as many times as needed. 269 | 270 | Note: the common name will normally be added automatically too. 271 | 272 | -v3san, -san 273 | Make a san from the common name even if there are no others. 274 | This is now required by Chrome in direct contravention of the 275 | requirements of RFC 2818 (HTTP over SSL). 276 | 277 | -v3ca, -ca 278 | Add normal V3 extensions for a CA certificate. 279 | 280 | -v3basicca 281 | Add only the basic extension for a CA certificate. 282 | 283 | -v3lastca, -lastca 284 | Make a V3 CA certificate with the 'pathlen:0' option to prevent 285 | certificates that this CA signs being CAs. 286 | 287 | -v3nokeyid, -nokeyid 288 | Disable the "Subject Key Identifier" and "Authority Key Identifier" 289 | additions. 290 | 291 | -v3usr, -v3user, -usr, -user 292 | Prevent this V3 certificate begin used as a CA. 293 | 294 | -v3server, -server 295 | Add V3 key usage extensions to prevent the certificate being used 296 | as anything except an SSL server. 297 | NB: Can be combined with -v3client or -v3email to allow those. 298 | 299 | -v3client, -client 300 | Add V3 key usage extensions to prevent the certificate being used 301 | as anything except an SSL client. 302 | NB: Can be combined with -v3server or -v3email to allow those. 303 | 304 | -v3email, -email 305 | Add V3 key usage extensions to prevent the certificate being used 306 | as anything except a MIME signing or encryption certificate. 307 | NB: Can be combined with -v3server or -v3client to allow those. 308 | 309 | -v3codesign, -codesign 310 | Add V3 key usage extensions to prevent the certificate being used 311 | as anything except a code signing certificate. 312 | 313 | -v3timestamp, -timestamp 314 | Add V3 key usage extensions for timestamp signing. This can NOT 315 | be combined with other key usages. 316 | 317 | -v3ns, -ns 318 | Add the netscape Client/server extensions too. 319 | 320 | -v3ns=... 321 | -ns=... 322 | Add a specific list of netscape usages. 323 | 324 | -v3crit, -v3critical, -critical 325 | Mark CA, key usage and extended key usage extensions as critical. 326 | Beware: 327 | https://en.wikipedia.org/wiki/X.509#Implementation_issues 328 | 329 | -poltext=FileName 330 | Add an "all uses" policy extension and include the text from 331 | FileName as an "issuer statement". 332 | 333 | -policy=OID 334 | The first one of these sets the OID for the '-poltext' option if 335 | present. Additional '-policy' arguments add extra policy extensions. 336 | 337 | -tlsfeature=... 338 | Add the V3 extension that states a server will send a 'status_request' 339 | or 'status_request_v2' response when requested. 340 | 341 | -must-staple 342 | Alias for -tlsfeature=status_request 343 | Can also be entered as -v3=1.3.6.1.5.5.7.1.24=DER:30:03:02:01:05 344 | 345 | -v3permit=..., -permit=..., -v3exclude..., -exclude... 346 | Add the V3 extension for 'Name constraints', they consist of domain 347 | names or IP addresses with subnet masks in the form of tagged SAN 348 | names. 349 | 350 | -v3crluri=..., -crlurl=... 351 | Add a "CRL Distribution Point" item to the certificate extensions. 352 | 353 | -v3dv|-dv ) 354 | Make the certificate policy "Domain validated" and set most 355 | of the other stuff that's supposed to be set. 356 | BUT would also need: 357 | The certificate must be signed by a CA (not self-signed) 358 | Add authorityInformationAccess extension 359 | with OCSP over HTTP 360 | and HTTP URL for issuing certificate 361 | 362 | -v3='OpenSSL extension config' 363 | Add any x509 v3 extension config to the OpenSSL config file used 364 | to create the certificate. 365 | 366 | -v3xt='OpenSSL extension config' 367 | -xt='OpenSSL extension config' 368 | Add any x509 v3 extension config to the OpenSSL config file used 369 | to create the certificate. 370 | 371 | This item is placed after the all "-v3=" items so can be used when 372 | the config file needs additional sections. 373 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The mk-cert program 2 | =================== 3 | 4 | The `mk-cert` program is used to make private keys, certificates and 5 | certificate requests. To "make a certificate" all you have to do is run 6 | the script, it will allow openssl to prompt you for the name to put on 7 | the certificate and create a small but secure key and certificate on 8 | the standard output ready to copy & paste to where you need them. 9 | 10 | If instead you give the script a non-option (without a hyphen) argument 11 | this will be used as the "commonName". Addition arguments will be used 12 | as SAN entries if they look kinda like DNS names or IP addresses and 13 | "organizationalUnitName" field values otherwise. (If you don't want the 14 | script to guess, use the options mentioned below) 15 | 16 | The default period for the certificate is 20 years, if this is not right 17 | for you the `-days=365` option allows you to choose any period you wish, 18 | or the `-alldays` option increases the period to the maximum possible. 19 | 20 | Some applications don't understand the default "Elliptic curve" keys 21 | that this generates so the `-rsa` option switches to the traditional 22 | "RSA" style keys. Adding an argument to the option `-rsa:4096` allows 23 | you to choose a specific size for this key. 24 | 25 | RSA keys are significantly larger than their "EC" equivalents so you will 26 | probably want to write the output to a file with `-out=Filename`. If you 27 | want the key in a separate file the `-keyout=KeyFilename` will do that. 28 | 29 | Some applications may require a few of the many other possible items to 30 | be added to the "subject" of the certificate. The more usual ones are: 31 | `-subj-cn=String`, `-subj-ou=String`, `-subj-dc=String`, `-subj-o=String`, 32 | `-subj-l=String`, `-subj-st=String`, `-subj-c=String`. Others can be 33 | added by options like `-subj=favouriteDrink=Whisky` or even by using an 34 | "OID" number directly... `-subj:2.16.840.1.113883.19.5.1091='malty brew 35 | with a slight caramel sweetness'`. 36 | 37 | By default a "Version 1" certificate is created, this is a simple and 38 | secure type of certificate, but some applications insist that extensions 39 | be added (which converts the certificate to "Version 3"). 40 | 41 | The most useful extension is probably the "Subject Alternative Name" 42 | or SAN which web browsers use to specify multiple common names (instead 43 | of, you know, specifing multiple common names). Just adding `-san` adds 44 | an entry for the common name. If you specify an argument to the option 45 | like `-san=testhost.xy` this will be added and another name to the san 46 | list. Inserting a tag identifier `-san=DNS:example.com` specifies the 47 | type of this SAN entry. You can add as many as you need. 48 | 49 | BEWARE: Chrome requires the SAN or it will give a "Common name invalid" 50 | error even if the common name is perfectly correct and legal on it's own. 51 | 52 | Any extension can be added by using the `-v3=something` and the 53 | `-v3xt=something` options, but those are really difficult to use. 54 | 55 | Much easier are the options `-server`, `-client` and `-email`. Any 56 | combination of these options can be added and the certificate will be 57 | constructed to allow those usages and deny others. 58 | 59 | The problem with this is that an application that insists on this 60 | sort of configuration is likely to also be unhappy with a self-signed 61 | certificate. The simplest and most secure solution to this problem is the 62 | "Single use CA" (option `-singleuseca` or `-suca`). 63 | 64 | This option instructs the script to create two certificates with different 65 | keys, the first is a "CA certificate". It's key used to sign the second 66 | certificate and then discarded. It's certificate is output normally (and 67 | can be saved in it's own file using the `-caout=filename` option). The 68 | second certificate is limited to non-CA usage and it and it's key are 69 | output. The CA certificate should be put into the trusted store at one 70 | end of the link and the non-CA certificate and it's key are used at the 71 | other end. This it usually sufficient for any application. 72 | 73 | Except; occasionally an application will insist that a CRL be 74 | available... the `-crlout=filename` option will create an empty, signed 75 | "certificate revocation list" just before discarding the CA key to placate 76 | these applications. The application may also need a "CRL Distribution 77 | Point" extension with the URL it can download the CRL from, the option 78 | `-crlurl=http://example.com/path/file.crl` does this. 79 | 80 | Using even these options to create a certificate that matches a "Domain 81 | validated" certificate you might get from a public CA is still quite 82 | long. The `-dv` option configures the script to do this. The main 83 | missing item is the `authorityInformationAccess` extension that would 84 | need you to provide a website just for that item. 85 | 86 | Many of these options can be used when creating a CSR (or "Certificate 87 | signing request") just choose the options as normal and add `-csr` to the 88 | end. Note when making a CSR the script will not send it and the private 89 | key to the same output, you will need to use the `-keyout=KeyFilename` 90 | option. This is so that it's much less likely to for the key to be 91 | accidentally sent over an insecure link. 92 | 93 | If you want to use a long term CA certificate then `-v3ca` option 94 | will give it the extensions expected for a certificate and the 95 | `-sign=ca-file.pem` will use it to sign a certificate. The `-csrin=file` 96 | can also be added in this case if you need to make a certificate based 97 | on a CSR rather than creating the key and certificate together. Beware, 98 | however, that this script can only create an empty CRL, if you ever 99 | need to create an actually useful CRL, that is revoke a certificate, 100 | you'll have to do it separately. 101 | 102 | If you're creating files for Windows you'll need the `-pfx=file.pfx` 103 | option to create a file that Windows can load the private key from. It's 104 | default password is empty (leave the fields on the import wizard blank) 105 | but `-pass=123456` or `-pass=[openssl_passphrase_option]` can be used 106 | to change this. 107 | 108 | There are other options; use the `-help` option for details. 109 | 110 | -------------------------------------------------------------------------------- /mk-cert: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck shell=bash disable=SC2230 3 | if [ -z "$BASH_VERSION" ];then exec bash "$0" "$@";else set +o posix;fi 4 | (eval 'f() { local i=$((3+(6)));[[ $i = "$(cat <(echo 9))" ]];};f')\ 5 | 2>/dev/null || { echo >&2 "This version of bash is too old";exit 1;} 6 | 7 | set -e ; PROGRAM="$0" 8 | 9 | show_help_text() { 10 | if [[ -t 1 && -t 0 ]] 11 | then show_help_text | "${PAGER:-more}" ; exit 12 | fi 13 | 14 | echo "Usage: $(basename "$PROGRAM") [Common Name] [arguments]" 15 | version_warnings 16 | 17 | cat <<\! 18 | 19 | This script can create most styles of x509 certificate from the command 20 | line. All options can be configured from arguments and the standard 21 | OpenSSL configuration file is not used. The default is to prompt for a 22 | common name (plus OU and dnQualifier) and generate an ec:prime256r1 key 23 | and certificate which is sent to the standard output. 24 | 25 | Options: 26 | 27 | -cn=String 28 | -subj-cn=String 29 | -subj-o=String 30 | -subj-ou=String 31 | -subj-dc=String 32 | -subj-l=String 33 | -subj-st=String 34 | -subj-c=String 35 | -subj:oid=value 36 | 37 | Set a component of the certificate subject. Some items such as 38 | DC, OU and CN are allowed to be duplicated. OpenSSL will accept 39 | duplicates for any item. 40 | 41 | The long names are: 42 | "CN" Common name, put the domainname here. 43 | "O" Organization Name 44 | "OU" Organizational Unit Name 45 | "DC" Domain Component 46 | "L" Locality Name: American for "City", "Town", etc 47 | "ST" State or Province: "County" for rest of world. 48 | "C" ISO two alpha Country code or "XX" 49 | 50 | The "-cn=" option is taken as both a common name, a SAN and the 51 | official common name for generic use. The "-subj-cn=" option 52 | string will only be used in the subject. 53 | 54 | The OU is the most likely to be accepted as a place to store 55 | any unusual part of a name you may wish to include. 56 | 57 | Any arguments without a "-" to make them an option are assumed 58 | to be names too, the first one is a common name, like the "-cn" 59 | option. The rest are san names if they look like domain names 60 | or IP addresses and OU items if they don't. 61 | 62 | If a argument begins with a "/" and contains an "=" it is 63 | treated as an OpenSSL subject string. This conflicts with the 64 | other options for building a subject. 65 | 66 | The bare "-subj:" version copies the string "oid=value" to the 67 | end of the section of the configuration. This allows any of the 68 | possible name parts (surname, info, favouriteDrink, CSPName, etc) 69 | to be added in any order. If OpenSSL doesn't know a particular 70 | oid the numeric value can be used. 71 | 72 | If no subject is specified one will be prompted for by OpenSSL 73 | unless a SAN is requested in which case an error is generated 74 | unless "-no-prompt" is used. 75 | 76 | -days=7304 77 | How many days should the certificate run for? 78 | 79 | -days=2038-01-18 80 | What day should the certificate expire on? 81 | 82 | -alldays 83 | Make the certificate run until 9999-12-31 84 | 85 | -out=FileName 86 | Put the certificate, and any other items in a file. 87 | 88 | -addcert=FileName 89 | Filename contains a certificate, put it in the same location as 90 | the created certificate. (Used for intermediate CA certificates) 91 | 92 | -keyout=FileName 93 | Put the private key in a specific file not "-out=" or stdout. 94 | 95 | -crl 96 | Generate an empty CRL using the certificate used to sign. 97 | 98 | -crlout=FileName 99 | Generate an empty CRL using the certificate used to sign. 100 | 101 | -caout=FileName 102 | Put the certificate used to sign in a specific file. 103 | 104 | -ser=0123456789abcdef 105 | -serial=0123456789abcdef 106 | Set the certificate serial number (in hexadecimal). 107 | Various noise characters will be removed. 108 | 109 | -dnq 110 | Generate and add a dnQualifier item to the subject. 111 | This is the original way of making sure that the subject of 112 | a certificate is unique. It was only needed within an org 113 | as the other DN components were sufficient for identifing 114 | organisations. 115 | 116 | -dnq=... 117 | Add a specific dnQualifier item to the subject. 118 | 119 | -nodnq 120 | Do not add a dnQualifier item to any subject; even if told to. 121 | 122 | -no-prompt 123 | Prevent OpenSSL prompting for any part of the subject. 124 | 125 | -ed25519 126 | -ed448 127 | Generate an ed25519 or ed448 private key. 128 | 129 | -ec:prime256v1 130 | -ec:256 131 | -ec:curve:encoding 132 | -ec:curve:encoding:no_seed 133 | -ec:ed25519 134 | Use OpenSSL ecparam to generate an EC key. This includes ed25519, ed448 135 | and their X25519 and X448 counterparts. The numbers 256,384 and 521 are 136 | shorthands for the curves prime256v1, secp384r1 and secp521r1. 137 | 138 | -list-curves 139 | -show-curves 140 | List the EC curves that OpenSSL supports. 141 | 142 | -rsa 143 | -rsa=2048 144 | Generate RSA private keys. 145 | 146 | -dsa 147 | -dsa=1024 148 | Generate DSA private keys. 149 | NB: This also makes the default digest SHA1. 150 | 151 | -keyfile=FileName 152 | Don't generate a key, use the one in FileName. If you're 153 | generating a "single use ca" the second throwaway key (for the 154 | CA) will be generated to be the same type as the key in this 155 | file, if possible. 156 | Note that if it's a PFX file it must have a ".pfx" extension. 157 | 158 | -sign=FileName 159 | Use the key and certificate in FileName to sign the certificate. 160 | Note that if it's a PFX file it must have a ".pfx" extension. 161 | 162 | -csrin=FileName 163 | Use the CSR from FileName instead of generating a new key. 164 | This cannot be used to create a self-signed certificate. 165 | 166 | -pubin=FileName 167 | Like -csrin this will use the public key from the item in FileName 168 | instead of generating a new key. The file can be any recognised 169 | file that contains a public key, not just a CSR. But if it's 170 | a CSR or a certificate it's common name will also be used if 171 | nothing else is specified. 172 | 173 | -csr 174 | Create a certificate signing request not a certificate 175 | Must use -keyout= option too. 176 | 177 | -showcsr 178 | Dump any CSR we use to the output too. 179 | 180 | -singleuseca 181 | -suca 182 | Generate a new CA key for the CA certificate and to sign the standard 183 | certificate. Discard the CA key and send the CA certificate to the 184 | output. 185 | 186 | -v3singleuseca 187 | -v3suca 188 | Same but always make the CA a V3 certificate with the CA flag. 189 | 190 | -v1suca 191 | Same but always make the CA a V1 certificate. 192 | 193 | -casubj=... 194 | -caname=... 195 | Set the subject name for the single use CA, first is a CN or 196 | organisation, the rest are OU items. 197 | 198 | -pkcs12=FileName 199 | -pfx=FileName 200 | Create a "pfx" file with the certificate, private key and the 201 | CA certificate used to sign the certificate. The "-pkcs12" variant 202 | saves the file base64 encoded. 203 | Also changes the default key type to RSA. 204 | 205 | -pass:password 206 | -pass=OpenSSL:pass 207 | Set a password on the key. 208 | 209 | -cipher=name 210 | Select the cipher used for encrypting the key with a password. 211 | 212 | -dgst=sha1 213 | -ripemd160, -sha, -sha1, -sha224, -sha256, -sha384, -sha512, -whirlpool 214 | Select the digest used for signing. 215 | 216 | -openssl=/path/to/executable/openssl 217 | Use the specified copy of openssl, not the one in the PATH. 218 | 219 | -showconf 220 | Debugging option, show the config files generated for OpenSSL 221 | 222 | Version 3 certificate extensions. 223 | 224 | Note V3 extensions are used to limit the uses that a certificate 225 | can be put to. If you're not concerned with limiting use of the 226 | private key (ie the user is trusted) then these generally have 227 | no use. However, some validators insist on certain V3 extensions 228 | being present. 229 | 230 | The SAN extension is a minor exception to this as it allows multiple 231 | wildcard DNS names whereas browsers only accept one wildcard CN 232 | item in the subject. In addition some browsers are rejecting all 233 | certificates that don't have a SAN extension. 234 | 235 | -v3 236 | Make the certificate V3, don't add any extensions though. 237 | 238 | -v3san=SanName 239 | -san=SanName 240 | Add a SAN name. 241 | It may include the type prefix ie: DNS:www.example.com otherwise 242 | a dumb huristic is used to identify the type of the item. This 243 | option can be repeated as many times as needed. 244 | 245 | Note: the common name will normally be added automatically too. 246 | 247 | -v3san, -san 248 | Make a san from the common name even if there are no others. 249 | This is now required by Chrome in direct contravention of the 250 | requirements of RFC 2818 (HTTP over SSL). 251 | 252 | -v3ca, -ca 253 | Add normal V3 extensions for a CA certificate. 254 | 255 | -v3basicca 256 | Add only the basic extension for a CA certificate. 257 | 258 | -v3lastca, -lastca 259 | Make a V3 CA certificate with the 'pathlen:0' option to prevent 260 | certificates that this CA signs being CAs. 261 | 262 | -v3nokeyid, -nokeyid 263 | Disable the "Subject Key Identifier" and "Authority Key Identifier" 264 | additions. 265 | 266 | -v3usr, -v3user, -usr, -user 267 | Prevent this V3 certificate begin used as a CA. 268 | 269 | -v3server, -server 270 | Add V3 key usage extensions to prevent the certificate being used 271 | as anything except an SSL server. 272 | NB: Can be combined with -v3client or -v3email to allow those. 273 | 274 | -v3client, -client 275 | Add V3 key usage extensions to prevent the certificate being used 276 | as anything except an SSL client. 277 | NB: Can be combined with -v3server or -v3email to allow those. 278 | 279 | -v3email, -email 280 | Add V3 key usage extensions to prevent the certificate being used 281 | as anything except a MIME signing or encryption certificate. 282 | NB: Can be combined with -v3server or -v3client to allow those. 283 | 284 | -v3codesign, -codesign 285 | Add V3 key usage extensions to prevent the certificate being used 286 | as anything except a code signing certificate. 287 | 288 | -v3timestamp, -timestamp 289 | Add V3 key usage extensions for timestamp signing. This can NOT 290 | be combined with other key usages. 291 | 292 | -v3ns, -ns 293 | Add the netscape Client/server extensions too. 294 | 295 | -v3ns=... 296 | -ns=... 297 | Add a specific list of netscape usages. 298 | 299 | -v3crit, -v3critical, -critical 300 | Mark CA, key usage and extended key usage extensions as critical. 301 | Beware: 302 | https://en.wikipedia.org/wiki/X.509#Implementation_issues 303 | 304 | -poltext=FileName 305 | Add an "all uses" policy extension and include the text from 306 | FileName as an "issuer statement". 307 | 308 | -policy=OID 309 | The first one of these sets the OID for the '-poltext' option if 310 | present. Additional '-policy' arguments add extra policy extensions. 311 | 312 | -tlsfeature=... 313 | Add the V3 extension that states a server will send a 'status_request' 314 | or 'status_request_v2' response when requested. 315 | 316 | -must-staple 317 | Alias for -tlsfeature=status_request 318 | Can also be entered as -v3=1.3.6.1.5.5.7.1.24=DER:30:03:02:01:05 319 | 320 | -v3permit=..., -permit=..., -v3exclude..., -exclude... 321 | Add the V3 extension for 'Name constraints', they consist of domain 322 | names or IP addresses with subnet masks in the form of tagged SAN 323 | names. 324 | 325 | -v3crluri=..., -crlurl=... 326 | Add a "CRL Distribution Point" item to the certificate extensions. 327 | 328 | -v3dv|-dv ) 329 | Make the certificate policy "Domain validated" and set most 330 | of the other stuff that's supposed to be set. 331 | BUT would also need: 332 | The certificate must be signed by a CA (not self-signed) 333 | Add authorityInformationAccess extension 334 | with OCSP over HTTP 335 | and HTTP URL for issuing certificate 336 | 337 | -v3='OpenSSL extension config' 338 | Add any x509 v3 extension config to the OpenSSL config file used 339 | to create the certificate. 340 | 341 | -v3xt='OpenSSL extension config' 342 | -xt='OpenSSL extension config' 343 | Add any x509 v3 extension config to the OpenSSL config file used 344 | to create the certificate. 345 | 346 | This item is placed after the all "-v3=" items so can be used when 347 | the config file needs additional sections. 348 | ! 349 | 350 | exit 0 351 | } 352 | 353 | version_warnings() { 354 | 355 | [[ "$BASH3" = yes || "$NOMIXING" = yes || "$BASHREGEX" = no ]] && { 356 | echo "" 357 | echo "BEWARE: Some features are broken or work poorly on this version" 358 | echo "of bash: $BASH_VERSION" 359 | } 360 | 361 | [[ "$NOPKEY" = yes || "$NOECPARAM" = yes ]] && { 362 | echo "" 363 | echo "BEWARE: Some features are broken or work poorly on this version" 364 | echo "of openssl: $(openssl version)" 365 | } 366 | 367 | return 0 368 | } 369 | 370 | edit_readme_help_text() { 371 | [[ -f README && -f mk-cert && -f README.md ]] || { 372 | echo This needs to be run in the source tree 373 | exit 1 374 | } 375 | sed -i '/^Usage: mk-cert/,$d' README 376 | bash mk-cert -help >> README 377 | exit 0 378 | } 379 | 380 | main() { 381 | eval "typeset -A SANDUP # bash4/ksh" 2>/dev/null && BASH3=no || BASH3=yes 382 | 383 | init_vars 384 | test_for_windows 385 | decode_args "$@" 386 | default_options 387 | check_args 388 | 389 | [[ "$KFN" != '' && "$KFN" != /dev/null && -s "$KFN" ]] && 390 | { echo >&2 "ERROR: File $KFN already exists" ; exit 1 ; } 391 | 392 | [[ "$FN" != '' && -s "$FN" ]] && 393 | { echo >&2 "ERROR: File $FN already exists" ; exit 1 ; } 394 | 395 | [[ "$CAFN" != '' && -s "$CAFN" ]] && 396 | { echo >&2 "ERROR: File $CAFN already exists" ; exit 1 ; } 397 | 398 | [[ "$PKCS12FN" != '' && -s "$PKCS12FN" ]] && 399 | { echo >&2 "ERROR: File $PKCS12FN already exists" ; exit 1 ; } 400 | 401 | [[ "$CRLFN" != '' && -s "$CRLFN" ]] && 402 | { echo >&2 "ERROR: File $CRLFN already exists" ; exit 1 ; } 403 | 404 | build_v3_extensions 405 | apply_options 406 | create_main_cert # or csr 407 | create_suca_pem 408 | 409 | if [[ "$CSRFN" != "" && "$SIGNPEM" = '' ]] 410 | then echo 'ERROR: Cannot make a self signed certificate from a csr' >&2 411 | exit 1; 412 | fi 413 | if [[ "$FORCEPUB" != "" && "$SIGNPEM" = '' ]] 414 | then echo 'ERROR: Cannot make a self signed certificate from a public key' >&2 415 | exit 1; 416 | fi 417 | 418 | sign_created_csr 419 | 420 | [[ "$MAKECRL" = yes ]] && 421 | CRLPEM=$(create_crl_file) 422 | 423 | create_pfx_file 424 | 425 | [[ "$KFN" = '' ]] && umask 077 426 | # shellcheck disable=SC2094 # ironic. 427 | if [[ "$FN" != '' ]] ; then outp "$FN" > "$FN" ; else outp ; fi 428 | 429 | exit 0 430 | } 431 | 432 | init_vars() { 433 | NL=' 434 | ' 435 | export OPENSSL_CONF=/dev/null 436 | 437 | NOPKEY="$(openssl no-pkey >/dev/null 2>&1 && echo yes || echo no)" 438 | NOECPARAM="$(openssl no-ecparam >/dev/null 2>&1 && echo yes || echo no)" 439 | NOMIXING="$(eval 'x=1;[ "$(set -- ${x:+0 "$x"} 2;echo "$2")" = "1" ]' && echo no || echo yes)" 440 | 441 | FN= 442 | KFN= 443 | ADDCERT= 444 | CRLFN= 445 | CSRFN= 446 | PUBKEYFN= 447 | PUBDATA= 448 | CAFN= 449 | FORCEPUB= 450 | SERIAL= 451 | CERT_DAYS= 452 | EC=prime256v1 453 | RSABITS=2048 454 | 455 | # shellcheck disable=SC2046 # I knooow 456 | JTODAY=$(jdayjdat $(date '+%d %m %Y') ) 457 | 458 | if [[ "$NOPKEY" = no ]] || \ 459 | [[ "$(date +%Y -d @$((2**31)) 2>/dev/null)" = 2038 ]] 460 | then DEFAULT_DAYS=7304 461 | else 462 | # Older versions have a date bug on 32bit machines. 463 | END_OF_TIME=$(jdayjdat 18 1 2038) 464 | DEFAULT_DAYS=$((END_OF_TIME - JTODAY)) 465 | # Minimum of 10 years 466 | [[ "$DEFAULT_DAYS" -lt 3652 ]] && 467 | DEFAULT_DAYS=3652 468 | fi 469 | 470 | V3=no 471 | V3HEAD= 472 | V3SAN= 473 | V3OPTS= 474 | V3LIST= 475 | V3EXTRAS= 476 | V3CERTONLY= 477 | V3CA= 478 | V3CAPATH= 479 | V3CLIENT=no 480 | V3SERVER=no 481 | V3EMAIL=no 482 | V3CODESIGN=no 483 | V3TIMESTAMP=no 484 | V3CRITICAL=no 485 | V3NS=no 486 | V3ENS= 487 | V3NAMECONS= 488 | 489 | SANSECT= 490 | SAN_ID=0 491 | 492 | BASHREGEX= 493 | NOSETSERIAL= 494 | KEYPASS= 495 | WANTCSR=no 496 | MAKECSR=no 497 | ADDDNQ= 498 | SHOWCSR=no 499 | MAKECRL=no 500 | SHOWCONF= 501 | CSRTEXT= 502 | SIGNPFX= 503 | SIGNPEM= 504 | CRLPEM= 505 | SINGLEUSECA= 506 | V3SUCA= 507 | PKCS12FN= 508 | PKCS12ASCII= 509 | POLICYTEXT= 510 | POLICYOID= 511 | NOPROMPT= 512 | NOKEYID= 513 | WANTDVCERT= 514 | DEFAULTCN= 515 | TLSFEATURE= 516 | NOSUCAEKU= 517 | 518 | CIPHER=-aes128 519 | DGST= 520 | 521 | SUBJTYPE= 522 | SUBJ= 523 | CASUBJ= 524 | 525 | SUBJ_ID=0 526 | SUBJ_C= 527 | SUBJ_ST= 528 | SUBJ_L= 529 | SUBJ_O= 530 | SUBJ_DC= 531 | SUBJ_OU= 532 | SUBJ_CN= 533 | SUBJ_DNQ= 534 | SUBJ_XX= 535 | CRLURI= 536 | CRLURI_ID=0 537 | EXTRA_OIDS= 538 | SSKEY= 539 | 540 | # Faketime prefix 541 | ft() { "$@" ; } 542 | 543 | # This is my default for making a key file. 544 | # Versions before "pkey" didn't do EC+SHA256 545 | if [[ "$NOECPARAM" = no && "$NOPKEY" = no ]]; then 546 | mkkey() { openssl ecparam -name "$EC" -genkey -noout ;} 547 | mktmpkey() { openssl ecparam -name "$EC" -genkey -noout ;} 548 | KEYCLASS=ec 549 | else 550 | mkkey() { openssl genrsa $RSABITS ; } 551 | mktmpkey() { openssl genrsa 512 2>/dev/null ; } 552 | KEYCLASS=rsa 553 | fi 554 | KEYSOURCE= 555 | # See also: 556 | # mkkey() { openssl genpkey -paramfile <(openssl genpkey -genparam -algorithm ec -pkeyopt ec_paramgen_curve:prime256v1) ;} 557 | 558 | X509SECT=req_x509 559 | V3INIT="x509_extensions=$X509SECT${NL}req_extensions=$X509SECT${NL}[$X509SECT]${NL}" 560 | 561 | UTF8= 562 | if [[ "$(locale charmap 2>/dev/null)" = UTF-8 ]] 563 | then 564 | if openssl req -help 2>&1 | grep -q -e -utf8 565 | then UTF8=-utf8 566 | fi 567 | fi 568 | } 569 | 570 | decode_args() { 571 | for ar 572 | do case "$ar" in 573 | -h|-help|--help) show_help_text ;; 574 | -edit-readme ) edit_readme_help_text ;; 575 | 576 | -showconf ) SHOWCONF=yes ;; 577 | 578 | -days ) CERT_DAYS='' ;; 579 | -alldays ) CERT_DAYS=all ;; 580 | -days[=:]* ) CERT_DAYS="${ar#*[=:]}" ;; 581 | 582 | -out[=:]* ) FN="${ar#*[=:]}" ;; 583 | -keyout[=:]* ) KFN="${ar#*[=:]}" ;; 584 | -crl ) MAKECRL=yes ;; 585 | -crlout[=:]* ) CRLFN="${ar#*[=:]}" ; MAKECRL=yes ;; 586 | -caout[=:]* ) CAFN="${ar#*[=:]}" ;; 587 | 588 | -ser[=:]??*|-serial[=:]??* ) 589 | SERIAL="${ar#*[=:]}" 590 | SERIAL="${SERIAL//[-: .,;]/}" 591 | ;; 592 | 593 | -dnq ) [[ "$ADDDNQ" = no ]] || ADDDNQ=yes ;; 594 | -nodnq|-no-dnq ) ADDDNQ=no ;; 595 | 596 | -dnq[=:]*|-cn[=:]* ) 597 | SUBJTYPE="${SUBJTYPE:-conf}" 598 | if [[ "$SUBJTYPE" != "conf" ]] ; then 599 | echo >&2 "Duplicate subject name definition at '$ar'"; exit 1 600 | fi 601 | 602 | case "$ar" in 603 | -dnq* ) 604 | SUBJ_ID=$((SUBJ_ID+1)) 605 | SUBJ_DNQ="$SUBJ_DNQ${NL}$SUBJ_ID.dnQualifier=${ar#*[=:]}" 606 | ;; 607 | -cn* ) 608 | if [[ "${ar#*[=:]}" != '' ]] 609 | then SUBJ_ID=$((SUBJ_ID+1)) 610 | SUBJ_CN="$SUBJ_CN${NL}$SUBJ_ID.commonName=${ar#*[=:]}" 611 | else SUBJ_CN="$SUBJ_CN${NL}" 612 | fi 613 | NOPROMPT=yes 614 | if [[ "$CN" = '' ]] 615 | then CN="${ar#*[=:]}" 616 | else add_good_san "${ar#*[=:]}" ||: 617 | fi 618 | ;; 619 | esac 620 | ;; 621 | 622 | -rsa[=:]* ) 623 | KEYCLASS=rsa 624 | KEYSOURCE="$KEYSOURCE:rsa" 625 | eval "mkkey() { openssl genrsa '${ar#*[=:]}' ; }" 626 | ;; 627 | -dsa[=:]* ) 628 | KEYCLASS= 629 | KEYSOURCE="$KEYSOURCE:dsa" 630 | eval "mkkey() { openssl dsaparam -noout -genkey '${ar#*[=:]}' ; }" 631 | ;; 632 | -ec[=:]* ) 633 | [[ "$NOECPARAM" = no ]] || { 634 | echo >&2 "Elliptic Curve keys not supported by OpenSSL" 635 | exit 1 636 | } 637 | ar="${ar#*[=:]}:" ; curve="${ar%%:*}" 638 | ar="${ar#*:}:" ; param="${ar%%:*}" 639 | ar="${ar#*:}:" ; noseed="${ar%%:*}" 640 | 641 | case "$curve" in 642 | 192 ) curve=prime192v1 ;; 643 | 224 ) curve=secp224r1 ;; 644 | 256 ) curve=prime256v1 ;; 645 | 384 ) curve=secp384r1 ;; 646 | 521 ) curve=secp521r1 ;; 647 | 648 | [0-9]*[0-9] ) 649 | ncurve="$(openssl ecparam -list_curves| grep " $curve bit" | 650 | awk -F: '/:/{print $1;exit;}' | tr -d ' ' )" 651 | [[ "$ncurve" != "" ]] && curve="$ncurve" 652 | ;; 653 | esac 654 | 655 | case "$curve" in 656 | X25519|x25519|X448|x448 ) 657 | echo >&2 "WARNING: The ${curve} EC algorithm cannot do signatures, did you mean the ED version?" 658 | KEYCLASS=nosig 659 | KEYSOURCE="$KEYSOURCE:X" 660 | SSKEY=$(openssl genpkey -algorithm "$curve") 661 | PUBDATA=$(echo "$SSKEY" | openssl pkey -pubout) 662 | eval "mkkey() { openssl genpkey -algorithm 'ed${curve#?}' ; }" 663 | ;; 664 | 665 | ED25519|ed25519|ED448|ed448 ) 666 | eval "mkkey() { openssl genpkey -algorithm '${curve}' ; }" 667 | KEYCLASS= 668 | KEYSOURCE="$KEYSOURCE:ed" 669 | ;; 670 | 671 | *) 672 | eval "mkkey() { openssl ecparam -name '$curve' ${param:+-param_enc $param} ${noseed:+-no_seed} -genkey -noout ; }" 673 | KEYCLASS=ec 674 | KEYSOURCE="$KEYSOURCE:ec" 675 | ;; 676 | esac 677 | 678 | ;; 679 | 680 | -ED25519|-ed25519|-ED448|-ed448 ) 681 | eval "mkkey() { openssl genpkey -algorithm '${ar#?}' ; }" 682 | KEYCLASS= 683 | KEYSOURCE="$KEYSOURCE:ed" 684 | ;; 685 | 686 | -dsa ) 687 | # FIPS used to force 1024, so lots of hardware and protocols did too. 688 | # 1024 bits is now considered unsafe against the most capable 689 | # attackers and barely safe against publicly known attacks. 690 | DGST="${DGST:--sha1}" 691 | mkkey() { openssl dsaparam -noout -genkey 1024; } 692 | KEYCLASS= 693 | KEYSOURCE="$KEYSOURCE:dsa" 694 | ;; 695 | 696 | -rsa ) 697 | mkkey() { openssl genrsa $RSABITS ; } 698 | KEYCLASS=rsa 699 | KEYSOURCE="$KEYSOURCE:rsa" 700 | ;; 701 | 702 | -ec ) 703 | [[ "$NOECPARAM" = no ]] || { 704 | echo >&2 "Elliptic Curve keys not supported by OpenSSL" 705 | exit 1 706 | } 707 | mkkey() { openssl ecparam -name "$EC" -genkey -noout ; } 708 | KEYCLASS=ec 709 | KEYSOURCE="$KEYSOURCE:ec" 710 | ;; 711 | 712 | -list[-_]curves|-show[_-]curves ) 713 | openssl ecparam -list_curves ; exit ;; 714 | 715 | -keyfile[=:]*|-key[=:]*.pfx ) 716 | eval "mkkey() { openssl pkcs12 -nodes -out - -in '${ar#*[=:]}' | openssl pkey; }" 717 | [[ "$KFN" = "" ]] && KFN=/dev/null 718 | KEYCLASS='file' 719 | KEYSOURCE="$KEYSOURCE:keyfile" 720 | ;; 721 | 722 | -keyfile[=:]*|-key[=:]* ) 723 | eval "mkkey() { cat < '${ar#*[=:]}' ; }" 724 | [[ "$KFN" = "" ]] && KFN=/dev/null 725 | KEYCLASS='file' 726 | KEYSOURCE="$KEYSOURCE:keyfile" 727 | ;; 728 | 729 | -csr ) WANTCSR=yes ; MAKECSR=yes ;; 730 | -csrin[=:]* ) 731 | CSRFN="${ar#*[=:]}" 732 | KFN=/dev/null 733 | KEYCLASS= 734 | KEYSOURCE="$KEYSOURCE:csr" 735 | ;; 736 | -showcsr ) SHOWCSR=yes ;; 737 | 738 | -pubin[=:]* ) 739 | PUBKEYFN="${ar#*[=:]}" 740 | KFN=/dev/null 741 | KEYCLASS= 742 | KEYSOURCE="$KEYSOURCE:pubkey" 743 | ;; 744 | 745 | -sign[=:]*.pfx ) 746 | SIGNPFX="${ar#*[=:]}" 747 | MAKECSR=yes 748 | ;; 749 | 750 | -sign[=:]* ) 751 | SIGNPEM="$(cat "${ar#*[=:]}")" 752 | MAKECSR=yes 753 | ;; 754 | 755 | -addcert[=:]* ) 756 | [[ "$ADDCERT" != '' ]] && ADDCERT="$ADDCERT$NL" 757 | ADDCERT="$ADDCERT$( 758 | openssl x509 -subject -serial -dates -in "${ar#*[=:]}" 759 | )" 760 | ;; 761 | 762 | -singleuseca|-suca ) MAKECSR=yes ; SINGLEUSECA=yes ;; 763 | -v1suca ) MAKECSR=yes ; SINGLEUSECA=yes ; V3SUCA=no ;; 764 | -v3singleuseca|-v3suca ) MAKECSR=yes ; SINGLEUSECA=yes ; V3SUCA=yes ;; 765 | -nocaeku ) NOSUCAEKU=yes ;; 766 | 767 | -pfx[=:]* ) 768 | PKCS12FN="${ar#*[=:]}" 769 | [[ "$FN" = "" ]] && FN=/dev/null 770 | # Default PFX files to RSA 771 | [ "$KEYSOURCE" = '' ] && { 772 | mkkey() { openssl genrsa $RSABITS ; } 773 | KEYCLASS=rsa 774 | } 775 | ;; 776 | 777 | -pkcs12[=:]* ) 778 | PKCS12FN="${ar#*[=:]}" 779 | [[ "$FN" = "" ]] && FN=/dev/null 780 | PKCS12ASCII=yes 781 | # Default PFX files to RSA 782 | [ "$KEYSOURCE" = '' ] && { 783 | mkkey() { openssl genrsa $RSABITS ; } 784 | KEYCLASS=rsa 785 | } 786 | ;; 787 | 788 | -pass[=:]*:* ) KEYPASS="${ar#*[=:]}" ;; 789 | -pass[=:]* ) KEYPASS="pass:${ar#*[=:]}" ;; 790 | 791 | -ripemd160|-sha|-sha1|-sha224|-sha256|-sha384|-sha512|-whirlpool) 792 | DGST=$ar ;; 793 | 794 | -dgst[=:]* ) DGST="${ar#*[=:]}" ; DGST="-${DGST%-}" ;; 795 | -cipher[=:]* ) CIPHER="${ar#*[=:]}" ; CIPHER="-${CIPHER%-}" ;; 796 | -openssl[:=]* ) eval "openssl() { command '${ar#*[=:]}' \"\$@\" ; }" ;; 797 | 798 | -v3 ) V3=yes ;; 799 | -v3ca|-ca ) V3=yes ; V3CA=yes ;; 800 | -v3lastca|-lastca ) V3=yes ; V3CA=yes ; V3CAPATH=0 ;; 801 | -v3usr|-usr|-v3user|-user ) V3=yes ; V3CA=no ;; 802 | -v3server|-server ) V3=yes ; V3SERVER=yes ;; 803 | -v3client|-client ) V3=yes ; V3CLIENT=yes ;; 804 | -v3email|-email ) V3=yes ; V3EMAIL=yes ;; 805 | -v3codesign|-codesign ) V3=yes ; V3CODESIGN=yes ;; 806 | -v3timestamp|-timestamp ) V3=yes ; V3TIMESTAMP=yes ; V3CRITICAL=yes ;; 807 | -v3crit|-v3critical|-critical ) 808 | V3=yes ; V3CRITICAL=yes ; V3CA=${V3CA:-no} 809 | ;; 810 | -v3nokeyid|-nokeyid|-no-keyid) NOKEYID=yes ;; 811 | 812 | -v3basicca ) 813 | V3=yes 814 | V3LIST="$V3LIST${NL}basicConstraints = CA:TRUE" 815 | ;; 816 | 817 | -v3policytext[=:]*|-policytext[=:]*|-poltext[=:]* ) 818 | V3=yes 819 | POLICYTEXT="$(cat "${ar#*[=:]}")" 820 | ;; 821 | 822 | -v3policy[=:]*|-policy[=:]* ) 823 | V3=yes 824 | POLICYOID="$POLICYOID,${ar#*[=:]}" 825 | ;; 826 | 827 | -v3ns|-ns ) V3=yes ; V3NS=yes ;; 828 | -v3ns[=:]*|-ns[=:]* ) 829 | V3=yes 830 | V3NS=yes 831 | V3ENS="${ar#*[=:]}" 832 | ;; 833 | 834 | -v3[=:]* ) 835 | V3=yes 836 | V3LIST="$V3LIST$NL${ar#*[=:]}" 837 | ;; 838 | 839 | -v3xt[=:]*|-xt[=:]* ) 840 | V3=yes 841 | V3EXTRAS="$V3EXTRAS$NL${ar#*[=:]}$NL" 842 | ;; 843 | 844 | -v3san[=:]*|-san[=:]* ) 845 | if ! add_good_san "${ar#*[=:]}" 846 | then 847 | san="${ar#*[=:]}" 848 | case "$san" in 849 | [0-9a-fA-F]*:*:*[0-9a-fA-F]|::*[0-9a-fA-F] ) 850 | add_san "IP:$san" ;; 851 | *@* ) add_san "email:$san" ;; 852 | *[:-?]*|*[0-9]|[0-9]* ) 853 | echo "Undecidable san name '$san', please label" >&2 854 | exit 1 855 | ;; 856 | * ) add_san "DNS:$san" ;; 857 | esac 858 | fi 859 | ;; 860 | 861 | -v3san|-san ) V3=yes ; V3SAN=yes ;; 862 | 863 | -v3tlsfeature[=:]*|-tlsfeature[=:]*|-must-staple ) 864 | V3=yes 865 | if [[ "$TLSFEATURE" = '' ]] 866 | then TLSFEATURE='tlsfeature = ' 867 | else TLSFEATURE="$TLSFEATURE," 868 | fi 869 | FEATURE="${ar#*[=:]}" 870 | [[ "$FEATURE" = "-must-staple" || "$FEATURE" = "must-staple" ]] && 871 | FEATURE=status_request 872 | TLSFEATURE="$TLSFEATURE$FEATURE" 873 | ;; 874 | 875 | -v3permit[=:]*|-permit[=:]* ) 876 | add_name_constraint "${ar#*[=:]}" permitted 877 | ;; 878 | -v3exclude[=:]*|-exclude[=:]* ) 879 | add_name_constraint "${ar#*[=:]}" excluded 880 | ;; 881 | 882 | -v3crlur[il][=:]*|-crlur[il][=:]* ) 883 | V3=yes 884 | CRLURI_ID=$((CRLURI_ID+1)) 885 | CRLURI="$CRLURI${NL}URI.$CRLURI_ID=${ar#*[=:]}" 886 | ;; 887 | 888 | -v3dv|-dv ) 889 | # Make the certificate policy "Domain validated" and set most 890 | # of the other stuff that's supposed to be set. 891 | # BUT would also need: 892 | # Add authorityInformationAccess extension 893 | # with OCSP over HTTP extension 894 | # and HTTP URL for issuing certificate 895 | # 896 | # For example: 897 | # -v3='authorityInfoAccess = OCSP;URI:http://ocsp.my.host/,caIssuers;URI:http://my.ca/ca.html' 898 | # 899 | # In addition the commonName is "deprecated", 900 | # but all public CA's still include it. 901 | WANTDVCERT=yes 902 | V3=yes 903 | V3SAN=yes 904 | V3SERVER=yes 905 | V3CLIENT=yes 906 | V3CRITICAL=yes 907 | POLICYOID="$POLICYOID,2.23.140.1.2.1" 908 | DEFAULT_DAYS=398 909 | ;; 910 | 911 | /*=* ) 912 | if [[ "$SUBJTYPE" != "" ]] ; then 913 | echo >&2 "Duplicate subject name definition at '$ar'"; exit 1 914 | fi 915 | SUBJ="$ar" 916 | SUBJTYPE=arg 917 | ;; 918 | 919 | -noprompt|-no-prompt ) NOPROMPT=yes ;; 920 | 921 | -subj[-_]c[=:]*|-subj[-_]st[=:]*|-subj[-_]l[=:]*|-subj[-_]o[=:]*|\ 922 | -subj[-_]dc[=:]*|-subj[-_]ou[=:]*|-subj[-_]cn[=:]* ) 923 | SUBJTYPE="${SUBJTYPE:-conf}" 924 | if [[ "$SUBJTYPE" != "conf" ]] ; then 925 | echo >&2 "Duplicate subject name definition at '$ar'"; exit 1 926 | fi 927 | 928 | V="${ar#??????}" 929 | C="${V/=*/}" 930 | # shellcheck disable=SC2018,SC2019 931 | C="$(echo "$C" | tr 'a-z' 'A-Z')" 932 | 933 | SUBJ_ID=$((SUBJ_ID+1)) 934 | eval "SUBJ_$C=\"\$SUBJ_$C\${NL}\$SUBJ_ID.$C=\${ar#*[=:]}\"" 935 | 936 | ;; 937 | 938 | -subj[=:]* ) 939 | SUBJTYPE="${SUBJTYPE:-conf}" 940 | if [[ "$SUBJTYPE" != "conf" ]] ; then 941 | echo >&2 "Duplicate subject name definition at '$ar'"; exit 1 942 | fi 943 | V="${ar#*[=:]}" 944 | V1="${V/=*/}" 945 | V2="${V#*=}" 946 | case "$V1" in 947 | [0-9]*.*[0-9] ) # Looks kinda like an OID ? 948 | V3="oid_${V1//./_}" 949 | EXTRA_OIDS="$EXTRA_OIDS$NL$V3=$V1" 950 | SUBJ_XX="$SUBJ_XX$NL$V3=$V2" 951 | ;; 952 | * ) SUBJ_XX="$SUBJ_XX$NL$V" 953 | ;; 954 | esac 955 | SUBJ_ID=$((SUBJ_ID+1)) 956 | ;; 957 | 958 | -casubj[=:]*|-caname[=:]* ) 959 | V="${ar#*[=:]}" 960 | case "$V" in 961 | *\ * ) CASUBJ="${CASUBJ}O=$V${NL}" ;; 962 | * ) if [[ "$CASUBJ" = '' ]] 963 | then CASUBJ="CN=$V${NL}" 964 | else CASUBJ="${CASUBJ}OU=$V${NL}" 965 | fi 966 | ;; 967 | esac 968 | ;; 969 | 970 | -*) echo >&2 "Unknown option: $ar, use -help for help text." ; exit 1 ;; 971 | 972 | * ) SUBJTYPE="${SUBJTYPE:-conf}" 973 | if [[ "$SUBJTYPE" != "conf" ]] ; then 974 | echo >&2 "Duplicate subject name definition at '$ar'"; exit 1 975 | fi 976 | 977 | if [[ "$ar" = "" ]] 978 | then SUBJ_CN="$SUBJ_CN${NL}" 979 | NOPROMPT=yes 980 | elif [[ "$SUBJ_CN" = "" ]] 981 | then SUBJ_ID=$((SUBJ_ID+1)) 982 | SUBJ_CN="$SUBJ_CN${NL}${SUBJ_ID}.CN=$ar" 983 | CN="$ar" 984 | else 985 | add_good_san "$ar" || { 986 | SUBJ_ID=$((SUBJ_ID+1)) 987 | SUBJ_OU="$SUBJ_OU${NL}${SUBJ_ID}.OU=$ar" 988 | } 989 | fi 990 | ;; 991 | esac 992 | done 993 | 994 | return 0 995 | } 996 | 997 | # Bash regex's are conditional so we can still mostly work on V2 bash. 998 | if (eval 'x=1.12.123;[[ "$x" =~ ^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$ ]]') \ 999 | 2>/dev/null 1000 | then 1001 | BASHREGEX=yes 1002 | eval "$(cat <<\! 1003 | add_good_san() { 1004 | local san= 1005 | 1006 | if [[ "$1" =~ ^(IP|DNS|email|URI|RID|dirName): ]] 1007 | then san="$1" 1008 | elif [[ "$1" =~ ^otherName:.*\;.*: ]] 1009 | then san="$1" 1010 | elif [[ "$1" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] 1011 | then san="IP:$1" 1012 | elif [[ "$1" =~ ^(\*\.)?([a-zA-Z][-a-zA-Z0-9]*\.)*[a-zA-Z][-a-zA-Z0-9]+$ ]] 1013 | then san="DNS:$1" 1014 | fi 1015 | [[ "$san" = '' ]] && return 1 1016 | add_san "$san" ${2:+"$2"} 1017 | } 1018 | 1019 | add_name_constraint() { 1020 | local san= 1021 | 1022 | if [[ "$1" =~ ^(IP|DNS|email|URI|RID|dirName): ]] 1023 | then san="$1" 1024 | elif [[ "$1" =~ ^otherName:.*\;.*: ]] 1025 | then san="$1" 1026 | elif [[ "$1" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] 1027 | then san="IP:$1" 1028 | elif [[ "$1" =~ ^(\*\.)?([a-zA-Z][-a-zA-Z0-9]*\.)*[a-zA-Z][-a-zA-Z0-9]+$ ]] 1029 | then san="DNS:$1" 1030 | fi 1031 | [[ "$san" = '' ]] && { 1032 | echo >&2 "Cannot guess '$1', please tag it" 1033 | exit 1 1034 | } 1035 | 1036 | V3=yes ; V3NAMECONS=yes 1037 | 1038 | if [[ "$NAMESECT" = '' ]] 1039 | then NAMESECT="${NAMESECT}$2;$san" 1040 | else NAMESECT="${NAMESECT},$2;$san" 1041 | fi 1042 | 1043 | # Hmmm, long form doesn't seem to work. 1044 | # NAME_ID=$((NAME_ID+1)) 1045 | # NAMESECT="${NAMESECT}${1/:/.$NAME_ID=$2;}${NL}" 1046 | 1047 | #' 1048 | } 1049 | ! 1050 | )" 1051 | else 1052 | BASHREGEX=no 1053 | add_good_san() { 1054 | case "$1" in 1055 | DNS:* ) add_san "$1" ${2:+"$2"} ; return 0 ;; 1056 | IP:* ) add_san "$1" ${2:+"$2"} ; return 0 ;; 1057 | email:* ) add_san "$1" ${2:+"$2"} ; return 0 ;; 1058 | esac 1059 | return 1; 1060 | } 1061 | 1062 | add_name_constraint() { 1063 | echo >&2 "You need to upgrade your version of BASH for 'add_name_constraint'" 1064 | exit 1 1065 | } 1066 | 1067 | fi 1068 | 1069 | add_san() { 1070 | V3=yes ; V3SAN=yes 1071 | [[ "$BASH3" = no ]] && { 1072 | [[ ${SANDUP[$1]} = 1 ]] && return 0 1073 | SANDUP[$1]=1 1074 | } 1075 | 1076 | SAN_ID=$((SAN_ID+1)) 1077 | if [[ "$2" = '' ]] 1078 | then 1079 | SANSECT="${SANSECT}${1/:/.$SAN_ID=}${NL}" 1080 | else 1081 | SANSECT="${1/:/.$SAN_ID=}${NL}${SANSECT}" 1082 | fi 1083 | return 0 1084 | } 1085 | 1086 | check_args() { 1087 | [[ "$WANTCSR" = yes && "$SINGLEUSECA" = yes ]] && { 1088 | echo >&2 "Options -csr and -singleuseca conflict" 1089 | exit 1 1090 | } 1091 | 1092 | [[ "$WANTCSR" = yes && "$KFN" = '' ]] && { 1093 | echo >&2 "ERROR: Use the -keyout= option when you use -csr" 1094 | echo >&2 "INFO: A CSR and private key should never be put in the same file." 1095 | exit 1 1096 | } 1097 | 1098 | # Extract a (passworded) pfx file. 1099 | [[ "$SIGNPFX" != '' ]] && SIGNPEM="$(openssl pkcs12 -nodes -in "$SIGNPFX")" 1100 | 1101 | # Force a CA signed certificate for DV certs 1102 | [[ "$WANTDVCERT" = yes && "$WANTCSR" != yes && "$SIGNPEM" = '' ]] && { 1103 | MAKECSR=yes ; SINGLEUSECA=yes 1104 | } 1105 | 1106 | # If -suca and -sign= don't complain. 1107 | [[ "$SIGNPEM" != '' && "$SINGLEUSECA" = yes ]] && 1108 | SINGLEUSECA=no 1109 | 1110 | [[ "$KEYCLASS" = nosig && "$SINGLEUSECA" != yes && "$SIGNPEM" = '' ]] && { 1111 | echo >&2 "WARNING: Turning on -singleuseca mode because the key cannot self-sign" 1112 | MAKECSR=yes ; SINGLEUSECA=yes 1113 | } 1114 | 1115 | # Due to Chrome, default -suca to a certificate with SANs 1116 | [[ "$SINGLEUSECA" = yes && "$V3SUCA" != no && "$CN" != '' ]] && { 1117 | V3=yes ; V3SAN=yes ; V3SUCA=yes 1118 | } 1119 | 1120 | # Put the commonName at the start of the san list 1121 | [[ "$V3SAN" = yes && "$CN" != "" ]] && { 1122 | add_good_san "$CN" start ||: 1123 | } 1124 | 1125 | if [[ "$NOPROMPT" != yes && "$V3SAN" = yes && "$CSRFN" = '' ]] && 1126 | [[ "$SUBJTYPE" = '' || "$SUBJTYPE.$SUBJ_ID" = conf.0 ]] 1127 | then 1128 | if [[ "$WANTDVCERT" = yes ]] 1129 | then echo >&2 "The -dv option cannot be used with OpenSSL prompting for the name" 1130 | else echo >&2 "The -san option cannot be used with OpenSSL prompting for the name" 1131 | fi 1132 | exit 1 1133 | fi 1134 | 1135 | if [[ "$V3TIMESTAMP" = yes && "$V3CLIENT$V3SERVER$V3EMAIL$V3CODESIGN" != nononono ]] 1136 | then 1137 | echo >&2 "A timestamping certificate can only be for timestamping." 1138 | exit 1 1139 | fi 1140 | 1141 | # Problems? 1142 | if ! openssl no-req 2>&1 | grep -qie ^req 1143 | then 1144 | echo >&2 'No suitable OpenSSL command found.' 1145 | echo >&2 'The -openssl=/path/exe can be used to choose one.' 1146 | exit 1 1147 | fi 1148 | 1149 | if ! openssl x509 -help 2>&1 | grep -qie -set_serial 1150 | then NOSETSERIAL=yes 1151 | fi 1152 | 1153 | if openssl req -help 2>&1 | grep -qie -subject 1154 | then REQSUBJECT=-subject 1155 | else REQSUBJECT=-text 1156 | fi 1157 | 1158 | if [[ "$SERIAL" != '' && "$NOSETSERIAL" = yes ]] 1159 | then echo >&2 "ERROR: Cannot set the serial with this version of openssl." 1160 | exit 1 1161 | fi 1162 | 1163 | if [[ "$SERIAL" != '' && "$NOMIXING" = yes ]] 1164 | then echo >&2 "ERROR: Cannot set the serial with this shell." 1165 | exit 1 1166 | fi 1167 | 1168 | if [[ "$SUBJ" != '' && "$NOMIXING" = yes ]] 1169 | then echo >&2 "ERROR: /CN=.../ style option not available with this shell." 1170 | exit 1 1171 | fi 1172 | 1173 | if [[ "$CASUBJ" != '' && "$NOMIXING" = yes ]] 1174 | then echo >&2 "ERROR: Cannot set the CA subject using /CN=/ with this shell." 1175 | exit 1 1176 | fi 1177 | 1178 | case "$KEYSOURCE" in 1179 | :*:* ) 1180 | echo >&2 "ERROR: Duplicate key source locations: '$KEYSOURCE'" 1181 | exit 1 1182 | esac 1183 | 1184 | if ! (echo|openssl dgst "$DGST" >/dev/null 2>&1) 1185 | then 1186 | echo >&2 "Digest '$DGST' not supported by openssl" 1187 | exit 1 1188 | fi 1189 | } 1190 | 1191 | build_v3_extensions() { 1192 | # Build some V3 certificate extension sets. 1193 | # Like the certificate extensions themselves this is very complex. 1194 | # Your best option is probably: 1195 | # No option: A v1 certificate, usually perfectly acceptable. 1196 | # -v3 An bare v3 certificate; eg: a strict verifiable self signed. 1197 | # -v3ca A v3 CA certificate 1198 | # -v3usr A v3 certificate labeled as NOT a CA. 1199 | # -server A TLS (eg web) server 1200 | # -client A TLS client, eg a client certificate for a web server. 1201 | # 1202 | # This ignores the worst of the complexity, most of which was designed to 1203 | # allow CA's to charge more for "special" certificates. 1204 | # 1205 | # NOTE: Using the SAN fields forces this to be a v3 certificate. 1206 | 1207 | [[ "$V3" != yes ]] && return 0 1208 | 1209 | V3HEAD="$V3INIT" 1210 | V3XKU=; V3EKU= 1211 | [[ "$V3CRITICAL" != yes ]] && CR= || CR=critical, 1212 | 1213 | if [[ "$V3SERVER" = yes || "$V3EMAIL" = yes || "$V3CLIENT" = yes || "$V3CODESIGN" = yes || "$V3TIMESTAMP" = yes ]] 1214 | then 1215 | if [[ "$SIGNPEM" != "" || "$SINGLEUSECA" = yes ]] 1216 | then V3CA="${V3CA:-no}" 1217 | fi 1218 | fi 1219 | 1220 | V3P=${V3CAPATH:+, pathlen:$V3CAPATH} 1221 | 1222 | [[ "$V3CA" = yes ]] && V3OPTS="$V3OPTS${NL}basicConstraints = ${CR}CA:TRUE${V3P}" 1223 | [[ "$V3CA" = yes ]] && V3XKU=keyCertSign,cRLSign, 1224 | [[ "$V3CA" = no ]] && V3OPTS="$V3OPTS${NL}basicConstraints = ${CR}CA:FALSE" 1225 | 1226 | [[ "$V3SERVER" = yes ]] && V3EKU=$V3EKU,serverAuth 1227 | [[ "$V3CLIENT" = yes ]] && V3EKU=$V3EKU,clientAuth 1228 | [[ "$V3EMAIL" = yes ]] && V3EKU=$V3EKU,emailProtection 1229 | [[ "$V3CODESIGN" = yes ]] && V3EKU=$V3EKU,codeSigning,msCodeInd,msCodeCom 1230 | [[ "$V3TIMESTAMP" = yes ]] && V3EKU=$V3EKU,timeStamping 1231 | 1232 | [[ "$V3EKU" != '' ]] && 1233 | V3OPTS="$V3OPTS${NL}extendedKeyUsage = $CR${V3EKU#,}" 1234 | 1235 | if [[ "$WANTDVCERT" = yes && "$KEYCLASS" = ec ]] 1236 | then KEYENCY= ; KEYAGR=keyAgreement, 1237 | elif [[ "$WANTDVCERT" = yes && "$KEYCLASS" = rsa ]] 1238 | then KEYENCY=keyEncipherment, ; KEYAGR= 1239 | else KEYENCY=keyEncipherment, ; KEYAGR=keyAgreement, 1240 | fi 1241 | 1242 | if [[ "$V3CA" = '' ]] 1243 | then : 1244 | elif [[ "$V3SERVER" = yes && "$V3CA" != yes ]] 1245 | then 1246 | V3OPTS="$V3OPTS${NL}keyUsage = ${CR}${V3XKU}${KEYENCY}${KEYAGR}digitalSignature" 1247 | elif [[ "$V3CODESIGN" = yes && "$V3CA" != yes ]] 1248 | then 1249 | V3OPTS="$V3OPTS${NL}keyUsage = ${CR}${V3XKU}${KEYENCY}digitalSignature" 1250 | elif [[ "$V3EMAIL" = yes && "$V3CA" != yes ]] 1251 | then 1252 | V3OPTS="$V3OPTS${NL}keyUsage = ${CR}${V3XKU}${KEYENCY}digitalSignature" 1253 | elif [[ "$V3CLIENT" = yes && "$V3CA" != yes ]] 1254 | then 1255 | V3OPTS="$V3OPTS${NL}keyUsage = ${CR}${V3XKU}digitalSignature" 1256 | elif [[ "$V3TIMESTAMP" = yes && "$V3CA" != yes ]] 1257 | then 1258 | V3OPTS="$V3OPTS${NL}keyUsage = ${CR}${V3XKU}nonRepudiation,digitalSignature" 1259 | elif [[ "$V3XKU" != '' ]] 1260 | then 1261 | V3OPTS="$V3OPTS${NL}keyUsage = ${CR}${V3XKU%,}" 1262 | fi 1263 | 1264 | [[ "$V3SAN" = yes && "$SANSECT" != '' ]] && { 1265 | local SUBCRIT= 1266 | [[ "$V3CRITICAL" = yes ]] && 1267 | [[ "$SUBJTYPE" = '' || "$SUBJTYPE.$SUBJ_ID" = conf.0 ]] && 1268 | SUBCRIT=critical, 1269 | V3OPTS="$V3OPTS${NL}subjectAltName=${SUBCRIT}@san" 1270 | V3EXTRAS="$V3EXTRAS${NL}[san]${NL}${SANSECT}" 1271 | } 1272 | 1273 | [[ "$V3NAMECONS" = yes && "$NAMESECT" != '' ]] && { 1274 | local NAMECRIT= 1275 | [[ "$V3CRITICAL" = yes ]] && 1276 | NAMECRIT=critical, 1277 | V3OPTS="$V3OPTS${NL}nameConstraints=${NAMECRIT}${NAMESECT}" 1278 | 1279 | # V3OPTS="$V3OPTS${NL}nameConstraints=${NAMECRIT}@namec" 1280 | # V3EXTRAS="$V3EXTRAS${NL}[namec]${NL}${NAMESECT}" 1281 | } 1282 | 1283 | if [[ "$V3CA" = yes ]] 1284 | then 1285 | [[ "$V3SERVER" = yes ]] && V3ENS=$V3ENS,sslCA 1286 | [[ "$V3CLIENT" = yes ]] && V3ENS=$V3ENS,sslCA 1287 | [[ "$V3EMAIL" = yes ]] && V3ENS=$V3ENS,emailCA 1288 | if [[ "$V3SERVER" = no && "$V3CLIENT" = no && \ 1289 | "$V3EMAIL" = no && "$V3TIMESTAMP" = no ]] 1290 | then 1291 | V3ENS=$V3ENS,sslCA,emailCA 1292 | fi 1293 | else 1294 | [[ "$V3SERVER" = yes ]] && V3ENS=$V3ENS,server 1295 | [[ "$V3CLIENT" = yes ]] && V3ENS=$V3ENS,client 1296 | [[ "$V3EMAIL" = yes ]] && V3ENS=$V3ENS,email 1297 | [[ "$V3CODESIGN" = yes ]] && V3ENS=$V3ENS,objsign 1298 | fi 1299 | 1300 | [[ "$V3NS" = yes && "$V3ENS" != '' ]] && 1301 | V3OPTS="$V3OPTS${NL}nsCertType = ${V3ENS#,}" 1302 | 1303 | [[ "$V3CA" != '' && "$NOKEYID" != yes ]] && { 1304 | V3CERTONLY="$V3CERTONLY${NL}subjectKeyIdentifier=hash" 1305 | V3CERTONLY="$V3CERTONLY${NL}authorityKeyIdentifier=keyid,issuer${NL}" 1306 | } 1307 | 1308 | [[ "$CRLURI" != "" ]] && { 1309 | V3OPTS="$V3OPTS${NL}crlDistributionPoints=crl_uri_sect${NL}" 1310 | 1311 | # crlDistributionPoints ==> W.T.F. 1312 | V3EXTRAS="$V3EXTRAS 1313 | ${NL}[ crl_uri_sect ] 1314 | ${NL}fullname=@crl_url_fn 1315 | ${NL}[ crl_url_fn ] 1316 | ${NL}$CRLURI$NL" 1317 | } 1318 | 1319 | [[ "$POLICYTEXT" != "" || "$POLICYOID" != "" ]] && { 1320 | POLICYOID="${POLICYOID#,}" 1321 | [[ "$POLICYOID" = '' ]] && POLICYOID=2.5.29.32.0 1322 | 1323 | POLICYOID1="${POLICYOID%%,*}" 1324 | POLICYOIDX="${POLICYOID#"$POLICYOID1"}" 1325 | 1326 | V3OPTS="$V3OPTS${NL}certificatePolicies=@x509_pol${POLICYOIDX}${NL}" 1327 | 1328 | build_poltext() { 1329 | echo '[ x509_pol ]' 1330 | echo "policyIdentifier = $POLICYOID1" 1331 | [[ "$POLICYTEXT" != "" ]] && { 1332 | echo 'userNotice=@notice' 1333 | echo '[ notice ]' 1334 | echo "$POLICYTEXT" | 1335 | tr -d '"'"'" | 1336 | sed -e '1s/^/explicitText=/; $ b end; s:$:\\r\\n\\:; :end' 1337 | } 1338 | echo 1339 | } 1340 | V3EXTRAS="$V3EXTRAS${NL}$(build_poltext)$NL" 1341 | unset -f build_poltext 1342 | } 1343 | 1344 | [[ "$TLSFEATURE" != '' ]] && { 1345 | V3OPTS="$V3OPTS${NL}${TLSFEATURE}${NL}" 1346 | } 1347 | 1348 | return 0 1349 | } 1350 | 1351 | mk_rand() { 1352 | local RH RL=16 1353 | RH="$(openssl rand -hex "$RL" 2>/dev/null ||:)" 1354 | [[ "$RH" = '' ]] && 1355 | RH="$(openssl rand "$RL" | od -x | sed -n '1{s/^[^ ]*//;s/ //gp;}' )" 1356 | echo "$RH" 1357 | } 1358 | 1359 | default_options() { 1360 | # If they want San names we need the CN 1361 | [[ "$CSRFN" != '' && "$V3SAN" = yes && "$CN" = '' ]] && { 1362 | PUBKEYFN="$CSRFN" 1363 | CSRFN= 1364 | } 1365 | 1366 | [[ "$PUBKEYFN" != '' && "$PUBDATA" = '' ]] && 1367 | PUBDATA="$(cat -v "$PUBKEYFN")" 1368 | 1369 | [[ "$PUBDATA" != '' ]] && { 1370 | FORCEPUB="$(extract_pubkey <(echo "$PUBDATA") "$PUBKEYFN")" 1371 | DEFAULTCN="$( 1372 | { 1373 | echo "$PUBDATA" | 1374 | openssl req -subject -noout -nameopt multiline 2>/dev/null 1375 | echo "$PUBDATA" | 1376 | openssl x509 -subject -noout -nameopt multiline 2>/dev/null 1377 | } | sed -n 's/^ *commonName *= *\([^ ]*\) *$/\1/p' | 1378 | head -1 )" 1379 | } 1380 | 1381 | [[ "$DEFAULTCN" != '' && "$SUBJTYPE" = '' ]] && { 1382 | SUBJTYPE="conf" 1383 | SUBJ_ID=$((SUBJ_ID+1)) 1384 | SUBJ_CN="$SUBJ_CN${NL}$SUBJ_ID.commonName=$DEFAULTCN" 1385 | if [[ "$CN" = '' ]] 1386 | then CN="$DEFAULTCN" 1387 | else add_good_san "$DEFAULTCN" ||: 1388 | fi 1389 | } 1390 | 1391 | [[ "$DGST" = "" ]] && { 1392 | if [[ "$NOPKEY" = no ]] 1393 | then HAVESHA256=yes 1394 | else HAVESHA256="$(:|openssl dgst -sha256 >/dev/null 2>&1 && echo yes || echo no)" 1395 | fi 1396 | if [[ "$HAVESHA256" = yes ]] 1397 | then DGST=-sha256 1398 | else DGST=-sha 1399 | fi 1400 | } 1401 | 1402 | case "$CERT_DAYS" in 1403 | 1404 | all ) 1405 | # Try to use faketime to set: 1406 | # notBefore=Jan 1 12:00:00 1999 GMT 1407 | # notAfter=Dec 31 12:00:00 9999 GMT 1408 | CERT_DAYS= 1409 | if [[ "$NOPKEY" = no ]] 1410 | then 1411 | if faketime -h 2>/dev/null | grep -qi libfaketime 1412 | then 1413 | ft() { TZ=UTC faketime -f '1999-01-01 12:00:00' "$@" ; } 1414 | if [ "$(ft date +%Y-%m-%d 2>/dev/null)" = '1999-01-01' ] 1415 | then CERT_DAYS=2922304 1416 | else ft() { "$@"; } 1417 | fi 1418 | fi 1419 | fi 1420 | 1421 | # Otherwise just try to hit the right day. 1422 | [ "$CERT_DAYS" = '' ] && { 1423 | END_OF_TIME=$(jdayjdat 31 12 9999) 1424 | CERT_DAYS=$((END_OF_TIME - JTODAY)) 1425 | } 1426 | ;; 1427 | 1428 | *-*-* ) 1429 | # YYYY-MM-DD 1430 | Y="${CERT_DAYS%%-*}" 1431 | M="${CERT_DAYS#*-}" 1432 | M="${M%-*}" 1433 | D="${CERT_DAYS##*-}" 1434 | END_DATE=$(jdayjdat "$D" "$M" "$Y") 1435 | CERT_DAYS=$((END_DATE - JTODAY)) 1436 | ;; 1437 | esac 1438 | 1439 | return 0 1440 | } 1441 | 1442 | apply_options() { 1443 | [[ "$MAKECSR" = yes ]] && X509= || X509=-x509 1444 | 1445 | if [[ "$NOPROMPT" = yes ]] 1446 | then 1447 | if [[ "$SUBJTYPE" = '' || "$SUBJTYPE.$SUBJ_ID" = conf.0 ]] 1448 | then 1449 | if [[ "$ADDDNQ" = yes ]] 1450 | then SUBJTYPE=conf 1451 | else SUBJTYPE=arg ; SUBJ=/ 1452 | fi 1453 | fi 1454 | elif [[ "$SUBJTYPE" = '' && "$ADDDNQ" != no ]] 1455 | then ADDDNQ=yes 1456 | fi 1457 | 1458 | [[ "$ADDDNQ" = yes ]] && { 1459 | [[ "$SERIAL" = "" ]] && DNQ="$(mk_rand)" || DNQ="$SERIAL" 1460 | 1461 | [[ "$SERIAL" = "" && "$NOSETSERIAL" != yes ]] && 1462 | SERIAL="$DNQ" 1463 | 1464 | case "$SUBJTYPE" in 1465 | arg ) SUBJ="${SUBJ%/}/dnQualifier=$DNQ/" ;; 1466 | conf ) SUBJ_DNQ="$SUBJ_DNQ${NL}dnQualifier=$DNQ" ;; 1467 | esac 1468 | } 1469 | 1470 | return 0 1471 | } 1472 | 1473 | mkreqconf() { 1474 | echo "# Configure file for WANTCSR=$1 MAKECSR=$2" 1475 | echo 'oid_section = oid_section' 1476 | echo '[req]' 1477 | echo 'distinguished_name = req_distinguished_name' 1478 | [[ "$SUBJTYPE" = conf ]] && 1479 | echo 'prompt = no' 1480 | 1481 | if [[ "$2" != no || "$1" = no ]] 1482 | then 1483 | if [[ "$V3OPTS$V3LIST$V3EXTRAS" != "" || "$1" != "yes" ]] 1484 | then 1485 | echo "$V3HEAD" 1486 | echo "$V3OPTS" 1487 | [[ "$1" != yes ]] && 1488 | echo "$V3CERTONLY" 1489 | echo "$V3LIST" 1490 | echo "$V3EXTRAS" 1491 | fi 1492 | fi 1493 | echo '[ req_distinguished_name ]' 1494 | case "$SUBJTYPE" in 1495 | conf ) 1496 | [[ "$ADDDNQ" = no ]] || echo "$SUBJ_DNQ" 1497 | echo "$SUBJ_C" 1498 | echo "$SUBJ_ST" 1499 | echo "$SUBJ_L" 1500 | echo "$SUBJ_O" 1501 | echo "$SUBJ_DC" 1502 | echo "$SUBJ_OU" 1503 | echo "$SUBJ_CN" 1504 | echo "$SUBJ_XX" 1505 | ;; 1506 | "") 1507 | echo 'commonName = Common Name' 1508 | echo 'organizationalUnitName = Organizational Unit Name' 1509 | echo "dnQualifier= Additional subject qualifier" 1510 | if [[ "$SERIAL" != "" ]] 1511 | then echo "dnQualifier_default=$SERIAL" 1512 | else echo "dnQualifier_default=$(mk_rand)" 1513 | fi 1514 | ;; 1515 | esac 1516 | echo '[oid_section]' 1517 | echo "$EXTRA_OIDS" 1518 | echo '#END' 1519 | } 1520 | 1521 | create_main_cert() { 1522 | if [[ "$CSRFN" != "" ]] 1523 | then SSCERT="$(cat "$CSRFN")" 1524 | elif [[ "$FORCEPUB" != "" ]] 1525 | then : 1526 | else 1527 | test_enddate 1528 | 1529 | [[ "$SHOWCONF" = yes ]] && mkreqconf $MAKECSR $WANTCSR 1530 | 1531 | [[ "$SSKEY" = '' ]] && 1532 | SSKEY="$(mkkey)" 1533 | 1534 | [[ "$KEYCLASS" = file ]] && { 1535 | # Decrypt if necesary. 1536 | [[ "$NOPKEY" = no ]] && 1537 | SSKEY=$(echo "$SSKEY" | openssl pkey) 1538 | 1539 | clone_key_type "$SSKEY" 1540 | } 1541 | 1542 | [[ "$SERIAL" = "" && "$NOSETSERIAL" != yes ]] && SERIAL="$(mk_rand)" 1543 | 1544 | # openssl complains about unused -days. 1545 | if [[ "$MAKECSR" != yes ]] 1546 | then CERT_DAYS_OPT="-days ${CERT_DAYS:-$DEFAULT_DAYS}" 1547 | else CERT_DAYS_OPT= 1548 | fi 1549 | 1550 | # shellcheck disable=SC2086 # Known to be correctly split. 1551 | SSCERT="$(ft openssl req -new $UTF8 $X509 $DGST \ 1552 | ${CERT_DAYS_OPT} \ 1553 | ${SERIAL:+-set_serial 0x"$SERIAL"} \ 1554 | ${SUBJ:+-subj "$SUBJ"} \ 1555 | -key <(echo "$SSKEY") \ 1556 | -config <(mkreqconf $MAKECSR $WANTCSR) )" 1557 | 1558 | [[ "$MAKECSR" = yes ]] && CSRTEXT="$SSCERT" 1559 | fi 1560 | 1561 | return 0 1562 | } 1563 | 1564 | create_suca_pem() { 1565 | [[ "$SINGLEUSECA" != yes || "$SIGNPEM" != '' ]] && return 0 1566 | 1567 | mksucareqconf() { 1568 | echo "# Configure file for a private CA" 1569 | echo '[req]' 1570 | echo 'distinguished_name = req_distinguished_name' 1571 | echo 'prompt = no' 1572 | if [[ "$V3" = yes && "$V3SUCA" != no ]] || [[ "$V3SUCA" = yes ]] 1573 | then echo "$V3INIT" 1574 | if [[ "$V3CA" != yes ]] 1575 | then 1576 | [[ "$V3CRITICAL" != yes ]] && CR= || CR=critical, 1577 | echo "basicConstraints = ${CR}CA:TRUE,pathlen:0" 1578 | if [[ "$V3CA" = no && "$NOKEYID" != yes ]] 1579 | then 1580 | echo "subjectKeyIdentifier=hash" 1581 | echo "authorityKeyIdentifier=keyid,issuer" 1582 | fi 1583 | 1584 | # Simple EKU to match the certificate. 1585 | if [ "$NOSUCAEKU" != yes ] 1586 | then 1587 | [[ "$V3CODESIGN" = yes ]] && 1588 | [[ "$V3SERVER" != yes ]] && 1589 | [[ "$V3CLIENT" != yes ]] && 1590 | [[ "$V3EMAIL" != yes ]] && 1591 | [[ "$V3TIMESTAMP" != yes ]] && 1592 | echo extendedKeyUsage = codeSigning,msCodeInd,msCodeCom 1593 | fi 1594 | 1595 | else echo "basicConstraints = CA:TRUE" 1596 | fi 1597 | fi 1598 | echo '[ req_distinguished_name ]' 1599 | 1600 | if [[ "$CASUBJ" != '' ]] 1601 | then 1602 | echo "$CASUBJ" 1603 | if [[ "$ADDDNQ" = yes ]] 1604 | then 1605 | if [[ "$SERIAL" != '' ]] 1606 | then echo "dnQualifier=$SERIAL" 1607 | else echo "dnQualifier=$(mk_rand)" 1608 | fi 1609 | fi 1610 | else 1611 | if [[ "$WANTDVCERT" != yes || "$ADDDNQ" = yes ]] 1612 | then 1613 | if [[ "$ADDDNQ" != no ]] 1614 | then 1615 | if [[ "$SERIAL" != '' ]] 1616 | then echo "dnQualifier=$SERIAL" 1617 | else echo "dnQualifier=$(mk_rand)" 1618 | fi 1619 | fi 1620 | fi 1621 | 1622 | # DV certificates need the issuer subject to have a country 1623 | # code and organisation. The XX code is valid for a CA, the 1624 | # org is supposed to be the CA's company name. 1625 | if [[ "$WANTDVCERT" = yes ]] 1626 | then echo "C=XX" 1627 | echo "O=Above reproach CA" 1628 | echo "CN=${CN:-CA}" 1629 | elif [[ "$CN" != "" ]] 1630 | then echo "O=Above reproach CA" 1631 | echo "CN=$CN" 1632 | else echo "O=Above reproach CA" 1633 | echo "CN=Private CA" 1634 | fi 1635 | fi 1636 | 1637 | echo '#END' 1638 | } 1639 | 1640 | [[ "$SERIAL" = "" && "$NOSETSERIAL" != yes ]] && SERIAL="$(mk_rand)" 1641 | CAKEY="$(mkkey)" 1642 | 1643 | [[ "$NOSETSERIAL" != yes ]] && CASER="$(mk_rand)" 1644 | 1645 | [[ "$SHOWCONF" = yes ]] && mksucareqconf 1646 | 1647 | SIGNPEM="$(ft openssl req -new $UTF8 -x509 "$DGST" \ 1648 | -days "${CERT_DAYS:-$DEFAULT_DAYS}" \ 1649 | ${CASER:+-set_serial 0x"$CASER"} \ 1650 | -key <(echo "$CAKEY") \ 1651 | -config <(mksucareqconf) 1652 | echo "$CAKEY" )" 1653 | 1654 | unset -f mksucareqconf 1655 | } 1656 | 1657 | sign_created_csr() { 1658 | 1659 | [[ "$SIGNPEM" = "" ]] && return 0 1660 | 1661 | [[ "$SERIAL" = "" ]] && SERIAL="$(mk_rand)" 1662 | 1663 | # Default to days left on CA certificate 1664 | [[ "$CERT_DAYS" = "" ]] && { 1665 | # shellcheck disable=SC2046 # I knooow 1666 | ENDDAY="$(openssl x509 -noout -enddate -in <(echo "$SIGNPEM") | 1667 | sed 's/^[^=]*=//' | 1668 | jdayjdat $(awk '{print $2,$1,$4;}') )" 1669 | CERT_DAYS=$((ENDDAY - JTODAY)) 1670 | } 1671 | 1672 | # Do we have to override the CSR's common name? 1673 | if [[ "$CSRFN" != "" && "$SUBJTYPE" != "" ]] || [[ "$FORCEPUB" != '' ]] 1674 | then 1675 | if ! openssl x509 -help 2>&1 | grep -qie -force_pubkey 1676 | then echo >&2 "ERROR: Cannot override public key with x509 $(openssl version)" 1677 | exit 1 1678 | fi 1679 | 1680 | # First extract the public key from the existing CSR. 1681 | [[ "$FORCEPUB" = '' ]] && 1682 | FORCEPUB="$(echo "$SSCERT" | openssl req -pubkey -noout)" 1683 | 1684 | # Openssl req requires a key, make it something quick. 1685 | TMPKEY="$(mktmpkey)" 1686 | 1687 | [[ "$SHOWCONF" = yes ]] && mkreqconf yes no 1688 | 1689 | # Make the new request with the new name. 1690 | SSCERT="$(openssl req -new $UTF8 \ 1691 | ${SUBJ:+-subj "$SUBJ"} \ 1692 | -key <(echo "$TMPKEY") \ 1693 | -config <(mkreqconf yes no) )" 1694 | fi 1695 | 1696 | # Quite old versions of openssl ... 1697 | test_enddate 1698 | 1699 | # Very old versions of openssl ... 1700 | if [[ "$NOSETSERIAL" = yes && "$SERIAL" != '' ]] 1701 | then 1702 | SERIALFN=/tmp/_$$.tmp 1703 | echo "$SERIAL" > "$SERIALFN" 1704 | unset SERIAL 1705 | else SERIALFN= 1706 | fi 1707 | 1708 | [[ "$SHOWCONF" = yes && "$V3HEAD" != "" ]] && mkreqconf 1709 | 1710 | SSCERT="$(ft openssl x509 -req "$DGST" \ 1711 | -days "${CERT_DAYS:-$DEFAULT_DAYS}" \ 1712 | ${SERIAL:+-set_serial 0x"$SERIAL"} \ 1713 | ${SERIALFN:+-CAserial "$SERIALFN"} \ 1714 | -in <(echo "$SSCERT") \ 1715 | -CA <(echo "$SIGNPEM") \ 1716 | -CAkey <(echo "$SIGNPEM") \ 1717 | ${FORCEPUB:+-force_pubkey <(echo "$FORCEPUB") } \ 1718 | ${V3HEAD:+-extfile <(mkreqconf) -extensions "$X509SECT"} )" 1719 | 1720 | [[ "$SERIALFN" != '' ]] && rm "$SERIALFN" # Sigh 1721 | 1722 | return 0 1723 | } 1724 | 1725 | create_crl_file() { 1726 | [[ "$WANTCSR" = yes ]] && { 1727 | echo >&2 "ERROR: Cannot make a CRL for a CSR" 1728 | exit 1 1729 | } 1730 | 1731 | if [[ "$SIGNPEM" != '' ]] 1732 | then CRLSIGN="$SIGNPEM" 1733 | elif [[ "$SSCERT" != '' && "$SSKEY" != '' ]] 1734 | then CRLSIGN="$SSCERT$NL$SSKEY" 1735 | else echo 'ERROR: No keys available to sign the CRL.' >&2 1736 | exit 1 1737 | fi 1738 | 1739 | mkcacrlconf() { 1740 | echo '[ca]' 1741 | echo 'default_ca = CA_default' 1742 | echo '[CA_default]' 1743 | echo "database = $TMP/index.txt" 1744 | if [[ "$NOPKEY" = no ]] 1745 | then echo 'default_md = default' 1746 | else echo 'default_md = sha256' 1747 | fi 1748 | } 1749 | 1750 | TMP=/tmp/ca_tmp.$$ 1751 | mkdir -m u=rwx "$TMP" 1752 | touch $TMP/index.txt 1753 | touch $TMP/index.txt.attr 1754 | 1755 | ft openssl ca \ 1756 | -config <(mkcacrlconf) \ 1757 | -gencrl \ 1758 | -crldays "${CERT_DAYS:-$DEFAULT_DAYS}" \ 1759 | -keyfile <(echo "$CRLSIGN") \ 1760 | -cert <(echo "$CRLSIGN") 1761 | 1762 | rm -fr "$TMP" 1763 | 1764 | unset -f mkcacrlconf 1765 | return 0 1766 | } 1767 | 1768 | create_pfx_file() { 1769 | [[ "$PKCS12FN" = "" ]] && return 0 1770 | 1771 | [[ "$WANTCSR" = yes ]] && { echo >&2 "PKCS12 files can't store certificate signing requests."; exit 1;} 1772 | 1773 | [[ "$SSKEY" = '' ]] && { 1774 | echo 'ERROR: A pfx file needs a private key' >&2 1775 | exit 1 1776 | } 1777 | 1778 | if [ "$PKCS12ASCII" != yes ] 1779 | then do_pfx "$PKCS12FN" 1780 | else 1781 | ( 1782 | umask 077 1783 | { 1784 | echo -----BEGIN PKCS12----- 1785 | do_pfx '' | openssl base64 -e 1786 | echo -----END PKCS12----- 1787 | } > "$PKCS12FN" 1788 | ) 1789 | fi 1790 | 1791 | return 0 1792 | } 1793 | 1794 | do_pfx() { 1795 | 1796 | openssl pkcs12 -export -passout "${KEYPASS:-pass:}" \ 1797 | ${1:+-out "$PKCS12FN"} \ 1798 | -name "$CN til: $(echo "$SSCERT" | openssl x509 -noout -enddate | 1799 | sed -n 's/.*= *//p')" \ 1800 | -inkey <(echo "$SSKEY") \ 1801 | -in <(echo "$SSCERT") \ 1802 | ${SIGNPEM:+-certfile <(echo "$SIGNPEM" | openssl x509) } \ 1803 | ${SIGNPEM:+-caname "$(echo "$SIGNPEM" | openssl x509 -noout \ 1804 | -subject -nameopt multiline | sed -n 's/ *commonName *= //p' 1805 | ) CA til: $(echo "$SIGNPEM" | openssl x509 -noout -enddate | 1806 | sed -n 's/.*= *//p' )"} 1807 | } 1808 | 1809 | outp() { 1810 | if [[ "$WANTCSR" = yes ]] 1811 | then echo "$SSCERT" | openssl req $REQSUBJECT 1812 | else echo "$SSCERT" | openssl x509 -subject -issuer -fingerprint -serial -dates 1813 | fi 1814 | 1815 | [[ "$SINGLEUSECA" = yes || "$CAFN" != '' ]] && { 1816 | if [[ "$SIGNPEM" = '' && "$CAFN" != '' && "$CAFN" != "$1" ]] 1817 | then echo "$SSCERT" | openssl x509 -subject -serial -dates > "$CAFN" 1818 | elif [[ "$CAFN" != '' && "$CAFN" != "$1" ]] 1819 | then echo "$SIGNPEM" | openssl x509 -subject -serial -dates >"$CAFN" 1820 | else echo "$SIGNPEM" | openssl x509 -subject -serial -dates 1821 | fi 1822 | } 1823 | 1824 | [[ "$ADDCERT" != '' ]] && echo "$ADDCERT" 1825 | 1826 | [[ "$CSRTEXT" != "" && "$SHOWCSR" = yes ]] && 1827 | echo "$CSRTEXT" 1828 | 1829 | if [[ "$KFN" = '/dev/null' ]] 1830 | then : 1831 | elif [[ "$KFN" != '' ]] 1832 | then umask 077 1833 | outk > "$KFN" 1834 | else outk 1835 | fi 1836 | 1837 | [[ "$MAKECRL" = yes ]] && { 1838 | if [[ "$CRLFN" = '' || "$CRLFN" = "$1" || "$CRLFN" = "$KFN" ]] 1839 | then echo "$CRLPEM" | openssl crl -text 1840 | else echo "$CRLPEM" | openssl crl -text > "$CRLFN" 1841 | fi 1842 | } 1843 | 1844 | return 0 1845 | } 1846 | 1847 | outk() { 1848 | if [[ "$KEYPASS" != "" && "$KEYPASS" != 'pass:' ]] 1849 | then echo "$SSKEY" | openssl pkey ${KEYPASS:+$CIPHER -passout "$KEYPASS"} 1850 | echo 'Public key for above key is:' 1851 | echo "$SSKEY" | openssl pkey -pubout 1852 | else echo "$SSKEY" 1853 | fi 1854 | } 1855 | 1856 | extract_pubkey() { 1857 | local KFNAME KEYFILE PUBKEY ITEM PEMTYPE 1858 | KFNAME="$1" 1859 | KEYFILE="$(cat "$KFNAME")" 1860 | 1861 | for PEMTYPE in x509 req pkey pubkey rsa ec dsa 1862 | do 1863 | PUBKEY= 1864 | if [ "$PEMTYPE" = 'pubkey' ] 1865 | then ITEM="$(echo "$KEYFILE" | openssl pkey -pubin 2>/dev/null)" 1866 | elif [[ "$PEMTYPE" = rsa || "$PEMTYPE" = dsa || "$PEMTYPE" = ec || "$PEMTYPE" = pkey ]] 1867 | then ITEM="$(echo "$KEYFILE" | openssl "$PEMTYPE" -passin pass:0000 2>/dev/null)" 1868 | else ITEM="$(echo "$KEYFILE" | openssl "$PEMTYPE" 2>/dev/null)" 1869 | fi 1870 | 1871 | [ "$ITEM" = "" ] && continue 1872 | 1873 | case "$PEMTYPE" in 1874 | x509 ) PUBKEY="$(echo "$ITEM" | openssl x509 -pubkey -noout 2>/dev/null)" ;; 1875 | req ) PUBKEY="$(echo "$ITEM" | openssl req -pubkey -noout 2>/dev/null)" ;; 1876 | pkey ) PUBKEY="$(echo "$ITEM" | openssl pkey -pubout 2>/dev/null)" ;; 1877 | pubkey ) PUBKEY="$(echo "$ITEM" | openssl pkey -pubin 2>/dev/null)" ;; 1878 | 1879 | rsa|ec|dsa ) 1880 | PUBKEY="$(echo "$ITEM" | openssl "$PEMTYPE" -pubout 2>/dev/null)" ;; 1881 | esac 1882 | 1883 | [ "$PUBKEY" = "" ] || break 1884 | done 1885 | 1886 | [ "$PUBKEY" = '' ] && { 1887 | echo >&2 "Cannot find public key in ${2:-the file}, it needs to be unencrypted and in PEM format" 1888 | exit 1 1889 | } 1890 | 1891 | echo "$PUBKEY" 1892 | } 1893 | 1894 | clone_key_type() { 1895 | local INKEY="$1" ECTYPE RSABITS DSALEN DSABITS 1896 | 1897 | # Match a named EC curve using an ASN1 OID. 1898 | # Recommend: 1899 | # secp256r1, secp384r1, secp521r1 Supported by browsers. 1900 | 1901 | ECTYPE=$(echo "$INKEY" | openssl ec -noout -text 2>&1 | 1902 | awk '/^ASN1 OID:/ {print $3;}') 1903 | 1904 | # Do we have an EC curve name ? 1905 | [ "$ECTYPE" != "" ] && { 1906 | eval "mkkey() { openssl ecparam -name '$ECTYPE' -genkey -noout ; }" 1907 | return 1908 | } 1909 | 1910 | # Match the RSA bitsize of the key 1911 | RSABITS=$(echo "$INKEY" | openssl rsa -text -noout 2>&1 | 1912 | awk '/Key: .*bit/{sub("bit.*","",$0);gsub("[^0-9]","",$0); print $0;}') 1913 | 1914 | # Do we have an RSA bit size ? 1915 | [ "$RSABITS" != "" ] && { 1916 | eval "mkkey() { openssl genrsa '$RSABITS' ; }" 1917 | return 1918 | } 1919 | 1920 | # Match the DSA bitsize of the key 1921 | DSABITS=$(echo "$INKEY" | openssl dsa -text -noout 2>&1 | 1922 | awk '/Key: .*bit/{sub("bit.*","",$0);gsub("[^0-9]","",$0); print $0;}') 1923 | 1924 | DSALEN=$(echo "$INKEY" | 1925 | openssl dsa -modulus -noout 2>/dev/null | 1926 | sed -n 's/.*=//p' | wc -c) 1927 | [ "$DSALEN" -gt 0 ] && DSABITS="$((DSALEN*4/8*8))" 1928 | 1929 | # Do we have a DSA bit size ? 1930 | [ "$DSABITS" != "" ] && { 1931 | eval "mkkey() { openssl dsaparam -noout -genkey '$DSABITS' ; }" 1932 | return 1933 | } 1934 | 1935 | # Humm, dunno what we had, hopefully this will do. 1936 | mkkey() { openssl ecparam -name prime256v1 -genkey -noout ; } 1937 | 1938 | return 0 1939 | } 1940 | 1941 | jdayjdat() { 1942 | awk '#!/usr/bin/awk -f 1943 | 1944 | BEGIN{ 1945 | days[0] = "Sunday"; 1946 | days[1] = "Monday"; 1947 | days[2] = "Tuesday"; 1948 | days[3] = "Wednesday"; 1949 | days[4] = "Thursday"; 1950 | days[5] = "Friday"; 1951 | days[6] = "Saturday"; 1952 | 1953 | months["jan"] = 1; 1954 | months["feb"] = 2; 1955 | months["mar"] = 3; 1956 | months["apr"] = 4; 1957 | months["may"] = 5; 1958 | months["jun"] = 6; 1959 | months["jul"] = 7; 1960 | months["aug"] = 8; 1961 | months["sep"] = 9; 1962 | months["oct"] = 10; 1963 | months["nov"] = 11; 1964 | months["dec"] = 12; 1965 | 1966 | if(ARGC == 2) { 1967 | j = ARGV[1]+0; 1968 | if (j>0 && j<65536) j += 2400000 1969 | split(jdate(j),A); 1970 | print A[1]; 1971 | } else if(ARGC >= 4) { 1972 | d = ARGV[1]; m = ARGV[2]; y = ARGV[3]; 1973 | 1974 | if (d+0 > 999 && y < 100) { ms = d; d = y; y = ms; } 1975 | ms = tolower(substr(m, 1, 3)); 1976 | if (ms in months) m = months[ms]; 1977 | else { 1978 | ms = tolower(substr(d, 1, 3)); 1979 | if (ms in months) { 1980 | d = m; 1981 | m = months[ms]; 1982 | } 1983 | } 1984 | 1985 | if( y>31 && y<70 ) y+=2000; 1986 | if( y>31 && y<100) y+=1900; 1987 | if( m<1) {m+=12; y--;} 1988 | if( m>12) {m-=12; y++;} 1989 | 1990 | jd = jday(d,m,y); 1991 | if (ARGC > 4) { 1992 | printf("Input: %04d-%02d-%02d\n", y, m, d); 1993 | print jd 1994 | js = jdate(jd); 1995 | split(js,A,/[- ]/); 1996 | print js; 1997 | print A[1], A[2], A[3], days[A[4]]; 1998 | } else 1999 | print jd 2000 | } else 2001 | if(ARGC == 1) { 2002 | "date +%s" | getline t 2003 | t += 43200; 2004 | j = int(t / 86400); t -= j * 86400; 2005 | j = int(j + 2440587); 2006 | print jdate(j), t "s"; 2007 | } else 2008 | print "Incorrect arguments" 2009 | } 2010 | 2011 | function jdate(j, y,m,d,dow) 2012 | { 2013 | # Julian date converter. Takes a julian date (the number of days since 2014 | # some distant epoch or other) and returns the broken out date. 2015 | # d = day of month; 2016 | # m = month; 2017 | # y = year (actual year, like 1977, not 77 unless it was 77 a.d.); 2018 | # dow = day of week (0->Sunday to 6->Saturday) 2019 | # These are Gregorian. 2020 | # Copied from Algorithm 199 in Collected algorithms of the CACM 2021 | # Author: Robert G. Tantzen, Translators: Nat Howard, Robert de Bath 2022 | 2023 | j = j + 1 2024 | dow = (j + 1)%7; 2025 | j -= 1721119; 2026 | # This should be a Euclidean division. But dates before 0001-01-01 dont 2027 | # make much sense anyway. Or even Friday, 15 October 1582 when the 2028 | # Gregorian calendar started. 2029 | y = int((4 * j - 1)/146097); 2030 | j = 4 * j - 1 - 146097 * y; 2031 | d = int(j/4); 2032 | j = int((4 * d + 3)/1461); 2033 | d = 4 * d + 3 - 1461 * j; 2034 | d = int((d + 4)/4); 2035 | m = int((5 * d - 3)/153); 2036 | d = 5 * d - 3 - 153 * m; 2037 | d = int((d + 5) / 5); 2038 | y = 100 * y + j; 2039 | if(m < 10) m += 3; else { m -= 9; ++y; } 2040 | 2041 | return sprintf("%04d-%02d-%02d %d", y, m, d, dow); 2042 | } 2043 | 2044 | function jday(d,m,y, c,ya,j) 2045 | { 2046 | # Takes a date, and returns a Julian day. A Julian day is the number of 2047 | # days since some base date (in the very distant past). 2048 | # Handy for getting date of x number of days after a given Julian date 2049 | # (use jdate to get that from the Gregorian date). 2050 | # Author: Robert G. Tantzen, translators: Nat Howard, Robert de Bath 2051 | # Translated from the algol original in Collected Algorithms of CACM 2052 | # (This and jdate are algorithm 199). 2053 | 2054 | if(m>2) m -=3; else { m +=9; --y; } 2055 | c = int(y/100); 2056 | ya = y - (100*c); 2057 | j = int(146097*c/4) + int(1461*ya/4) + int((153*m+2)/5) + d + 1721119; 2058 | 2059 | return j - 1; 2060 | } 2061 | ' "$@" 2062 | } 2063 | 2064 | test_enddate() { 2065 | # Before this end dates may be bad. 2066 | if [[ "$NOPKEY" = yes ]]; then 2067 | TMPKEY=$(mktmpkey) 2068 | TESTCRT=$(openssl req -new -x509 -nodes \ 2069 | -days "${CERT_DAYS:-$DEFAULT_DAYS}" \ 2070 | -key <(echo "$TMPKEY") \ 2071 | -config <( 2072 | echo "[req]" ; echo "distinguished_name = rdn" 2073 | echo "prompt = no" ; echo "[rdn]" ; echo "CN=localhost" 2074 | ) ) 2075 | 2076 | # shellcheck disable=SC2046 # jdayjdat again 2077 | ENDDAY="$(openssl x509 -noout -enddate -in <(echo "$TESTCRT") | 2078 | sed 's/^[^=]*=//' | 2079 | jdayjdat $(awk '{print $2,$1,$4;}') )" 2080 | 2081 | TEST_DAYS=$((ENDDAY - JTODAY)) 2082 | 2083 | [[ "$TEST_DAYS" -lt 0 ]] && { 2084 | echo >&2 Error setting certificate end date, \ 2085 | "${CERT_DAYS:-$DEFAULT_DAYS} days gives $(jdayjdat "$ENDDAY")" 2086 | 2087 | echo >&2 \ 2088 | "You might try -days=2038-01-18 or a newer version of openssl" 2089 | exit 1 2090 | } 2091 | fi 2092 | return 0 2093 | } 2094 | 2095 | test_for_windows() { 2096 | # MINGW has two copies of openssl, one compiled for Windows and one 2097 | # compiled for Unix. The Windows version cannot be used with process 2098 | # substitution ( the <(...) stuff ). Check to see if the default is 2099 | # working, if not try to find the other one. 2100 | local pgm 2101 | 2102 | if ! openssl no-req 2>&1 | grep -qie ^req 2103 | then openssl() { echo >&2 "OpenSSL failed to run." ; return 1; } 2104 | elif [ "$(openssl enc -a -in <(echo ok) 2>/dev/null )" != b2sK ] 2105 | then 2106 | for pgm in $(which -a openssl) 2107 | do 2108 | eval "openssl() { command '$pgm' \"\$@\"; }" 2109 | 2110 | if [ "$(openssl enc -a -in <(echo ok) 2>/dev/null )" = b2sK ] 2111 | then break 2112 | fi 2113 | done 2114 | fi 2115 | } 2116 | 2117 | main "$@" 2118 | -------------------------------------------------------------------------------- /mk-ovpn: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | [ "$#" = 0 ] && { echo >&2 "Usage: $0 CommonName [Options]" ; exit 1; } 3 | # 4 | # This script creates certificates for OpenVPN tls setups. 5 | # Each key has a different CA certificate created for it and the setup 6 | # is designed so that any machine only has the CA certificates for the 7 | # machines that it is allowed to connect to. 8 | # 9 | # Note none of ns-cert-type, remote-cert-tls or verify-x509-name are 10 | # required to make this secure. But it is suggested that verify-x509-name 11 | # is used to stop OpenVPN complaining as this is what's required for 12 | # certificates signed by a public CA. 13 | # 14 | # For OpenVPN the certificates can be V1 or V3, however, V1 certificates 15 | # can be used as intermediate CA certificates or direct validation. If this 16 | # is a problem the V3 certificates with the CA constraint should be used. 17 | 18 | do_commonname() { 19 | CN="$1" ; SIGN=-suca 20 | ( umask 0077 ; mkdir -p "$CERT_DIR" ) 21 | CRTFILE="$CERT_DIR"/"$CN".crt 22 | 23 | [ -s "$CRTFILE" ] && { echo "ERROR: Name '$CN' already exists" ; return ; } 24 | 25 | mkc() { 26 | ./mk-cert \ 27 | ${CERT_DAYS:+-days="$CERT_DAYS"} \ 28 | "$SIGN" \ 29 | "$CN" \ 30 | -ed25519 \ 31 | -casubj="$CN" \ 32 | "$@" > "$CRTFILE" 33 | } 34 | 35 | [ -f "$CERT_DIR/${CERTTYPE:-client}-ca.pem" ] && 36 | SIGN="-sign=$CERT_DIR/${CERTTYPE:-client}-ca.pem" 37 | 38 | if [ "$CERTTYPE" = '' ] 39 | then if [ "$CERTVER" = v1 ] 40 | then mkc -casubj="Private CA" 41 | else mkc -casubj="Private CA" -v3critical 42 | fi 43 | else 44 | if [ "$CERTVER" = v1 ] 45 | then 46 | mkc -casubj="OpenVPN $CERTTYPE CA" "OpenVPN $CERTTYPE" 47 | elif [ "$CERTVER" = v3 ] 48 | then 49 | mkc -casubj="OpenVPN $CERTTYPE CA" "OpenVPN $CERTTYPE" -v3critical -v3ns="$CERTTYPE" 50 | else 51 | mkc -casubj="OpenVPN $CERTTYPE CA" "OpenVPN $CERTTYPE" -v3critical "-$CERTTYPE" 52 | fi 53 | fi 54 | } 55 | 56 | CERT_DIR="${CERT_DIR:-certs}" 57 | CERTVER=v1 ; CERTTYPE= 58 | 59 | for e in n y 60 | do 61 | for v 62 | do case "$v" in 63 | -s|-server ) CERTTYPE=server ;; 64 | -c|-client ) CERTTYPE=client ;; 65 | -v1 ) CERTVER=v1 ;; 66 | -v3 ) CERTVER=v3 ;; 67 | -v3ext ) CERTVER=v3x ;; 68 | 69 | -days=[1-9]*[0-9] ) CERT_DAYS="${v#*=}" ;; 70 | -dir=* ) CERT_DIR="${v#*=}" ;; 71 | 72 | -* ) echo >&2 "Option $v not recognised"; exit 1;; 73 | 74 | *) [ "$e" = y ] && do_commonname "$v" 75 | ;; 76 | esac 77 | done 78 | done 79 | -------------------------------------------------------------------------------- /mk-ovpn2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ -z "$BASH_VERSION" ];then exec bash "$0" "$@";else set +o posix;fi 3 | 4 | set -e 5 | umask 077 6 | export OPENSSL_CONF=/dev/null 7 | [ "$CERT_DIR" = "" ] && [ "$1" != "" ] && [ -d "$1" ] && CERT_DIR="$1" 8 | CERT_DIR="${CERT_DIR:-certs}" 9 | 10 | CONF_DIR="$CERT_DIR-conf" 11 | TMP=/tmp/ca_tmp.$$ 12 | trap 'rm -rf "$TMP"' 0 1 2 13 | 14 | [ -d "$CERT_DIR" ] || { echo >&2 ERROR: Certificate directory missing ; exit 1;} 15 | 16 | build_files() { 17 | rm -rf "$TMP" 18 | mkdir "$TMP" 19 | mkdir -p "$CONF_DIR" 20 | 21 | SERVERCOUNT=0 22 | SERVERNAME= 23 | SERVEROU= 24 | 25 | rm -f "$TMP"/clients.CA-list.pem "$TMP"/servers.CA-list.pem ||: 26 | 27 | # Add all the server CA certificates. 28 | for cert in "$CERT_DIR"/*.crt 29 | do 30 | CN="$(basename "$cert" .crt)" 31 | echo >&2 Checking "$CN" 32 | fetch_pems "$CN" >&2 33 | [ -s "$TMP"/client.ca ] || continue 34 | 35 | save_certs "$CN" 36 | restore_certs "$CN" 37 | 38 | SUSAGE=$(openssl x509 -purpose -noout -in "$TMP"/client.crt 2>/dev/null | 39 | grep -q 'SSL server *: *Yes' && 40 | echo yes || echo no) 41 | 42 | CUSAGE=$(openssl x509 -purpose -noout -in "$TMP"/client.crt 2>/dev/null | 43 | grep -q 'SSL client *: *Yes' && 44 | echo yes || echo no) 45 | 46 | [ "$SUSAGE" = yes ] && 47 | SUSAGE=$(openssl verify -check_ss_sig -purpose sslserver \ 48 | -CAfile "$TMP"/client.ca "$TMP"/client.crt 2>&1 | grep -iq ^error && 49 | echo no || echo yes) 50 | 51 | [ "$CUSAGE" = yes ] && 52 | CUSAGE=$(openssl verify -check_ss_sig -purpose sslclient \ 53 | -CAfile "$TMP"/client.ca "$TMP"/client.crt 2>&1 | grep -iq ^error && 54 | echo no || echo yes) 55 | 56 | DESC="$(openssl x509 -subject -issuer -in "$TMP"/client.crt)" 57 | 58 | case "$CUSAGE$SUSAGE" in 59 | yesyes ) 60 | case "$DESC" in 61 | *lient* ) touch "$TMP"/type.client."$CN".flg ;; 62 | *erver* ) touch "$TMP"/type.server."$CN".flg ;; 63 | * ) touch "$TMP"/type.client."$CN".flg ;; 64 | esac 65 | ;; 66 | yesno ) touch "$TMP"/type.client."$CN".flg ;; 67 | noyes ) touch "$TMP"/type.server."$CN".flg ;; 68 | * ) 69 | openssl verify -check_ss_sig -purpose sslserver \ 70 | -CAfile "$TMP"/client.ca "$TMP"/client.crt >&2 ||: 71 | echo >&2 "ERROR: Skipped $CN, unusable certificate." 72 | continue 73 | ;; 74 | esac 75 | 76 | # Dup check 77 | ISSU="$(openssl x509 -issuer_hash -noout < "$TMP"/client.crt 2>/dev/null)" 78 | CAPUB="$(openssl x509 -pubkey -noout < "$TMP"/client.ca 2>/dev/null | 79 | openssl md5 | awk '{print $NF;}' )" 80 | 81 | [ -f "$TMP"/type.server."$CN".flg ] && { 82 | 83 | SERVERCOUNT=$((SERVERCOUNT+1)) 84 | SERVERNAME="$(openssl x509 -noout -in "$TMP"/client.crt \ 85 | -subject -nameopt multiline | sed -n 's/ *commonName *= //p')" 86 | 87 | OU="$(openssl x509 -noout -in "$TMP"/client.crt -subject \ 88 | -nameopt multiline | sed -n 's/ *organizationalUnitName *= //p')" 89 | 90 | # If they have a common (simple) OU 91 | if [ "$SERVERCOUNT" = 1 ] 92 | then SERVEROU="$OU" 93 | else [ "$OU" != "$SERVEROU" ] && SERVEROU="" 94 | fi 95 | [ "$(echo "$OU" | wc -l)" != 1 ] && SERVEROU="" 96 | 97 | [ -f "$TMP/dup.server.$ISSU.$CAPUB.flg" ] || { 98 | 99 | touch "$TMP/dup.server.$ISSU.flg" 100 | touch "$TMP/dup.server.$ISSU.$CAPUB.flg" 101 | openssl x509 -subject -serial -dates -in "$TMP"/client.ca >> "$TMP"/servers.CA-list.pem 102 | } 103 | } 104 | 105 | [ -f "$TMP"/type.client."$CN".flg ] && { 106 | [ -f "$TMP/dup.client.$ISSU.$CAPUB.flg" ] || { 107 | touch "$TMP/dup.client.$ISSU.flg" 108 | touch "$TMP/dup.client.$ISSU.$CAPUB.flg" 109 | { 110 | openssl x509 -subject -serial -dates -in "$TMP"/client.ca 111 | echo 112 | } >> "$TMP"/clients.CA-list.pem 113 | } 114 | } 115 | done 116 | 117 | [ -s "$TMP"/servers.CA-list.pem ] || { 118 | echo >&2 ERROR: No servers have been defined. 119 | exit 1 120 | } 121 | 122 | [ -s "$TMP"/clients.CA-list.pem ] || { 123 | echo >&2 ERROR: No clients have been defined. 124 | exit 1 125 | } 126 | 127 | FLG=1 128 | for file in "$CERT_DIR"/*pattern*.ovpn 129 | do 130 | if [ -f "$file" ] 131 | then tfile="$(basename "$file")" 132 | tfile="${tfile/pattern/$CN}" 133 | else continue 134 | fi 135 | FLG=0 136 | done 137 | 138 | [ "$FLG" = 0 ] || { 139 | echo >&2 ERROR: No ovpn pattern files defined. 140 | exit 1 141 | } 142 | 143 | # Protect the TLS handshake from s-kiddies. 144 | [ -f "$CERT_DIR"/tls-crypt.pvk ] || 145 | openvpn --genkey --secret "$CERT_DIR"/tls-crypt.pvk 146 | 147 | # For all the client CA certificates 148 | for cert in "$CERT_DIR"/*.crt 149 | do 150 | CN="$(basename "$cert" .crt)" 151 | [ "$CN" = "*" ] && continue 152 | [ -f "$TMP"/type.client."$CN".flg ] || continue 153 | 154 | echo Client "$CN" 155 | restore_certs "$CN" 156 | [ -s "$TMP"/client.ca ] || continue 157 | 158 | CLIENTNAME="$(openssl x509 -noout -in "$TMP"/client.crt \ 159 | -subject -nameopt multiline | sed -n 's/ *commonName *= //p')" 160 | 161 | [ "$CLIENTNAME" != "$CN" ] && 162 | echo "WARNING: Certificate common name is '$CLIENTNAME'" 163 | 164 | ISSU="$(openssl x509 -issuer_hash -noout < "$TMP"/client.crt 2>/dev/null)" 165 | [ -f "$TMP/dup.server.$ISSU.flg" ] && 166 | echo WARNING: This client has the same CA as a server. 167 | 168 | for file in "$CERT_DIR"/*pattern*.ovpn 169 | do 170 | if [ -f "$file" ] 171 | then tfile="$(basename "$file")" 172 | tfile="${tfile/pattern/$CN}" 173 | else continue 174 | fi 175 | 176 | { 177 | echo "# Name: $CN" 178 | cat "$file" 179 | echo 180 | 181 | # Stop the deamon whinging. 182 | # This has NO security effect as the CAs are unique. 183 | # (Even if the clients are the same OU) 184 | if [ "$SERVERCOUNT" -eq 1 ] && [ "$SERVERNAME" != "" ] 185 | then 186 | # Not on 2.4.1 Windows, ENABLE_X509ALTUSERNAME missing 187 | # echo x509-username-field CN 188 | echo "verify-x509-name '$SERVERNAME' name" 189 | elif [ "$SERVEROU" != "" ] 190 | then 191 | echo x509-username-field OU 192 | echo "verify-x509-name '$SERVEROU' name" 193 | fi 194 | echo 195 | 196 | [ -s "$CERT_DIR"/tls-crypt.pvk ] && { 197 | if ! grep -q tls-auth "$file" 198 | then 199 | echo '' 200 | cat "$CERT_DIR"/tls-crypt.pvk 201 | echo '' 202 | fi 203 | } 204 | 205 | echo '' 206 | openssl x509 -subject -serial -dates -in "$TMP"/client.crt 207 | echo '' 208 | 209 | echo '' 210 | openssl pkey -in "$TMP"/client.key 211 | echo '' 212 | 213 | echo '' 214 | cat "$TMP"/servers.CA-list.pem 215 | echo '' 216 | 217 | } | sed 's/$/ /' > "$TMP"/"$tfile" 218 | 219 | try_file "$CONF_DIR" "$tfile" 220 | done 221 | 222 | rm -f "$TMP"/client.key "$TMP"/client.crt ||: 223 | done 224 | 225 | # Copy all the certificates and keys for the servers. 226 | for cert in "$CERT_DIR"/*.crt 227 | do 228 | CN="$(basename "$cert" .crt)" 229 | [ -f "$TMP"/type.server."$CN".flg ] || continue 230 | 231 | echo "Server $CN" 232 | restore_certs "$CN" 233 | [ -s "$TMP"/client.ca ] || continue 234 | 235 | ISSU="$(openssl x509 -issuer_hash -noout < "$TMP"/client.crt 2>/dev/null)" 236 | [ -f "$TMP/dup.client.$ISSU.flg" ] && 237 | echo WARNING: This server has the same CA as a client. 238 | 239 | openssl x509 -subject -serial -dates -in "$TMP"/client.crt > "$TMP"/"$CN".crt 240 | openssl pkey -in "$TMP"/client.key > "$TMP"/"$CN".key 241 | 242 | cp -p "$TMP"/clients.CA-list.pem "$TMP"/"$CN".ca-list.pem 243 | 244 | cp -p "$CERT_DIR"/tls-crypt.pvk "$TMP"/"$CN".tls-crypt.pvk 245 | 246 | try_file "$CONF_DIR" "$CN".crt 247 | try_file "$CONF_DIR" "$CN".key 248 | try_file "$CONF_DIR" "$CN".ca-list.pem 249 | try_file "$CONF_DIR" "$CN".tls-crypt.pvk 250 | 251 | [ -f "$CERT_DIR"/dhparam.pem ] && { 252 | cp -p "$CERT_DIR"/dhparam.pem "$TMP"/"$CN".dhparam.pem 253 | try_file "$CONF_DIR" "$CN".dhparam.pem 254 | } 255 | done 256 | } 257 | 258 | fetch_pems() { 259 | CN="$1" 260 | # This checks the certificates and allows us to put them anywhere 261 | 262 | rm -f "$TMP"/client.key "$TMP"/client.crt "$TMP"/client.ca \ 263 | "$TMP"/client.pfx "$TMP"/client.pem "$TMP"/client.ca.pem 264 | 265 | touch "$TMP"/client.crt "$TMP"/client.key 266 | for i in "$CERT_DIR"/"$CN".key "$CERT_DIR"/"$CN".crt "$CERT_DIR"/"$CN".pem "$CERT_DIR"/"$CN".ca.crt "$CERT_DIR"/"$CN".ca.cer 267 | do 268 | [ -s "$i" ] || continue 269 | [ -s "$TMP"/client.crt ] || { 270 | openssl x509 -in "$i" > "$TMP"/client.crt 2>/dev/null ||: 271 | } 272 | [ -s "$TMP"/client.key ] || { 273 | openssl pkey -in "$i" > "$TMP"/client.key 2>/dev/null ||: 274 | } 275 | 276 | cat "$i" >> "$TMP"/client.ca.pem 277 | done 278 | 279 | [ -s "$CERT_DIR"/ca.crt ] && cat "$CERT_DIR"/ca.crt >> "$TMP"/client.ca.pem 280 | [ -s "$CERT_DIR"/ca.pem ] && cat "$CERT_DIR"/ca.pem >> "$TMP"/client.ca.pem 281 | for i in "$CERT_DIR"/*[-.]ca.* 282 | do [ -s "$i" ] && cat "$i" >> "$TMP"/client.ca.pem 283 | done 284 | 285 | CERTPUB="$(openssl x509 -pubkey -noout -in "$TMP"/client.crt 2>/dev/null ||:)" 286 | if [ "$CERTPUB" = '' ] 287 | then echo "ERROR: Skipped $CN, no certificate." 288 | rm -f "$TMP"/client.pfx "$TMP"/client.pem "$TMP"/client.ca.pem 289 | rm -f "$TMP"/client.crt "$TMP"/client.ca "$TMP"/client.key 290 | return 291 | fi 292 | 293 | PKEYPUB="$(openssl pkey -pubout -in "$TMP"/client.key 2>/dev/null ||:)" 294 | if [ "$CERTPUB" != "$PKEYPUB" ] 295 | then echo "ERROR: Skipped $CN, public keys don't match" 296 | rm -f "$TMP"/client.pfx "$TMP"/client.pem "$TMP"/client.ca.pem 297 | rm -f "$TMP"/client.crt "$TMP"/client.ca "$TMP"/client.key 298 | return 299 | fi 300 | 301 | ISSU="$(openssl x509 -issuer_hash -noout -in "$TMP"/client.crt 2>/dev/null ||:)" 302 | SUBJ="#" 303 | 304 | k=0 305 | while : 306 | do ((k+=1)) 307 | awk -v k=$k '/^-----BEGIN/ && k>1 {k--;next;} /^-----BEGIN/&& k {disp=1;} disp{print;} /^-----END/&&disp{disp=0;k=0;}' \ 308 | < "$TMP"/client.ca.pem > "$TMP"/client.ca 309 | 310 | [ -s "$TMP"/client.ca ] || break 311 | 312 | SUBJ="$(openssl x509 -subject_hash -noout < "$TMP"/client.ca 2>/dev/null ||:)" 313 | 314 | [ "$SUBJ" == "$ISSU" ] && break 315 | done 316 | 317 | rm -f "$TMP"/client.pem "$TMP"/client.ca.pem 318 | 319 | # Check them. 320 | SUBJ="$(openssl x509 -subject_hash -noout < "$TMP"/client.ca 2>/dev/null ||:)" 321 | # CASER="$(openssl x509 -serial -noout < "$TMP"/client.ca 2>/dev/null ||:)" 322 | 323 | if [ "$SUBJ" != "$ISSU" ] 324 | then echo "ERROR: Skipped $CN, issuer not found" 325 | rm -f "$TMP"/client.crt "$TMP"/client.ca "$TMP"/client.key 326 | return 0 327 | fi 328 | return 0 329 | } 330 | 331 | save_certs() { 332 | CN="$1" 333 | mv "$TMP"/client.crt "$TMP"/saved."$CN".crt 334 | mv "$TMP"/client.key "$TMP"/saved."$CN".key 335 | mv "$TMP"/client.ca "$TMP"/saved."$CN".ca 336 | } 337 | 338 | restore_certs() { 339 | CN="$1" 340 | rm -f "$TMP"/client.key "$TMP"/client.crt "$TMP"/client.ca "$TMP"/client.pfx "$TMP"/client.pem "$TMP"/client.ca.pem 341 | cp "$TMP"/saved."$CN".crt "$TMP"/client.crt 342 | cp "$TMP"/saved."$CN".key "$TMP"/client.key 343 | cp "$TMP"/saved."$CN".ca "$TMP"/client.ca 344 | } 345 | 346 | try_file() { 347 | [ ! -s "$1"/"$2" ] && 348 | cp -p "$TMP"/"$2" "$1"/"$2" 349 | if ! cmp -s "$TMP"/"$2" "$1"/"$2" 350 | then cp -p "$TMP"/"$2" "$1"/"$2" 351 | fi 352 | [ -f "$TMP"/"$2" ] && rm "$TMP"/"$2" 353 | } 354 | 355 | build_files 356 | -------------------------------------------------------------------------------- /mk-ss: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # mk-ss bash script 4 | # ================= 5 | # 6 | # This is a small bash function to create a self-signed certificate that 7 | # expires in January 2038. If given no arguments it uses 'localhost' as 8 | # the common name. The first argument is the Common Name and is copied 9 | # into the SAN list additional arguments are set as extra SAN hosts. 10 | # 11 | # The certificate and key are 2048 bit RSA and sent out the standard output. 12 | # 13 | # This is about the same as: mk-cert -rsa -san -dnq $* 14 | 15 | make_ss() { 16 | local CN="$1" SERIAL SSKEY SSCERT 17 | [ "$CN" = "" ] && CN=localhost 18 | 19 | local -r make_ss_reqconf="$(typeset -f make_ss_reqconf)" 20 | make_ss_reqconf() { 21 | local SANLIST='' 22 | SANLIST="$(echo "$*" | xargs)" 23 | [ "$SANLIST" != "" ] && SANLIST="DNS:${SANLIST// /,DNS:}" 24 | echo '[req]' 25 | echo 'distinguished_name = rdn' 26 | echo 'prompt = no' 27 | [ "$SANLIST" != "" ] && echo 'x509_extensions = x509v3' 28 | echo '[rdn]' 29 | echo "dnQualifier=$SERIAL" 30 | echo "CN=$CN" 31 | [ "$SANLIST" != "" ] && { 32 | echo '[x509v3]' 33 | echo "subjectAltName=$SANLIST" 34 | } 35 | } 36 | 37 | SERIAL="$(openssl rand -hex 8)" 38 | SSKEY="$(openssl genrsa)" 39 | SSCERT="$(openssl req -new -x509 \ 40 | -days $((24842 - $(date +%s)/86400)) \ 41 | -set_serial 0x"$SERIAL" \ 42 | -key <(echo "$SSKEY") \ 43 | -config <(make_ss_reqconf "$@") )" 44 | 45 | unset -f make_ss_reqconf ; eval "$make_ss_reqconf" 46 | 47 | echo "$SSCERT" | openssl x509 -subject -serial -dates 48 | echo "$SSKEY" 49 | } 50 | 51 | make_ss "$@" 52 | -------------------------------------------------------------------------------- /testrun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh - 2 | if [ -z "$BASH_VERSION" ];then exec bash "$0" "$@";else set +o posix;fi 3 | 4 | if [ "$(dirname "$0")" = . ] 5 | then export PATH="$(pwd):$PATH" 6 | else export PATH="$(dirname "$0"):$PATH" 7 | fi 8 | 9 | make_certs() { 10 | mkdir -p certs 11 | 12 | mk-cert ss-host > certs/default-host.pem 13 | mk-cert -ca ss-ca > certs/default-ca.pem 14 | mk-cert -lastca ss-lastca > certs/default-lastca.pem 15 | mk-cert -suca suca-host suca-host.local localhost > certs/default-suca.pem 16 | mk-cert -dv dv-host dv-host.local localhost > certs/default-dv.pem 17 | 18 | mk-cert -san -suca -pubin=certs/default-host.pem \ 19 | suca-san-duphost.local suca-san-duphost localhost > certs/default-host-dup.pem 20 | 21 | mk-cert -v3ca -lastca -out=certs/v3-lastca.pem 'Private CA' 22 | mk-cert server-host1.local -server -out=certs/v3-server-host1.pem -sign=certs/v3-lastca.pem 23 | mk-cert server-host2.local -server -out=certs/v3-server-host2.pem -sign=certs/v3-lastca.pem 24 | mk-cert server-host3.local -server -out=certs/v3-server-host3.pem -sign=certs/v3-lastca.pem 25 | mk-cert client-host1.local -client -out=certs/v3-client-host1.pem -sign=certs/v3-lastca.pem 26 | 27 | mk-cert -suca -ed25519 \ 28 | suca-ed25519.local ed25519 localhost > certs/suca-curve25519-Edwards.pem 29 | 30 | mk-cert -suca -ec=x25519 \ 31 | suca-x25519.local x25519 localhost > certs/suca-curve25519-Montgomery.pem 32 | 33 | } 34 | 35 | mktestsets() { 36 | 37 | # ft() { faketime -f '1999-01-01 12:00:00' "$@" ; } 38 | # DAYS=-days=$((24842 - $(ft date +%s)/86400)) 39 | 40 | ft() { "$@" ; } 41 | DAYS=-days=3652 42 | 43 | CA1K=$(ft mk-cert -rsa:1024 -sha1 -v3basicca -dnq "$DAYS" \ 44 | "hostmaster@$DOM" -subj-o="Above reproach CA" ) 45 | 46 | CA2K=$(ft mk-cert -rsa:2048 -sha256 -v3basicca -dnq "$DAYS" \ 47 | "hostmaster@$DOM" -subj-o="Above reproach CA" ) 48 | 49 | run_cert "$CA1K" -ec > ca1k-${CN}.ec.1k.pem 50 | run_cert "$CA1K" -sha1 -rsa:1024 > ca1k-${CN}.rsa.1k.pem 51 | run_cert "$CA1K" -sha1 -dsa:1024 > ca1k-${CN}.dsa.1k.pem 52 | 53 | run_cert "$CA2K" -ec > ca2k-${CN}.ec.2k.pem 54 | run_cert "$CA2K" -sha256 -rsa:2048 > ca2k-${CN}.rsa.2k.pem 55 | run_cert "$CA2K" -sha256 -dsa:2048 > ca2k-${CN}.dsa.2k.pem 56 | 57 | [ -f dhparam-2k.pem ] || 58 | openssl dhparam 2048 > dhparam-2k.pem 59 | 60 | [ -f dhparam-1k.pem ] || 61 | openssl dhparam 1024 > dhparam-1k.pem 62 | 63 | # IE6 keys 64 | # DH parameter can be 2048 if TLS1.0 is DISabled, 65 | # BUT only 1024 if TLS1.0 is ENabled. 66 | cat ca1k-${CN}.dsa.1k.pem dhparam-1k.pem > ca1k-${CN}.dsa.tls1.1k.pem 67 | cat ca1k-${CN}.dsa.1k.pem dhparam-2k.pem > ca1k-${CN}.dsa.ssl3.1k+2k.pem 68 | 69 | # Later DHE-DSS 70 | cat ca2k-${CN}.dsa.2k.pem dhparam-2k.pem > ca2k-${CN}.dsa.dhe.2k.pem 71 | 72 | { 73 | ft mk-cert \ 74 | -suca -rsa:1024 -sha1 \ 75 | $DAYS \ 76 | -subj-cn=tvisiontech.co.uk \ 77 | -subj-ou='SSLv2 Certificate' \ 78 | -casubj='SSLv2 CA' \ 79 | -san="$CN" \ 80 | -san="$DOM" \ 81 | -san='*.'"$DOM" 82 | 83 | std_dhparam; 84 | }> suca-${CN}.dsa.ssl2.1k.pem 85 | 86 | # Only for DHE-RSA 87 | # cat ca1k-${CN}.rsa.1k.pem dhparams-1k.pem > ca1k-${CN}.rsa.mixed.1k.pem 88 | # cat ca2k-${CN}.rsa.2k.pem dhparams-2k.pem > ca2k-${CN}.rsa.mixed.2k.pem 89 | 90 | } 91 | 92 | run_cert() { 93 | CA="$1" 94 | shift 95 | ft mk-cert \ 96 | -v3user -nokeyid \ 97 | "$DAYS" \ 98 | -sign=<(echo "$CA") \ 99 | -addcert=<(echo "$CA") \ 100 | -cn="$CN" \ 101 | -san="$SANHOSTS" \ 102 | "$@" 103 | } 104 | 105 | std_dhparam() { 106 | cat <<\! 107 | -----BEGIN DH PARAMETERS----- 108 | MIGHAoGBALu8LcrYRnSQfEP89YDpz9vZWKP1aLQtSwju1OsPs1BMbAMCducQgAxc 109 | y7qokiYUxb7spWWl/fHSh6K8BJvmd4Bg6RqSp1fjBI9osHb302zI8pul34HcLKcl 110 | 7OZicMyaUDXYzs7vnqAnSmOrHlj6/UmI0PZdFGdX2gcd8EXP4WubAgEC 111 | -----END DH PARAMETERS----- 112 | ! 113 | } 114 | 115 | example_dhparam() { 116 | 117 | [ -f dhparam-1k.pem ] || cat > dhparam-1k.pem <<\! 118 | -----BEGIN DH PARAMETERS----- 119 | MIGHAoGBANivd72m35sC+gpcOhBl0ohkI29eQEpgwhUslAF1YJ68A7lzaJHtkRut 120 | d7gGx48sRq9m2M/upexh8eMBf6T5U4LVmI5WCAwRcHFDkyws6COny1m+8h4mkdAR 121 | zUaFF64r997JILnTZmdFKb2eydk86byw2DZ3iGpT1sBNivT8tDEjAgEC 122 | -----END DH PARAMETERS----- 123 | ! 124 | 125 | [ -f dhparam-2k.pem ] || cat > dhparam-2k.pem <<\! 126 | -----BEGIN DH PARAMETERS----- 127 | MIIBCAKCAQEAqrIS6rUaTYkO6+0p9bRIduBvrK0Kmn3QpUIJjlWXu4SO/SJKKrdu 128 | tWZ3A0enTI83R5YTC9TJc5a6iusT+ymzbDmyVLmNDPIEdCplhjb9/B6P99uI8SKV 129 | Wj/Hjc3pgKySpy+l5vZ3Ad28i1KMuCPqq8AHPXnBHkaw7xDSNuLOc7RjtFTcyB9z 130 | hRDuVbClYOrIFws6J7OB4sA23BeumlLkiWsW+OebWD83k5GlOgLIjxCpqveyfBxi 131 | vuoWTT8Ptsh19uWA8m54agKiQK6qMlbLGao+kWUqN9T622wjyK73PH89e+yccuVP 132 | P09VLZTn7AWSMipcbd5if/4YqExx+taIYwIBAg== 133 | -----END DH PARAMETERS----- 134 | ! 135 | 136 | } 137 | 138 | main() { 139 | rm -rf certs 140 | 141 | echo Making certificates 142 | make_certs 143 | 144 | echo Making certificate set 145 | CN=localhost ; X1=tvision ; X2=tech ; X3=tvt ; X4=co.uk 146 | DOM=$X1$X2.$X4 147 | SANHOSTS="DNS:$X1$X2.$X4, DNS:*.$X1$X2.$X4, DNS:*.$X3.local, DNS:$X1.tk, DNS:$X1.$X2" 148 | 149 | ( cd certs ; example_dhparam ; mktestsets ) 150 | 151 | echo Scanning certificates 152 | 153 | whatssl certs/*.pem > certs/res-new.txt 154 | 155 | echo Filtering hex codes 156 | 157 | sed < certs/res-new.txt \ 158 | -e 's/Not Before: ... .*/Not Before: .../' \ 159 | -e 's/Not After : ... .*/Not After : .../' \ 160 | -e 's/dnQualifier=[0-9a-fA-F]*/dnQualifier=.../' \ 161 | | 162 | awk '/:[0-9a-fA-F][0-9a-fA-F]/ { 163 | s = $0 164 | 165 | for(i=0;i<2;i++) { 166 | if (i==0) { 167 | m = "[0-9a-fA-F][0-9a-fA-F]"; 168 | m = m ":" m ":" m ":" m "(:" m ")*"; 169 | d=3;c=1; 170 | } else { 171 | m = "[0-9a-fA-F][0-9a-fA-F]"; 172 | m = "DER:" m "(" m ")*"; 173 | d=2;c=-4; 174 | } 175 | while (match(s, m)) { 176 | v = substr(s, RSTART, RLENGTH) 177 | if (v in keys) { 178 | v = keys[v]; 179 | } else { 180 | keys[v] = "IDTXT/" (++idnum) "/" ((RLENGTH+c)/d*8); 181 | v = keys[v]; 182 | } 183 | 184 | s = substr(s, 1, RSTART-1) v substr(str, RSTART+1) 185 | } 186 | } 187 | 188 | print s; 189 | next; 190 | } 191 | /^Contents/ { delete keys; idnum = 0; } 192 | {print;} 193 | ' 194 | 195 | echo Done 196 | } 197 | 198 | main 199 | -------------------------------------------------------------------------------- /whatssl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # shellcheck disable=SC1007 disable=SC2230 disable=SC2236 3 | 4 | if [ ! -n "$BASH_VERSION" ];then exec bash "$0" "$@";else set +o posix;fi 5 | 6 | main() { 7 | [ $# -eq 0 ] && set -- - 8 | 9 | init_vars 10 | test_for_windows 11 | 12 | for file 13 | do 14 | [ "$#" -gt 1 ] && echo "Contents of File $file" 15 | 16 | case ".$file" in 17 | .- ) XDATA="$(cat | openssl enc -base64)" ;; 18 | .https://* ) 19 | RHOST="${file##https://}" 20 | XDATA="$(:| 21 | openssl s_client -servername "$RHOST" -connect "$RHOST":443 | 22 | openssl enc -base64)" 23 | ;; 24 | * ) XDATA="$(openssl enc -base64 <"$file")" ;; 25 | esac 26 | 27 | decode_binary_data "$XDATA" || continue 28 | 29 | decode_data "$DATA" 30 | 31 | [ "$#" -gt 1 ] && echo 32 | done 33 | exit 0 34 | } 35 | 36 | init_vars() { 37 | NL=' 38 | ' 39 | MYPASS="$PASSWORD" 40 | DICTIONARY=(${MYPASS:+"$MYPASS"} 0000 1234 12345 123456 12345678 password qwerty) 41 | MYPASS="${MYPASS:-slartibartfast}" 42 | NOPKEY="$(openssl no-pkey >/dev/null 2>&1 && echo yes || echo no)" 43 | PW=(-passin pass:"$MYPASS") 44 | 45 | UTF8= 46 | if [ "$(locale charmap 2>/dev/null)" = UTF-8 ] 47 | then 48 | if openssl req -help 2>&1 | grep -qe -utf8 49 | then UTF8=yes 50 | fi 51 | fi 52 | 53 | REQOPT= 54 | if openssl req -help 2>&1 | grep -qe -reqopt 55 | then REQOPT=yes 56 | fi 57 | 58 | return 0 59 | } 60 | 61 | pubkey_id() { 62 | local TKEY H keyid 63 | 64 | [[ "$1" = '' ]] && { echo ' Public key not available' ; return 0; } 65 | 66 | keyid="$(echo "$1" | 67 | openssl asn1parse | tr ':' ' ' | awk '/OBJECT/{print $NF;}' | 68 | tail -1 | sed -e 's/ *$//' -e 's/PublicKey//' -e 's/Encryption//' 69 | )" 70 | 71 | keyid="$keyid $( 72 | H=$(echo "$1" | 73 | openssl rsa -pubin -modulus -noout 2>/dev/null | 74 | sed -n 's/.*=//p' | wc -c) 75 | [ "$H" -gt 0 ] && echo "$((H*4/8*8)) bit" 76 | )" 77 | keyid="${keyid% }" 78 | 79 | keyid="$keyid $( 80 | H=$(echo "$1" | 81 | openssl dsa -pubin -modulus -noout 2>/dev/null | 82 | sed -n 's/.*=//p' | wc -c) 83 | [ "$H" -gt 0 ] && echo "$((H*4/8*8)) bit" 84 | )" 85 | keyid="${keyid% }" 86 | 87 | if openssl x509 -help 2>&1 | grep -qie -force_pubkey 88 | then 89 | # This is a somewhat insane way to get the "Subject Key Identifier", 90 | # well, it seems to be the only way that works! 91 | keyident="$( 92 | TKEY=$(openssl ecparam -name prime256v1 -genkey -noout) 93 | openssl x509 -req 2>/dev/null \ 94 | -in <(openssl req 2>/dev/null -subj / -new -key <(echo "$TKEY") ) \ 95 | -CA <(openssl req 2>/dev/null -subj / -new -key <(echo "$TKEY") -x509) \ 96 | -CAkey <(echo "$TKEY") -set_serial 00 \ 97 | -force_pubkey <(echo "$1") \ 98 | -extfile <( echo subjectKeyIdentifier=hash ) | 99 | openssl x509 2>/dev/null -noout -text -certopt no_pubkey | 100 | awk 'f==1{print $1;exit;} /Subject Key Identifier/{f=1;}' 101 | )" 102 | else keyident='' 103 | fi 104 | 105 | if [[ "$keyident" = '' ]] 106 | then 107 | local -r Z=$(:|openssl sha1 -hex | awk '{print $NF;}') 108 | H=$(echo "$1" | openssl rsa -pubin -outform DER 2>/dev/null | openssl sha1 -hex | awk '{print $NF;}' ) 109 | [[ "$H" = "$Z" ]] && H=$(echo "$1" | openssl dsa -pubin -outform DER 2>/dev/null | openssl sha1 -hex | awk '{print $NF;}' ) 110 | [[ "$H" = "$Z" ]] && H=$(echo "$1" | openssl ec -pubin -outform DER 2>/dev/null | openssl sha1 -hex | awk '{print $NF;}' ) 111 | [[ "$H" = "$Z" ]] && H='?' 112 | keyid="$keyid DER:$H" 113 | else 114 | keyid="$keyid ${keyident}" 115 | fi 116 | 117 | echo " $keyid" 118 | } 119 | 120 | decode_binary_data() { 121 | local XDATA="$1" NOLOOP="$2" 122 | 123 | if echo "$XDATA" | openssl enc -base64 -d 2>/dev/null | grep -iq ^-----BEGIN 124 | then DATA="$(echo "$XDATA" | openssl enc -base64 -d )" 125 | elif echo "$XDATA" | openssl enc -base64 -d | openssl asn1parse -inform der >/dev/null 2>&1 126 | then 127 | echo "This is a DER (ASN1) format file${NOLOOP:+, encoded in base64}" 128 | DATA='' 129 | for cmd in x509 req pkey crl 130 | do [ "$DATA" != "" ] && break 131 | DATA="$(echo "$XDATA" | openssl enc -base64 -d | openssl $cmd -inform DER 2>/dev/null)" 132 | done 133 | 134 | [ "$DATA" = "" ] && 135 | DATA="$(echo "$XDATA" | openssl enc -base64 -d | openssl pkcs8 -inform DER "${PW[@]}" 2>/dev/null)" 136 | [ "$DATA" = "" ] && 137 | DATA="$(echo "$XDATA" | openssl enc -base64 -d | openssl pkcs12 -nodes -passin pass: 2>/dev/null)" 138 | 139 | [ "$DATA" = "" ] && 140 | for PASS in "${DICTIONARY[@]}" 141 | do 142 | [ "$DATA" = "" ] && 143 | DATA="$(echo "$XDATA" | openssl enc -base64 -d | openssl pkcs8 -inform DER ${PASS:+-passin pass:"$PASS"} 2>/dev/null)" 144 | [ "$DATA" = "" ] && 145 | DATA="$(echo "$XDATA" | openssl enc -base64 -d | openssl pkcs12 -nodes ${PASS:+-passin pass:"$PASS"} 2>/dev/null)" 146 | [ "$DATA" != '' ] && { 147 | [[ "$PASS" != "$PASSWORD" ]] && 148 | echo "NOTE: Data encrypted with trivial password '$PASS'" 149 | break 150 | } 151 | done 152 | 153 | [ "$DATA" = "" ] && { 154 | if echo "$XDATA" | openssl enc -base64 -d | openssl asn1parse -inform DER | grep -qi 'OBJECT *:pkcs7-signedData' 155 | then echo "PKCS7 format data file:" 156 | DATA="$(echo "$XDATA" | openssl enc -base64 -d | openssl pkcs7 -inform der -print_certs)" 157 | elif echo "$XDATA" | openssl enc -base64 -d | openssl asn1parse -inform DER | grep -qi 'OBJECT *:pkcs7-data' 158 | then echo "PKCS7/12 format data, encrypted pfx?" 159 | return 1 160 | else 161 | if [[ "$NOLOOP" != yes ]] 162 | then 163 | OBJ="$(echo "$XDATA" | openssl enc -base64 -d | openssl asn1parse -inform DER | awk '/OBJECT/{print $NF;exit;}')" 164 | echo "File contents not identified${OBJ:+; starts with$OBJ}" 165 | fi 166 | return 1 167 | fi 168 | } 169 | elif echo "$XDATA" | openssl enc -base64 -d | openssl enc -base64 -d | openssl asn1parse -inform der >/dev/null 2>&1 170 | then 171 | decode_binary_data "$(echo "$XDATA" | openssl enc -base64 -d)" yes 172 | return 173 | 174 | elif [ "$XDATA" = '' ] 175 | then return 1 176 | else 177 | echo >&2 "File not in PEM or DER format" 178 | return 1 179 | fi 180 | 181 | return 0 182 | } 183 | 184 | decode_data() { 185 | local ITEM FIRSTCERTPUB FIRSTPKEYPUB= 186 | local PKEYPARAM PUBKEY PUBKEYPEM STRICT SUBJ ENCKEY PKCS7DATA 187 | local failed success purpose tested 188 | local k=0 found=0 ISSU= CHAIN= PREVCERTPUB= C= 189 | 190 | local DATA="$1" NOLOOP="$2" 191 | 192 | PUBKEY="$(echo "$DATA" | openssl x509 -pubkey -noout 2>/dev/null)" 193 | [ "$PUBKEY" != "" ] && PUBKEY="$(echo "$PUBKEY" | openssl md5 | awk '{print $NF;}')" 194 | FIRSTCERTPUB="$PUBKEY" 195 | 196 | [[ "$NOPKEY" = no ]] && { 197 | PUBKEY="$(echo "$DATA" | openssl pkey -pubout "${PW[@]}" 2>/dev/null)" 198 | [ "$PUBKEY" != "" ] && PUBKEY="$(echo "$PUBKEY" | openssl md5 | awk '{print $NF;}')" 199 | FIRSTPKEYPUB="$PUBKEY" 200 | } 201 | 202 | while : 203 | do 204 | ((k+=1)) 205 | ITEM="$(echo "$DATA" | awk -v k=$k '/^-----BEGIN/ && k>1 {k--;next;} /^-----BEGIN/&& k {disp=1;} disp{print;} /^-----END/&&disp{disp=0;k=0;}')" 206 | [ "$ITEM" = "" ] && break 207 | ((found+=1)) 208 | 209 | for PEMTYPE in x509 req pkey dsaparam ecparam pkeyparam pubkey rsa ec dsa crl 210 | do if [[ "$PEMTYPE" = 'pubkey' ]] 211 | then PEMITEM="$(echo "$ITEM" | openssl pkey -pubin 2>/dev/null)" 212 | elif [[ "$PEMTYPE" = 'pkey' || "$PEMTYPE" = rsa || "$PEMTYPE" = dsa || "$PEMTYPE" = ec ]] 213 | then PEMITEM="$(echo "$ITEM" | openssl "$PEMTYPE" "${PW[@]}" 2>/dev/null)" 214 | else PEMITEM="$(echo "$ITEM" | openssl "$PEMTYPE" 2>/dev/null)" 215 | fi 216 | [[ "$PEMITEM" != "" ]] && break 217 | done 218 | 219 | [[ "$PEMITEM" = '' ]] && { 220 | for PASS in "${DICTIONARY[@]}" 221 | do for PEMTYPE in pkey rsa ec dsa 222 | do 223 | PEMITEM="$(echo "$ITEM" | openssl "$PEMTYPE" ${PASS:+-passin pass:"$PASS"} 2>/dev/null)" 224 | [ "$PEMITEM" != '' ] && { 225 | [[ "$PASS" != "$PASSWORD" ]] && 226 | echo "NOTE: Data encrypted with trivial password '$PASS'" 227 | 228 | MYPASS="$PASS" 229 | PW=(-passin pass:"$MYPASS") 230 | break 2 231 | } 232 | done 233 | done 234 | } 235 | [[ "$PEMITEM" = '' ]] && PEMTYPE='' 236 | 237 | PUBKEY= 238 | case "$PEMTYPE" in 239 | x509 ) PUBKEY="$(echo "$ITEM" | openssl x509 -pubkey -noout 2>/dev/null)" ;; 240 | req ) PUBKEY="$(echo "$ITEM" | openssl req -pubkey -noout 2>/dev/null)" ;; 241 | pkey ) PUBKEY="$(echo "$ITEM" | openssl pkey -pubout "${PW[@]}" 2>/dev/null)" ;; 242 | pubkey ) PUBKEY="$(echo "$ITEM" | openssl pkey -pubin 2>/dev/null)" ;; 243 | 244 | rsa|ec|dsa ) 245 | PUBKEY="$(echo "$ITEM" | openssl "$PEMTYPE" -pubout "${PW[@]}" 2>/dev/null)" ;; 246 | 247 | pkeyparam|dsaparam|ecparam ) 248 | PKEYPARAM="$(echo "$ITEM" | openssl "$PEMTYPE" -text -noout 2>/dev/null)" ;; 249 | esac 250 | 251 | PUBKEYPEM="$PUBKEY" 252 | [ "$PUBKEY" != "" ] && PUBKEY="$(echo "$PUBKEY" | openssl md5 | awk '{print $NF;}')" 253 | 254 | if [ "$PEMTYPE" = "x509" ] 255 | then 256 | PREVCERTPUB="$PUBKEY" 257 | 258 | if openssl x509 -help 2>&1 | grep -qie -issuer_hash_old 259 | then 260 | echo "$ITEM" | 261 | openssl x509 -noout -text -fingerprint \ 262 | -certopt no_sigdump,no_pubkey \ 263 | -nameopt esc_ctrl${UTF8:+,utf8,-esc_msb} 264 | elif openssl x509 -help 2>&1 | grep -qie -issuer_hash 265 | then 266 | echo "$ITEM" | 267 | openssl x509 -noout -text -fingerprint \ 268 | -certopt compatible,no_sigdump,no_pubkey 269 | else 270 | echo "$ITEM" | 271 | openssl x509 -noout -text 272 | fi 273 | 274 | echo " Public key is:" 275 | pubkey_id "$PUBKEYPEM" 276 | 277 | SUBJ="$(echo "$ITEM" | openssl x509 -subject_hash -noout 2>/dev/null)" 278 | [[ "$SUBJ" = "$ISSU" && "$ISSU" != '' ]] && 279 | echo ' This certificate is the issuer for the previous one' 280 | 281 | ISSU="$(echo "$ITEM" | openssl x509 -issuer_hash -noout 2>/dev/null)" 282 | 283 | if openssl verify -help 2>&1 | grep -q -e -no-CApath 284 | then NOCAPATH=-no-CApath 285 | else NOCAPATH='-CApath /dev/null' 286 | fi 287 | 288 | if [ "$SUBJ" = "$ISSU" ] 289 | then 290 | # shellcheck disable=SC2086 291 | if ( openssl verify $NOCAPATH \ 292 | -CAfile <(echo "$ITEM") <(echo "$ITEM") 2>&1 || 293 | echo error ) | grep -iq ^error 294 | then 295 | if echo "$ITEM" | openssl x509 -checkend 0 >/dev/null 2>&1 296 | then echo " This self signed certificate fails validation" 297 | else echo " This self signed certificate has expired" 298 | fi 299 | else 300 | for STRICT in '-x509_strict' '' 301 | do 302 | success= 303 | failed= 304 | tested= 305 | for purpose in '' \ 306 | sslclient sslserver nssslserver \ 307 | smimesign smimeencrypt \ 308 | crlsign ocsphelper timestampsign 309 | do 310 | tested="$tested $purpose" 311 | [ "$purpose" = '' ] && tested="${tested}unspecified" 312 | 313 | if ! ( openssl verify $STRICT ${STRICT:+-check_ss_sig} \ 314 | ${purpose:+-purpose $purpose} \ 315 | $NOCAPATH \ 316 | -CAfile <(echo "$ITEM") \ 317 | ${CHAIN:+-untrusted <(echo "$CHAIN")} \ 318 | <(echo "$DATA") 2>&1 || echo error ) | 319 | grep -iq ^error 320 | then success="$success $purpose" 321 | [ "$purpose" = '' ] && 322 | success="${success}unspecified" 323 | elif [ "$purpose" = '' ] 324 | then break 325 | else failed="$failed $purpose" 326 | fi 327 | done 328 | [ "$success" ] && break 329 | done 330 | 331 | if [ "$tested" = "$success" ] 332 | then 333 | echo " This self signed certificate verifies the chain${STRICT:+ in strict mode} for all purposes" 334 | elif [ "$success" != "" ] 335 | then 336 | echo " This self signed certificate verifies the chain${STRICT:+ in strict mode}" 337 | echo " purposes:$success" 338 | [ "$failed" != "" ] && 339 | echo " failed for:$failed" 340 | 341 | elif ! openssl verify -check_ss_sig \ 342 | -CAfile <(echo "$ITEM") \ 343 | $NOCAPATH \ 344 | ${CHAIN:+-untrusted <(echo "$CHAIN")} \ 345 | <(echo "$DATA") 2>&1 | grep -iq ^error 346 | then 347 | echo ' This self signed certificate verifies the chain when not strict' 348 | else 349 | echo ' This certificate is self signed' 350 | fi 351 | fi 352 | elif [ "$PUBKEY" != "$FIRSTCERTPUB" ] 353 | then 354 | CHAIN="$CHAIN$NL$ITEM" 355 | fi 356 | 357 | elif [ "$PEMTYPE" = "req" ] 358 | then 359 | PREVCERTPUB= 360 | echo "$ITEM" | openssl req -noout -text ${REQOPT:+-reqopt no_sigdump,no_pubkey} 361 | [[ "$PUBKEYPEM" != '' ]] && { 362 | echo " Public key is:" 363 | pubkey_id "$PUBKEYPEM" 364 | } 365 | 366 | if [[ "$FIRSTPKEYPUB" != '' && "$PUBKEY" = "$FIRSTPKEYPUB" ]] 367 | then echo " WARNING: The file contains a private key that matches this request" 368 | elif [[ "$FIRSTCERTPUB" != '' && "$PUBKEY" = "$FIRSTCERTPUB" ]] 369 | then echo " This request matches the first certificate" 370 | fi 371 | 372 | elif [[ "$PEMTYPE" = 'pkey' || "$PEMTYPE" = rsa || "$PEMTYPE" = dsa || "$PEMTYPE" = ec ]] 373 | then 374 | 375 | if [[ "$PUBKEY" != "" && "$PUBKEY" = "$PREVCERTPUB" ]] 376 | then echo ' The file contains a private key that matches this certificate.' 377 | elif [ "$PUBKEY" != "" ] 378 | then 379 | echo 'A private key for this public key:' 380 | pubkey_id "$PUBKEYPEM" 381 | if [[ "$PUBKEY" = "$FIRSTCERTPUB" ]] 382 | then echo ' This key matches the first certificate.' 383 | fi 384 | else 385 | echo "Item $k is a private key ($PEMTYPE)" 386 | fi 387 | 388 | elif [[ "$PEMTYPE" = pkeyparam || "$PEMTYPE" = dsaparam || "$PEMTYPE" = ecparam ]] 389 | then 390 | echo "Parameter file ($PEMTYPE):" 391 | echo "$PKEYPARAM" 392 | 393 | elif [ "$PEMTYPE" = "pubkey" ] 394 | then 395 | echo "Public key:" 396 | pubkey_id "$ITEM" 397 | 398 | elif [ "$PEMTYPE" = "crl" ] 399 | then 400 | C=$(echo "$ITEM" | openssl crl -noout -text | grep -c 'Serial Number:') 401 | echo "A CRL file with $C entries:" 402 | echo "$ITEM" | openssl crl -noout \ 403 | -lastupdate -nextupdate -issuer \ 404 | -nameopt multiline,esc_ctrl${UTF8:+,utf8,-esc_msb} \ 405 | 2>/dev/null 406 | 407 | else 408 | ENCKEY=$(echo "$ITEM" | openssl pkey -pubout "${PW[@]}" 2>&1 | grep -qi 'bad.\(decrypt\|pass\)' && echo yes) 409 | PKCS7DATA=$(echo "$ITEM" | openssl pkcs7 -print_certs 2>/dev/null ||:) 410 | 411 | if [ "$ENCKEY" = "yes" ] 412 | then 413 | echo Item $k is an encrypted key 414 | elif [ "$PKCS7DATA" != "" ] 415 | then 416 | echo A PKCS7 bag with the contents ... 417 | decode_data "$PKCS7DATA" yes 418 | echo 419 | echo End of PKCS7 file 420 | echo 421 | else 422 | FIRSTOBJ="$(echo "$ITEM" | openssl asn1parse 2>/dev/null | awk '/OBJECT/{print $NF;exit;}')" 423 | PEM="$(echo "$ITEM" | sed -n 's/^-----BEGIN \(.*\)$/\1/p' | sed 's/-*\s*$//')" 424 | if [[ "$FIRSTOBJ" != '' && "$NOLOOP" = '' ]] 425 | then 426 | echo "PEM item $k with name $PEM and object $FIRSTOBJ" 427 | ( 428 | decode_binary_data "$ITEM" yes && 429 | decode_data "$DATA" yes 430 | ) 431 | elif [[ "$NOLOOP" != yes ]] 432 | then echo "PEM style item $k not identified: $PEM" 433 | fi 434 | fi 435 | fi 436 | done 437 | [ "$found" -eq 0 ] && 438 | echo No PEM data found in "$file" 439 | 440 | return 0 441 | } 442 | 443 | test_for_windows() { 444 | # MINGW has two copies of openssl, one compiled for Windows and one 445 | # compiled for Unix. The Windows version cannot be used with process 446 | # substitution ( the <(...) stuff ). Check to see if the default is 447 | # working, if not try to find the other one. 448 | local pgm 449 | 450 | if [ "$(openssl enc -a -in <(echo ok) 2>/dev/null )" != b2sK ] 451 | then 452 | for pgm in $(which -a openssl) 453 | do 454 | eval "openssl() { command '$pgm' \"\$@\"; }" 455 | 456 | if [ "$(openssl enc -a -in <(echo ok) 2>/dev/null )" = b2sK ] 457 | then break 458 | fi 459 | done 460 | fi 461 | } 462 | 463 | main "$@" 464 | --------------------------------------------------------------------------------