How credctl Uses the macOS Secure Enclave for Cloud Credentials | credctl Skip to content
How credctl Uses the macOS Secure Enclave for Cloud Credentials
Mar 7, 2026<br>Mat Evans
This post explains the architecture of credctl — a CLI tool that replaces long-lived AWS access keys with short-lived, hardware-bound credentials using the macOS Secure Enclave. For the problem statement and product overview, read Eliminating Plaintext Cloud Keys. This post is the “how.”
What the Secure Enclave Actually Is<br>Section titled “What the Secure Enclave Actually Is”
The Secure Enclave is a hardware security subsystem isolated from the main processor. On Apple Silicon Macs (M1–M4), it’s a dedicated core within the SoC. On Intel Macs with T2, it’s a separate chip. Either way, it has its own boot process, its own encrypted memory, and its own secure storage.
What matters for credctl:
Key generation happens inside the hardware. When you ask the Secure Enclave to create a key pair, the private key is generated inside the chip and never leaves it. There is no API to export it. There is no memory region to read it from. The hardware enforces this — it’s not a software policy.
Signing happens inside the hardware. You send data to the Secure Enclave, it signs it with the private key, and returns the signature. The private key is never exposed to the application, the operating system, or the CPU.
User presence verification. The Secure Enclave gates key operations behind biometric (Touch ID) or passcode verification. Even with root access, a process cannot silently use a Secure Enclave key without the user’s explicit approval.
FIPS 140-2 validated. Apple’s corecrypto module, which underpins Secure Enclave operations, has FIPS 140-2 validation. This is the same level of certification used by government and financial systems.
The Secure Enclave supports ECDSA with the P-256 curve (also known as secp256r1 or prime256v1). This maps directly to the ES256 algorithm in JWTs — which is exactly what credctl needs.
credctl’s Architecture<br>Section titled “credctl’s Architecture”
credctl is a Go CLI that bridges the Secure Enclave’s cryptographic capabilities to cloud provider credential APIs. The architecture has five components:
graph TB
subgraph "credctl CLI"
CMD[Command Layercobra]
CFG[Config Manager~/.credctl/config.json]
ENC[Enclave Interfacecgo → Security.framework]
JWT[JWT BuilderES256 tokens]
STS_C[AWS STS ClientAssumeRoleWithWebIdentity]
end
subgraph "Hardware"
SE[Secure EnclaveECDSA P-256]
end
subgraph "AWS"
STS[AWS STS]
OIDC_P[OIDC ProviderS3 + CloudFront]
end
CMD --> CFG
CMD --> ENC
CMD --> JWT
CMD --> STS_C
ENC --> SE
JWT --> ENC
STS_C --> STS
STS --> OIDC_P
CFG CMD --> ENC CMD --> JWT CMD --> STS_C ENC --> SE JWT --> ENC STS_C --> STS STS --> OIDC_P">
The Enclave Interface<br>Section titled “The Enclave Interface”
This is the critical piece. The Secure Enclave is accessed through Apple’s Security framework, which is an Objective-C/C API. Go doesn’t call C APIs natively, so credctl uses cgo — Go’s foreign function interface for C code.
The Enclave interface abstracts hardware operations:
type Enclave interface {
Available() bool
GenerateKey(tag string) (*ecdsa.PublicKey, error)
LoadKey(tag string) (*ecdsa.PublicKey, error)
DeleteKey(tag string) error
Sign(tag string, data []byte) ([]byte, error)
On macOS, the implementation calls Security framework functions via cgo:
GenerateKey calls SecKeyCreateRandomKey with kSecAttrTokenIDSecureEnclave to create an ECDSA P-256 key pair inside the Secure Enclave. The key is tagged with a unique identifier so it can be loaded later. The function returns the public key — the private key stays in hardware.
Sign calls SecKeyCreateSignature with the kSecKeyAlgorithmECDSASignatureMessageX962SHA256 algorithm. The Secure Enclave signs the data and returns the DER-encoded signature. By default (--biometric=any), this triggers a Touch ID prompt with passcode fallback. This behaviour is configurable via the --biometric flag at credctl init time.
LoadKey retrieves a previously generated key by its tag using SecItemCopyMatching.
Build tags dispatch the implementation:
darwin.go — the real Secure Enclave implementation via cgo
other.go — a stub that returns clear errors on unsupported platforms
This cgo dependency is a deliberate architectural choice. It means credctl can’t be cross-compiled with a simple go build — it needs Xcode and an Apple provisioning profile. That’s a trade-off: harder distribution in exchange for real hardware security. The binary is distributed as a code-signed, notarised macOS app bundle.
The OIDC Flow<br>Section titled “The OIDC Flow”
This is where the Secure Enclave connects to AWS. The flow uses OIDC (OpenID Connect) federation — specifically, AWS STS AssumeRoleWithWebIdentity. Here’s how the pieces fit together:
Setup (one-time)<br>Section titled “Setup (one-time)”
credctl init generates the key pair in the Secure Enclave. It...