Cryptography Fundamentals: Symmetric, Asymmetric, and Hashing Techniques

Explore the fundamentals of cryptography: symmetric, asymmetric, and hashing techniques, including real-world examples and best practices.

Misunderstanding cryptography basics leads directly to insecure systems, broken authentication, and catastrophic data leaks. If you’re building or defending applications, you need a deep grasp of how symmetric encryption, asymmetric encryption, and cryptographic hashing work—and where they fit in real-world architectures. This guide breaks down each technique, shows real-world usage (and misusage), and gives you actionable checklists to audit your own systems for cryptographic resilience.

Cryptography Fundamentals: Symmetric, Asymmetric, and Hashing Techniques

Key Takeaways:

  • Master the differences and proper applications of symmetric, asymmetric, and hashing algorithms
  • Analyze real-world code showing secure and insecure cryptographic patterns
  • Apply actionable checklists to audit your cryptographic usage
  • Reference security standards (OWASP, NIST, CVE, CWE) for defensible implementations
  • Detect and monitor cryptographic misuse in deployed systems

Why Cryptography Matters in Modern Systems

Cryptography is the backbone of secure communications, protecting everything from online banking to encrypted messaging. The three pillars—symmetric encryption, asymmetric encryption, and cryptographic hashing—each solve different security problems:

You landed the Cloud Storage of the future internet. Cloud Storage Services Sesame Disk by NiHao Cloud

Use it NOW and forever!

Support the growth of a Team File sharing system that works for people in China, USA, Europe, APAC and everywhere else.
  • Symmetric encryption provides fast, bulk data confidentiality (e.g., encrypting a database field or file).
  • Asymmetric encryption enables secure key exchange and digital signatures (e.g., TLS handshakes, code signing).
  • Cryptographic hashing ensures data integrity and secures passwords (e.g., verifying file downloads, password storage).

Incorrectly applying these mechanisms exposes systems to devastating attacks such as:

  • Data breaches via weak or reused keys (CWE-320: Key Management Errors)
  • Privilege escalation through signature forgery (CWE-347: Improper Verification of Cryptographic Signature)
  • Credential theft via insecure password hashing (CWE-916: Use of Weak Hash Function for Password Storage)

OWASP’s Top Ten lists cryptographic failures as a leading risk. Modern compliance frameworks (such as NIST SP 800-53) mandate correct cryptographic controls. Auditing cryptography is not optional—it’s foundational.

  • Checklist: When reviewing any system, ask:
    • Which cryptography primitives are in use?
    • Are they applied according to best practices?
    • Are keys and secrets properly managed?

Symmetric Encryption: Principles, Use Cases, and Pitfalls

Symmetric encryption uses a single secret key for both encryption and decryption. It’s widely used for encrypting large volumes of data due to its speed and efficiency. Modern algorithms include AES (Advanced Encryption Standard) and ChaCha20.

How Symmetric Encryption Works

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import os

# Key and IV should be random and secret
key = os.urandom(32)  # AES-256 requires a 32-byte key
iv = os.urandom(16)   # 16-byte IV for AES

cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()
plaintext = b"Sensitive transaction data: $1000000"
# Pad plaintext to block size (not shown for brevity)
ciphertext = encryptor.update(plaintext) + encryptor.finalize()

This code encrypts a piece of sensitive data using AES in CBC mode. In a real system, you must handle padding and securely manage the key and iv (initialization vector).

Strengths and Weaknesses

  • Strengths: Extremely fast, suitable for encrypting files, databases, or communication streams (VPNs, TLS symmetric phase).
  • Weaknesses: Key distribution is hard—if the key is exposed, all data is compromised. No built-in authentication (unless using an AEAD mode like GCM).

Common Mistakes (with Example)

# BAD: Reusing IVs leads to ciphertext leakage (CVE-2017-15361)
iv = b"\x00" * 16  # Static IV - extremely insecure!
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())

Reusing IVs (or using static IVs) can allow attackers to infer relationships between plaintexts. Always use a unique, random IV per message.

  • Checklist:
    • Are you using a modern cipher (AES, ChaCha20)?
    • Are keys generated securely and stored out of code/configs?
    • Are IVs/nonces random and unique per encryption?
    • Are you using authenticated encryption modes (GCM, CCM, or ChaCha20-Poly1305)?

For further reading, see the CWE-329: Not Using a Random IV with CBC Mode.

Asymmetric Encryption: Key Exchange and Digital Signatures

Asymmetric encryption (public-key cryptography) uses a key pair: one public, one private. Algorithms like RSA and Elliptic Curve Cryptography (ECC) enable secure key exchange, digital signatures, and certificate-based authentication. Asymmetric crypto is slower than symmetric, but essential for secure internet protocols.

How Asymmetric Encryption Works

from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes

# Generate a 2048-bit RSA key pair
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()

# Encrypt with public key (anyone can encrypt)
message = b"Confidential API key"
ciphertext = public_key.encrypt(
    message,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    )
)

# Decrypt with private key (only owner can decrypt)
plaintext = private_key.decrypt(
    ciphertext,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    )
)

This code demonstrates encrypting a secret with the public key and decrypting it with the private key. In practice, you’d use this to exchange a symmetric key, not to encrypt bulk data due to performance constraints.

