Created
February 21, 2020 16:27
-
-
Save jamesthomasonjr/8e2d25241f9bec0a12368ec2bdd85ccd to your computer and use it in GitHub Desktop.
Easily Generate & Trust a Self Signed Certificate on OS X
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
set -euo pipefail | |
IFS=$'\n\t' | |
# Configuration | |
# Certifiate TTL | |
DAYS=3650 | |
# RSA Encryption Bits | |
BITS=2048 | |
# Hostnames | |
HOSTS=() | |
hash jq 2>/dev/null || { | |
echo >&2 "jq is required to run this script. Aborting." | |
exit 1 | |
} | |
echo "What hostnames should this certificate be generated for?" | |
echo "Leave blank to default to only localhost. Terminate list with an empty line." | |
read -p "hostname [localhost]: " hostname | |
if [ -z "$hostname" ]; then | |
HOSTS=( 'localhost' ) | |
else | |
while [ ! -z "$hostname" ]; do | |
HOSTS+=( $hostname ) | |
read -p "alternate name [press enter to skip]: " hostname | |
done | |
fi | |
LOCATION_INFORMATION=$(curl https://ipapi.co/json/) | |
LOCATION_COUNTRY=$(echo $LOCATION_INFORMATION | jq --raw-output ".country") | |
LOCATION_REGION=$(echo $LOCATION_INFORMATION | jq --raw-output ".region") | |
LOCATION_CITY=$(echo $LOCATION_INFORMATION | jq --raw-output ".city") | |
# Create initial config | |
CONFIG=$(cat <<EOF | |
[ req ] | |
default_bits = $BITS | |
prompt = no | |
default_md = sha256 | |
distinguished_name = dn | |
req_extensions = req_ext | |
[ dn ] | |
C = $LOCATION_COUNTRY | |
ST = $LOCATION_REGION | |
L = $LOCATION_CITY | |
O = $(whoami) | |
OU = dev | |
CN = ${HOSTS[0]} | |
emailAddress = $(whoami)@$(hostname) | |
[ req_ext ] | |
subjectAltName = @alt_names | |
[ alt_names ] | |
EOF | |
) | |
# Add additional hosts to config | |
for i in ${!HOSTS[@]}; do | |
CONFIG+=$'\n'"DNS.$((${i} + 1)) = ${HOSTS[$i]}" | |
done | |
echo | |
echo "Creating Configuration File..." | |
echo "${CONFIG}" >> "${HOSTS[0]}.csr.config" | |
echo | |
echo "Creating Certificate Key..." | |
openssl genrsa -out ${HOSTS[0]}.key $BITS | |
echo | |
echo "Creating Certificate Signing Request..." | |
openssl req -new -sha256 -out ${HOSTS[0]}.csr -key ${HOSTS[0]}.key -config ${HOSTS[0]}.csr.config | |
echo | |
echo "Creating Certificate..." | |
openssl x509 -req -sha256 -days $DAYS -in ${HOSTS[0]}.csr -signkey ${HOSTS[0]}.key -out ${HOSTS[0]}.crt -extensions req_ext -extfile ${HOSTS[0]}.csr.config | |
# The security CLI app doesn't quite work for trusting certs, so we use OS X APIs through Swift... | |
# And the OS X API requires DER format. | |
echo | |
echo "Converting from PEM format to DER format..." | |
openssl x509 -inform PEM -outform DER -in ${HOSTS[0]}.crt -out ${HOSTS[0]}.der.crt | |
echo | |
echo "Certificate generated!" | |
echo "Run the 'trust-cert' script to add the certificate to your keychain. (You will be asked for your password)" | |
echo | |
echo "Automatically run trust-cert script for ${HOSTS[0]}.der.crt?" | |
options=("Yes" "No") | |
select opt in "${options[@]}"; do | |
if [ "${opt}" = "Yes" ]; then | |
( | |
trust_cert_script="$(dirname ${BASH_SOURCE[0]})/trust-cert" | |
$trust_cert_script ${HOSTS[0]}.der.crt | |
) | |
else | |
break | |
fi | |
done |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env swift | |
// This script is written in swift because the CLI app (/usr/bin/security) doesn't seem to work | |
import Foundation | |
import Security | |
if CommandLine.argc < 2 { | |
print("No certificate file passed!") | |
exit(1) | |
} | |
let filePath = CommandLine.arguments[1] | |
var data:NSData | |
do { | |
let file:URL = URL(fileURLWithPath: filePath) | |
data = try NSData(contentsOf: file) | |
} catch { | |
print("Couldn't get data from file: \(error).") | |
exit(1) | |
} | |
let certificate:SecCertificate! = SecCertificateCreateWithData(nil, data) | |
if (certificate == nil) { | |
print("Certificate was invalid.") | |
exit(1) | |
} | |
let certificates:Array<SecCertificate> = [certificate] | |
let domain:SecTrustSettingsDomain = SecTrustSettingsDomain.user | |
let trustSettings:CFTypeRef? = nil // Null TrustSettings = Always Trust | |
let keychain:SecKeychain? = nil // Null Keychain = Default (usually login) | |
var status = SecTrustSettingsSetTrustSettings(certificate, domain, nil) | |
if (status != errSecSuccess) { | |
let message:Any = SecCopyErrorMessageString(status, nil) as Any | |
print(message) | |
exit(status) | |
} | |
status = SecCertificateAddToKeychain(certificate, keychain) | |
if (status == errSecDuplicateItem) { | |
status = errSecSuccess; | |
} | |
if (status != errSecSuccess) { | |
let message:Any = SecCopyErrorMessageString(status, nil) as Any | |
print(message) | |
exit(status) | |
} | |
exit(status) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment