Hi everyone! I've been using swift-crypto in server-side and cross-platform projects, so here's a quick overview with patterns I've found useful.
What is swift-crypto?
swift-crypto is Apple's open-source version of the CryptoKit API.
- On Apple platforms,
import Cryptore-exports the system CryptoKit, so you get the OS implementation. - On Linux, Windows, and Android, it uses a vendored copy of BoringSSL behind the same API.
This means one codebase with no #if canImport(CryptoKit) branches for most use cases.
Installation
.package(url: "https://github.com/apple/swift-crypto.git", "3.0.0"..<"5.0.0"),
// target:
.product(name: "Crypto", package: "swift-crypto"),
1. Hashing
import Crypto
let digest = SHA256.hash(data: Data("hello".utf8))
let hex = digest.map { String(format: "%02x", $0) }.joined()
Insecure.MD5 and Insecure.SHA1 exist only for legacy interop. The namespace is a deliberate warning.
2. HMAC (e.g. webhook signature verification)
let key = SymmetricKey(data: Data(secret.utf8))
let isValid = HMAC<SHA256>.isValidAuthenticationCode(
receivedMAC, authenticating: body, using: key
)
Use isValidAuthenticationCode rather than comparing bytes with ==, because it compares in constant time.
3. Authenticated encryption (AES-GCM / ChaChaPoly)
let key = SymmetricKey(size: .bits256)
let sealed = try AES.GCM.seal(plaintext, using: key)
let wire = sealed.combined! // nonce + ciphertext + tag
let box = try AES.GCM.SealedBox(combined: wire)
let decrypted = try AES.GCM.open(box, using: key)
seal generates a random nonce for you. If you supply your own, never reuse a nonce with the same key. ChaChaPoly has an identical API and is a good choice on hardware without AES acceleration.
4. Signatures (Curve25519 / P256)
let signingKey = Curve25519.Signing.PrivateKey()
let signature = try signingKey.signature(for: message)
let ok = signingKey.publicKey.isValidSignature(signature, for: message)
P256/P384/P521 keys support pemRepresentation and derRepresentation, which helps when interoperating with OpenSSL, JWTs (ES256), or other languages.
5. Key agreement + HKDF
let alice = P256.KeyAgreement.PrivateKey()
let bob = P256.KeyAgreement.PrivateKey()
let shared = try alice.sharedSecretFromKeyAgreement(with: bob.publicKey)
let sessionKey = shared.hkdfDerivedSymmetricKey(
using: SHA256.self,
salt: salt,
sharedInfo: Data("my-app v1 session".utf8),
outputByteCount: 32
)
Never use the raw SharedSecret directly as a key. Always run it through HKDF.
_CryptoExtras
The package also ships _CryptoExtras, which has APIs that aren't in CryptoKit, such as RSA (for legacy interop) and AES-CBC/CTR. The underscore means the API may change, so pin versions carefully.
Tips
- Keep keys out of source code. Load them from the Keychain, a secrets manager, or environment variables.
- Use
SymmetricKey(size:)for random keys rather than building your own fromData. - Prefer the high-level AEAD APIs over hand-rolled encrypt-then-MAC.
- Test on both macOS and Linux in CI. The API is the same, but the backends differ.
Happy to hear how others are using swift-crypto, especially on server-side or Android. Feedback and corrections are welcome!