Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Keychain Services from Terminal

The macOS Keychain is a secure, encrypted database that stores passwords, certificates, encryption keys, and secure notes. While most users interact with Keychain through the Keychain Access GUI application, the security command-line tool provides full access to Keychain services for automation, scripting, and administrative tasks.

Understanding Keychains

macOS maintains several keychains by default:

┌─────────────────────────────────────────────────────────────────┐
│                    Keychain Hierarchy                            │
├─────────────────────────────────────────────────────────────────┤
│  System Keychain                                                 │
│  /Library/Keychains/System.keychain                             │
│  - System-wide certificates and passwords                        │
│  - Requires admin authentication                                 │
├─────────────────────────────────────────────────────────────────┤
│  System Roots                                                    │
│  /System/Library/Keychains/SystemRootCertificates.keychain      │
│  - Apple's trusted root certificates                             │
│  - Protected by SIP                                              │
├─────────────────────────────────────────────────────────────────┤
│  Login Keychain                                                  │
│  ~/Library/Keychains/login.keychain-db                          │
│  - User's personal passwords and certificates                    │
│  - Unlocked at login by default                                  │
├─────────────────────────────────────────────────────────────────┤
│  Local Items (iCloud Keychain)                                   │
│  ~/Library/Keychains/[UUID]/                                    │
│  - Synced across devices via iCloud                              │
│  - Protected by Secure Enclave                                   │
└─────────────────────────────────────────────────────────────────┘

The security Command

Keychain Management

# List all keychains in search list
$ security list-keychains
    "/Users/david/Library/Keychains/login.keychain-db"
    "/Library/Keychains/System.keychain"

# Show default keychain
$ security default-keychain
    "/Users/david/Library/Keychains/login.keychain-db"

# Set default keychain
$ security default-keychain -s ~/Library/Keychains/login.keychain-db

# Show login keychain
$ security login-keychain
    "/Users/david/Library/Keychains/login.keychain-db"

# Get keychain info
$ security show-keychain-info ~/Library/Keychains/login.keychain-db
Keychain "/Users/david/Library/Keychains/login.keychain-db"
    lock-on-sleep
    timeout=21600s

Creating and Deleting Keychains

# Create a new keychain
$ security create-keychain -p "password" ~/Library/Keychains/custom.keychain

# Create with password prompt (more secure)
$ security create-keychain ~/Library/Keychains/custom.keychain
password for new keychain:

# Add to search list
$ security list-keychains -s ~/Library/Keychains/login.keychain-db \
    ~/Library/Keychains/custom.keychain

# Delete a keychain
$ security delete-keychain ~/Library/Keychains/custom.keychain

Locking and Unlocking

# Lock a keychain
$ security lock-keychain ~/Library/Keychains/login.keychain-db

# Unlock a keychain (prompts for password)
$ security unlock-keychain ~/Library/Keychains/login.keychain-db

# Unlock with password (for scripts - be careful with password exposure)
$ security unlock-keychain -p "password" ~/Library/Keychains/login.keychain-db

# Lock all keychains
$ security lock-keychain -a

# Set keychain to lock after timeout
$ security set-keychain-settings -t 3600 ~/Library/Keychains/login.keychain-db
# Locks after 1 hour of inactivity

# Set to lock on sleep
$ security set-keychain-settings -l ~/Library/Keychains/login.keychain-db

Managing Passwords

Finding Passwords

# Find a generic password
$ security find-generic-password -a "username" -s "service" \
    ~/Library/Keychains/login.keychain-db

# Output includes metadata:
# keychain: "/Users/david/Library/Keychains/login.keychain-db"
# class: "genp"
# attributes:
#     "acct"<blob>="username"
#     "svce"<blob>="service"
#     ...

# Get just the password (to stdout)
$ security find-generic-password -a "username" -s "service" -w
mypassword

# Find internet password (website credentials)
$ security find-internet-password -a "username" -s "example.com" -w

# Find by label
$ security find-generic-password -l "My Label" -w

# Find with domain and protocol
$ security find-internet-password -s "github.com" -r "htps" -w

Adding Passwords

# Add a generic password
$ security add-generic-password \
    -a "username" \
    -s "service-name" \
    -w "password" \
    -l "Human Readable Label" \
    ~/Library/Keychains/login.keychain-db

# Add without specifying password (prompts)
$ security add-generic-password \
    -a "username" \
    -s "service-name" \
    -l "My Service" \
    -T "" \
    ~/Library/Keychains/login.keychain-db

# Add an internet password
$ security add-internet-password \
    -a "username" \
    -s "example.com" \
    -w "password" \
    -r "htps" \
    -l "Example Website" \
    ~/Library/Keychains/login.keychain-db

# Update existing password (use -U flag)
$ security add-generic-password \
    -a "username" \
    -s "service-name" \
    -w "new-password" \
    -U \
    ~/Library/Keychains/login.keychain-db

Password Options

# -a : Account name
# -s : Service name
# -w : Password (or -w to read from stdin)
# -l : Label
# -c : Creator (4-char code)
# -C : Type (4-char code)
# -D : Kind description
# -r : Protocol (htps, http, etc.)
# -p : Path
# -P : Port
# -j : Comment
# -T : Application path for ACL (use "" for no apps)
# -A : Allow all applications to access
# -U : Update existing item

Deleting Passwords

# Delete a generic password
$ security delete-generic-password -a "username" -s "service-name" \
    ~/Library/Keychains/login.keychain-db

# Delete an internet password
$ security delete-internet-password -a "username" -s "example.com" \
    ~/Library/Keychains/login.keychain-db

# Delete by label
$ security delete-generic-password -l "My Label" \
    ~/Library/Keychains/login.keychain-db

Managing Certificates

Viewing Certificates

# List all certificates in a keychain
$ security find-certificate -a ~/Library/Keychains/login.keychain-db

# List with details
$ security find-certificate -a -p ~/Library/Keychains/login.keychain-db
# Outputs in PEM format

# Find certificate by name
$ security find-certificate -c "Developer ID" -a

# Find certificate by email
$ security find-certificate -e "your@email.com" -a

# Show certificate details
$ security find-certificate -c "Developer ID" -p | openssl x509 -noout -text

# Count certificates
$ security find-certificate -a | grep -c "keychain:"

Adding Certificates

# Add a certificate to keychain
$ security add-certificates ~/path/to/certificate.cer

# Add to specific keychain
$ security add-certificates -k ~/Library/Keychains/login.keychain-db \
    ~/path/to/certificate.cer

# Add as trusted root (system keychain)
$ sudo security add-trusted-cert -d -r trustRoot \
    -k /Library/Keychains/System.keychain \
    ~/path/to/ca-certificate.cer

Managing Trust Settings

# Get trust settings for a certificate
$ security dump-trust-settings -d
# -d : Admin domain
# -s : System domain (read-only)

# Add trust setting
$ sudo security add-trusted-cert -d -r trustRoot \
    -p ssl -p basic \
    -k /Library/Keychains/System.keychain \
    ~/path/to/certificate.cer

# Remove trust setting
$ sudo security remove-trusted-cert -d ~/path/to/certificate.cer

# Verify certificate trust
$ security verify-cert -c ~/path/to/certificate.cer

Exporting Certificates

# Export certificate to PEM
$ security find-certificate -c "Certificate Name" -p > cert.pem

# Export certificate and key as PKCS12
$ security export -k ~/Library/Keychains/login.keychain-db \
    -t identities \
    -f pkcs12 \
    -o identity.p12

# Export specific identity
$ security find-identity -v -p codesigning
# Note the SHA-1 hash
$ security export -k ~/Library/Keychains/login.keychain-db \
    -t identities \
    -f pkcs12 \
    -o identity.p12 \
    -P "export-password"

Importing Certificates and Keys

# Import PKCS12 file
$ security import ~/path/to/identity.p12 \
    -k ~/Library/Keychains/login.keychain-db \
    -f pkcs12 \
    -P "password"

# Import PEM certificate
$ security import ~/path/to/certificate.pem \
    -k ~/Library/Keychains/login.keychain-db \
    -f pem

# Import with application access
$ security import ~/path/to/identity.p12 \
    -k ~/Library/Keychains/login.keychain-db \
    -f pkcs12 \
    -P "password" \
    -A  # Allow all applications

Managing Identities (Certificate + Private Key)

