#!/usr/bin/env bash # # Author: Georg Voell - georg.voell@standby.cloud # Version: @(#)key-management 3.3.0 19.01.2026 (c)2026 Standby.cloud # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ # # This script can be used free of charge. Use it as is or customize as needed. It is not guaranteed to be # error free and you will not be reimbursed for any damage it may cause. # #@ Manage the ssh keys. #@ #@Usage: key-management [options] action [action-parameter] #@ Options: #@ -h, --help : Displays helptext. #@ -v, --version : Displays the version of the script. #@ -f, --force : Don't ask questions. #@ -u, --username : Name of user e.g. "name@org.com" or of a secret (only allowed in action "show"). #@ -p, --passphrase : Passphrase for key encryption. #@ Action: #@ list [output] : List all local user with keys. Output can be "table" (default), "line", "etsv", "tsv", "csv", "json", "keys" or "plain". #@ secret : Create secret file to upload to vault #@ delete : Delete all the keys for user specified by username. #@ install : Install 'get-authkeys' software. Need to be root to proceed. #@ remove : Remove 'get-authkeys' software. Need to be root to proceed. #@ create [local] : Create private and public keypair for ssh and putty. If optional paramter "local" is given, don't use internet API. #@ change : Change old passphrase specified by option to new passphrase "newpass". #@ activate : Activate stage "latest" or "previous" to make these keys the current keys. #@ show : Display keys or fingerprint. Allowed values: "pub", "ssh", "api", "pk8", "ppk", 'fp' or vaultsecret ocid. #@ check : Display type of key. #@ import : Import a private key (and create all ather key formats) from file. #@ push : Copy keys to destination ("hostname" specified in ".ssh/config" with parameter "Host" e.g. "Linux7"). #@ add : Adding public key to $HOME/.ssh/authorized_keys file. #@ subtract : Delete public key from $HOME/.ssh/authorized_keys file. #@ Format: #@ pub | public : Display ssh public key (username required). #@ rsa | priv | ssh: Display ssh private key (username required). #@ api | pem : Display public key in PEM format (OCI API key - username required). #@ pk8 | pkcs8 : Display private key in PEM format (PKCS#8 - username required). #@ ppk | putty : Display putty private key (PPK V2 - username required). #@ pp3 : Display putty private key version 3 (PPK V3 - username required). #@ fp | fingerprint: Display fingerprint of private key (username required). #@ Only allowed with action "show" #@ -u, --username : Display vaultsecret. Specify a vaultsecret ocid e.g. ocid1.vaultsecret.oc1.. #@ #@Examples: #@ key-management create --username "opc" #@ Create keys for user "opc" #@ key-management list #@ Show all user (which have keys created with this tool) #@ key-management show api --username "opc" #@ Display OCI API public key in PEM format. #@ key-management show ssh --username "opc" #@ Display ssh private key. #@ key-management show fp --username "opc" #@ Display fingerprint for ssh private key (needed by OCI). # # Exit codes: # 01: Unknown or wrong parameter. # 02: No username specified. # 03: Error while creating / changing / deleting keys. # 04: Key (or user) does not exist. # 05: No second parameter given. # 06: Could not copy keys to destination. # 07: Could not read keyfile. # 08: Could not import keyfile. # 09: Passphrase needed. # 10: Could not install 'get-authkeys'. # 99: User interrupt. # # See also: # **install-scripts**(1) # # ToDo: # # Known bugs: # # Update history: # # V 3.0.0 11.06.2020 New version # V 3.0.1 22.06.2020 Only use print-table if tcsh is available (obsolete now - print-table was rewritten using bash) # V 3.0.2 25.06.2020 New function: Import private key from file # V 3.0.3 09.07.2020 New function: Install (get-authkeys) # V 3.0.4 11.07.2020 New function: Show secret key (vaultsecret) from oci vault # V 3.0.5 16.09.2020 Use new download URL: https://standby.cloud/download # V 3.0.6 28.09.2020 New action "add" # V 3.0.7 17.10.2020 Enter passphrase if it wa not given by import # V 3.0.8 17.01.2021 Invoking get-authkey rather then installing it # V 3.0.9 23.03.2021 New action "subtract" # V 3.0.10 16.12.2021 Small changes # V 3.1.0 05.06.2023 New copyright # V 3.1.1 28.08.2023 openssl V3.x is fips enabled # V 3.1.2 07.09.2023 Fix username with blanks and new parameter --force # V 3.1.3 15.11.2023 Convert Winows CR+LF to Linux LF # V 3.2.0 12.08.2024 New minor version # V 3.2.1 10.10.2024 Import unencrypted id_rsa without puttygen # V 3.2.2 01.08.2025 Append "OCI_API_KEY" to PK8 key (needed for oci cli) # V 3.2.3 04.08.2025 Small redesign and buxfix # V 3.2.4 14.09.2025 Different output formats with show. # V 3.2.5 30.09.2025 Better integration with vault and secrets # V 3.2.6 26.10.2025 Introducing key versions and 'activate' # V 3.3.0 19.01.2026 Revised with support of Claude Code # # Find executable bash library and source it lib=$(command -v lib.bash 2>/dev/null) if [[ -n "$lib" ]]; then source "$lib" else progdir=$(dirname "$0") if [[ -r "${progdir}/lib.bash" ]]; then source "${progdir}/lib.bash" else echo "Unexpected error: Unable to locate bash library 'lib.bash'." exit 1 fi fi # Set some deaults baseurl="https://standby.cloud/download" keygenurl="https://standby.cloud/ssh-keygen/keygen.pl" bits=4096 # 2048 is less secure sshstr=".ssh" authkeysstr="authorized_keys" configstr="config" keysstr="keys" infostr="info.txt" ppstr="passphrase.txt" keybasename="id_rsa" puttybasename="id_putty" apibasename="api_pub" sshdfolder="/etc/ssh" sshdconfig="${sshdfolder}/sshd_config" getauthkeys="get-authkeys" sshfolder="${REALHOME}/$sshstr" keysfolder="${sshfolder}/$keysstr" sshconfig="${sshfolder}/$configstr" ppfile="${keysfolder}/$ppstr" # Do extra cleanup function ExtraCleanup { filecheck -rm "${scratchfile}.keys" filecheck -rm "${scratchfile}.user" filecheck -rm "${scratchfile}.non" filecheck -rm "${scratchfile}.old" filecheck -rm "${scratchfile}.new" filecheck -rm "${scratchfile}.rsa" filecheck -rm "${scratchfile}.pre" filecheck -rm "${scratchfile}.err" filecheck -rm "${scratchfile}.fp" } # Returns a URI string for passing to curl (or transfer) function UriEncode { # We can do translation only if we have jq in PATH if [[ "$jq" != "" ]]; then $jq -nr --arg v "$1" '$v|@uri' else echo "$1" fi } # Create a secret file from keys function CreateSecret { local output=${1} local fingerprint="" local apifile="${keysfolder}/${username}/${apibasename}.pem" local privfile="${keysfolder}/${username}/${keybasename}.pk8" local puttyfile="${keysfolder}/${username}/${puttybasename}.ppk" local puttyfilev3="${keysfolder}/${username}/${puttybasename}.pp3" local pubfilessh="${keysfolder}/${username}/${keybasename}.pub" local privfilessh="${keysfolder}/${username}/$keybasename" if [[ "$username" != "" && "$output" != "" ]]; then # Check if output is writable output=$(filecheck -w "$output") if [[ "$output" != "" ]]; then fingerprint="$(GetFingerprintFromInfo "${keysfolder}/${username}/$infostr")" # Check for old version of api pem file if [[ ! -f "$apifile" ]]; then apifile="${keysfolder}/${username}/${keybasename}.pem" fi if [[ "$fingerprint" = "" ]]; then fingerprint=$(GetFingerprint "$apifile") fi printf '{"fingerprint":"%s' "$fingerprint" > "$scratchfile" if [[ -f "$apifile" ]]; then printf '","publicKey":"' >> "$scratchfile" cat "$apifile" | tr -d '\r' | sed '$!s|$|\\n|g' | tr -d '\n' >> "$scratchfile" fi if [[ -f "$privfile" ]]; then printf '","privateKey":"' >> "$scratchfile" cat "$privfile" | tr -d '\r' | sed '$!s|$|\\n|g' | tr -d '\n' >> "$scratchfile" fi if [[ -f "$puttyfile" ]]; then printf '","puttyKey":"' >> "$scratchfile" cat "$puttyfile" | tr -d '\r' | sed '$!s|$|\\n|g' | tr -d '\n' >> "$scratchfile" fi if [[ -f "$puttyfilev3" ]]; then printf '","puttyKeyV3":"' >> "$scratchfile" cat "$puttyfilev3" | tr -d '\r' | sed '$!s|$|\\n|g' | tr -d '\n' >> "$scratchfile" fi if [[ -f "$pubfilessh" ]]; then printf '","publicKeySsh":"' >> "$scratchfile" cat "$pubfilessh" | tr -d '\r' | sed '$!s|$|\\n|g' | tr -d '\n' >> "$scratchfile" fi if [[ -f "$privfilessh" ]]; then printf '","privateKeySsh":"' >> "$scratchfile" cat "$privfilessh" | tr -d '\r' | sed '$!s|$|\\n|g' | tr -d '\n' >> "$scratchfile" fi printf '"}' >> "$scratchfile" # Encode file cat "$scratchfile" | base64 --wrap=0 > $output filecheck -rm "$scratchfile" fi fi } # Cleanup a key file function CleanupKeyFile { # Transfer parameters local privkey=${1} local outfile=${2} # Preset local stat=1 if [[ "$privkey" != "" && "$outfile" != "" ]]; then if [[ -r "$privkey" ]]; then # Set correct file access rights touch "$scratchfile" chmod 600 "$scratchfile" # Cleanup key - remove empty lines beginning of file or end of file if [[ "$iconv" != "" ]]; then $iconv -c -f UTF-8 -t ASCII "$privkey" | grep -v 'OCI_API_KEY' | grep -v '^$' | sed '/^DEK-Info:.*/G' > "$scratchfile" stat=$? else cat "$privkey" | tr -d '\15\32' | grep -v 'OCI_API_KEY' | grep -v '^$' | sed '/^DEK-Info:.*/G' > "$scratchfile" stat=$? fi if (( stat == 0 )); then # Move converted file to outfile if [[ "$scratchfile" != "$outfile" ]]; then mv -f "$scratchfile" "$outfile" stat=$? fi else # Cleanup rm -f "$scratchfile" fi fi fi return $stat } # Return the key type function KeyType { # Transfer parameters local privkey=${1} # Preset local keytype="" local keysignature="" local result="" local encrypted="" # Possible results: # # UNKNOWN or INVALID # # RSA-PRIVATE-KEY NONE or RSA-PRIVATE-KEY CIPHER # PRIVATE-KEY NONE or PRIVATE-KEY CIPHER # OPENSSH-PRIVATE-KEY-Vx NONE or OPENSSH-PRIVATE-KEY-Vx CIPHER # PUTTY-PRIVATE-KEY-Vx NONE or PUTTY-PRIVATE-KEY-Vx CIPHER # # SSH-PUBLIC-KEY # SSH # RSA-PUBLIC-KEY # RSA # PUBLIC-KEY # API if [[ -r "$privkey" ]]; then # Cleanup key and get first line and lastline from key CleanupKeyFile "$privkey" "$scratchfile" # Get first non empty line and last non empty line from key and delete Windows CR keysignature=$(cat "$scratchfile" | head -n 1) result=$(cat "$scratchfile" | taillastline) case "$keysignature" in "-----BEGIN RSA PUBLIC KEY-----") # Created with: openssl rsa -pubin -in -RSAPublicKey_out # Or: ssh-keygen -f key.pem -y -e -m PEM if [[ "$result" = "-----END RSA PUBLIC KEY-----" ]]; then keytype="RSA-PUBLIC-KEY" else keytype="INVALID" fi ;; "-----BEGIN RSA PRIVATE KEY-----") if [[ "$result" = "-----END RSA PRIVATE KEY-----" ]]; then encrypted=$(grep "ENCRYPTED" "$scratchfile") if [[ "$encrypted" != "" ]]; then result=$(grep "^DEK-Info: " "$scratchfile" | cut -d' ' -f2 | cut -d',' -f1) keytype="RSA-PRIVATE-KEY $result" else keytype="RSA-PRIVATE-KEY NONE" fi else keytype="INVALID" fi ;; "-----BEGIN OPENSSH PRIVATE KEY-----") if [[ "$result" = "-----END OPENSSH PRIVATE KEY-----" ]]; then # result=$(ssh-keygen -y -P "" -f $scratchfile 2>/dev/null) result=$(grep -v "^-----" "$scratchfile" | $base64 -d | tr '\n' ' ' | tr '\06' ' ' | tr '\04' ' ' | tr '\00' ' ' | tr -s ' ' \ | sed 's|^openssh-key-||' | ToUpper) encrypted=$(echo "$result" | cut -d' ' -f2) if [[ "$encrypted" = "" ]]; then encrypted="UNKNOWN" fi result=$(echo "$result" | cut -d' ' -f1) keytype="OPENSSH-PRIVATE-KEY-$result $encrypted" else keytype="INVALID" fi ;; "-----BEGIN PUBLIC KEY-----") if [[ "$result" = "-----END PUBLIC KEY-----" ]]; then keytype="PUBLIC-KEY" else keytype="INVALID" fi ;; "-----BEGIN PRIVATE KEY-----") if [[ "$result" = "-----END PRIVATE KEY-----" ]]; then keytype="PRIVATE-KEY NONE" else keytype="INVALID" fi ;; "-----BEGIN ENCRYPTED PRIVATE KEY-----") if [[ "$result" = "-----END ENCRYPTED PRIVATE KEY-----" ]]; then if [[ "$openssl" != "" ]]; then result=$($openssl asn1parse -inform PEM -in "$scratchfile" 2>/dev/null | grep "prim: OBJECT" | taillastline | cut -d':' -f4 | ToUpper) else result="UNKNOWN" fi keytype="PRIVATE-KEY $result" else keytype="INVALID" fi ;; "---- BEGIN SSH2 PUBLIC KEY ----") # ssh-keygen -f key.pem -y -e -m rfc4716 if [[ "$result" = "---- END SSH2 PUBLIC KEY ----" ]]; then keytype="SSH2-PUBLIC-KEY" else keytype="INVALID" fi ;; "---- BEGIN SSH2 PRIVATE KEY ----") if [[ "$result" = "-----END SSH2 PRIVATE KEY-----" ]]; then keytype="SSH2-PRIVATE-KEY NONE" else keytype="INVALID" fi ;; "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----") # Created with: puttygen keyfile -O private-sshcom -o newkey if [[ "$result" = "---- END SSH2 ENCRYPTED PRIVATE KEY ----" ]]; then result=$(grep -v "^----" "$scratchfile" | grep -v "^Comment:" | $base64 -d | tr '\n' ' ' | tr -cd '[:print:]' | tr -s ' ' \ | cut -d' ' -f1 | cut -d'}' -f4 | ToUpper) keytype="SSH2-PRIVATE-KEY $result" else keytype="INVALID" fi ;; "PuTTY-User-Key-File-2: ssh-rsa" | "PuTTY-User-Key-File-3: ssh-rsa") encrypted=$(grep '^Encryption: ' "$scratchfile" | cut -d ' ' -f2- | ToUpper) result=$(echo "$result" | grep '^Private-MAC: ' | cut -d ' ' -f2-) if [[ "$result" = "" ]]; then keytype="INVALID" else result=$(echo "$keysignature" | sed 's|^PuTTY-User-Key-File-|V|' | sed 's|: ssh-rsa$||') keytype="PUTTY-PRIVATE-KEY-$result $encrypted" fi ;; *) if [[ "$keysignature" = "$result" ]]; then # Just one line in file result=$(echo "$keysignature" | grep '^ssh-rsa ') if [[ "$result" != "" ]]; then # Line begins with ssh-rsa result=$(echo "$keysignature" | cut -d' ' -f2) if [[ "$result" != "" ]]; then # We have a key behind ssh-rsa signature keytype="SSH-PUBLIC-KEY" else keytype="INVALID" fi fi else keytype="UNKNOWN" fi esac fi echo "$keytype" } function GetKeyType { local privkey=${1} # Preset local response="ERROR NONE false" local keytype="" local private="" local cipher="" local comment="" if [[ "$privkey" != "" ]]; then keytype=$(KeyType "$privkey") if [[ "$keytype" != "" ]]; then cipher=$(echo "$keytype" | cut -d ' ' -f2) comment=$(echo "$keytype" | cut -d ' ' -f1 | cut -d '-' -f4) keytype=$(echo "$keytype" | cut -d ' ' -f1 | cut -d '-' -f1-3) private=$(echo "$keytype" | grep "PRIVATE") if [[ "$private" != "" ]]; then private="true" else private="false" cipher="NONE" fi # Assemble response response="$keytype $cipher $private $comment" fi fi echo "$response" } # Get the fingerprint from public api key or private key function GetFingerprint { local privkey=${1} local pp=${2} # Preset local fp="" local encrypted="" local result="" local stat=1 if [[ -r "$privkey" ]]; then if [[ "$openssl" != "" ]]; then result=$(KeyType "$privkey") encrypted=$(echo "$result" | cut -d ' ' -f2) result=$(echo "$result" | cut -d ' ' -f1) if [[ "$result" = "PUBLIC-KEY" ]]; then $openssl pkey -pubin -in "$privkey" -pubout -outform DER -out "${scratchfile}.fp" >/dev/null 2>&1 stat=$? else if [[ "$encrypted" = "NONE" || "$pp" = "" ]]; then $openssl rsa -inform PEM -in "$privkey" -pubout -outform DER -out "${scratchfile}.fp" >/dev/null 2>&1 stat=$? else $openssl rsa -inform PEM -in "$privkey" -passin "pass:$pp" -pubout -outform DER -out "${scratchfile}.fp" >/dev/null 2>&1 stat=$? fi fi if (( stat == 0 )); then # Does not work in newer versions of openssl: md5 -non-fips-allow if [[ -f "${scratchfile}.fp" ]]; then if [[ "$fipsenabled" = "true" ]]; then fp=$(cat "${scratchfile}.fp" | $md5sum | cut -d' ' -f1 | sed 's|..|\:&|2g') else fp=$(cat "${scratchfile}.fp" | $openssl md5 -c 2>/dev/null | cut -d ' ' -f2) # Old: sed 's|(stdin)= ||g' fi fi fi else if [[ "$puttygen" != "" ]]; then fp="" # Does not work: # if [ "$pp" = "" ]; then # $puttygen -q "$privkey" -E md5 -O fingerprint -o ${scratchfile}.fp # stat=$? # else # $puttygen -q "$privkey" --old-passphrase "$pp" -E md5 -O fingerprint -o ${scratchfile}.fp # stat=$? # fi else if [[ "$keygen" != "" ]]; then fp="" # Does not work: # $keygen -l -E md5 -f "$privkey" 2>/dev/null > ${scratchfile}.fp # stat=$? # if [ $stat -eq 0 -a -f "${scratchfile}.fp" ]; then # fp=$(cat "${scratchfile}.fp" | cut -d' ' -f2 | sed 's|MD5:||') # fi fi fi fi # Cleanup filecheck -rm "${scratchfile}.fp" fi # Return result echo "$fp" } # Check if cipher is valid # Currently unsused function CheckCipher { local cipher=${1} # Preset if [[ "$cipher" != "" ]]; then # lowercase cipher string cipher=$(echo "$cipher" | ToLower | sed 's|-cbc$||' | sed 's|-ctr$||' | sed 's|des-ede3|des3|' | sed 's|-||') fi case "$cipher" in des | des3) cipher="$cipher" ;; aes128 | aes192 | aes256) cipher="$cipher" ;; idea | seed) cipher="$cipher" ;; camellia128 | camellia192 | camellia256) cipher="$cipher" ;; *) # Default cipher="des3" esac echo "$cipher" } # Create putty private keys function CreatePrivatePuttyKeys { local username=${1} local privkey=${2} local pp=${3} # Preset local stat=1 local result="" local puttyfile="${keysfolder}/${username}/$puttybasename" if [[ "$puttygen" != "" && -r "$privkey" ]]; then touch "$scratchfile" chmod 600 "$scratchfile" echo "$pp" > "$scratchfile" # Remove old files filecheck -rm "${keyfile}.ppk" filecheck -rm "${keyfile}.pp3" # Check version of puttygen result=$(check-version "$puttygen" | cut -d' ' -f2) result=$(compare-version "0.76" "$result") if [[ "$result" = "same" || "$result" = "newer" ]]; then $puttygen "$privkey" -P --old-passphrase "$scratchfile" --new-passphrase "$scratchfile" -C "$username" \ --ppk-param version=2 -O private -o "${puttyfile}.ppk" 2>/dev/null stat=$? if (( stat == 0 )); then $puttygen -q "$privkey" -P --old-passphrase "$scratchfile" --new-passphrase "$scratchfile" -C "$username" \ --ppk-param version=3 -O private -o "${puttyfile}.pp3" 2>/dev/null stat=$? fi else $puttygen -q "$privkey" -P --old-passphrase "$scratchfile" --new-passphrase "$scratchfile" -C "$username" \ -O private -o "${puttyfile}.ppk" 2>/dev/null stat=$? fi # Cleanup rm -f "$scratchfile" fi return $stat } # Generate public key in PEM format function CreatePublicApiKey { local privkey=${1} local pp=${2} # Preset local stat=1 local apifile="${keysfolder}/${username}/$apibasename" if [[ -r "$privkey" && "$openssl" != "" ]]; then # Remove old key filecheck -rm "${apifile}.pem" $openssl rsa -inform PEM -in "$privkey" -passin "pass:$pp" -pubout -outform PEM -out "${apifile}.pem" 2>/dev/null stat=$? fi return $stat } # Create private key with PKCS#8 format function CreatePrivatePkcs8Key { local privkey=${1} local pp=${2} # Preset local stat=1 if [[ -r "$privkey" && "$openssl" != "" ]]; then # Remove old key filecheck -rm "${keyfile}.pk8" touch "${keyfile}.pk8" chmod 600 "${keyfile}.pk8" touch "${scratchfile}.old" "${scratchfile}.new" chmod 600 "${scratchfile}.old" "${scratchfile}.new" echo "" > "${scratchfile}.old" echo "$pp" > "${scratchfile}.new" if [[ "$pp" != "" ]]; then $openssl pkcs8 -topk8 -v2 aes-256-cbc -inform PEM -in "$privkey" -passin "file:"${scratchfile}.ol"d" -passout "file:"${scratchfile}.ne"w" \ -outform PEM -out "${privkey}.pk8" 2>/dev/null stat=$? else $openssl pkcs8 -topk8 -inform PEM -in "$privkey" -outform PEM -nocrypt -out "${privkey}.pk8" 2>/dev/null stat=$? fi # Cleanup rm -f "${scratchfile}.old" "${scratchfile}.new" if (( stat > 0 )); then # Could not create key filecheck -rm "${privkey}.pk8" else # Append Key Signature needed for API Access echo "OCI_API_KEY" >> "${privkey}.pk8" fi fi return $stat } # Create SSH-RSA public key from private key function CreateRSAPublicKey { # Transfer parameters local username=${1} local privkey=${2} local pp=${3} # Preset local stat=1 local result="" if [[ "$username" != "" && "$privkey" != "" ]]; then if [[ "$puttygen" != "" ]]; then touch "$scratchfile" chmod 600 "$scratchfile" echo "$pp" > "$scratchfile" $puttygen -q "$privkey" -P --old-passphrase "$scratchfile" --new-passphrase "$scratchfile" -O public-openssh \ -C "$username" -o "${privkey}.pub" 2>/dev/null stat=$? rm -f "$scratchfile" else if [[ "$keygen" != "" ]]; then result=$($keygen -y -f "$privkey" -P "$pp" 2>/dev/null) stat=$? if (( stat == 0 )); then echo "$result $username" > "${privkey}.pub" fi fi fi fi # Set correct filerights if [[ $stat -eq 0 && -f "${privkey}.pub" ]]; then chmod 644 "${privkey}.pub" fi return $stat } # Creeate the plain RSA key, encrypted key function ModifyRSAKeys { # Transfer parameters local keytype=${1} local username=${2} local privkey=${3} local oldpp=${4} local newpp=${5} # Preset local stat=1 if [[ "$privkey" != "" ]]; then # Create password files touch "${scratchfile}.rsa" "${scratchfile}.old" "${scratchfile}.new" "${scratchfile}.non" chmod 600 "${scratchfile}.rsa" "${scratchfile}.old" "${scratchfile}.new" "${scratchfile}.non" echo "$oldpp" > "${scratchfile}.old" echo "$newpp" > "${scratchfile}.new" echo "" > "${scratchfile}.non" # Private keys: "PUTTY-PRIVATE-KEY", "SSH2-PRIVATE-KEY", "OPENSSH-PRIVATE-KEY", "RSA-PRIVATE-KEY", "PRIVATE-KEY" # Create RSA private key without password if [[ "$keygen" != "" && "$keytype" = "OPENSSH-PRIVATE-KEY" ]]; then cp -f "$privkey" "${scratchfile}.rsa" $keygen -q -p -P "$oldpp" -N "" -m pem -f "${scratchfile}.rsa" >/dev/null 2>&1 stat=$? else if [[ "$openssl" != "" && "$keytype" = "PRIVATE-KEY" ]]; then if [[ "$fipsenabled" = "false" ]]; then # Create unencrypted RSA private key with openssl / flag -traditional will not work with older versions of openssl $openssl rsa -in "$privkey" -passin "file:"${scratchfile}.ol"d" -passout "file:"${scratchfile}.no"n" $osslopt -out "${scratchfile}.rsa" 2>/dev/null stat=$? else # Create unencrypted RSA private key with openssl / flag -traditional will not work with older versions of openssl $openssl rsa -in "$privkey" -passin "file:"${scratchfile}.ol"d" -passout "file:"${scratchfile}.no"n" -out "${scratchfile}.rsa" 2>/dev/null stat=$? fi else if [[ "$puttygen" != "" ]]; then # Create unencrypted RSA private key with puttygen (does not work with PRIVATE-KEY or RSA_PRIVATE_KEY not encrypted with des3) $puttygen "$privkey" -P --old-passphrase "${scratchfile}.old" --new-passphrase "${scratchfile}.non" -O private-openssh -o "${scratchfile}.rsa" 2>/dev/null stat=$? fi fi fi # Check if we now have a non encrypted RSA key result=$(GetKeyType "${scratchfile}.rsa") keytype=$(echo "$result" | cut -d ' ' -f1) result=$(echo "$result" | cut -d ' ' -f2) # Move scratch to keyfile in case of no error and create RSA public key if [[ $stat -eq 0 && "$keytype" = "RSA-PRIVATE-KEY" && "$result" = "NONE" ]]; then if [[ "$newpp" != "" ]]; then # Create encrypted RSA key if [[ "$openssl" != "" ]]; then # Use openssl with cipher des3 for more compatibility - aes256 in fips enabled environments if [[ "$fipsenabled" = "false" ]]; then $openssl rsa -in "${scratchfile}.rsa" -passin "file:"${scratchfile}.no"n" -passout "file:"${scratchfile}.ne"w" $osslopt -des3 -out "${privkey}.enc" 2>/dev/null stat=$? else $openssl pkey -in "${scratchfile}.rsa" -passin "file:"${scratchfile}.no"n" -passout "file:"${scratchfile}.ne"w" -aes256 -out "${privkey}.enc" 2>/dev/null stat=$? fi else if [[ "$puttygen" != "" ]]; then if [[ "$fipsenabled" = "false" ]]; then # puttygen -O private-openssh creates RSA-PRIVATE-KEY with cipher DES-EDE3-CBC (des3) $puttygen "$"${scratchfile}.rs"a" -P --old-passphrase "${scratchfile}.non" --new-passphrase "${scratchfile}.new" -O private-openssh -o "${privkey}.enc" 2>/dev/null stat=$? else # puttygen -O private-openssh-new creates OPENSSH-PRIVATE-KEY keys with cipher AES256-CTR (aes256) $puttygen "${scratchfile}.rsa" -P --old-passphrase "${scratchfile}.non" --new-passphrase "${scratchfile}.new" -O private-openssh-new -o "${privkey}.enc" 2>/dev/null stat=$? fi else if [[ "$keygen" != "" && "$fipsenabled" = "false" ]]; then # ssh-keygen with option -m -PEM creates encrypted RDA keys only with cipher aes128 (-Z for cipher does not worh with option -m) cp -f "${scratchfile}.rsa" "${privkey}.enc" $keygen -q -p -P "" -N "$newpp" -m pem -f "${privkey}.enc" >/dev/null 2>&1 stat=$? fi fi fi # Check if key is now encrypted result=$(GetKeyType "${privkey}.enc" | cut -d ' ' -f2) if [[ "$result" = "NONE" ]]; then stat=1 fi fi # Check if all was successful if (( stat == 0 )); then mv -f "${scratchfile}.rsa" "$privkey" stat=$? if (( stat == 0 )); then # Create the ssh-rsa public key CreateRSAPublicKey "$username" "$privkey" "" stat=$? fi fi fi # Cleanup filecheck -rm "${scratchfile}.rsa" rm -f "${scratchfile}.old" "${scratchfile}.new" "${scratchfile}.non" fi # Exit with status code return $stat } ### Only for Debug # Creeate the plain RSA key without passphrase function CheckPlainRSAKey { # Transfer parameters local username=${1} local privkey=${2} local pp=${3} # Preset local stat=1 local keytype="" local cipher="" local private="" if [[ -r "$privkey" ]]; then result=$(GetKeyType "$privkey") keytype=$(echo "$result" | cut -d ' ' -f1) cipher=$(echo "$result" | cut -d ' ' -f2) private=$(echo "$result" | cut -d ' ' -f3) # Check if key is a private key echo "private: '$private'" if [[ "$private" = "true" ]]; then echo "keytype: '$keytype'" echo "cipher: '$cipher'" if [[ "$cipher" != "NONE" ]]; then ModifyRSAKeys "$keytype" "$username" "$privkey" "$pp" "$pp" stat=$? else ModifyRSAKeys "$keytype" "$username" "$privkey" "" "" stat=$? fi fi fi return $stat } # Create private and public RSA keys - If encrypted: AES-128-CBC (ssh-keygen) or DES-EDE3-CBC (puttygen) # https://www.thedigitalcatonline.com/blog/2018/04/25/rsa-keys/ function CreateRSAKeys { local username=${1} local privkey=${2} local pp=${3} # Preset local stat=1 if [[ "$username" != "" && "$privkey" != "" ]]; then if [[ ! -r "$privkey" && ! -r "${privkey}.pub" ]]; then # Create standard rsa private key with puttygen touch "$scratchfile" chmod 600 "$scratchfile" echo "" > "$scratchfile" # We craete the keys first unencrypted - that is why we don't write $pp to scratch if [[ "$puttygen" != "" ]]; then $puttygen -q -t rsa -b $bits -P --old-passphrase "$scratchfile" --new-passphrase "$scratchfile" -O private-openssh \ -C "$username" -o "$privkey" 2>/dev/null stat=$? if (( stat == 0 )); then # Create ssh-rsa public key CreateRSAPublicKey "$username" "$privkey" "" stat=$? fi else # Create unencrypted keys with ssh-keygen if [[ "$keygen" != "" ]]; then $keygen -q -t rsa -N "" -b $bits -C "$username" -m PEM -f "$privkey" 2>/dev/null # PEM not pem or PKCS8 stat=$? fi fi # Remove scratch with passphrase (empty when we create keys first) rm -f "$scratchfile" if [[ $stat -eq 0 && "$pp" != "" ]]; then # Encrypt private key ModifyRSAKeys "$keytype" "$username" "$keyfile" "$pp" "$pp" stat=$? fi fi fi return $stat } # Create an info file with all values function CreateInfoFile { local user=${1} local fp=${2} local stat=0 if [[ "$user" != "" ]]; then infofile="${keysfolder}/${user}/$infostr" printf " Public Key file (ssh): '%s'\n\n" "${keybasename}.pub" > "$infofile" printf " Private Key file (ssh): '%s'\n" "$keybasename" >> "$infofile" printf " Private Key file (PKCS#8): '%s'\n" "${keybasename}.pk8" >> "$infofile" if [[ -f "${keysfolder}/${user}/${puttybasename}.ppk" ]]; then printf "\n Private Key file (putty): '%s' (older format v2)\n" "${puttybasename}.ppk" >> "$infofile" fi if [[ -f "${keysfolder}/${user}/${puttybasename}.pp3" ]]; then printf " Private Key file (putty): '%s' (newer format v3)\n" "${puttybasename}.pp3" >> "$infofile" fi printf "\n Public Key file (OCI-API): '%s'\n" "${apibasename}.pem" >> "$infofile" printf " Fingerprint: '%s'\n" "$fp" >> "$infofile" else stat=1 fi return $stat } # Create PK8, API and PPK keys function CreateSpecialKeys { local username=${1} local privkey=${2} local pp=${3} local fp="" local stat=0 if [[ "$puttygen" != "" ]]; then CreatePrivatePuttyKeys "$username" "$privkey" "$pp" stat=$? fi if (( stat == 0 )); then CreatePrivatePkcs8Key "$privkey" "$pp" stat=$? if (( stat == 0 )); then CreatePublicApiKey "$privkey" "$pp" stat=$? if (( stat == 0 )); then fp=$(GetFingerprint "$privkey" "$pp") CreateInfoFile "$username" "$fp" stat=$? fi fi fi # Move the optional created encrypted key if [[ -f "${keyfile}.enc" ]]; then mv -f "${keyfile}.enc" "$keyfile" fi # Exit with status return $stat } # Write a new entrry to passphrase file function WriteToPassphraseFile { local username=${1} local passphrase=${2} ### Having a passphrase file is insecure # # Create passphrase file - if does not exists yet # if [ ! -f "$ppfile" ]; then # printf "username\tpassphrase\n" > "$ppfile" # chmod 600 "$ppfile" # fi # # # Write username and passphrase to ppfile # printf "%s\t%s\n" "$username" "$passphrase" >> "$ppfile" } # Delete an entrry from passphrase file function DeleteFromPassphraseFile() { local username=${1} ### Having a passphrase file is insecure # if [ -r "$ppfile" ]; then # mv -f "$ppfile" $scratchfile # grep -v "^$username " $scratchfile > "$ppfile" # rm -f $scratchfile # chmod 600 "$ppfile" # fi } # Delete an entry from passphrase file function GetFromPassphraseFile { local username=${1} local pp="" if [[ -s "$ppfile" ]]; then pp=$(grep "^$username " "$ppfile" | cut -d$'\t' -f2) fi echo "$pp" } # Change passphrase of any private key function ChangePassphrase { local username=${1} local oldpp=${2} local newpp=${3} # Preset local stat=0 local keytype="" local cipher="" local private="" if [[ "$username" = "" ]]; then stat=2 else if [[ -d "${keysfolder}/$username" ]]; then keyfile="${keysfolder}/${username}/$keybasename" if [[ -r "$keyfile" ]]; then result=$(GetKeyType "$keyfile") keytype=$(echo "$result" | cut -d ' ' -f1) cipher=$(echo "$result" | cut -d ' ' -f2) private=$(echo "$result" | cut -d ' ' -f3) # Check if key is a private key if [[ "$private" != "true" ]]; then stat=3 else # Check if we have a passphrase for encryption if [[ "$cipher" != "NONE" && "$oldpp" = "" ]]; then while [[ "$oldpp" = "" ]]; do printf "Enter the private key passphrase for keyfile '$keyfile': " read -s inp printf "\n" if [[ "$inp" != "" ]]; then oldpp="$inp" fi done fi if (( stat == 0 )); then # Encrypt private key ModifyRSAKeys "$keytype" "$username" "$keyfile" "$oldpp" "$newpp" stat=$? # In case that we could't decrypt RSA key, try PKCS8 key if [[ $stat -ne 0 && -r "${keyfile}.pk8" ]]; then echo "Using Key: '${keyfile}.pk8'". mv -f "$keyfile" "${scratchfile}.fp" cp -f "${keyfile}.pk8" "$keyfile" result=$(GetKeyType "$keyfile") keytype=$(echo "$result" | cut -d ' ' -f1) ModifyRSAKeys "$keytype" "$username" "$keyfile" "$oldpp" "$newpp" stat=$? if (( stat != 0 )); then mv -f "${scratchfile}.fp" "$keyfile" else rm -f "${scratchfile}.fp" fi fi fi if (( stat == 0 )); then CreateSpecialKeys "$username" "$keyfile" "$newpp" stat=$? fi fi else stat=7 fi else stat=8 fi fi return $stat } # Import any private key function ImportPrivateKey { local privkey=${1} local username=${2} local passphrase=${3} # Preset local stat=0 local keytype="" local cipher="" local private="" local ext="" if [[ "$username" = "" ]]; then # Username can not be empty stat=2 else if [[ -r "$privkey" ]]; then result=$(GetKeyType "$privkey") keytype=$(echo "$result" | cut -d ' ' -f1) cipher=$(echo "$result" | cut -d ' ' -f2) private=$(echo "$result" | cut -d ' ' -f3) # Check if key is a private key if [[ "$private" != "true" ]]; then # Unknown key type or no private key stat=3 else # Check if we have a passphrase for encryption if [[ "$cipher" != "NONE" && "$passphrase" = "" ]]; then while [[ "$passphrase" = "" ]]; do printf "Enter the private key passphrase for keyfile '$privkey': " read -s inp printf "\n" if [[ "$inp" != "" ]]; then passphrase="$inp" fi done fi if [[ ! -d "${keysfolder}/$username" ]]; then # User does not exist yet mkdir -m 0700 -p "${keysfolder}/$username" keyfile="${keysfolder}/${username}/$keybasename" # Copy private key to destination CleanupKeyFile "$privkey" "$keyfile" stat=$? if (( stat == 0 )); then # Create the RSA private key and the encrypted RSA private key if [[ "$cipher" != "NONE" ]]; then ModifyRSAKeys "$keytype" "$username" "$keyfile" "$passphrase" "$passphrase" stat=$? else ModifyRSAKeys "$keytype" "$username" "$keyfile" "" "" stat=$? fi if (( stat == 0 )); then # Create putty keys and PKCS8 private key CreateSpecialKeys "$username" "$keyfile" "$passphrase" stat=$? if (( stat == 0 )); then WriteToPassphraseFile "$username" "$passphrase" printf "Keys imported in folder '${keysfolder}/$username'.\n" else # Unable to create special keys stat=5 fi else # Unable to modify RSA keys stat=4 fi else # Unable to clean key stat=3 fi if (( stat > 0 )); then # Key import wasn't successful - delete user key folder rm -fR "${keysfolder}/$username" fi else # User Key folder does not exist stat=7 fi fi else # Can not read private key stat=6 fi fi return $stat } # Get the fingerprint from infofile function GetFingerprintFromInfo { local infofile=${1} local fingerprint="" if [[ "$username" != "" ]]; then if [[ -s "$infofile" ]]; then fingerprint=$(grep "Fingerprint:" "$infofile" | cut -d':' -f2- | sed 's|^ *||' | tr -d "'") fi fi echo "$fingerprint" } # Activate a key stage (latest or previous) for user function ActivateStage { local username=${1} local stage=${2} local current="" local latest="" local previous="" local files="" local stat=1 if [[ "$username" != "" ]]; then # Lowercase stage if [[ "$stage" = "" ]]; then stage="latest" else stage="$(ToLower "$stage")" fi if [[ "$stage" = "latest" || "$stage" = "previous" ]]; then # Define folder current="${keysfolder}/${username}" latest="${keysfolder}/${username}/beta" previous="${keysfolder}/${username}/previous" if [[ "$stage" = "latest" && -d "$latest" ]]; then if [[ -d "$previous" ]]; then if [[ "$force" = true ]]; then stat=0 else confirm "Do you want overwrite keys in stage 'previous'?" --yes "y/[yes]" --no "n/no" stat=$? fi if (( stat == 0 )); then rm -fR "$previous" fi fi # Move current files to previous if [[ ! -d "$previous" ]]; then files=$(find "$current" -maxdepth 1 -type f) if [[ "$files" != "" ]]; then mkdir "$previous" chmod 700 "$previous" mv -f $files "$previous" # Move latest files to current files=$(find "$latest" -maxdepth 1 -type f) if [[ "$files" != "" ]]; then mv -f $files "$current" rmdir "$latest" stat=$? fi fi fi else if [[ "$stage" = "previous" && -d "$previous" ]]; then if [[ -d "$latest" ]]; then if [[ "$force" = true ]]; then stat=0 else confirm "Do you want overwrite keys in stage 'latest'?" --yes "y/[yes]" --no "n/no" stat=$? fi if (( stat == 0 )); then rm -fR "$latest" fi fi # Move current files to latest if [[ ! -d "$latest" ]]; then files=$(find "$current" -maxdepth 1 -type f) if [[ "$files" != "" ]]; then mkdir "$latest" chmod 700 "$latest" mv -f $files "$latest" # Move previous files to current files=$(find "$previous" -maxdepth 1 -type f) if [[ "$files" != "" ]]; then mv -f $files "$current" rmdir "$previous" stat=$? fi fi fi fi fi fi fi if (( stat == 0 )); then echo "Keys for user '$username' moved from '$stage' to '$current'." fi return $stat } # Generate tab separated list with infos function WriteToKeylist { local username=${1} local stage=${2} local folder="" local keyfile="" local pubfile="" local figerprint="" local pp="" if [[ "$username" != "" ]]; then # Lowercase stage if [[ "$stage" = "" ]]; then stage="current" else stage="$(ToLower "$stage")" fi # Define folder case "$stage" in previous | latest) folder="${keysfolder}/${username}/$stage" ;; *) folder="${keysfolder}/${username}" esac # Preset keyfile="${folder}/$keybasename" pubfile="${folder}/$apibasename" if [[ -d "$folder" && -s "$keyfile" ]]; then fingerprint="$(GetFingerprintFromInfo "${folder}/$infostr")" if [[ "$fingerprint" = "" ]]; then # Try to get fingerprint from api public key if [[ -r "${pubfile}.pem" ]]; then fingerprint=$(GetFingerprint "${pubfile}.pem") else if [[ -r "${keyfile}.pem" ]]; then fingerprint=$(GetFingerprint "${keyfile}.pem") fi fi if [[ "$fingerprint" = "" ]]; then # That did not work so far - get fingerprint from private key pp=$(GetFromPassphraseFile "$username") if [[ -r "${keyfile}.pk8" ]]; then fingerprint=$(GetFingerprint "${keyfile}.pk8" "$pp") else fingerprint=$(GetFingerprint "$keyfile" "$pp") fi fi fi encrypted=$(grep "ENCRYPTED" "$keyfile") if [[ "$encrypted" = "" ]]; then havepassphrase="false" else havepassphrase="true" fi printf "%s\t%s\t%s\t%s\t%s\n" "$username" "$stage" "$havepassphrase" "$fingerprint" "$keyfile" >> "${scratchfile}.keys" fi fi } # Copy keys to another host function PushKeys { local username=${1} local destination=${2} local action=${3} local errcode=0 if [[ ! -r "$sshconfig" ]]; then printf "No '$sshconfig' found. Exiting.\n" errcode=6 else result=$(cat "$sshconfig" | grep "^Host $destination$") if [[ "$result" = "" ]]; then printf "Please specify the destination 'hostname' specified in '$sshconfig' with parameter 'Host'. Exiting.\n" errcode=5 else if [[ "$username" != "" ]]; then if [[ -d "${keysfolder}/$username" ]]; then if [[ "$action" = "push" ]]; then # Copy user folder with keys to host scp -q -r "${keysfolder}/$username" "${destination}:/tmp" >/dev/null 2>&1 stat=$? else scp -q -r "${keysfolder}/${username}/${keybasename}.pub" "${destination}:/tmp/key.pub" >/dev/null 2>&1 stat=$? fi if (( stat == 0 )); then # Create move script echo 'if [[ ! -d "$HOME/'$sshstr'" ]]; then mkdir -m 0700 -p "$HOME/'$sshstr'"; fi' > "$scratchfile" echo 'if [[ ! -d "$HOME/'$sshstr'/'$keysstr'" ]]; then mkdir -m 0700 "$HOME/'$sshstr'/'$keysstr'"; fi' >> "$scratchfile" if [[ "$action" = "push" ]]; then echo 'if [[ -d "$HOME/'$sshstr'/'$keysstr'/'$username'" ]]; then' >> "$scratchfile" echo ' rm -fR "$HOME/'$sshstr'/'$keysstr'/'$username'"' >> "$scratchfile" echo 'fi' >> "$scratchfile" echo 'mv "/tmp/'$username'" "$HOME/'$sshstr'/'$keysstr'"' >> "$scratchfile" fi if [[ "$action" = "add" ]]; then echo 'pubkey="$(cat "/tmp/key.pub")"' >> "$scratchfile" echo 'result="$(grep "$pubkey" "$HOME/'$sshstr'/'$authkeysstr'")"' >> "$scratchfile" echo 'if [[ "$result" = "" ]]; then echo "$pubkey" >> "$HOME/'$sshstr'/'$authkeysstr'"; fi' >> "$scratchfile" echo 'rm -f /tmp/key.pub' >> "$scratchfile" fi if [[ "$action" = "subtract" ]]; then echo 'pubkey="$(cat "/tmp/key.pub")"' >> "$scratchfile" echo 'result="$(grep "$pubkey" "$HOME/'$sshstr'/'$authkeysstr'")"' >> "$scratchfile" echo 'if [[ "$result" != "" ]]; then' >> "$scratchfile" echo ' mv -f "$HOME/'$sshstr'/'$authkeysstr'" "$HOME/'$sshstr'/'$authkeysstr'.old"' >> "$scratchfile" echo ' cat "$HOME/'$sshstr'/'$authkeysstr'.old" | grep -v "$pubkey" > "$HOME/'$sshstr'/'$authkeysstr'"' >> "$scratchfile" echo ' chmod 600 "$HOME/'$sshstr'/'$authkeysstr'"' >> "$scratchfile" echo 'fi' >> "$scratchfile" echo 'rm -f /tmp/key.pub' >> "$scratchfile" fi echo "rm -f $scratchfile" >> "$scratchfile" scp -q "$scratchfile" "${destination}:$scratchfile" >/dev/null 2>&1 ssh "$destination" "cat "$scratchfile" | bash" >/dev/null 2>&1 stat=$? if (( stat == 0 )); then printf "Push keys (ssh) succeded. Action: '$action'.\n" else printf "Push keys (ssh) failed. Exiting.\n" errcode=6 fi else printf "Push keys (scp) failed. Exiting.\n" errcode=6 fi else printf "Keys do not exist in folder '${keysfolder}/$username'. Exiting.\n" errcode=4 fi else printf "Username not specified.\n" errcode=2 fi fi fi return $errcode } # Create keys if user folder doesn't exists function CreateKeys { local username=${1} local passphrase=${2} local infofile="" local errcode=0 if [[ "$username" != "" ]]; then if [[ ! -d "${keysfolder}/$username" ]]; then keyfile="${keysfolder}/${username}/$keybasename" infofile="${keysfolder}/${username}/$infostr" myip=$(get-ip ip) if [[ "$myip" != "" && "$param2" != "local" ]]; then # We have internet access - get the keys from api uriusername=$(UriEncode "$username") if [[ "$passphrase" != "" ]]; then uripassphrase=$(UriEncode "$passphrase") myurl="${keygenurl}?username=${uriusername}&passphrase=${uripassphrase}" else myurl="${keygenurl}?username=${uriusername}" fi transfer --quiet "$myurl" --export "${keysfolder}/${username}.zip" stat=$? # If we got an error - remove zip file if it does exisits if (( stat != 0 )); then printf 'transfer error\n' filecheck -rm "${keysfolder}/${username}.zip" fi # If we could download zip with keys - continue if [[ -f "${keysfolder}/${username}.zip" ]]; then unzip -q "${keysfolder}/${username}.zip" -d "$keysfolder" chmod 700 "${keysfolder}/$username" rm -f "${keysfolder}/${username}.zip" # Write username and passphrase to ppfile and create info file WriteToPassphraseFile "$username" "$passphrase" fingerprint="$(GetFingerprintFromInfo "$infofile")" if [[ "$fingerprint" = "" ]]; then fingerprint=$(GetFingerprint "${keyfile}.pk8" "$passphrase") fi CreateInfoFile "$username" "$fingerprint" printf "Keys created in folder '${keysfolder}/$username'.\n" else printf "Keys could not be created.\n" errcode=3 fi else # No internet access - create the keys local # Create the user folder mkdir -m 0700 -p "${keysfolder}/$username" infofile="${keysfolder}/${username}/$infostr" CreateRSAKeys "$username" "$keyfile" "$passphrase" errcode=$? if (( errcode == 0 )); then CreateSpecialKeys "$username" "$keyfile" "$passphrase" errcode=$? if (( errcode == 0 )); then WriteToPassphraseFile "$username" "$passphrase" printf "Keys created local in folder '${keysfolder}/$username'.\n" fi fi if (( errcode > 0 )); then # Key creation wasn't successful - delete user key folder rm -fR "${keysfolder}/$username" printf "Keys could not be created.\n" errcode=3 fi fi else printf "Keys exists in folder '${keysfolder}/$username'. Leaving keys unchanged.\n" errcode=3 fi else printf "Username not specified.\n" errcode=2 fi return $errcode } # Restart sshd function RestartSSHD { case "$OS" in Linux) version_main=$(get-platform version_main) if (( version_main > 6 )); then systemctl restart sshd else /etc/init.d/sshd reload service sshd restart fi ;; SunOS) svcadm restart ssh ;; Darwin) launchctl stop com.openssh.sshd launchctl start com.openssh.sshd ;; esac } # Install GetAuthkeys function InstallGetAuthkeys { local keysconfig="/etc/ssh/keys_config" local errcode=0 local owner="" ga=$(filecheck -x $getauthkeys) if [[ "$ga" != "" ]]; then owner=$(ls -ld $ga | cut -d' ' -f3) fi if [[ -f "$sshdconfig" && "$owner" = "root" ]]; then found=$(grep '^#AuthorizedKeysCommand none$' "$sshdconfig") if [[ "$found" != "" ]]; then cp -f "$sshdconfig" "${sshdconfig}.org" chmod 600 "${sshdconfig}.org" cat "${sshdconfig}.org" | sed 's|^#AuthorizedKeysCommand none$|AuthorizedKeysCommand '${progdir}/$getauthkeys' "%u"|' \ | sed 's|^#AuthorizedKeysCommandUser nobody$|AuthorizedKeysCommandUser nobody|' \ | sed 's|^#PubkeyAuthentication yes$|PubkeyAuthentication yes|' > "$sshdconfig" chmod 600 "$sshdconfig" if (( errcode > 0 )); then mv -f "${sshdconfig}.org" "$sshdconfig" else if [[ ! -s "$keysconfig" ]]; then echo "https://standby.cloud/download/samples/authorized_keys.json" > "$keysconfig" chmod 644 "$keysconfig" fi RestartSSHD fi else errcode=2 fi else errcode=1 fi return $errcode } function RemoveGetAuthkeys { errcode=0 if [[ -f "${sshdconfig}.org" ]]; then mv -f "${sshdconfig}.org" "$sshdconfig" RestartSSHD else errcode=1 fi return $errcode } # Call oci cli and try several times if it fails (maybe because of connection errors) function CallOCI { local max=10 local i=0 local stat=1 if [[ "$oci" != "" ]]; then while [[ $stat -ne 0 && $i -lt $max ]]; do $oci "$@" >"${scratchfile}.pre" 2>"${scratchfile}.err" stat=$? # Increase counter (( i++ )) if (( stat == 0 )); then if [[ -s "${scratchfile}.pre" ]]; then norm-json --quiet --import "${scratchfile}.pre" stat=$? # norm-json failed - normally this should never be the case if (( stat != 0 )); then cat "${scratchfile}.pre" stat=$? fi else cat "${scratchfile}.pre" stat=$? fi else if (( i < max )); then # Command was not successful. Wait for a few seconds until we try again sleep 3 fi fi done filecheck -rm "${scratchfile}.pre" fi return $stat } # Preset force=false param1="" param2="" username="" passphrase="" # Check parameters: Loop until all parameters are used up while [[ $# -gt 0 ]]; do pname=${1} case "$pname" in -u | --username) shift if [[ "$1" != "" ]]; then username=${1} shift else errstr="Please specify a username (e.g. 'name@org.com') after parameter '$pname'." fi ;; -p | --passphrase) shift if [[ "$1" != "" ]]; then passphrase=${1} shift else errstr="Please specify a passphrase (e.g. 'MySecret') after parameter '$pname'." fi ;; -v | --version) shift showversion=true ;; -h | --help) shift showhelp=true ;; -f | --force) shift force=true ;; *) shift if [[ "$pname" == -* ]]; then # Options begin with '-' errstr="Unknown option '$pname'." else if [[ "$param1" = "" ]]; then param1="$(ToLower "$pname")" else if [[ "$param2" = "" ]]; then param2="$pname" else errstr="Unknown additional parameter: '$pname'." fi fi fi esac done # Plausibility check if [[ "$passphrase" != "" ]]; then pwlen=$(echo -n "$passphrase" | wc -m) if (( pwlen < 5 )); then errstr="Passphrase has to have at least 5 chars." fi fi # Display help or error message DisplayHelp ### Main # Create ssh folder if it doesn't exists if [[ ! -d "$sshfolder" ]]; then mkdir -m 0700 -p "$sshfolder" chown ${REALUSER}:$REALGROUP "$sshfolder" fi # Create keys folder if it doesn't exists if [[ ! -d "$keysfolder" ]]; then mkdir -m 0700 "$keysfolder" chown ${REALUSER}:$REALGROUP "$keysfolder" fi # Create ssh config if it doesn't exists if [[ ! -f "$sshconfig" ]]; then printf "# Created by tool '$progstr'.\n\n" > "$sshconfig" printf "# Default settings Version 1.1\n" >> "$sshconfig" printf 'Host *\n' >> "$sshconfig" printf '\tForwardAgent no\n' >> "$sshconfig" printf '\tForwardX11 no\n' >> "$sshconfig" printf '\tForwardX11Trusted no\n' >> "$sshconfig" printf '\tPort 22\n' >> "$sshconfig" printf '\tProtocol 2\n' >> "$sshconfig" printf '\tServerAliveInterval 60\n' >> "$sshconfig" printf '\tServerAliveCountMax 30\n' >> "$sshconfig" printf '\tStrictHostKeyChecking no\n' >> "$sshconfig" printf '\tHostKeyAlgorithms +ssh-rsa\n' >> "$sshconfig" printf '\tPubkeyAcceptedKeyTypes +ssh-rsa\n' >> "$sshconfig" printf '\tUserKnownHostsFile /dev/null\n' >> "$sshconfig" printf '\tLogLevel error\n' >> "$sshconfig" chmod 600 "$sshconfig" chown ${REALUSER}:$REALGROUP "$sshconfig" fi # Create a list with all existing users and check if user already exists stat=0 ls "$keysfolder" > "${scratchfile}.user" if [[ -s "${scratchfile}.user" ]]; then haveuser="true" fi if [[ "$haveuser" != "" && "$username" != "" ]]; then user="$(grep "^$username$" "${scratchfile}.user")" else user="" fi # If we don't have an action specified - list all keys if [[ "$param1" = "" ]]; then param1="list" fi # Check for tools if [[ "$param1" != "list" ]]; then keygen=$(filecheck -x ssh-keygen) puttygen=$(filecheck -x puttygen) openssl=$(filecheck -x openssl) base64=$(filecheck -x base64) md5sum=$(filecheck -x md5sum) iconv=$(filecheck -x iconv) oci=$(filecheck -x oci) jq=$(filecheck -x jq) # Check for openssl version if [[ "$openssl" != "" ]]; then osslvers=$($openssl version | head -n 1 | cut -d' ' -f2) osslmain=$(echo "$osslvers" | cut -d'.' -f1) osslopt=$($openssl pkey -help 2>&1 | grep "\-traditional") if [[ "$osslopt" != "" ]]; then osslopt="-traditional" fi fi # Check if we are on a fips enabled platform result=$(sysctl crypto.fips_enabled 2>/dev/null) if [[ "$result" = "crypto.fips_enabled = 1" ]]; then fipsenabled="true" else fipsenabled="false" fi fi ### Debug #echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" #files=$(ls *) #for keyfile in ${files}; do # CleanupKeyFile "$keyfile" keyfile # result=$(KeyType "$keyfile") # echo "Keyfile '$keyfile': '$result'" # stat=$? # if [ $stat -eq 0 ]; then # ls -als keyfile 2>&1 # cat keyfile 2>&1 # fi # rm -f keyfile # echo "========================================================================================" #done #echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" #i=1 #files=$(ls *) #for keyfile in ${files}; do # CleanupKeyFile "$keyfile" keyfile # echo "Keyfile '$keyfile'" # stat=$? # if [ $stat -eq 0 ]; then # user="User$i" # echo "User: '$user'" # CheckPlainRSAKey "$user" keyfile "Georg" # stat=$? # if [ $stat -eq 0 ]; then # ls -als keyfile* 2>&1 # cat keyfile 2>&1 # cat keyfile.enc 2>&1 # cat keyfile.pub 2>&1 # else # echo "No private key found" # fi # fi # rm -f keyfile # filecheck -rm keyfile.enc # filecheck -rm keyfile.pub # echo "========================================================================================" # (( i++ )) #done #echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" #i=1 #files=$(ls *) #for keyfile in ${files}; do # echo "Keyfile '$keyfile'" # user="User$i" # echo "User: '$user'" # ImportPrivateKey "$keyfile" "$user" "" # stat=$? # if [ $stat -ne 0 ]; then # echo "Error while importing key" # fi # echo "========================================================================================" # (( i++ )) #done #Cleanup #exit ### End Debug case "$param1" in secret) if [[ "$username" = "" ]]; then exitcode=2 errormsg $exitcode "($progstr) No username specified." else if [[ "$param2" = "" ]]; then exitcode=5 errormsg $exitcode "($progstr) No secretfile specified." else if [[ ! -r "$param2" ]]; then # Create secret file with different keys CreateSecret "$param2" else exitcode=7 errormsg $exitcode "($progstr) File '$param2' already exists." fi fi fi ;; check) if [[ "$param2" = "" ]]; then exitcode=5 errormsg $exitcode "($progstr) No keyfile specified." else if [[ -r "$param2" ]]; then # Check keys result=$(KeyType "$param2") echo "$result" else exitcode=7 errormsg $exitcode "($progstr) Could not read keyfile '$param2'." fi fi ;; import) if [[ "$username" = "" ]]; then exitcode=2 errormsg $exitcode "($progstr) No username specified." else if [[ "$param2" = "" ]]; then exitcode=5 errormsg $exitcode "($progstr) No keyfile specified." else if [[ -r "$param2" ]]; then if [[ "$username" = "$user" ]]; then # Keys already exist for user if [[ "$force" = true ]]; then exitcode=0 else confirm "Keys for username '$username' already exist. Do you want save current keys in stage 'previous'?" --yes "y/[yes]" --no "n/no" exitcode=$? fi if (( exitcode == 0 )); then if [[ -d "${keysfolder}/${username}/previous" ]]; then rm -fR "${keysfolder}/${username}/previous" fi mv -f "${keysfolder}/$username" "${keysfolder}/previous" else rm -fR "${keysfolder}/$username" fi fi # Import key if (( exitcode == 0 )); then ImportPrivateKey "$param2" "$username" "$passphrase" stat=$? if (( stat == 0 )); then if [[ -d "${keysfolder}/previous" ]]; then mv -f "${keysfolder}/previous" "${keysfolder}/${username}/previous" fi else exitcode=8 errormsg $exitcode "($progstr) Could not import key: '$param2'." "Reason: $stat" if [[ -d "${keysfolder}/previous" ]]; then mv -f "${keysfolder}/previous" "${keysfolder}/$username" fi fi if (( exitcode == 0 )); then if [[ -d "${keysfolder}/previous" ]]; then mv -f "${keysfolder}/previous" "${keysfolder}/${username}/previous" fi if [[ -d "${keysfolder}/${username}/previous/beta" ]]; then mv -f "${keysfolder}/${username}/previous/beta" "${keysfolder}/$username" fi else if [[ -d "${keysfolder}/previous" ]]; then mv -f "${keysfolder}/previous" "${keysfolder}/$username" printf "Originsl keys restored.\n" fi fi else exitcode=3 fi else exitcode=7 errormsg $exitcode "($progstr) Could not read keyfile '$param2'." fi fi fi ;; change) if [[ "$username" = "" ]]; then exitcode=2 errormsg $exitcode "($progstr) No username specified." else if [[ "$username" = "$user" ]]; then # Keys exist for user if [[ "$param2" != "" ]]; then pwlen=$(echo -n "$param2" | wc -m) if (( pwlen < 5 )); then exitcode=1 errormsg $exitcode "($progstr) New passphrase has to have at least 5 chars. Leaving keys unchanged." fi fi if (( exitcode == 0 )); then ChangePassphrase "$username" "$passphrase" "$param2" exitcode=$? if (( exitcode == 0 )); then DeleteFromPassphraseFile "$username" WriteToPassphraseFile "$username" "$param2" printf "Passphrase in keys changed for user '$username'.\n" else exitcode=3 errormsg $exitcode "($progstr) Could not change passphrase. Leaving keys unchanged." fi fi else exitcode=4 errormsg $exitcode "($progstr) Keys do not exist for username '$username'." fi fi ;; delete) if [[ "$username" = "" ]]; then exitcode=2 errormsg $exitcode "($progstr) No username specified." else if [[ "$username" = "$user" ]]; then # Keys exist for user if [[ "$force" = true ]]; then exitcode=0 else confirm "Are you sure to delete all keys for username '$username'?" --yes "y/yes" --no "n/[no]" exitcode=$? fi if (( exitcode == 0 )); then rm -fR "${keysfolder}/$username" DeleteFromPassphraseFile "$username" else exitcode=3 printf "Leaving keys unchanged.\n" fi else exitcode=4 errormsg $exitcode "($progstr) Keys do not exist for username '$username'." fi fi ;; create) if [[ "$username" = "" ]]; then exitcode=2 errormsg $exitcode "($progstr) No username specified." else if [[ "$username" = "$user" ]]; then # Keys already exist for user if [[ "$force" = true ]]; then exitcode=0 else confirm "Keys for username '$username' already exist. Do you want save current keys in stage 'previous'?" --yes "y/[yes]" --no "n/no" exitcode=$? fi if (( exitcode == 0 )); then if [[ -d "${keysfolder}/${username}/previous" ]]; then rm -fR "${keysfolder}/${username}/previous" fi mv -f "${keysfolder}/$username" "${keysfolder}/previous" else rm -fR "${keysfolder}/$username" fi fi if (( exitcode == 0 )); then CreateKeys "$username" "$passphrase" exitcode=$? if (( exitcode == 0 )); then if [[ -d "${keysfolder}/previous" ]]; then mv -f "${keysfolder}/previous" "${keysfolder}/${username}/previous" fi if [[ -d "${keysfolder}/${username}/previous/beta" ]]; then mv -f "${keysfolder}/${username}/previous/beta" "${keysfolder}/$username" fi else errormsg $exitcode "($progstr) Could not create keys for user '$username'." if [[ -d "${keysfolder}/previous" ]]; then mv -f "${keysfolder}/previous" "${keysfolder}/$username" fi fi else exitcode=3 fi fi ;; push | add | subtract) if [[ "$username" = "" ]]; then exitcode=2 errormsg $exitcode "($progstr) No username specified." else if [[ "$username" = "$user" ]]; then # Keys exist for user PushKeys "$username" "$param2" "$param1" exitcode=$? else exitcode=4 errormsg $exitcode "($progstr) Keys do not exist for username '$username'." fi fi ;; install) if [[ "$USER" = "root" ]]; then InstallGetAuthkeys stat=$? else stat=5 fi if (( stat > 0 )); then exitcode=10 errormsg $exitcode "($progstr) Could not install '$getauthkeys'." "Code: $stat" else printf "\nScript '$getauthkeys' installed in '$sshdconfig'.\n" fi ;; remove) if [[ "$USER" = "root" ]]; then RemoveGetAuthkeys stat=$? else stat=5 fi if (( stat > 0 )); then exitcode=10 errormsg $exitcode "($progstr) Could not remove '$getauthkeys'." "Code: $stat" else printf "\nScript '$getauthkeys' removed from '$sshdconfig'.\n" fi ;; activate) if [[ "$username" = "" ]]; then exitcode=2 errormsg $exitcode "($progstr) No username specified." else if [[ "$param2" = "" ]]; then exitcode=2 errormsg $exitcode "($progstr) No stage specified. Please choose between 'latest' and 'previous'." else param2="$(ToLower "$param2")" case "$param2" in latest | previous) ActivateStage "$username" "$param2" stat=$? if (( stat != 0 )); then exitcode=2 errormsg $exitcode "($progstr) Stage '$param2' could not be activated." fi ;; *) exitcode=2 errormsg $exitcode "($progstr) Unknown stage '$param2' specified. Please choose between 'latest' and 'previous'." esac fi fi ;; list) if [[ "$haveuser" != "" ]]; then if [[ "$param2" = "" ]]; then param2="table" fi if [[ "$username" != "" ]]; then if [[ "$username" != "$user" ]]; then exitcode=4 errormsg $exitcode "($progstr) Keys do not exist for username '$username'." fi else if [[ "$param2" = "table" ]]; then printf "Please wait a few seconds." fi fi if (( exitcode == 0 )); then printf "username\tstage\tencrypted\tfingerprint\tprivate-key\n" > "${scratchfile}.keys" if [[ "$username" != "" ]]; then if [[ "$username" = "$user" ]]; then WriteToKeylist "$username" "current" WriteToKeylist "$username" "latest" WriteToKeylist "$username" "previous" else exitcode=4 errormsg $exitcode "($progstr) Keys do not exist for username '$username'." fi else # List all user while read -r username; do WriteToKeylist "$username" "current" WriteToKeylist "$username" "latest" WriteToKeylist "$username" "previous" done < "${scratchfile}.user" fi # Print result if [[ "$param2" != "table" ]]; then print-table --import "${scratchfile}.keys" --output "$param2" else # Print all keys print-table --import "${scratchfile}.keys" --output table --bold "current" > "$scratchfile" if [[ "$username" = "" ]]; then printf "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b \n" else printf "\n" fi cat "$scratchfile" printf "\n" fi fi else printf "No keys created yet.\n" fi ;; show) if [[ "$username" = "" ]]; then exitcode=2 errormsg $exitcode "($progstr) No username specified." else result=$(echo "$username" | grep '^ocid1.vaultsecret.') if [[ "$result" != "" ]]; then # OCI vault secret was specified - get key from vault CallOCI secrets secret-bundle get --secret-id "$username" > "$scratchfile" result=$(filecheck -sl "$scratchfile") if [[ "$result" != "" ]]; then encoding=$(cat "$scratchfile" | browse-json secretBundleContent/contentType --select 1 --quiet) if [[ "$encoding" = "BASE64" ]]; then cat "$scratchfile" | browse-json secretBundleContent/content --select 1 --quiet | $base64 -d > "${scratchfile}.keys" else cat "$scratchfile" | browse-json secretBundleContent/content --select 1 --quiet > "${scratchfile}.keys" fi result=$(head -c 1 "${scratchfile}.keys") if [[ "$result" = '{' && "$jq" != "" && "$param2" != "" ]]; then # Found json content case "$param2" in rsa | priv | ssh) param2="privateKeySsh" ;; api | pem) param2="publicKey" ;; pub | public) param2="publicKeySsh" ;; pk8 | pkcs8) param2="privateKey" ;; ppk | putty) param2="puttyKey" ;; pp3) param2="puttyKeyV3" ;; fp | fingerprint) param2="fingerprint" ;; *) exitcode=1 errormsg $exitcode "($progstr) Unknown format '$param2'." esac if (( exitcode == 0 )); then $jq -r ".$param2" "${scratchfile}.keys" > "$scratchfile" result=$(cat "$scratchfile") if [[ "$result" != "null" ]]; then if [[ "$param2" = "fingerprint" ]]; then cat "$scratchfile" else printf "\n" cat "$scratchfile" printf "\n" fi else exitcode=4 fi fi else printf "\n" cat "${scratchfile}.keys" printf "\n\n" fi else exitcode=4 errormsg $exitcode "($progstr) Key does not exist for ocid '$username' or not allowed to read." fi filecheck -rm "$scratchfile" filecheck -rm "${scratchfile}.keys" else if [[ "$username" = "$user" ]]; then if [[ "$param2" != "" ]]; then # Keys exist for user case "$param2" in rsa | priv | ssh) keyfile="${keysfolder}/${username}/$keybasename" if [[ -r "$keyfile" ]]; then printf "\n" cat "$keyfile" printf "\n" else exitcode=4 fi ;; api | pem) keyfile="${keysfolder}/${username}/${keybasename}.pem" if [[ -r "$keyfile" ]]; then printf "\n" cat "$keyfile" printf "\n" else keyfile="${keysfolder}/${username}/${apibasename}.pem" if [[ -r "$keyfile" ]]; then printf "\n" cat "$keyfile" printf "\n" else exitcode=4 fi fi ;; pub | public) keyfile="${keysfolder}/${username}/${keybasename}.pub" if [[ -r "$keyfile" ]]; then printf "\n" cat "$keyfile" printf "\n" else exitcode=4 fi ;; pk8 | pkcs8) keyfile="${keysfolder}/${username}/${keybasename}.pk8" if [[ -r "$keyfile" ]]; then printf "\n" cat "$keyfile" printf "\n" else exitcode=4 fi ;; ppk | putty) keyfile="${keysfolder}/${username}/${keybasename}.ppk" if [[ -r "$keyfile" ]]; then printf "\n" cat "$keyfile" printf "\n" else keyfile="${keysfolder}/${username}/${puttybasename}.ppk" if [[ -r "$keyfile" ]]; then printf "\n" cat "$keyfile" printf "\n" else exitcode=4 fi fi ;; pp3) keyfile="${keysfolder}/${username}/${puttybasename}.pp3" if [[ -r "$keyfile" ]]; then printf "\n" cat "$keyfile" printf "\n" else exitcode=4 fi ;; fp | fingerprint) fingerprint="$(GetFingerprintFromInfo "${keysfolder}/${username}/$infostr")" if [[ "$fingerprint" != "" ]]; then echo "$fingerprint" else keyfile="${keysfolder}/${username}/${apibasename}.pem" if [[ -r "$keyfile" ]]; then fingerprint=$(GetFingerprint "$keyfile") if [[ "$fingerprint" != "" ]]; then printf '\n%s\n\n' "$fingerprint" else exitcode=4 fi else keyfile="${keysfolder}/${username}/${keybasename}.pk8" if [[ ! -r "$keyfile" ]]; then keyfile="${keysfolder}/${username}/$keybasename" fi if [[ -r "$keyfile" ]]; then encrypted=$(grep "ENCRYPTED" "$keyfile") if [[ "$encrypted" = "" ]]; then fingerprint=$(GetFingerprint "$keyfile" "") else mypassphrase="$(GetFromPassphraseFile "$username")" if [[ "$mypassphrase" = "" ]]; then mypassphrase="$passphrase" fi fingerprint=$(GetFingerprint "$keyfile" "$mypassphrase") fi if [[ "$fingerprint" != "" ]]; then printf '\n%s\n\n' "$fingerprint" else exitcode=4 fi else exitcode=4 fi fi fi ;; *) exitcode=1 errormsg $exitcode "($progstr) Unknown format '$param2'." esac else exitcode=5 errormsg $exitcode "($progstr) No format specified." fi else exitcode=4 errormsg $exitcode "($progstr) Keys do not exist for username '$username'." fi fi fi ;; *) errormsg 1 "($progstr) Unknown action '$param1'." esac # Cleanup and exit Cleanup exit $exitcode