Skip to content

Modern Cryptography and Public Key Infrastructure (Criptografia Moderna e Infraestrutura de Chaves Públicas)

This section covers modern cryptographic techniques that form the backbone of internet security, including asymmetric encryption, digital signatures, key exchange protocols, and public key infrastructure. These concepts are essential for understanding how secure communications work on the web.

Overview

Modern cryptography addresses limitations of classical methods by solving the fundamental problem: how to securely communicate when you have no pre-existing shared secret. This is achieved through mathematical problems that are easy to compute in one direction but computationally infeasible to reverse.

Key Concepts Covered

  1. Asymmetric Encryption: Using key pairs (public/private) for secure communication
  2. Digital Signatures: Ensuring authenticity and non-repudiation
  3. Key Exchange Protocols: Securely establishing shared secrets over insecure channels
  4. Public Key Infrastructure (PKI): Managing digital certificates and trust

Mathematical Foundations

The Discrete Logarithm Problem

Given a prime $p$, a generator $g$, and value $y = g^x \mod p$, finding $x$ given $(p, g, y)$ is computationally difficult. This forms the basis of: - Diffie-Hellman Key Exchange - ElGamal Encryption

The Integer Factorization Problem

Given a large composite number $n = p \times q$ (where $p, q$ are primes), finding $p$ and $q$ is difficult. This forms the basis of: - RSA Encryption

Elliptic Curve Discrete Logarithm Problem

Finding $k$ given points $P$ and $Q = kP$ on an elliptic curve is computationally hard, even for relatively small key sizes. This enables: - ECC (Elliptic Curve Cryptography) - smaller keys with equivalent security to RSA

Asymmetric Encryption (Criptografia Assimétrica)

Asymmetric encryption uses a pair of mathematically related keys: - Public Key: Can be shared openly, used for encryption or signature verification - Private Key: Must be kept secret, used for decryption or signing

RSA (Rivest-Shamir-Adleman)

RSA is based on the difficulty of factoring large integers.

Mathematical Foundation

Given: - Two large prime numbers $p$ and $q$ - Modulus $n = p \times q$ - Euler's totient function $\phi(n) = (p-1)(q-1)$ - Public exponent $e$ (typically 65537, coprime to $\phi(n)$) - Private exponent $d$ such that $ed \equiv 1 \mod \phi(n)$

Encryption: $C = M^e \mod n$
Decryption: $M = C^d \mod n$

Java Implementation

import java.math.BigInteger;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;

public class RSAEncryption {

    private static final int KEY_SIZE = 2048;

    /**
     * Generates a new RSA key pair.
     */
    public static KeyPair generateKeyPair() throws Exception {
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        keyGen.initialize(KEY_SIZE);
        return keyGen.generateKeyPair();
    }

    /**
     * Encrypts data using the recipient's public key.
     */
    public static byte[] encrypt(byte[] plaintext, PublicKey publicKey) throws Exception {
        java.security.spec.EncryptedPrivateKeyInfo enc = 
            new java.security.spec.EncryptedPrivateKeyInfo(publicKey.getEncoded());

        // For RSA encryption, use Cipher class instead
        javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, publicKey);

        return cipher.doFinal(plaintext);
    }

    /**
     * Decrypts data using the private key.
     */
    public static byte[] decrypt(byte[] ciphertext, PrivateKey privateKey) throws Exception {
        javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(javax.crypto.Cipher.DECRYPT_MODE, privateKey);

        return cipher.doFinal(ciphertext);
    }

    public static void main(String[] args) throws Exception {
        KeyPair keyPair = generateKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();

        String message = "Secret message";

        // Encrypt with public key
        byte[] encrypted = encrypt(message.getBytes(), publicKey);
        System.out.println("Encrypted length: " + encrypted.length);

        // Decrypt with private key
        String decrypted = new String(decrypt(encrypted, privateKey));
        System.out.println("Decrypted: " + decrypted);
    }
}

Elliptic Curve Cryptography (ECC)

ECC provides equivalent security to RSA with much smaller keys.

Security Level RSA Key Size ECC Key Size
128-bit 3072 bits 256 bits
192-bit 7680 bits 384 bits
256-bit 15360 bits 512 bits