# List all identities (cert + key pairs)
$ security find-identity -v
  1) ABC123... "Developer ID Application: Your Name (TEAM)"
  2) DEF456... "Apple Development: your@email.com (TEAM)"
     2 valid identities found

# List code signing identities
$ security find-identity -v -p codesigning

# List SSL identities
$ security find-identity -v -p ssl

# Available policies:
# basic, ssl, smime, eap, ipsec, ichat, codesigning, sysdefault, timestamping

Managing Keys

# Generate a symmetric key
$ security generate-key -a AES -b 256 -l "My Encryption Key"

# List keys
$ security dump-keychain ~/Library/Keychains/login.keychain-db | grep -A5 "class: key"

# Import a key
$ security import ~/path/to/private.key \
    -k ~/Library/Keychains/login.keychain-db \
    -f pem \
    -t priv

# Export key
$ security export -k ~/Library/Keychains/login.keychain-db \
    -t privKeys \
    -f pem \
    -o private.pem

Access Control Lists (ACLs)

Control which applications can access keychain items:

# View ACL for a keychain item
$ security dump-keychain -a ~/Library/Keychains/login.keychain-db | grep -A20 "access:"

# Add password with specific app access
$ security add-generic-password \
    -a "username" \
    -s "service" \
    -w "password" \
    -T /Applications/Safari.app/Contents/MacOS/Safari \
    ~/Library/Keychains/login.keychain-db

# Add with no app access (requires manual approval each time)
$ security add-generic-password \
    -a "username" \
    -s "service" \
    -w "password" \
    -T "" \
    ~/Library/Keychains/login.keychain-db

# Allow all applications (less secure)
$ security add-generic-password \
    -a "username" \
    -s "service" \
    -w "password" \
    -A \
    ~/Library/Keychains/login.keychain-db

# Set ACL partition list (for codesigning)
$ security set-key-partition-list -S apple-tool:,apple:,codesign: \
    -s -k "keychain-password" \
    ~/Library/Keychains/login.keychain-db

Practical Scripts

Store and Retrieve Secrets in Scripts

#!/bin/bash
# secret-manager.sh - Manage secrets via keychain

SERVICE="com.example.myapp"
KEYCHAIN="$HOME/Library/Keychains/login.keychain-db"

store_secret() {
    local name="$1"
    local value="$2"

    security add-generic-password \
        -a "$name" \
        -s "$SERVICE" \
        -w "$value" \
        -U \
        "$KEYCHAIN" 2>/dev/null

    if [[ $? -eq 0 ]]; then
        echo "Secret '$name' stored successfully"
    else
        echo "Failed to store secret"
        return 1
    fi
}

get_secret() {
    local name="$1"

    security find-generic-password \
        -a "$name" \
        -s "$SERVICE" \
        -w "$KEYCHAIN" 2>/dev/null
}

delete_secret() {
    local name="$1"

    security delete-generic-password \
        -a "$name" \
        -s "$SERVICE" \
        "$KEYCHAIN" 2>/dev/null

    if [[ $? -eq 0 ]]; then
        echo "Secret '$name' deleted"
    else
        echo "Secret not found"
        return 1
    fi
}

# Usage
case "$1" in
    set)
        store_secret "$2" "$3"
        ;;
    get)
        get_secret "$2"
        ;;
    delete)
        delete_secret "$2"
        ;;
    *)
        echo "Usage: $0 {set|get|delete} name [value]"
        exit 1
        ;;
esac

CI/CD Keychain Setup

#!/bin/bash
# ci-keychain-setup.sh - Setup keychain for CI/CD builds

set -e

KEYCHAIN_NAME="build.keychain"
KEYCHAIN_PATH="$HOME/Library/Keychains/$KEYCHAIN_NAME"
KEYCHAIN_PASSWORD="${CI_KEYCHAIN_PASSWORD:-build}"

# Create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"

# Set keychain settings (don't lock automatically)
security set-keychain-settings -t 3600 -u "$KEYCHAIN_PATH"

# Add to search list
security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | tr -d '"')

# Unlock keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"