Digital Signatures

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding

# Signing
data = b"Critical business logic update"
signature = private_key.sign(
    data,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH
    ),
    hashes.SHA256()
)

# Verifying
public_key.verify(
    signature,
    data,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH
    ),
    hashes.SHA256()
)

This pattern underpins software update verification, certificate authorities, and secure messaging.

  • Checklist:
    • Are keys at least 2048 bits (RSA) or using modern ECC curves?
    • Are you using OAEP or PSS padding (never “raw” RSA)?
    • Is private key access tightly restricted and audited?
    • Are certificates validated against trusted authorities (mitigating MITM)?

See CWE-780: Use of RSA Algorithm without OAEP for more.

Cryptographic Hashing: From Integrity to Password Storage

Cryptographic hashing transforms input data into a fixed-size digest that is (ideally) irreversible and collision-resistant. Hashes are used for ensuring integrity, verifying downloads, and (with care) password storage. Common algorithms: SHA-256, BLAKE2, and password-specific algorithms like bcrypt, scrypt, and Argon2.

How Hashing Works

import hashlib

data = b"Sensitive configuration file"
digest = hashlib.sha256(data).hexdigest()
print("SHA-256:", digest)

This example creates a SHA-256 hash of a file. If even one byte changes, the digest changes completely, making it useful for integrity checks.

Password Hashing: Why Simple Hashes Are Not Enough

import bcrypt

password = b"user_password_123"
# Generate salt and hash
hashed = bcrypt.hashpw(password, bcrypt.gensalt())

# To verify:
bcrypt.checkpw(password, hashed)

Unlike fast general-purpose hashes, bcrypt (and similar) are intentionally slow and include a salt to defeat rainbow tables and brute force attacks. Never use SHA-1, SHA-256, or MD5 for password storage—their speed is a liability.

  • Checklist:
    • Are you using a slow, salted password hash (bcrypt, scrypt, Argon2)?
    • Are you verifying file integrity with hashes from trusted sources?
    • Is input length checked to prevent hash DoS (CVE-2022-37434)?
    • Are you monitoring for hash collisions or deprecated algorithms?

See OWASP Password Storage Cheat Sheet for best practices.

Comparison Table: Symmetric vs Asymmetric vs Hashing

PropertySymmetric EncryptionAsymmetric EncryptionHashing
Key TypeSingle shared secretPublic/private key pairN/A (no key, but salt for password hashing)
Main UseBulk data encryptionKey exchange, signaturesData integrity, password storage
SpeedFastestSlowestFast (except password hashing)
ConfidentialityYesYesNo
AuthenticationNo (unless AEAD mode)Yes (signatures)No (only integrity)
Common AlgorithmsAES, ChaCha20RSA, ECCSHA-256, bcrypt, Argon2
Key Management ComplexityHigh (needs secure exchange/storage)Moderate (public keys can be shared)N/A
  • Checklist: Map your use case to the correct column; using the wrong primitive is a critical vulnerability.

Common Pitfalls and Pro Tips

1. Building Custom Crypto

Never roll your own cryptography. Even experts make mistakes; always use vetted libraries like cryptography (Python), libsodium, or BoringSSL. Avoid obsolete algorithms (MD5, SHA-1, DES, RC4).

2. Reusing Keys or IVs

Key/IV reuse destroys confidentiality. Always generate fresh, random values for each session/message. Use a secure random generator, not predictable sources (e.g., os.urandom or secrets in Python).

3. Using Hash Functions for Passwords

General-purpose hashes like SHA-256 are too fast for password storage. Use a dedicated algorithm (bcrypt, scrypt, Argon2) with a unique salt per user. Upgrade legacy password storage as a priority.

4. Weak Key Management

Storing keys in source code or environment variables is a direct path to compromise. Use secure key vaults (AWS KMS, HashiCorp Vault, Azure Key Vault) and restrict access by least privilege.

5. Failure to Monitor and Audit

Monitor for deprecated algorithms, misuse of primitives, and access patterns around key material. Audit cryptographic controls at least annually and after any incident.

  • Checklist:
    • Are all cryptographic libraries and dependencies up to date?
    • Are there regular reviews for algorithm deprecation?
    • Are secrets rotated according to policy?
    • Are logs monitored for suspicious access to key material?

Conclusion & Next Steps

Applying cryptography securely requires understanding the strengths and limitations of symmetric algorithms, asymmetric algorithms, and hash functions. Audit your systems with the checklists above, map each security requirement to the correct primitive, and stay up to date with standards from OWASP and NIST. Next, review your application’s key management practices and perform a cryptographic health check using automated scanners or manual code review. For deeper dives, explore topics like authenticated encryption (AEAD), HMACs, and secure key rotation policies.

Start Sharing and Storing Files for Free

You can also get your own Unlimited Cloud Storage on our pay as you go product.
Other cool features include: up to 100GB size for each file.
Speed all over the world. Reliability with 3 copies of every file you upload. Snapshot for point in time recovery.
Collaborate with web office and send files to colleagues everywhere; in China & APAC, USA, Europe...
Tear prices for costs saving and more much more...
Create a Free Account Products Pricing Page