Safe Libraries
Pick the cryptography library once per language and stay with it. The right libraries have constant-time primitives, are audited, and present misuse-resistant APIs. Roll-your-own crypto, deprecated wrappers, and obscure ports are how applications grow a vulnerability they cannot patch without a migration.
The right crypto library per ecosystem
EXAMPLE
# ===== Python =====
# pip install cryptography
# Use:
# AES-GCM: cryptography.hazmat.primitives.ciphers.aead.AESGCM
# X25519: cryptography.hazmat.primitives.asymmetric.x25519
# Ed25519: cryptography.hazmat.primitives.asymmetric.ed25519
# HKDF: cryptography.hazmat.primitives.kdf.hkdf.HKDF
# Argon2id: argon2-cffi (separate package)
# Constant time compare: hmac.compare_digest
# Avoid: pycrypto (unmaintained), Crypto.* in old codebases, hand-rolled ChaCha.
# ===== Node.js =====
# Built-in: node:crypto (covers most needs)
# AES-GCM: createCipheriv('aes-256-gcm', ...)
# X25519/Ed25519: generateKeyPair / sign / verify
# HKDF: crypto.hkdfSync('sha256', ...)
# Argon2id: argon2 (npm package)
# timingSafeEqual for compares
# When you need libsodium semantics: npm i libsodium-wrappers-sumo
# Avoid: crypto-js (browser-only string toys), js-sha256 hand-rolls.
# ===== Go =====
# Standard library covers most:
# crypto/aes + crypto/cipher.NewGCM
# crypto/ed25519, golang.org/x/crypto/curve25519
# crypto/hkdf (Go 1.22+)
# golang.org/x/crypto/argon2
# crypto/subtle.ConstantTimeCompare
# When you want a higher-level API: github.com/google/tink-go.
# Avoid: rolling your own AES mode; importing 'unsafe' crypto packages from obscure repos.
# ===== Rust =====
# Use one of:
# ring — fast, audited, opinionated; default for new code
# rustls (TLS) — pairs with ring; ships in Hyper, Reqwest
# aes-gcm, chacha20poly1305 — RustCrypto org crates (also good)
# ed25519-dalek — Ed25519 signing
# x25519-dalek — X25519 ECDH
# argon2 — RustCrypto argon2
# Avoid: openssl bindings for greenfield code (linking pain, larger attack surface).
# ===== PHP =====
# Built-in:
# sodium_* (libsodium wrappers, since PHP 7.2) — preferred
# password_hash / password_verify (argon2id by default in modern PHP)
# hash_equals for constant-time comparisons
# openssl_* — fine when sodium does not cover the use case
# Avoid: hand-rolling AES-CBC; phpseclib for new code unless required for legacy SSH.
# ===== Java =====
# Bouncy Castle (org.bouncycastle) for everything not in JDK.
# JDK itself ships AES/RSA/EC; default Cipher.getInstance('AES/GCM/NoPadding').
# Argon2: bcprov-jdk18on or de.mkammerer:argon2-jvm.
# Avoid: SecureRandom defaults that differ across JVMs — explicit instance per vendor.
# ===== .NET =====
# System.Security.Cryptography for AES-GCM, ECDH, ECDsa.
# Konscious.Security.Cryptography for Argon2id.
# Avoid: legacy RijndaelManaged (use Aes/Aes.Create()); DES; MD5 for anything but checksums.
# ===== Universal advice =====
# 1) Prefer the highest-level primitive available (AEAD over raw cipher,
# password hasher over hash function, HKDF over manual derivation).
# 2) NEVER ship cryptography you wrote yourself if you can avoid it.
# 3) Crypto libraries get audited; auditor reports are public — read them
# before betting your product on a crate / package.
# 4) Pin the version. Patch quickly. CVE disclosures in crypto libraries
# are usually critical and quick to exploit.
Why it matters
When choosing a crypto library, the published audit is the trust signal. ring, rustls, libsodium, BoringSSL, BouncyCastle — all have one. A library without a public audit report is not necessarily broken, but it is asking you to trust without verification, and crypto is the worst place to make that trade.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// Pick one, learn it well. // JS/TS: WebCrypto, libsodium-wrappers, node:crypto, jose, @simplewebauthn // Python: cryptography, pyca/nacl, jwt (PyJWT) // Go: crypto/* stdlib, x/crypto, jose-go // Rust: ring, RustCrypto, libsodium-rsTry it Yourself »
Discussion
Loading…