ECDSA (Elliptic Curve Digital Signature Algorithm)

ECDSA is used for digital signatures and provides the same security as RSA with smaller keys.

import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class ECDSASignature {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    public static void main(String[] args) throws Exception {
        // Generate key pair
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", "BC");
        keyGen.initialize(256);
        KeyPair keyPair = keyGen.generateKeyPair();

        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();

        // Sign data
        byte[] message = "Message to sign".getBytes();
        java.security.Signature signature = 
            java.security.Signature.getInstance("SHA256withECDSA", "BC");
        signature.initSign(privateKey);
        signature.update(message);
        byte[] signatureBytes = signature.sign();

        // Verify signature
        signature.initVerify(publicKey);
        signature.update(message);
        boolean isValid = signature.verify(signatureBytes);

        System.out.println("Signature valid: " + isValid);
    }
}

Digital Signatures (Assinaturas Digitais)

Digital signatures provide three security guarantees: 1. Authenticity: The message was signed by the claimed sender 2. Integrity: The message has not been modified 3. Non-repudiation: The signer cannot deny having signed it

How Digital Signatures Work

Signing Process:
1. Hash the message: h = H(M)
2. Encrypt hash with private key: s = D_priv(h)
   (This is the signature)

Verification Process:
1. Hash received message: h' = H(M')
2. Decrypt signature with public key: h'' = E_pub(s)
3. Compare: if h' == h'', signature is valid

RSA Digital Signature Implementation

import java.security.*;
import java.util.Base64;

public class DigitalSignature {

    /**
     * Signs a message using the private key.
     */
    public static String signMessage(byte[] message, PrivateKey privateKey) throws Exception {
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(message);

        byte[] signatureBytes = signature.sign();
        return Base64.getEncoder().encodeToString(signatureBytes);
    }

    /**
     * Verifies a message signature using the public key.
     */
    public static boolean verifySignature(byte[] message, String base64Signature, PublicKey publicKey) throws Exception {
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initVerify(publicKey);
        signature.update(message);

        byte[] signatureBytes = Base64.getDecoder().decode(base64Signature);
        return signature.verify(signatureBytes);
    }

    public static void main(String[] args) throws Exception {
        KeyPair keyPair = RSAEncryption.generateKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        PublicKey publicKey = keyPair.getPublic();

        String message = "Important document content";

        // Sign the message
        String signature = signMessage(message.getBytes(), privateKey);
        System.out.println("Signature: " + signature);

        // Verify the signature
        boolean isValid = verifySignature(message.getBytes(), signature, publicKey);
        System.out.println("Verification passed: " + isValid);
    }
}

Key Exchange Protocols (Protocolos de Troca de Chaves)

Diffie-Hellman Key Exchange

Diffie-Hellman allows two parties to establish a shared secret over an insecure channel without having exchanged any secret information beforehand.

Mathematical Foundation

Given: - A large prime $p$ - A generator $g$ (typically 2 or 3)

Alice: 1. Choose private key $a$ randomly 2. Compute public value $A = g^a \mod p$ 3. Send $A$ to Bob

Bob: 1. Choose private key $b$ randomly 2. Compute public value $B = g^b \mod p$ 3. Send $B$ to Alice

Shared Secret: Both compute $s = B^a \mod p = A^b \mod p$

Java Implementation

import java.math.BigInteger;
import java.security.SecureRandom;

public class DiffieHellman {

    // Standard parameters (RFC 7919)
    private static final BigInteger P = new BigInteger("FFFFFFFFFFFFFFFFC90FDAA2" +
            "2168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A087" +
            "98E3404DDEF9519B3CD3A431B302B026038C13ACFFFFFFFBCFC6EE1BBC7FF59" +
            "B88BB9BCB09CBECECDD4EBA3EDBD4547B9280CD73CDA250F164C406CBBA290" +
            "5F47E52BDF9D8EFF2A30E1F7BB4CC89B68F15D42A58ED30ABDDA62FFCF4F90" +
            "3EBC965FFC9BFD859AC479CA81E99A3ED9B6D1FE609731A", 16);

    private static final BigInteger G = new BigInteger("2");

