├── .gitignore ├── README.md ├── bin ├── create-client-cert.sh ├── create-server-cert.sh ├── der2pem.sh ├── init-interm.sh └── pem2pfx.sh └── init-ca.sh /.gitignore: -------------------------------------------------------------------------------- 1 | /* 2 | !init-ca.sh 3 | !README.md 4 | !bin/ 5 | !.gitignore 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenSSL CA scripts 2 | 3 | ## Description 4 | 5 | The repository is a set of scripts to automate creation of a CA on a base of the OpenSSL tool. It supports: 6 | 7 | - Creation of a CA 8 | - Creation of intermediate cetificates 9 | - Creation of server certificates that conform Google Chrome requirements 10 | - Creation of client certificates 11 | - (TODO) Revokation lists 12 | 13 | All of this was possible because of [the great article of Jamie Nguyen](https://jamielinux.com/docs/openssl-certificate-authority) and couple of other internet sources. 14 | 15 | The scripts are mostly intended to be used for generation of test certificates. If you plan to use it in production please read the article first and **examine the sources**. 16 | 17 | ## TL;DR; 18 | 19 | Here is a summary of supported commands: 20 | 21 | ```bash 22 | # create a CA 23 | ./init-ca root-ca 24 | # change the current folder 25 | cd root-ca 26 | # create an intermediate CA 27 | ./bin/init-interm.sh IntermCA 0 28 | # change the current folder 29 | cd IntermCA 30 | # create a server certificate 31 | ./bin/create-server-cert.sh test.example.com 32 | # create a client certificate 33 | ./bin/create-client-cert.sh alexander.sedov 20160101120000Z 20171231235959Z 34 | # create a PFX file from a client certificate 35 | ./bin/pem2pfx.sh alexander.sedov 36 | ``` 37 | 38 | ## Usage 39 | 40 | ### Folder structure 41 | 42 | Each of the commands that generate either a CA certificate or an intermediate certificate create the next folder structure: 43 | 44 | - `bin` the folder where the scripts go 45 | - `certs` contains PEM files with certificate data 46 | - `private` contains corresponding PEM files with key data 47 | - `csr` contains certificate requests PEM files 48 | - `crl` contains revocation lists files (TODO) 49 | - `openssl.cnf` is a configuration file that is used during the certificate creation on this level 50 | - `index.txt`, `serial`, `crlnumber` are internals that keep the state 51 | 52 | ### Creation of the CA certificate 53 | 54 | In the root of the repository run the next command 55 | 56 | ```bash 57 | $ ./init-ca.sh root-ca 58 | Creating the root key... 59 | Generating RSA private key, 4096 bit long modulus 60 | ................................................................................................................................................................................................++ 61 | ......................................................++ 62 | ``` 63 | 64 | Then you need to enter and repeet a pass phrase to protect your CA key. It may be an arbitrary sequence of symbols. You need to remember it! 65 | 66 | ```bash 67 | Enter pass phrase for private/ca.key.pem: 68 | Verifying - Enter pass phrase for private/ca.key.pem: 69 | ``` 70 | 71 | The next step is the creation of the root certificate itself. You need to enter the pass phrase of your CA key to proceed. Make sure to specify the common name and the email address fields. 72 | 73 | ```bash 74 | Creating the root certificate... 75 | Enter pass phrase for private/ca.key.pem: 76 | You are about to be asked to enter information that will be incorporated 77 | into your certificate request. 78 | What you are about to enter is what is called a Distinguished Name or a DN. 79 | There are quite a few fields but you can leave some blank 80 | For some fields there will be a default value, 81 | If you enter '.', the field will be left blank. 82 | ----- 83 | Country Name (2 letter code) [DE]: 84 | State or Province Name [Germany]: 85 | Locality Name []: 86 | Organization Name [Acme Test]: 87 | Organizational Unit Name []:CA 88 | Common Name []:Acme Test CA 89 | Email Address []:admin@acme.com 90 | ``` 91 | 92 | Now you have a self-signed CA certificate. 93 | 94 | ### Creation of an intermediate certificate 95 | 96 | In this example, we will create two nested intermediate certificates. Before the creation of an intermediate certificate you need to specify how many of nested intermediates are you going to allow. So for the first intermediate certificate we specify `1` as one more nested intermediate certificate is to be created. 97 | 98 | ```bash 99 | # Move to the generated folder of the CA first 100 | $ cd root-ca 101 | $ ./bin/init-interm.sh interm-adm 1 102 | Creating the interm-adm key... 103 | Generating RSA private key, 4096 bit long modulus 104 | ..........................................++ 105 | ..........................++ 106 | ``` 107 | 108 | Then you need to enter and repeat a pass phrase to protect your **intermediate** certificate's key. 109 | 110 | ```bash 111 | Enter pass phrase for private/interm-adm.key.pem: 112 | Verifying - Enter pass phrase for private/interm-adm.key.pem: 113 | ``` 114 | 115 | Reenter the pass phrase for your intermediate certificate's key to allow `openssl` to create a certificate request. Make sure to specify values for the common name and the email address fields. Also, in case of the intermediate certificates creation the script uses a strict policy that requres an intermediate certificate has the same values for `Country Name`, `State or Province Name` and `Organization Name` fields as in the CA certificate. 116 | 117 | ```bash 118 | Creating the interm-adm certificate request... 119 | Enter pass phrase for private/interm-adm.key.pem: 120 | You are about to be asked to enter information that will be incorporated 121 | into your certificate request. 122 | What you are about to enter is what is called a Distinguished Name or a DN. 123 | There are quite a few fields but you can leave some blank 124 | For some fields there will be a default value, 125 | If you enter '.', the field will be left blank. 126 | ----- 127 | Country Name (2 letter code) [DE]: <-- the same as in CA 128 | State or Province Name [Germany]: <-- the same as in CA 129 | Locality Name []: 130 | Organization Name [Acme Test]: <-- the same as in CA 131 | Organizational Unit Name []:Adm 132 | Common Name []:Acme Test interm Adm 133 | Email Address []:admin@acme.com 134 | ``` 135 | 136 | The next step is signing the intermediate certificate with the CA's key. So you will be asked to enter the **CA key**'s pass phrase. 137 | 138 | ```bash 139 | Creating the interm-adm certificate... 140 | Using configuration from /dev/fd/63 141 | Enter pass phrase for /path/to/root-ca/private/ca.key.pem: 142 | Check that the request matches the signature 143 | Signature ok 144 | ``` 145 | 146 | After that, you need to confirm twice and here it is - your intermediate certificate is ready and can be found in the `root-ca/interm-adm/certs` folder. It is issued and signed by the previously created CA certificate. 147 | 148 | The creation of a nested intermediate certificate is straightforward. You need to go on the level of the `interm-adm` (parent) certificate and execute the same commands as for the creation of the first intermediate certificate. 149 | 150 | ```bash 151 | cd interm-adm 152 | ./bin/init-interm.sh interm-hub 153 | ``` 154 | 155 | The certificate that is created is an edge level intermediate certificate so the number of allowed nested intermediates should be zero. Zero is the default value so you don't need to specify it as an argument. All other things are the same 156 | 157 | - Enter and repeat a pass phrase to protect the key of the new certificate 158 | - Enter it again to create a certificate request 159 | - Specify certificate's DN details. Don't forget to use the same values for `Country Name`, `State or Province Name` and `Organization Name` fields as in the CA certificate. Fields `Common Name` and `Email Address` are required. 160 | - Enter the pass phrase of the **parent** intermediate certificate (it is `interm-adm` in our case) to sign the new certificate 161 | 162 | That's it, the new intermediate certificate is ready. It is issued and signed by the parent intermediate certificate authority. 163 | 164 | ## Creation of a server certificate 165 | 166 | A server certificate is used during an SSL handshake to establish an HTTPS connection between a client browser and your site. Typically you want the server certificate to be accessible without entering the pass phrase of its key on each OS startup so we are going to generate the key without an encription. 167 | 168 | Run the next command and provide a DNS name of your site as an argument. 169 | 170 | ```bash 171 | $ cd root-ca/interm-adm/interm-hub 172 | $ ./bin/create-server-cert.sh test.example.com 173 | Creation of the test.example.com key... 174 | Generating RSA private key, 2048 bit long modulus 175 | ..........................................................+++ 176 | ......+++ 177 | ``` 178 | 179 | There will be no pass phrase request as we are not encrypting the key. Then, a certificate request is created and you are asked to provide distinguished name details for the server certificate. The required fields are 'Common Name' and `Email Address`. **Make sure to enter the main DNS of your site as a value for the Common Name field.** 180 | 181 | ```bash 182 | Creation of the hub.test certificate request... 183 | You are about to be asked to enter information that will be incorporated 184 | into your certificate request. 185 | What you are about to enter is what is called a Distinguished Name or a DN. 186 | There are quite a few fields but you can leave some blank 187 | For some fields there will be a default value, 188 | If you enter '.', the field will be left blank. 189 | ----- 190 | Country Name (2 letter code) [DE]: 191 | State or Province Name [Germany]: 192 | Locality Name []: 193 | Organization Name [Acme Test]: 194 | Organizational Unit Name []:Hub 195 | Common Name []:test.example.com <-- here it is 196 | Email Address []:admin@acme.com 197 | ``` 198 | 199 | After that, you need to provide the pass phrase of the parent intermediate certificate (interm-hub) to sign the server certificate. 200 | 201 | ```bash 202 | Creation of the hub.test certificate... 203 | Using configuration from /dev/fd/63 204 | Enter pass phrase for /path/to/root-ca/interm-adm/interm-hub/private/interm-hub.key.pem: 205 | Check that the request matches the signature 206 | Signature ok 207 | ``` 208 | 209 | The server certificate is ready. Check out the SAN section in the certificate 210 | 211 | ```plain 212 | X509v3 Subject Alternative Name: 213 | DNS:test.example.com 214 | ``` 215 | 216 | The section is required to make Google Chrome happy. Your site may be accessible via several different DNS names and you may want to use the same server certificate for all of them. In this case, please edit the `create-server-cert.sh` file and add more DNS names into the `[ alt_names ]` section. Lately it may be automated by passing all DNS names as arguments to the script. 217 | 218 | ### Creation of a client certificate 219 | 220 | Client certificates are used for the client certificate authentication during the SSL handshake. Imagine, you want to give an access to a site only to those users who have a certificate (and its private key) issued by your CA or an intermediate authority. 221 | 222 | Note that you are not asked to enter a pass phrase to protect the key of the cretificate. It is because scripts are intended to serve testing purposes. If you want to generate client certificates for production edit the `create-client-cert.sh` script and change the generation lines as follows. The difference here is `-aes256` parameter. 223 | 224 | ```bash 225 | openssl -aes256 genrsa -out private/$USER_NAME.key.pem 2048 226 | # openssl genrsa -out private/$USER_NAME.key.pem 2048 227 | ``` 228 | 229 | To create a client certificate use the next command, provide a meaningful certificate name as an argument. The name is used to name created files only. Also, optional start and end certificate dates are supported as the second and the thitd parameter. If either start or end date are omitted than the certificate is created with a validity period starting from now and 375 days long. 230 | 231 | ```bash 232 | $ cd root-ca/interm-adm/interm-hub 233 | $ ./bin/create-client-cert.sh "alexander sedov" 20160101120000Z 20171231235959Z 234 | Creation of the alexander_sedov key... 235 | Generating RSA private key, 2048 bit long modulus 236 | ........+++ 237 | ............................................................+++ 238 | ``` 239 | 240 | On the next step you are asked to provide values for the distinguished name of the certificate. Make sure to fill the `Common Name` and `Email Address`. 241 | 242 | ```bash 243 | Creation of the alexander_sedov certificate request... 244 | You are about to be asked to enter information that will be incorporated 245 | into your certificate request. 246 | What you are about to enter is what is called a Distinguished Name or a DN. 247 | There are quite a few fields but you can leave some blank 248 | For some fields there will be a default value, 249 | If you enter '.', the field will be left blank. 250 | ----- 251 | Country Name (2 letter code) [DE]: 252 | State or Province Name [Germany]: 253 | Locality Name []: 254 | Organization Name [Acme Test]: 255 | Organizational Unit Name []:Hub 256 | Common Name []:Alexander Sedov 257 | Email Address []:alexander.sedov@mail.com 258 | ``` 259 | 260 | Then, enter the pass phrase of the parent certificate (interm-hub) key to sign the client certificate. 261 | 262 | ```bash 263 | Creation of the alexander_sedov certificate... 264 | Using configuration from openssl.cnf 265 | Enter pass phrase for /path/to/root-ca/interm-adm/interm-hub/private/interm-hub.key.pem: 266 | Check that the request matches the signature 267 | Signature ok 268 | ``` 269 | 270 | The certificate is ready and may be found in the `root-ca/interm-adm/interm-hub/certs` folder. The corresponding key is in the `root-ca/interm-adm/interm-hub/private` folder. 271 | 272 | ## Troubleshooting 273 | 274 | - Check if the `openssl` is installed in your system and is in the PATH 275 | - Try to go through the process using the same pass phrase for all (CA and intermediates) keys. Then, if everything goes fine, try to start from the begining using different pass phrases. 276 | - Don't forget to `cd` before executing the `init-interm.sh` script 277 | - Plan the certificate path length from the begining. Basically, when you create a top level intermediate certificate you need to know how many nested intermediates you will create. 278 | -------------------------------------------------------------------------------- /bin/create-client-cert.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | BLUE='\033[0;34m' 4 | NC='\033[0m' # No Color 5 | 6 | echo_clr() { 7 | echo -e "${BLUE}$1${NC}" 8 | } 9 | 10 | read -r -d '' MESSAGE << EOM 11 | The command supports next arguments: 12 | - *required* a name of a user (example: alexander.sedov) 13 | - *optional* start date of the certificate in the format of YYMMDDHHMMSSZ (example: 20180412235500Z) 14 | - *optional* end date of the certificate in the format of YYMMDDHHMMSSZ (example: 20180412235500Z) 15 | EOM 16 | MESSAGE="The command supports a next parameters *required* parameter - a name of a user (example: alexander.sedov)" 17 | 18 | : ${1?$MESSAGE} 19 | if [ -z "$1" ]; then 20 | echo "$MESSAGE" 21 | exit 1 22 | fi 23 | 24 | # Replace all spaces with underscores 25 | USER_NAME=${1// /_} 26 | START_DATE=$2 27 | END_DATE=$3 28 | 29 | echo_clr "Creation of the $USER_NAME key..." 30 | # openssl -aes256 genrsa -out private/$USER_NAME.key.pem 2048 31 | openssl genrsa -out private/$USER_NAME.key.pem 2048 32 | 33 | # chmod 400 private/$USER_NAME.key.pem 34 | 35 | echo_clr "Creation of the $USER_NAME certificate request..." 36 | openssl req -config openssl.cnf \ 37 | -new -sha256 \ 38 | -key private/$USER_NAME.key.pem \ 39 | -out csr/$USER_NAME.csr.pem 40 | 41 | echo_clr "Creation of the $USER_NAME certificate..." 42 | if ! ([ -z "$START_DATE" ] || [ -z "$END_DATE" ]) 43 | then 44 | openssl ca \ 45 | -config openssl.cnf \ 46 | -extensions usr_cert \ 47 | -startdate $START_DATE \ 48 | -enddate $END_DATE \ 49 | -notext \ 50 | -md sha256 \ 51 | -in csr/$USER_NAME.csr.pem \ 52 | -out certs/$USER_NAME.cert.pem 53 | else 54 | openssl ca \ 55 | -config openssl.cnf \ 56 | -extensions usr_cert \ 57 | -days 375 \ 58 | -notext \ 59 | -md sha256 \ 60 | -in csr/$USER_NAME.csr.pem \ 61 | -out certs/$USER_NAME.cert.pem 62 | fi 63 | 64 | # chmod 444 certs/$USER_NAME.cert.pem 65 | 66 | echo_clr "Verify the certificate..." 67 | openssl verify -CAfile certs/ca-chain.cert.pem certs/$USER_NAME.cert.pem 68 | 69 | # Show the certificate 70 | openssl x509 -noout -text -in certs/$USER_NAME.cert.pem -------------------------------------------------------------------------------- /bin/create-server-cert.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | BLUE='\033[0;34m' 4 | NC='\033[0m' # No Color 5 | 6 | echo_clr() { 7 | echo -e "${BLUE}$1${NC}" 8 | } 9 | 10 | MESSAGE="The command supports a single *required* parameter - a fully qualified domain name (example: myserver.com)" 11 | 12 | : ${1?$MESSAGE} 13 | if [ -z "$1" ]; then 14 | echo "$MESSAGE" 15 | exit 1 16 | fi 17 | 18 | DOMAIN_NAME=$1 19 | 20 | echo_clr "Creation of the $DOMAIN_NAME key..." 21 | # openssl -aes256 genrsa -out private/$DOMAIN_NAME.key.pem 2048 22 | openssl genrsa -out private/$DOMAIN_NAME.key.pem 2048 23 | 24 | # chmod 400 private/$DOMAIN_NAME.key.pem 25 | 26 | echo_clr "Creation of the $DOMAIN_NAME certificate request..." 27 | openssl req \ 28 | -new -sha256 \ 29 | -config openssl.cnf \ 30 | -key private/$DOMAIN_NAME.key.pem \ 31 | -out csr/$DOMAIN_NAME.csr.pem 32 | 33 | echo_clr "Creation of the $DOMAIN_NAME certificate..." 34 | SERVER_CERT_EXT=" 35 | [ server_cert ] 36 | basicConstraints = CA:FALSE 37 | nsCertType = server 38 | nsComment = \"OpenSSL Generated Server Certificate\" 39 | subjectKeyIdentifier = hash 40 | authorityKeyIdentifier = keyid,issuer:always 41 | keyUsage = critical, digitalSignature, keyEncipherment 42 | extendedKeyUsage = serverAuth 43 | subjectAltName=@alt_names 44 | 45 | [ alt_names ] 46 | DNS.1 = $DOMAIN_NAME 47 | " 48 | openssl ca \ 49 | -config <(cat openssl.cnf <(printf "\n$SERVER_CERT_EXT")) \ 50 | -extensions server_cert \ 51 | -days 375 \ 52 | -notext \ 53 | -md sha256 \ 54 | -in csr/$DOMAIN_NAME.csr.pem \ 55 | -out certs/$DOMAIN_NAME.cert.pem 56 | 57 | # chmod 444 certs/$DOMAIN_NAME.cert.pem 58 | 59 | echo_clr "Verify the certificate..." 60 | openssl verify -CAfile certs/ca-chain.cert.pem certs/$DOMAIN_NAME.cert.pem 61 | 62 | # Show the certificate 63 | openssl x509 -noout -text -in certs/$DOMAIN_NAME.cert.pem 64 | 65 | # Create server certificate chain 66 | cat certs/$DOMAIN_NAME.cert.pem certs/ca-chain.cert.pem > certs/$DOMAIN_NAME-chain.cert.pem 67 | 68 | -------------------------------------------------------------------------------- /bin/der2pem.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | MESSAGE="The command supports a single *required* parameter - a file name of a DER-encoded certificate file (*.crt)" 4 | 5 | : ${1?$MESSAGE} 6 | if [ -z "$1" ]; then 7 | echo "$MESSAGE" 8 | exit 1 9 | fi 10 | 11 | CRT_FILE_NAME=$1 12 | FILE_NAME=${CRT_FILE_NAME%.*} 13 | PEM_FILE_NAME=$FILE_NAME.pem 14 | 15 | openssl x509 -inform der -in "$CRT_FILE_NAME" -out "$PEM_FILE_NAME" 16 | -------------------------------------------------------------------------------- /bin/init-interm.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | BLUE='\033[0;34m' 4 | NC='\033[0m' # No Color 5 | 6 | echo_clr() { 7 | echo -e "${BLUE}$1${NC}" 8 | } 9 | 10 | read -r -d '' MESSAGE << EOM 11 | The command supports next arguments: 12 | - *required* the name of the target folder where the intermediate CA should be created (the name is added to the current dir) 13 | - *optional* max lenght of a certificate path that can be created with this intermediate certificate (default: 0) 14 | EOM 15 | 16 | : ${1?"$MESSAGE"} 17 | if [ -z "$1" ]; then 18 | echo "$MESSAGE" 19 | exit 1 20 | fi 21 | 22 | TARGET_DIR=$PWD/$1 23 | NAME=$1 24 | PATHLEN=${2:-0} 25 | 26 | mkdir $TARGET_DIR 27 | cd $TARGET_DIR 28 | mkdir certs crl csr newcerts pfx private 29 | # chmod 700 private 30 | touch index.txt 31 | echo 1000 > serial 32 | echo 1000 > crlnumber 33 | 34 | cp -r ../bin . 35 | 36 | echo "[ ca ] 37 | default_ca = CA_default 38 | 39 | [ CA_default ] 40 | # Directory and file locations. 41 | dir = $TARGET_DIR 42 | certs = \$dir/certs 43 | crl_dir = \$dir/crl 44 | new_certs_dir = \$dir/newcerts 45 | database = \$dir/index.txt 46 | serial = \$dir/serial 47 | RANDFILE = \$dir/private/.rand 48 | 49 | # The root key and root certificate. 50 | private_key = \$dir/private/$NAME.key.pem 51 | certificate = \$dir/certs/$NAME.cert.pem 52 | 53 | # For certificate revocation lists. 54 | crlnumber = \$dir/crlnumber 55 | crl = \$dir/crl/$NAME.crl.pem 56 | crl_extensions = crl_ext 57 | default_crl_days = 30 58 | 59 | # SHA-1 is deprecated, so use SHA-2 instead. 60 | default_md = sha256 61 | 62 | name_opt = ca_default 63 | cert_opt = ca_default 64 | default_days = 375 65 | preserve = no 66 | policy = policy_loose 67 | 68 | [ policy_loose ] 69 | # Allow the intermediate CA to sign a more diverse range of certificates. 70 | countryName = optional 71 | stateOrProvinceName = optional 72 | localityName = optional 73 | organizationName = optional 74 | organizationalUnitName = optional 75 | commonName = supplied 76 | emailAddress = supplied 77 | 78 | [ req ] 79 | default_bits = 2048 80 | distinguished_name = req_distinguished_name 81 | string_mask = utf8only 82 | 83 | # SHA-1 is deprecated, so use SHA-2 instead. 84 | default_md = sha256 85 | 86 | # Extension to add when the -x509 option is used. 87 | x509_extensions = v3_ca 88 | 89 | [ req_distinguished_name ] 90 | # See . 91 | countryName = Country Name (2 letter code) 92 | stateOrProvinceName = State or Province Name 93 | localityName = Locality Name 94 | 0.organizationName = Organization Name 95 | organizationalUnitName = Organizational Unit Name 96 | commonName = Common Name 97 | emailAddress = Email Address 98 | 99 | # Optionally, specify some defaults. 100 | countryName_default = DE 101 | stateOrProvinceName_default = Germany 102 | localityName_default = 103 | 0.organizationName_default = Acme Test 104 | organizationalUnitName_default = 105 | emailAddress_default = 106 | 107 | [ v3_ca ] 108 | subjectKeyIdentifier = hash 109 | authorityKeyIdentifier = keyid:always,issuer 110 | basicConstraints = critical, CA:true 111 | keyUsage = critical, digitalSignature, cRLSign, keyCertSign 112 | 113 | [ usr_cert ] 114 | basicConstraints = CA:FALSE 115 | nsCertType = client, email 116 | nsComment = "OpenSSL Generated Client Certificate" 117 | subjectKeyIdentifier = hash 118 | authorityKeyIdentifier = keyid,issuer 119 | keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment 120 | extendedKeyUsage = clientAuth, emailProtection 121 | 122 | [ crl_ext ] 123 | authorityKeyIdentifier=keyid:always 124 | 125 | [ ocsp ] 126 | basicConstraints = CA:FALSE 127 | subjectKeyIdentifier = hash 128 | authorityKeyIdentifier = keyid,issuer 129 | keyUsage = critical, digitalSignature 130 | extendedKeyUsage = critical, OCSPSigning 131 | " > openssl.cnf 132 | 133 | echo_clr "Creating the $NAME key..." 134 | openssl genrsa -aes256 -out private/$NAME.key.pem 4096 135 | 136 | echo_clr "Creating the $NAME certificate request..." 137 | openssl req -config openssl.cnf -new -sha256 \ 138 | -key private/$NAME.key.pem \ 139 | -out csr/$NAME.csr.pem 140 | 141 | echo_clr "Creating the $NAME certificate..." 142 | cd .. 143 | V3_INTERM_CA=" 144 | [ v3_intermediate_ca ] 145 | subjectKeyIdentifier = hash 146 | authorityKeyIdentifier = keyid:always,issuer 147 | basicConstraints = critical, CA:true, pathlen:$PATHLEN 148 | keyUsage = critical, digitalSignature, cRLSign, keyCertSign 149 | " 150 | openssl ca \ 151 | -config <(cat openssl.cnf <(printf "\n$V3_INTERM_CA")) \ 152 | -extensions v3_intermediate_ca \ 153 | -days 3650 -notext -md sha256 \ 154 | -in $NAME/csr/$NAME.csr.pem \ 155 | -out $NAME/certs/$NAME.cert.pem 156 | 157 | cd $NAME 158 | 159 | # Read-only for everyone 160 | # chmod 444 $NAME/certs/$NAME.cert.pem 161 | 162 | 163 | echo_clr "Verify the certificate..." 164 | openssl verify -CAfile ../certs/ca-chain.cert.pem certs/$NAME.cert.pem 165 | 166 | # Show the certificate 167 | openssl x509 -noout -text -in certs/$NAME.cert.pem 168 | 169 | # Create the certificate chain file 170 | cat certs/$NAME.cert.pem ../certs/ca-chain.cert.pem > certs/ca-chain.cert.pem -------------------------------------------------------------------------------- /bin/pem2pfx.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | MESSAGE="The command supports a single *required* parameter - a file name of a certificate without an extension (example: alexander.sedov)" 4 | 5 | : ${1?$MESSAGE} 6 | if [ -z "$1" ]; then 7 | echo "$MESSAGE" 8 | exit 1 9 | fi 10 | 11 | CERT_NAME=$1 12 | 13 | cat private/${CERT_NAME}.key.pem certs/${CERT_NAME}.cert.pem > hold.pem 14 | # openssl pkcs12 -export -out pfx/$CERT_NAME.pfx -in hold.pem -name "$CERT_NAME" 15 | openssl pkcs12 -export \ 16 | -in certs/${CERT_NAME}.cert.pem \ 17 | -inkey private/${CERT_NAME}.key.pem \ 18 | -name "$CERT_NAME" \ 19 | -out pfx/$CERT_NAME.pfx \ 20 | -certfile certs/ca-chain.cert.pem 21 | rm hold.pem 22 | -------------------------------------------------------------------------------- /init-ca.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | BLUE='\033[0;34m' 4 | NC='\033[0m' # No Color 5 | 6 | echo_clr() { 7 | echo -e "${BLUE}$1${NC}" 8 | } 9 | 10 | MESSAGE="The command supports a single *required* parameter - the name of the target folder where the CA folder structure should be created (the name is added to the current dir)" 11 | 12 | : ${1?$MESSAGE} 13 | if [ -z "$1" ]; then 14 | echo "$MESSAGE" 15 | exit 1 16 | fi 17 | 18 | TARGET_DIR=$PWD/$1 19 | 20 | mkdir $TARGET_DIR 21 | cd $TARGET_DIR 22 | mkdir csr certs crl newcerts pfx private 23 | # chmod 700 private 24 | touch index.txt 25 | echo 1000 > serial 26 | 27 | cp -r ../bin . 28 | 29 | echo "[ ca ] 30 | default_ca = CA_default 31 | 32 | [ CA_default ] 33 | # Directory and file locations. 34 | dir = $TARGET_DIR 35 | certs = \$dir/certs 36 | crl_dir = \$dir/crl 37 | new_certs_dir = \$dir/newcerts 38 | database = \$dir/index.txt 39 | serial = \$dir/serial 40 | RANDFILE = \$dir/private/.rand 41 | 42 | # The root key and root certificate. 43 | private_key = \$dir/private/ca.key.pem 44 | certificate = \$dir/certs/ca.cert.pem 45 | 46 | # For certificate revocation lists. 47 | crlnumber = \$dir/crlnumber 48 | crl = \$dir/crl/ca.crl.pem 49 | crl_extensions = crl_ext 50 | default_crl_days = 30 51 | 52 | # SHA-1 is deprecated, so use SHA-2 instead. 53 | default_md = sha256 54 | 55 | name_opt = ca_default 56 | cert_opt = ca_default 57 | default_days = 375 58 | preserve = no 59 | policy = policy_strict 60 | 61 | [ policy_strict ] 62 | # The root CA should only sign intermediate certificates that match. 63 | countryName = match 64 | stateOrProvinceName = match 65 | organizationName = match 66 | organizationalUnitName = optional 67 | commonName = supplied 68 | emailAddress = supplied 69 | 70 | [ req ] 71 | default_bits = 2048 72 | distinguished_name = req_distinguished_name 73 | string_mask = utf8only 74 | 75 | # SHA-1 is deprecated, so use SHA-2 instead. 76 | default_md = sha256 77 | 78 | # Extension to add when the -x509 option is used. 79 | x509_extensions = v3_ca 80 | 81 | [ req_distinguished_name ] 82 | # See . 83 | countryName = Country Name (2 letter code) 84 | stateOrProvinceName = State or Province Name 85 | localityName = Locality Name 86 | 0.organizationName = Organization Name 87 | organizationalUnitName = Organizational Unit Name 88 | commonName = Common Name 89 | emailAddress = Email Address 90 | 91 | # Optionally, specify some defaults. 92 | countryName_default = DE 93 | stateOrProvinceName_default = Germany 94 | localityName_default = 95 | 0.organizationName_default = Acme Test 96 | organizationalUnitName_default = 97 | emailAddress_default = 98 | 99 | [ v3_ca ] 100 | subjectKeyIdentifier = hash 101 | authorityKeyIdentifier = keyid:always,issuer 102 | basicConstraints = critical, CA:true 103 | keyUsage = critical, digitalSignature, cRLSign, keyCertSign 104 | 105 | [ usr_cert ] 106 | basicConstraints = CA:FALSE 107 | nsCertType = client, email 108 | nsComment = "OpenSSL Generated Client Certificate" 109 | subjectKeyIdentifier = hash 110 | authorityKeyIdentifier = keyid,issuer 111 | keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment 112 | extendedKeyUsage = clientAuth, emailProtection 113 | 114 | [ server_cert ] 115 | basicConstraints = CA:FALSE 116 | nsCertType = server 117 | nsComment = "OpenSSL Generated Server Certificate" 118 | subjectKeyIdentifier = hash 119 | authorityKeyIdentifier = keyid,issuer:always 120 | keyUsage = critical, digitalSignature, keyEncipherment 121 | extendedKeyUsage = serverAuth 122 | 123 | [ crl_ext ] 124 | authorityKeyIdentifier=keyid:always 125 | 126 | [ ocsp ] 127 | basicConstraints = CA:FALSE 128 | subjectKeyIdentifier = hash 129 | authorityKeyIdentifier = keyid,issuer 130 | keyUsage = critical, digitalSignature 131 | extendedKeyUsage = critical, OCSPSigning 132 | " > openssl.cnf 133 | 134 | echo_clr "Creating the root key..." 135 | openssl genrsa -aes256 -out private/ca.key.pem 4096 136 | # chmod 400 private/ca.key.pem 137 | 138 | echo_clr "Creating the root certificate..." 139 | openssl req -config openssl.cnf \ 140 | -key private/ca.key.pem \ 141 | -new -x509 -days 7300 -sha256 -extensions v3_ca \ 142 | -out certs/ca.cert.pem 143 | 144 | # Read-only for everyone 145 | # chmod 444 carts/ca.cert.pem 146 | 147 | # Show the generated certificate 148 | openssl x509 -noout -text -in certs/ca.cert.pem 149 | 150 | # Create the certificate chain file 151 | cat certs/ca.cert.pem > certs/ca-chain.cert.pem 152 | --------------------------------------------------------------------------------