# Import certificate (base64 encoded in environment variable)
echo "$SIGNING_CERTIFICATE_P12_BASE64" | base64 --decode > /tmp/cert.p12
security import /tmp/cert.p12 \
    -k "$KEYCHAIN_PATH" \
    -P "$SIGNING_CERTIFICATE_PASSWORD" \
    -A \
    -T /usr/bin/codesign \
    -T /usr/bin/security

rm /tmp/cert.p12

# Allow codesign to access key without prompt
security set-key-partition-list -S apple-tool:,apple:,codesign: \
    -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"

echo "CI keychain setup complete"

Backup Keychain Items

#!/bin/bash
# backup-keychain.sh - Export keychain items (certificates only)

BACKUP_DIR="$HOME/keychain-backup-$(date +%Y%m%d)"
mkdir -p "$BACKUP_DIR"

# Export certificates
security find-certificate -a -p > "$BACKUP_DIR/all-certificates.pem"

# Export identities (will prompt for keychain password)
security export \
    -k ~/Library/Keychains/login.keychain-db \
    -t identities \
    -f pkcs12 \
    -o "$BACKUP_DIR/identities.p12"

echo "Backup saved to $BACKUP_DIR"
echo "WARNING: Secure this backup - it contains sensitive cryptographic material"

Certificate Expiration Check

#!/bin/bash
# check-cert-expiry.sh - Check certificate expiration dates

echo "=== Certificate Expiration Report ==="
echo

security find-certificate -a -p 2>/dev/null | \
awk '/-----BEGIN CERTIFICATE-----/{cert=""} {cert=cert$0"\n"} /-----END CERTIFICATE-----/{print cert}' | \
while read -r cert_block; do
    if [[ -n "$cert_block" ]]; then
        subject=$(echo "$cert_block" | openssl x509 -noout -subject 2>/dev/null | sed 's/subject=//')
        expiry=$(echo "$cert_block" | openssl x509 -noout -enddate 2>/dev/null | sed 's/notAfter=//')

        if [[ -n "$expiry" ]]; then
            expiry_epoch=$(date -j -f "%b %d %H:%M:%S %Y %Z" "$expiry" "+%s" 2>/dev/null)
            now_epoch=$(date "+%s")
            days_left=$(( (expiry_epoch - now_epoch) / 86400 ))

            if [[ $days_left -lt 30 ]]; then
                status="WARNING"
            else
                status="OK"
            fi

            printf "%-10s %-80s %s (%d days)\n" "[$status]" "${subject:0:80}" "$expiry" "$days_left"
        fi
    fi
done

Security Considerations

Keychain Security Best Practices

  1. Lock keychain when not in use

    $ security lock-keychain
    
  2. Set auto-lock timeout

    $ security set-keychain-settings -t 300 ~/Library/Keychains/login.keychain-db
    
  3. Use specific application ACLs

    $ security add-generic-password -T /path/to/specific/app -a user -s service -w pass
    
  4. Never put passwords directly in scripts

    # Bad
    PASSWORD="hardcoded"
    
    # Good
    PASSWORD=$(security find-generic-password -s "myservice" -w)
    
  5. Create separate keychains for automation

    $ security create-keychain -p "$SECURE_PASSWORD" ~/Library/Keychains/automation.keychain
    

Summary

The security command provides complete Keychain access from the terminal:

TaskCommand
List keychainssecurity list-keychains
Unlock keychainsecurity unlock-keychain
Find passwordsecurity find-generic-password -s "service" -w
Add passwordsecurity add-generic-password -s "service" -a "user" -w "pass"
List certificatessecurity find-certificate -a
Import certificatesecurity import cert.p12 -k keychain
List identitiessecurity find-identity -v -p codesigning

Key concepts:

  • Login keychain stores user credentials
  • System keychain stores system-wide items
  • Passwords can be stored/retrieved without GUI
  • ACLs control which apps can access items
  • Always use keychain for secrets in scripts
# Quick reference
$ security list-keychains                                    # List keychains
$ security find-generic-password -s "service" -w             # Get password
$ security add-generic-password -s "service" -a "user" -w "pass"  # Store password
$ security find-identity -v -p codesigning                   # List signing identities
$ security unlock-keychain                                   # Unlock keychain

The Keychain provides secure credential storage that integrates with macOS authentication, making it the proper place to store secrets rather than configuration files or environment variables.