    /**
     * Generates a Diffie-Hellman key pair.
     */
    public static KeyPair generateKeyPair() {
        SecureRandom random = new SecureRandom();

        // Generate private key (1024 bits)
        byte[] privateKeyBytes = new byte[128];
        random.nextBytes(privateKeyBytes);
        BigInteger privateKey = new BigInteger(1, privateKeyBytes);

        // Ensure private key is in valid range [2, P-2]
        while (privateKey.compareTo(BigInteger.ONE) <= 0 || 
               privateKey.compareTo(P.subtract(BigInteger.ONE)) >= 0) {
            random.nextBytes(privateKeyBytes);
            privateKey = new BigInteger(1, privateKeyBytes);
        }

        // Compute public key: Y = g^x mod p
        BigInteger publicKey = G.modPow(privateKey, P);

        return new KeyPair(publicKey, privateKey);
    }

    /**
     * Computes shared secret from peer's public key.
     */
    public static byte[] computeSharedSecret(BigInteger theirPublicKey, PrivateKey myPrivateKey) {
        // Shared secret: s = Y^x mod p
        BigInteger sharedSecret = theirPublicKey.modPow(myPrivateKey, P);

        // Hash the result to get a usable key
        java.security.MessageDigest digest;
        try {
            digest = java.security.MessageDigest.getInstance("SHA-256");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return digest.digest(sharedSecret.toByteArray());
    }

    public static void main(String[] args) throws Exception {
        // Alice generates key pair
        KeyPair aliceKeyPair = generateKeyPair();
        BigInteger alicePublic = (BigInteger) aliceKeyPair.getPublic();
        PrivateKey alicePrivate = (PrivateKey) aliceKeyPair.getPrivate();

        // Bob generates key pair
        KeyPair bobKeyPair = generateKeyPair();
        BigInteger bobPublic = (BigInteger) bobKeyPair.getPublic();
        PrivateKey bobPrivate = (PrivateKey) bobKeyPair.getPrivate();

        // Alice computes shared secret using Bob's public key
        byte[] aliceSecret = computeSharedSecret(bobPublic, alicePrivate);

        // Bob computes shared secret using Alice's public key
        byte[] bobSecret = computeSharedSecret(alicePublic, bobPrivate);

        System.out.println("Alice and Bob have the same shared secret: " + 
            java.util.Arrays.equals(aliceSecret, bobSecret));
    }
}

Ephemeral Diffie-Hellman (DHE) and Elliptic Curve DH (ECDH)

Modern implementations use ephemeral keys for forward secrecy:

  • DHE: Uses temporary Diffie-Hellman keys that are discarded after key exchange
  • ECDHE: Uses elliptic curve variants, providing better performance

These ensure that even if long-term private keys are compromised, past communications remain secure.

Public Key Infrastructure (PKI)

Digital Certificates

A digital certificate binds a public key to an identity and is signed by a Certificate Authority (CA).

Certificate Structure (X.509)

Certificate ::= SEQUENCE {
    tbsCertificate       TBSCertificate,
    signatureAlgorithm   AlgorithmIdentifier,
    signatureValue       BIT STRING
}

TBSCertificate ::= SEQUENCE {
    version         [0]  EXPLICIT Version DEFAULT v1,
    serialNumber         CertificateSerialNumber,
    signature            AlgorithmIdentifier,
    issuer               Name,
    validity             Validity,
    subject              Name,
    subjectPublicKeyInfo SubjectPublicKeyInfo,
    ...
}

Java PKI Implementation

import java.security.*;
import java.util.Date;

public class CertificateManagement {

    /**
     * Creates a self-signed certificate (for testing only).
     */
    public static X509Certificate createSelfSignedCertificate(PrivateKey privateKey, String cn) 
            throws Exception {

        // Create certificate builder
        X509V3CertificateBuilder builder = new X509V3CertificateBuilder(
            new GeneralName(Name.buildCN(cn)),  // Issuer
            new Date(System.currentTimeMillis()),  // Not before
            new Date(System.currentTimeMillis() + 365 * 24 * 60 * 60 * 1000),  // Not after (1 year)
            new GeneralName(Name.buildCN(cn)),  // Subject
            privateKey.getPublicKey(),  // Public key
            "SHA-256"  // Signature algorithm
        );

        // Generate and sign certificate
        JcaX509v3CertificateBuilder jcaBuilder = new JcaX509v3CertificateBuilder(
            builder, privateKey);

        ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSAEncryption")
            .setProvider("BC").build(privateKey);

        return jcaBuilder.build(signer, privateKey);
    }

