$30 off During Our Annual Pro Sale. View Details »

Send and Receive Messages Securely using Asymme...

Send and Receive Messages Securely using Asymmetric Cryptography

How to encrypt, decrypt, and verify a message using Asymmetric Encryption, with code examples in Javascript, Python and Java.

We will see why hybrid encryption is commonly employed to overcome the limitation of asymmetric encryption.

There will be code examples in Python, Javascript and Java to show how to use APIs to encrypt and decrypt using both RSA-OAEP and AES-256.

The source codes used in this video are available on github: https://github.com/matteobertozzi/snippets/tree/main/crypto

Matteo Bertozzi

June 27, 2023
Tweet

More Decks by Matteo Bertozzi

Other Decks in Programming

Transcript

  1. asymmetric In Practice cryptography Encryption Decryption Sfsflk S+/#@m I\njq\x0b%w ]*Osfokt1?

    zqKN3sOXgs K`QnUuf> 0.*_H-V@b; Plain Text Plain Text Cipher Text Public Key Private Key
  2. asymmetric Encryption symmetric Encryption Public Key Private Key ciphertext =

    encrypt(pubKey, data) data = decrypt(privKey, ciphertext) signature = sign(privKey, data) verify_signature(pubKey, signature, data) ciphertext = encrypt(key, data) data = decrypt(key, ciphertext) (AES) (RSA, elliptic-curve) Secret Key Encrypt With B Public Key Sfsf lkS+ #@mI\xnj q\x0b%rw ]*Osfokt 1?mR3zxF Decrypt With B Private Key Sign With A Private Key m3#afXlt kgf3#!/ Verify With A Public Key A B A Sfsf lkS+ #@mI\xnj q\x0b%rw ]*Osfokt 1?mR3zxF B secure communication, authentication and integrity
  3. asymmetric cryptography Private Key Public Key Keep it Private PKCS#8

    (Public-Key Cryptography Standard #8) Derived from the Private Key Can be publicly shared Ensure Integrity and Authenticity Load from a Trusted source using a secure channel Public Exponent: 65537 -> 0x010001 X.509/SPKI (SubjectPublicKeyInfo) Generate Keys RSA OAEP // Generate RSA key 4096-bit final KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); generator.initialize(4096); final KeyPair pair = generator.generateKeyPair(); final PublicKey publicKey = pair.getPublic(); final PrivateKey privateKey = pair.getPrivate(); final KeyFactory keyFactory = KeyFactory.getInstance("RSA"); // Export Public Key final X509EncodedKeySpec publicKeySpec = keyFactory.getKeySpec(publicKey, X509EncodedKeySpec.class); final byte[] spkiPublicKey = publicKeySpec.getEncoded(); // Export Private Key final PKCS8EncodedKeySpec privateKeySpec = keyFactory.getKeySpec(privateKey, PKCS8EncodedKeySpec.class); final byte[] pkcs8PrivateKey = privateKeySpec.getEncoded(); Java // Generate RSA key 4096-bit const keyPair = await crypto.subtle.generateKey( { name: "RSA-OAEP", modulusLength: 4096, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-512", }, true, ["encrypt", "decrypt"], ); // Export Public Key const publicKey = keyPair.publicKey; const spkiPublicKey = await crypto.subtle.exportKey("spki", publicKey); // Export Private Key const privateKey = keyPair.privateKey; const pkcs8PrivateKey = await crypto.subtle.exportKey("pkcs8", privateKey); Javascript from cryptography.hazmat.primitives.serialization import * from cryptography.hazmat.primitives.asymmetric import rsa # Generate Keys private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096) public_key = private_key.public_key() # Export Public Key public_key_der = public_key.public_bytes( encoding=Encoding.DER, format=PublicFormat.SubjectPublicKeyInfo ) # Export Private Key private_key_der = private_key.private_bytes( encoding=Encoding.DER, format=PrivateFormat.PKCS8, encryption_algorithm=NoEncryption() # Optional encryption algorithm ) Python pip install cryptography
  4. // load from somewhere… final byte[] publicKeyBytes = Base64.getDecoder().decode("MIIJQgIBAD..."); final

    byte[] privateKeyBytes = Base64.getDecoder().decode("MIICIjANBgk..."); final KeyFactory keyFactory = KeyFactory.getInstance("RSA"); // load PublicKey final EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes); final PublicKey publicKey = keyFactory.generatePublic(publicKeySpec); // load PrivateKey final PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes); final PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec); Java // load from somewhere… const privateKeyBytes = base64_decode(‘MIIJQgIBAD...') const publicKeyBytes = base64_decode('MIICIjANBgk...') // Convert to the PrivateKey object const privateKey = await crypto.subtle.importKey( "pkcs8", privateKeyBytes, { name: "RSA-OAEP", modulusLength: 4096, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-512", }, true, ["decrypt"] ); // Convert to the PublicKey object const publicKey = await crypto.subtle.importKey( "spki", publicKeyBytes, { name: "RSA-OAEP", hash: "SHA-512" }, true, ["encrypt"] ); Javascript from cryptography.hazmat.primitives.serialization import * # load from somewhere… private_key_bytes = base64.b64decode(b'MIIJQgIBAD...') public_key_bytes = base64.b64decode(b'MIICIjANBgk...') # Convert to the PublicKey object public_key = load_der_public_key(public_key_bytes) # Convert to the PrivateKey object private_key = load_der_private_key(private_key_bytes, password=None) Load Keys Python pip install cryptography asymmetric cryptography Private Key Public Key Keep it Private PKCS#8 (Public-Key Cryptography Standard #8) Derived from the Private Key Can be publicly shared Ensure Integrity and Authenticity Load from a Trusted source using a secure channel Public Exponent: 65537 -> 0x010001 X.509/SPKI (SubjectPublicKeyInfo)
  5. final String RSA_OAEP_ALGO = "RSA/ECB/OAEPWithSHA-512AndMGF1Padding"; final OAEPParameterSpec OAEP_PARAMS = new

    OAEPParameterSpec( "SHA-512", "MGF1", MGF1ParameterSpec.SHA512, PSpecified.DEFAULT ); final byte[] message = "hello world".getBytes(); // Encrypt using the PublicKey final Cipher encryptCipher = Cipher.getInstance(RSA_OAEP_ALGO); encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey, OAEP_PARAMS); final byte[] ciphertext = encryptCipher.doFinal(message); // Decrypt using the PrivateKey final Cipher decryptCipher = Cipher.getInstance(RSA_OAEP_ALGO); decryptCipher.init(Cipher.DECRYPT_MODE, privateKey, OAEP_PARAMS); final byte[] text = decryptCipher.doFinal(ciphertext); Java const message = new TextEncoder().encode('hello world'); // Encrypt using the PublicKey const ciphertext = await crypto.subtle.encrypt( { name: "RSA-OAEP" }, publicKey, message ); // Decrypt using the PrivateKey const text = await crypto.subtle.decrypt( { name: "RSA-OAEP" }, privateKey, ciphertext, ); Javascript OAEP_PADDING = padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA512()), algorithm=hashes.SHA512(), label=None ) message = b'hello world’ # Encrypt using the PublicKey ciphertext = public_key.encrypt(message, OAEP_PADDING) # Decrypt using the PrivateKey text = private_key.decrypt(ciphertext, OAEP_PADDING) Python pip install cryptography Only the owner of the Public Key Can decrypt with its Private Key Encrypt/Decrypt asymmetric cryptography A Encrypt With B Public Key Sfsf lkS+ #@mI\xnj q\x0b%rw ]*Osfokt1 ?mXfR3zxF Decrypt With B Private Key B Encrypt using the Recepient’s Public Key ciphertext = publicKey.encrypt(message) message = privateKey.decrypt(ciphertext) Public Private
  6. 2048-bit RSA key: (2048 / 8) - padding -> around

    245 bytes. 4096-bit RSA key: (4096 / 8) - padding -> around 501 bytes. Maximum Encryption Length = Key Size in Bytes - Padding Overhead Max Message Length! asymmetric cryptography Encrypt/Decrypt Larger Key sizes leads to Slower Encryption/Decryption
  7. Hybrid cryptography Generate AES Key Encryption Decryption Plain Text Plain

    Text Cipher Text Encrypt Data With AES Public Sfsflk S+/#@m I\njq\x0b%w ]*Osfokt1? zqKN3sOXgs K`QnUuf> 0.*_H-V@b; mRn#gN3s@ks2l Encrypt AES Key With PubKey Decrypt AES Key With Private Key Decrypt Data With AES Private Private Public Public Private Public aes_key = aes_generate_key() ciphertext = aes_encrypt(aes_key, plaintext); encrypted_aes_key = rsa_encrypt(target_public_key, aes_key); send(encrypted_aes_key, ciphertext); encrypted_aes_key, ciphertext = recv(); aes_key = rsa_decrypt(my_private_key, encrypted_aes_key) plaintext = aes_decrypt(aes_key, ciphertext);
  8. symmetric cryptography // Generate AES-256 key final KeyGenerator generator =

    KeyGenerator.getInstance("AES"); generator.init(256); final SecretKey key = generator.generateKey(); // Export the Raw Key final byte[] rawKey = key.getEncoded(); Java // Generate AES-256 Key const key = await crypto.subtle.generateKey( { name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"] ); // Export the Raw Key const rawKey = await crypto.subtle.exportKey("raw", key); Javascript import os # 256bit random data key = os.urandom(32) Python Generate & Load AES Key // load from somewhere... const rawKey = base64_decode(“bXv+JNZXLMj…”); // Convert to CryptoKey object const key = await crypto.subtle.importKey( "raw", rawKey, { name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"] ); // load from somewhere... final byte[] rawKey = Base64.getDecoder().decode("bXv+JNZXLMj…"); // Convert to SecretKey object final SecretKey key = new SecretKeySpec(rawKey, "AES"); GCM (Galois/Counter Mode): Counter mode with authentication. (Encryption and integrity) CTR (Counter): Encrypts blocks using a counter. (Parallel encryption, random access) XTS (XEX-based Tweaked Codebook Mode): For Disk encryption with manipulation protection. EAX (Encrypt-then-authenticate-then-translate): Provides encryption and integrity. CCM (Counter with CBC-MAC): Authenticated encryption with associated data SIV (Synthetic Initialization Vector): Deterministic encryption with unique authenticity. AES Modes AES Block Size: 128-bit Key Sizes: 128, 192 or 256 bits
  9. byte[] encrypt(final SecretKey key, final byte[] message) throws Exception {

    // Generate 96bit Initialization Vector final byte[] iv = new byte[12]; final SecureRandom rand = new SecureRandom(); rand.nextBytes(iv); // Init AES-GCM final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, iv)); try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { // add the IV baos.write(iv); // encrypt the 'message' baos.write(cipher.doFinal(message)); // Get the full cipherText: IV + Encrypted-Message + Auth-Tag final byte[] cipherText = baos.toByteArray(); return cipherText; } } byte[] decrypt(final SecretKey key, final byte[] cipherText) throws Exception { // Init AES-GCM, IV is in the first 12bytes of the ciphertext final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); final GCMParameterSpec gcmSpec = new GCMParameterSpec(128, cipherText, 0, 12); cipher.init(Cipher.DECRYPT_MODE, key, gcmSpec); // Decrypt the message (exclude the IV) final byte[] plainText = cipher.doFinal(cipherText, 12, cipherText.length - 12); return plainText; } Java async function aesEncrypt(key: CryptoKey, message: ArrayBuffer) { // Generate 96bit Initialization Vector const iv = new Uint8Array(12); crypto.getRandomValues(iv); // Encrypt the 'message' const aesData = await crypto.subtle.encrypt( { name: "AES-GCM", iv: iv }, key, message, ); // Compose the ciphertext: IV + Encrypted-Message + Auth-Tag const ciphertext = new Uint8Array(iv.length + aesData.byteLength); ciphertext.set(iv, 0); ciphertext.set(new Uint8Array(aesData), iv.length); return ciphertext; } async function aesDecrypt(key: CryptoKey, ciphertext: Uint8Array) { // IV is in the first 12bytes of the ciphertext const iv = ciphertext.slice(0, 12); // AES encrypted data + GCM auth-tag const aesData = ciphertext.slice(12); // decrypt data const plaintext = await crypto.subtle.decrypt( { name: "AES-GCM", iv: iv }, key, aesData ); return plaintext; } Javascript from cryptography.hazmat.primitives.ciphers import * import os def aes_encrypt(key: bytes, message: bytes) -> bytes: # Generate 96bit Initialization Vector iv = os.urandom(12) # Init AES-GCM cipher = Cipher(algorithms.AES(key), modes.GCM(iv)) encryptor = cipher.encryptor() # Encrypt 'message' aes_data = encryptor.update(message) + encryptor.finalize() # Get the full cipherText: IV + Encrypted-Message + Auth-Tag ciphertext = iv + aes_data + encryptor.tag return ciphertext def aes_decrypt(key: bytes, ciphertext: bytes) -> bytes: iv = ciphertext[0:12] aes_data = ciphertext[12:-16] tag = ciphertext[-16:] cipher = Cipher(algorithms.AES(key), modes.GCM(iv, tag)) decryptor = cipher.decryptor() text = decryptor.update(aes_data) + decryptor.finalize() return text Python pip install cryptography AES Encrypt/Decrypt symmetric cryptography AES-GCM IV (12bytes) AES Encrypted Data Auth Tag (16bytes) Secure Random Data GCM “Integrity Check” The Encrypted Message // Encrypt a message with AES-GCM iv = generate_secure_random(12); aes_gcm = new_aes_gcm(key, iv) ciphertext = aes_gcm.encrypt(message) send(iv, ciphertext, aes_gcm.auth_tag) // Decrypt a message with AES-GCM iv, ciphertext, gcm_auth_tag = recv() aes_gcm = new_aes_gcm(key, iv, gcm_auth_tag) message = aes_gcm.decrypt(ciphertext) GCM (Galois/Counter Mode): Counter mode with authentication. (Encryption and integrity) CTR (Counter): Encrypts blocks using a counter. (Parallel encryption, random access) XTS (XEX-based Tweaked Codebook Mode): For Disk encryption with manipulation protection. EAX (Encrypt-then-authenticate-then-translate): Provides encryption and integrity. CCM (Counter with CBC-MAC): Authenticated encryption with associated data SIV (Synthetic Initialization Vector): Deterministic encryption with unique authenticity. AES Modes
  10. Digital Signature Generate AES Key Encryption Decryption Plain Text Plain

    Text Cipher Text Encrypt Data With AES Public Sfsflk S+/#@m I\njq\x0b%w ]*Osfokt1? zqKN3sOXgs K`QnUuf> 0.*_H-V@b; mRn#gN3s@ks2l Encrypt AES Key With PubKey Decrypt AES Key With Private Key Decrypt Data With AES Private rz129lfp9mxa9 Private Sign The message Verify Signature Public Private Public Public Private Public Public aes_key = aes_generate_key() ciphertext = aes_encrypt(aes_key, plaintext); signature = rsa_sign(my_private_key, plaintext); encrypted_aes_key = rsa_encrypt(target_public_key, aes_key); send(encrypted_aes_key, ciphertext, signature); encrypted_aes_key, ciphertext, signature = recv(); aes_key = rsa_decrypt(my_private_key, encrypted_aes_key) plaintext = aes_decrypt(aes_key, ciphertext); rsa_verify_signature(sender_pub_key, signature, plaintext)
  11. final byte[] message = "hello world".getBytes(); // Sign using the

    PrivateKey final Signature signature = Signature.getInstance("SHA256withRSA"); signature.initSign(privateKey); signature.update(message); final byte[] signatureBytes = signature.sign(); // Verify the Signature using the PublicKey final Signature verifySignature = Signature.getInstance("SHA256withRSA"); verifySignature.initVerify(publicKey); verifySignature.update(message); final boolean v = verifySignature.verify(signatureBytes); Java const SIGN_PARAMS = { name: "RSASSA-PKCS1-v1_5", modulusLength: 4096, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256", }; // Reload the PrivateKey for signing const privateKey = await crypto.subtle.importKey( "pkcs8", privateKeyBytes, SIGN_PARAMS, true, ["sign"] ); // Reload the PublicKey for verifying const publicKey = await crypto.subtle.importKey( "spki", publicKeyBytes, SIGN_PARAMS, true, ["verify"] ); const message = new TextEncoder().encode('hello world'); // Verify the Signature using the PublicKey const signature = await crypto.subtle.sign( "RSASSA-PKCS1-v1_5", privateKey, message, ); // Verify the Signature using the PublicKey const verify = await crypto.subtle.verify( "RSASSA-PKCS1-v1_5", publicKey, signature, message ); Javascript message = b'hello world' # Sign using the PrivateKey signature = private_key.sign( message, padding.PKCS1v15(), hashes.SHA256() ) # Verify the Signature using the PublicKey public_key.verify( signature, message, padding.PKCS1v15(), hashes.SHA256() ) Python pip install cryptography Signature asymmetric cryptography A Sign With A Private Key m3#afXlt kgf3#!/ B Verify With A Public Key how can we verify the authenticity of the sender? signature = privateKey.sign(message) valid = publicKey.verify(signature, message)
  12. Send & Receive Messages Securely Asymmetric Encryption has 2 keys

    Public Key - Used To Encrypt Private Key - Used to Deecrypt Must stay Private! Max Msg Len - Small (Less than 1KB) Use symmetric Encryption Generate a new Key for every Message Encrypt the Message with symmetric encryption for larger messages A Encrypt With B Public Key Sfsf lkS+ #@mI\xnj q\x0b%rw ]*Osfokt1 ?mXfR3zxF Decrypt With B Private Key B Public Private A Sfsf lkS+ #@mI\xnj q\x0b%rw ]*Osfokt1 ?mXfR3zxF B Public mRn#gN3s@ks2l rz129lfp9mxa9 Private Public Private Sign Encrypt Key Encrypt Data Verify Decrypt Key Decrypt Data Encrypt the Key with Asymmetric encryption Sign the Message for authenticity