    /**
     * Verifies a certificate chain.
     */
    public static boolean verifyCertificateChain(X509Certificate[] chain) throws Exception {
        X509Certificate leaf = chain[0];

        // Verify signature of each certificate in the chain
        for (int i = 1; i < chain.length; i++) {
            X509Certificate issuer = chain[i];
            X509Certificate subject = chain[i - 1];

            PublicKey publicKey = issuer.getPublicKey();
            java.security.cert.Certificate cert = subject;

            // Verify that the issuer signed the subject's certificate
            if (!issuer.verify(cert.getEncoded())) {
                return false;
            }
        }

        // Verify leaf certificate signature
        return leaf.verify(leaf.getIssuerPublicKey());
    }
}

Certificate Authority (CA) Hierarchy

PKI uses a hierarchical trust model:

Root CA (Self-signed, trusted by browsers/OS)
    └── Intermediate CA 1 (Signed by Root CA)
        ├── Intermediate CA 2 (Signed by IC1)
        │   └── Server Certificate (Signed by IC2)
        └── Server Certificate (Signed by IC1)
    └── Server Certificate (Signed by Root CA)

Using Java's TrustStore

import java.security.KeyStore;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

public class SSLClient {

    public static void main(String[] args) throws Exception {
        // Load truststore (contains trusted CA certificates)
        KeyStore trustStore = KeyStore.getInstance("JKS");
        try (InputStream is = new FileInputStream("/path/to/truststore.jks")) {
            trustStore.load(is, "password".toCharArray());
        }

        // Initialize TrustManagerFactory
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(
            TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(trustStore);

        // Create SSLContext
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, tmf.getTrustManagers(), null);

        // Use for HTTPS connections
        HttpsURLConnection connection = (HttpsURLConnection) new URL("https://example.com").openConnection();
        connection.setSSLSocketFactory(sslContext.getSocketFactory());
    }
}

TLS/SSL Protocol

TLS (Transport Layer Security) is the protocol that secures web communications. It combines:

  1. Key Exchange: ECDHE for forward secrecy
  2. Authentication: Server certificate verification
  3. Encryption: AES-GCM or ChaCha20-Poly1305
  4. Integrity: AEAD (Authenticated Encryption with Associated Data)

TLS Handshake Overview

Client Hello: Client proposes cipher suites, compression methods
Server Hello: Server selects cipher suite, sends certificate
Certificate Verify: Client verifies server's certificate
Key Exchange: Both parties compute shared secret using ECDHE
Finished Messages: Both parties verify the handshake was successful
Application Data: Encrypted communication begins

Security Considerations

Best Practices

  1. Use strong algorithms: AES-256-GCM, RSA 4096+, ECC P-384 or higher
  2. Avoid deprecated algorithms: MD5, SHA-1, RC4, DES, 3DES
  3. Enable forward secrecy: Use ECDHE instead of RSA key exchange
  4. Use TLS 1.3: Provides better security and performance than TLS 1.2
  5. Implement certificate pinning for mobile applications

Common Vulnerabilities

Vulnerability Description Mitigation
Heartbleed Memory disclosure in OpenSSL Update libraries, regenerate keys
POODLE Padding oracle attack on SSL 3.0 Disable SSL 3.0, use TLS
BEAST Browser security update attack Use AES-GCM or ChaCha20
FREAK Forcing RSA downgrade to export ciphers Disable export ciphers

References

  1. RFC 6478: Elliptic Curve Diffie-Hellman (ECDH) Key Agreement Protocol
  2. RFC 5280: Internet X.509 Public Key Infrastructure Certificate Profile
  3. RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3
  4. Menezes, A. J., van Oorschot, P. C., & Vanstone, S. A. (1996). Handbook of Applied Cryptography. CRC Press.
  5. Bernstein, D. J., Lange, T., & Schwabe, P. (2017). "Post-Quantum Cryptography".