Encryption and hashing

Content:

  1. Introduction
  2. Symmetric encryption
  3. Hashing
    1. Password hashing
  4. Asymmetric encryption
  5. Combinations
    1. Key exchange
    2. Digital signature

Introduction:

Encryption and hashing are the building blocks for security and any developer should have at least a basic understanding of them.

Advice: do not attempt to create your own cryptographic algorithms - use well known and well tested cryptographic algorithms.

Symmetric encryption:

Principles:

Encryption means a reversible transformation like:

Symmetric encryption is called symmetric because it uses the same secret key for both encryption and decryption.

So:

A good encryption algorithm is an algorithm that has brute force as best strategy for finding original data from encrypted data.

Algorithms:

Well known algorithms:

Name(s) Key size(s) Status History
DES
Data Encryption Standard
56 bit (+8 parity bits) Obsolete and unsecure Invented by IBM and published in 1975.
The key size is too small and actual breaks where demonstrated in the late 1990's.
RC4
Rivest Cipher 4
ARC4
ARCFOUR
variable Obsolete and unsecure Created by RSA (Ron Rivest) in 1987.
Details leaked to the public in 1994.
Several weaknesses was found in late 90's amd early 00's.
From 2015 it is considered broken.
3DES
Tripple DES
TDES
168 bits (+24 parity bits) Obsolete and considered risky Published 1995.
The algorithm is basically DES encrypt + DES decrypt + DES encrypt.
AES
Advanced Encryption Standard
Rijndael
128/192/256 bits Good The Rijndael algorithms was published in 1998.
Three of them became the AES standard in 2001.
Since 2008 many x86-64 CPU's has had special instructions for speeding up AES.
Blowfish variable up to 448 bits Assumed secure Invented by Bruce Schneier in 1993.
Twofish 128/192/256 bit Assumed secure Invented by Bruce Schneier and other in 1998.
CAST-128
CAST5
variable up to 128 bit Assumed secure Invented by Carlisle Adams and Stafford Tavares 1996.
IDEA
International Data Encryption Algorithm
128 bit Assumed secure Invented by Xuejia Lai and James Massey in 1991.

Two key concepts for block ciphers (all of the above except RC4 are block ciphers) are block chaining and IV (Initalization Vector).

ECB mode simply encrypts (and decrypts) each block independently. That means that same plain data result in same ciper data, which leaks information.

CBC mode first XOR the plain data with the IV for the first block and with the previous ciper data for the following blocks before encrypting (and similar for decrypt). This prevents the information leak.

Other modes PCBC, CFB, OFB and CTR does similar just in sligthly different ways.

Advice: do not use obsolete algoithms with known vulnerabilities like DES and RC4.

Advice: do not use ECB mode - use CBC or other secure mode.

Advice: never use fixed IV - always use unique random IV.

Note that IV does not need to be secret - it just needs to be random.

Examples:

package encrypt;

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Random;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class TestSymmetric {
    // generate random IV
    private static Random rng = new SecureRandom();
    private static byte[] genIV(int size) {
        byte[] res = new byte[size];
        rng.nextBytes(res);
        return res;
    }
    // encrypt: byte[] plain + byte[] key -> byte[] cipher
    // decrypt: byte[] cipher + byte[] key -> byte[] plain
    public static byte[] encrypt(String algchainpad, String alg,  int blksize, byte[] plain, byte[] key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
        byte[] iv = genIV(blksize);
        Cipher c = Cipher.getInstance(algchainpad);
        c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, alg), new IvParameterSpec(iv));
        byte[] temp =  c.doFinal(plain);
        byte[] res = new byte[iv.length + temp.length];
        System.arraycopy(iv, 0, res, 0, iv.length);
        System.arraycopy(temp, 0, res, iv.length, temp.length);
        return res;
    }
    public static byte[] decrypt(String algchainpad, String alg,  int blksize, byte[] cipher, byte[] key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
        byte[] iv = new byte[blksize];
        System.arraycopy(cipher, 0, iv, 0, iv.length);
        byte[] temp = new byte[cipher.length - iv.length];
        System.arraycopy(cipher,  iv.length, temp, 0, temp.length);
        Cipher c = Cipher.getInstance(algchainpad);
        c.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, alg), new IvParameterSpec(iv));
        return c.doFinal(temp);
    }
    // Base64 encode: byte[] -> String
    // Base64 decode: String -> byte[]
    private static String encode(byte[] b) {
        return Base64.getEncoder().encodeToString(b);
    }
    private static byte[] decode(String s) {
        return Base64.getDecoder().decode(s);
    }
    // convert String key to byte[] key
    private static byte[] genKey(String key, int keysize) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] hash = md.digest(key.getBytes("UTF-8"));
        byte[] res = new byte[keysize];
        System.arraycopy(hash,  0, res,  0, keysize);
        return res;
    }
    // encrypt: String plain + String key -> Base64 String cipher
    // decrypt: Base64 String cipher + String key -> String plain
    public static String encrypt(String algchainpad, String alg, int blksize, String plain, String key, int keysize) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
        return encode(encrypt(algchainpad, alg, blksize, plain.getBytes("UTF-8"), genKey(key, keysize)));
    }
    public static String decrypt(String algchainpad, String alg, int blksize, String cipher, String key, int keysize) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
        return new String(decrypt(algchainpad, alg, blksize, decode(cipher), genKey(key, keysize)), "UTF-8");
    }
    // test
    private static final String TEXT = "This is some random text to be used to test encryption.";
    private static final String KEY = "This is a very long and super secret key that should really be like 4 times as long as keysize";
    private static void test(String label, String algchainpad, String alg, int keysize, int blksize) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
        System.out.println(label + ":");
        System.out.println("  " + TEXT);
        String cipher = encrypt(algchainpad, alg, blksize, TEXT, KEY, keysize);
        System.out.println("  " + cipher);
        String plain = decrypt(algchainpad, alg, blksize, cipher, KEY, keysize);
        System.out.println("  " + plain);
    }
    private static void test2() throws InvalidKeyException, UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
        if(!decrypt("AES/CBC/PKCS5Padding", "AES", 16, "Xc252As4sZuj12tLJtDk2l60tHxu1hk4FTB0l9IK/q96cWd+Uop4lXcId1hGEmm0SN1Xjua4YqVlr3rvT+Mw4hStrnz3h8TEfdENfUMqLMY=", KEY, 16).equals(TEXT)) {
            throw new RuntimeException("Ooops");
        }
    }
    public static void main(String[] args) throws Exception {
        test("AES 128 bit", "AES/CBC/PKCS5Padding", "AES", 16, 16);
        test("AES 192 bit", "AES/CBC/PKCS5Padding", "AES", 24, 16);
        test("AES 256 bit", "AES/CBC/PKCS5Padding", "AES", 32, 16);
        test("3DES 168 bit", "DESede/CBC/PKCS5Padding", "DESede", 24, 8);
        test("Blowfish 256 bit", "Blowfish/CBC/PKCS5Padding", "Blowfish", 32, 8);
        test2();
    }
}

By switching from standard JCE provider to open source BouncyCastle JCE provider it is possible to use more less known algorithms as well.

Code snippet:

        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        test("Twofish 128 bit", "Twofish/CBC/PKCS5Padding", "Twofish", 16, 16);
        test("Twofish 192 bit", "Twofish/CBC/PKCS5Padding", "Twofish", 24, 16);
        test("Twofish 256 bit", "Twofish/CBC/PKCS5Padding", "Twofish", 32, 16);
        test("CAST5 128 bit", "CAST5/CBC/PKCS5Padding", "CAST5", 16, 8);
        test("IDEA 128 bit", "IDEA/CBC/PKCS5Padding", "IDEA", 16, 8);

.NET comes with two implementations a managed (pure .NET) and a wrapper around the Windows CAPI.

using System;
using System.Security.Cryptography;
using System.Text;

namespace Encrypt
{
    public class TestSymmetric
    {
        // generate random IV
        private static RandomNumberGenerator rng = RandomNumberGenerator.Create();
        private static byte[] GenIV(int size)
        {
            byte[] res = new byte[size];
            rng.GetBytes(res);
            return res;
        }
        // encrypt: byte[] plain + byte[] key -> byte[] cipher
        // decrypt: byte[] cipher + byte[] key -> byte[] plain
        public static byte[] Encrypt(SymmetricAlgorithm alg, int blksize, byte[] plain, byte[] key) 
        {
            byte[] iv = GenIV(blksize);
            ICryptoTransform encrypt = alg.CreateEncryptor(key, iv);
            byte[] temp = encrypt.TransformFinalBlock(plain, 0, plain.Length);
            byte[] res = new byte[iv.Length + temp.Length];
            Array.Copy(iv, 0, res, 0, iv.Length);
            Array.Copy(temp, 0, res, iv.Length, temp.Length);
            return res;
        }
        public static byte[] Decrypt(SymmetricAlgorithm alg, int blksize, byte[] cipher, byte[] key)
        {
            byte[] iv = new byte[blksize];
            Array.Copy(cipher, 0, iv, 0, iv.Length);
            byte[] temp = new byte[cipher.Length - iv.Length];
            Array.Copy(cipher,  iv.Length, temp, 0, temp.Length);
            ICryptoTransform decrypt = alg.CreateDecryptor(key, iv);
            return decrypt.TransformFinalBlock(temp, 0, temp.Length);
        }
        // Base64 encode: byte[] -> string
        // Base64 decode: string -> byte[]
        private static string Encode(byte[] b)
        {
            return Convert.ToBase64String(b);
        }
        private static byte[] Decode(string s)
        {
            return Convert.FromBase64String(s);
        }
        // convert string key to byte[] key
        private static byte[] GenKey(string key, int keysize)
        {
            HashAlgorithm ha = new SHA256Managed();
            byte[] hash = ha.ComputeHash(Encoding.UTF8.GetBytes(key));
            byte[] res = new Byte[keysize];
            Array.Copy(hash, 0, res, 0, keysize);
            return res;
        }
        // encrypt: string plain + string key -> Base64 string cipher
        // decrypt: Base64 string cipher + string key -> string plain
        public static string Encrypt(SymmetricAlgorithm alg, int blksize, string plain, string key, int keysize) 
        {
            return Encode(Encrypt(alg, blksize, Encoding.UTF8.GetBytes(plain), GenKey(key, keysize)));
        }
        public static string Decrypt(SymmetricAlgorithm alg, int blksize, string cipher, string key, int keysize) 
        {
            return Encoding.UTF8.GetString(Decrypt(alg, blksize, Decode(cipher), GenKey(key, keysize)));
        }
        // test
        private const string TEXT = "This is some random text to be used to test encryption.";
        private const string KEY = "This is a very long and super secret key that should really be like 4 times as long as keysize";
        private static void Test(string label, SymmetricAlgorithm alg, int keysize, int blksize) 
        {
            Console.WriteLine(label + ":");
            Console.WriteLine("  " + TEXT);
            string cipher = Encrypt(alg, blksize, TEXT, KEY, keysize);
            Console.WriteLine("  " + cipher);
            string plain = Decrypt(alg, blksize, cipher, KEY, keysize);
            Console.WriteLine("  " + plain);
        }
        private static void Test2() 
        {
            if(Decrypt(new AesManaged { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7}, 16, "Xc252As4sZuj12tLJtDk2l60tHxu1hk4FTB0l9IK/q96cWd+Uop4lXcId1hGEmm0SN1Xjua4YqVlr3rvT+Mw4hStrnz3h8TEfdENfUMqLMY=", KEY, 16) != TEXT)
            {
                throw new Exception("Ooops");
            }
        }
        public static void Main(string[] args)
        {
            // managed implementations
            Test("AES 128 bit (managed)", new AesManaged { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7}, 16, 16);
            Test("AES 192 bit (managed)", new AesManaged { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7}, 24, 16);
            Test("AES 256 bit (managed)", new AesManaged { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7}, 32, 16);
            // CAPI implementations
            Test("AES 128 bit (CAPI)", new AesCryptoServiceProvider { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7}, 16, 16);
            Test("AES 192 bit (CAPI)", new AesCryptoServiceProvider { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7}, 24, 16);
            Test("AES 256 bit (CAPI)", new AesCryptoServiceProvider { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7}, 32, 16);
            Test("3DES 168 bit (CAPI)", new TripleDESCryptoServiceProvider { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7}, 24, 8);
            //
            Test2();
        }
    }
}

There is also a .NET version of the BouncyCastle encryption library known from the Java world.

using System;
using System.Text;

using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Security;

namespace Encrypt
{
    public class TestSymmetric
    {
        // generate random IV
        private static IRandomGenerator rng = new CryptoApiRandomGenerator();
        private static byte[] GenIV(int size)
        {
            byte[] res = new byte[size];
            rng.NextBytes(res);
            return res;
        }
        // encrypt: byte[] plain + byte[] key -> byte[] cipher
        // decrypt: byte[] cipher + byte[] key -> byte[] plain
        public static byte[] Encrypt(string alg, int blksize, byte[] plain, byte[] key) 
        {
            byte[] iv = GenIV(blksize);
            IBufferedCipher encrypt = CipherUtilities.GetCipher(alg);
            ICipherParameters param = new ParametersWithIV(new KeyParameter(key), iv);
            encrypt.Init(true, param);
            byte[] temp = encrypt.DoFinal(plain);
            byte[] res = new byte[iv.Length + temp.Length];
            Array.Copy(iv, 0, res, 0, iv.Length);
            Array.Copy(temp, 0, res, iv.Length, temp.Length);
            return res;
        }
        public static byte[] Decrypt(string alg, int blksize, byte[] cipher, byte[] key)
        {
            byte[] iv = new byte[blksize];
            Array.Copy(cipher, 0, iv, 0, iv.Length);
            byte[] temp = new byte[cipher.Length - iv.Length];
            Array.Copy(cipher,  iv.Length, temp, 0, temp.Length);
            IBufferedCipher decrypt = CipherUtilities.GetCipher(alg);
            ICipherParameters param = new ParametersWithIV(new KeyParameter(key), iv);
            decrypt.Init(false, param);
            return decrypt.DoFinal(temp);
        }
        // Base64 encode: byte[] -> string
        // Base64 decode: string -> byte[]
        private static string Encode(byte[] b)
        {
            return Convert.ToBase64String(b);
        }
        private static byte[] Decode(string s)
        {
            return Convert.FromBase64String(s);
        }
        // convert string key to byte[] key
        private static byte[] GenKey(string key, int keysize)
        {
            IDigest ha = new Sha256Digest();
            byte[] ba = Encoding.UTF8.GetBytes(key);
            ha.BlockUpdate(ba, 0, ba.Length);
            byte[] hash = new byte[32];
            ha.DoFinal(hash, 0);
            byte[] res = new Byte[keysize];
            Array.Copy(hash, 0, res, 0, keysize);
            return res;
        }
        // encrypt: string plain + string key -> Base64 string cipher
        // decrypt: Base64 string cipher + string key -> string plain
        public static string Encrypt(string alg, int blksize, string plain, string key, int keysize) 
        {
            return Encode(Encrypt(alg, blksize, Encoding.UTF8.GetBytes(plain), GenKey(key, keysize)));
        }
        public static string Decrypt(string alg, int blksize, string cipher, string key, int keysize) 
        {
            return Encoding.UTF8.GetString(Decrypt(alg, blksize, Decode(cipher), GenKey(key, keysize)));
        }
        // test
        private const string TEXT = "This is some random text to be used to test encryption.";
        private const string KEY = "This is a very long and super secret key that should really be like 4 times as long as keysize";
        private static void Test(string label, string alg, int keysize, int blksize) 
        {
            Console.WriteLine(label + ":");
            Console.WriteLine("  " + TEXT);
            string cipher = Encrypt(alg, blksize, TEXT, KEY, keysize);
            Console.WriteLine("  " + cipher);
            string plain = Decrypt(alg, blksize, cipher, KEY, keysize);
            Console.WriteLine("  " + plain);
        }
        private static void Test2() 
        {
            if(Decrypt("AES/CBC/PKCS7", 16, "Xc252As4sZuj12tLJtDk2l60tHxu1hk4FTB0l9IK/q96cWd+Uop4lXcId1hGEmm0SN1Xjua4YqVlr3rvT+Mw4hStrnz3h8TEfdENfUMqLMY=", KEY, 16) != TEXT)
            {
                throw new Exception("Ooops");
            }
        }
        public static void Main(string[] args)
        {
            Test("AES 128 bit", "AES/CBC/PKCS7", 16, 16);
            Test("AES 192 bit", "AES/CBC/PKCS7", 24, 16);
            Test("AES 256 bit", "AES/CBC/PKCS7", 32, 16);
            Test("3DES 168 bit", "DesEde/CBC/PKCS7", 24, 8);
            Test("Blowfish 256 bit", "Blowfish/CBC/PKCS7", 32, 8);
            Test("Twofish 128 bit", "Twofish/CBC/PKCS7", 16, 16);
            Test("Twofish 192 bit", "Twofish/CBC/PKCS7", 24, 16);
            Test("Twofish 256 bit", "Twofish/CBC/PKCS7", 32, 16);
            Test("CAST5 128 bit", "Cast5/CBC/PKCS7", 16, 8);
            Test("IDEA 128 bit", "Idea/CBC/PKCS7", 16, 8);
            Test2();
        }
    }
}

.NET comes with two implementations a managed (pure .NET) and a wrapper around the Windows CAPI.

Imports System
Imports System.Security.Cryptography
Imports System.Text

Namespace Encrypt
    Public Class TestSymmetric
        Private Shared rng As RandomNumberGenerator = RandomNumberGenerator.Create()
        ' generate random IV
        Private Shared Function GenIV(size As Integer) As Byte()
            Dim res As Byte() = New Byte(size - 1) {}
            rng.GetBytes(res)
            Return res
        End Function
        ' encrypt: Byte() plain + Byte() key -> Byte() cipher
        ' decrypt: Byte() cipher + Byte() key -> Byte() plain
        Public Shared Function Encrypt(alg As SymmetricAlgorithm, blksize As Integer, plain As Byte(), key As Byte()) As Byte()
            Dim iv As Byte() = GenIV(blksize)
            Dim encrypt__1 As ICryptoTransform = alg.CreateEncryptor(key, iv)
            Dim temp As Byte() = encrypt__1.TransformFinalBlock(plain, 0, plain.Length)
            Dim res As Byte() = New Byte(iv.Length + (temp.Length - 1)) {}
            Array.Copy(iv, 0, res, 0, iv.Length)
            Array.Copy(temp, 0, res, iv.Length, temp.Length)
            Return res
        End Function
        Public Shared Function Decrypt(alg As SymmetricAlgorithm, blksize As Integer, cipher As Byte(), key As Byte()) As Byte()
            Dim iv As Byte() = New Byte(blksize - 1) {}
            Array.Copy(cipher, 0, iv, 0, iv.Length)
            Dim temp As Byte() = New Byte(cipher.Length - iv.Length - 1) {}
            Array.Copy(cipher, iv.Length, temp, 0, temp.Length)
            Dim decrypt__1 As ICryptoTransform = alg.CreateDecryptor(key, iv)
            Return decrypt__1.TransformFinalBlock(temp, 0, temp.Length)
        End Function
        ' Base64 encode: Byte() -> String
        ' Base64 decode: String -> Byte()
        Private Shared Function Encode(b As Byte()) As String
            Return Convert.ToBase64String(b)
        End Function
        Private Shared Function Decode(s As String) As Byte()
            Return Convert.FromBase64String(s)
        End Function
        ' convert String key to Byte() key
        Private Shared Function GenKey(key As String, keysize As Integer) As Byte()
            Dim ha As HashAlgorithm = New SHA256Managed()
            Dim hash As Byte() = ha.ComputeHash(Encoding.UTF8.GetBytes(key))
            Dim res As Byte() = New [Byte](keysize - 1) {}
            Array.Copy(hash, 0, res, 0, keysize)
            Return res
        End Function
        ' encrypt: String plain + String key -> Base64 String cipher
        ' decrypt: Base64 String cipher + String key -> String plain
        Public Shared Function Encrypt(alg As SymmetricAlgorithm, blksize As Integer, plain As String, key As String, keysize As Integer) As String
            Return Encode(Encrypt(alg, blksize, Encoding.UTF8.GetBytes(plain), GenKey(key, keysize)))
        End Function
        Public Shared Function Decrypt(alg As SymmetricAlgorithm, blksize As Integer, cipher As String, key As String, keysize As Integer) As String
            Return Encoding.UTF8.GetString(Decrypt(alg, blksize, Decode(cipher), GenKey(key, keysize)))
        End Function
        ' test
        Private Const TEXT As String = "This is some random text to be used to test encryption."
        Private Const KEY As String = "This is a very long and super secret key that should really be like 4 times as long as keysize"
        Private Shared Sub Test(label As String, alg As SymmetricAlgorithm, keysize As Integer, blksize As Integer)
            Console.WriteLine(label & ":")
            Console.WriteLine("  " & TEXT)
            Dim cipher As String = Encrypt(alg, blksize, TEXT, KEY, keysize)
            Console.WriteLine("  " & cipher)
            Dim plain As String = Decrypt(alg, blksize, cipher, KEY, keysize)
            Console.WriteLine("  " & plain)
        End Sub
        Private Shared Sub Test2()
            If Decrypt(New AesManaged() With { _
                .Mode = CipherMode.CBC, _
                .Padding = PaddingMode.PKCS7 _
            }, 16, "Xc252As4sZuj12tLJtDk2l60tHxu1hk4FTB0l9IK/q96cWd+Uop4lXcId1hGEmm0SN1Xjua4YqVlr3rvT+Mw4hStrnz3h8TEfdENfUMqLMY=", KEY, 16) <> TEXT Then
                Throw New Exception("Ooops")
            End If
        End Sub
        Public Shared Sub Main(args As String())
            ' managed implementations
            Test("AES 128 bit (managed)", New AesManaged() With { _
                .Mode = CipherMode.CBC, _
                .Padding = PaddingMode.PKCS7 _
            }, 16, 16)
            Test("AES 192 bit (managed)", New AesManaged() With { _
                .Mode = CipherMode.CBC, _
                .Padding = PaddingMode.PKCS7 _
            }, 24, 16)
            Test("AES 256 bit (managed)", New AesManaged() With { _
                .Mode = CipherMode.CBC, _
                .Padding = PaddingMode.PKCS7 _
            }, 32, 16)
            ' CAPI implementations
            Test("AES 128 bit (CAPI)", New AesCryptoServiceProvider() With { _
                .Mode = CipherMode.CBC, _
                .Padding = PaddingMode.PKCS7 _
            }, 16, 16)
            Test("AES 192 bit (CAPI)", New AesCryptoServiceProvider() With { _
                .Mode = CipherMode.CBC, _
                .Padding = PaddingMode.PKCS7 _
            }, 24, 16)
            Test("AES 256 bit (CAPI)", New AesCryptoServiceProvider() With { _
                .Mode = CipherMode.CBC, _
                .Padding = PaddingMode.PKCS7 _
            }, 32, 16)
            Test("3DES 168 bit (CAPI)", New TripleDESCryptoServiceProvider() With { _
                .Mode = CipherMode.CBC, _
                .Padding = PaddingMode.PKCS7 _
            }, 24, 8)
            '
            Test2()
        End Sub
    End Class
End Namespace
<?php

// generate random IV
function gen_IV($size) {
    return openssl_random_pseudo_bytes($size);
}

// encrypt: binary plain + binary key -> binary cipher
// decrypt: binary cipher + binary key -> binary plain
function encrypt($algchain, $padding, $blksize, $plain, $key) {
    $iv = gen_IV($blksize);
    $temp = openssl_encrypt($plain, $algchain, $key, OPENSSL_RAW_DATA | $padding, $iv);
    return $iv. $temp;
}

function decrypt($algchain, $padding, $blksize, $cipher, $key) {
    $iv = substr($cipher, 0, $blksize);
    $temp = substr($cipher, $blksize, strlen($cipher) - $blksize);
    return openssl_decrypt($temp, $algchain, $key, OPENSSL_RAW_DATA | $padding, $iv);
}

// Base64 encode: binary -> text
// Base64 decode: text -> binary
function encode($b) {
    return base64_encode($b);   
}

function decode($s) {
    return base64_decode($s);
}

// convert text key to binary key
function gen_key($key, $keysize) {
    $hash = hash('sha256', $key, true);
    return substr($hash, 0, $keysize);
}

// encrypt: text plain + text key -> Base64 text cipher
// decrypt: Base64 text cipher + text key -> text plain
function encrypt_encode($algchain, $padding, $blksize, $plain, $key, $keysize) {
    return encode(encrypt($algchain, $padding, $blksize, $plain, gen_key($key, $keysize)));
}

function decode_decrypt($algchain, $padding, $blksize, $cipher, $key, $keysize) {
    return decrypt($algchain, $padding, $blksize, decode($cipher), gen_key($key, $keysize));
}

// test
define('TEXT', 'This is some random text to be used to test encryption.');
define('KEY', 'This is a very long and super secret key that should really be like 4 times as long as keysize');
define('OPENSSL_PKCS5_PADDING', 0); // really just not setting OPENSSL_ZERO_PADDING

function test($label, $algchain, $padding, $keysize, $blksize) {
    echo $label . ":\r\n";
    echo '  ' . TEXT . "\r\n";
    $cipher = encrypt_encode($algchain, $padding, $blksize, TEXT, KEY, $keysize);
    echo '  ' . $cipher . "\r\n";
    $plain = decode_decrypt($algchain, $padding, $blksize, $cipher, KEY, $keysize);
    echo '  ' . $plain . "\r\n";
}

function test2() {
    if(decode_decrypt('AES-128-CBC', OPENSSL_PKCS5_PADDING, 16, 'Xc252As4sZuj12tLJtDk2l60tHxu1hk4FTB0l9IK/q96cWd+Uop4lXcId1hGEmm0SN1Xjua4YqVlr3rvT+Mw4hStrnz3h8TEfdENfUMqLMY=', KEY, 16) != TEXT) {
        echo 'Ooops';
    }
}

test('AES 128 bit', 'AES-128-CBC', OPENSSL_PKCS5_PADDING, 16, 16);
test('AES 192 bit', 'AES-192-CBC', OPENSSL_PKCS5_PADDING, 24, 16);
test('AES 256 bit', 'AES-256-CBC', OPENSSL_PKCS5_PADDING, 32, 16);
test('3DES 168 bit', 'DES-EDE-CBC', OPENSSL_PKCS5_PADDING, 24, 8);
test('CAST5 128 bit', 'CAST5-CBC', OPENSSL_PKCS5_PADDING, 16, 8);
test('IDEA 128 bit', 'IDEA-CBC', OPENSSL_PKCS5_PADDING, 16, 8);
test2();

?>

A widely used encryption package for Delphi/Lazarus is DCPcrypt. It is available from various download sites. Note that the example is tested with Delphi. DCPcrypt is available for Lazarus, but I got some errors when trying with Lazarus.

program Dsym;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  DCPbase64,
  DCPcrypt2,
  DCPsha256,
  DCPrijndael,
  DCPdes,
  DCPblowfish,
  DCPcast128,
  DCPidea;

type
  bytearray = array of byte;

(* generate random iv *)
procedure genIV(var iv : bytearray);

var
  i : integer;

begin
  randomize;
  for i := Low(iv) to High(iv) do iv[i] := random(256);
end;

(*
encrypt: bytearray plain + bytearray key -> bytearray cipher
decrypt: bytearray ciper + bytearray key -> bytearray plain
*)
function encrypt(alg : TDCP_blockcipher; mode : TDCP_ciphermode; keysiz : integer; plain, key : bytearray): bytearray;

var
  iv, temp, cipher, res : bytearray;
  blksiz, i : integer;

begin
  blksiz := alg.GetBlockSize() div 8;
  SetLength(iv, blksiz);
  SetLength(cipher, ((Length(plain) + blksiz) div blksiz) * blksiz);
  SetLength(temp, Length(cipher));
  SetLength(res, Length(iv) + Length(cipher));
  genIV(iv);
  alg.CipherMode := mode;
  alg.Init(key[Low(key)], keysiz, iv);
  Move(plain[Low(plain)], temp[Low(temp)], Length(plain));
  for i := Length(plain) + 1 to Length(temp) do temp[Low(temp) + i - 1] := Length(temp) - Length(plain);
  alg.Encrypt(temp[Low(temp)], cipher[Low(cipher)], Length(cipher));
  Move(iv[Low(iv)], res[Low(res)], Length(iv));
  Move(cipher[Low(cipher)], res[Low(res) + Length(iv)], Length(cipher));
  encrypt := res;
end;

function decrypt(alg : TDCP_blockcipher; mode : TDCP_ciphermode; keysiz : integer; cipher, key : bytearray): bytearray;

var
  iv, temp, res : bytearray;
  blksiz : integer;

begin
  blksiz := alg.GetBlockSize() div 8;
  SetLength(iv, blksiz);
  SetLength(temp, Length(cipher) - blksiz);
  SetLength(res, Length(temp));
  Move(cipher[Low(cipher)], iv[Low(iv)], blksiz);
  alg.CipherMode := mode;
  alg.Init(key[Low(key)], keysiz, iv);
  Move(cipher[Low(cipher) + blksiz], temp[Low(temp)], Length(cipher) - blksiz);
  alg.Decrypt(temp[Low(temp)], res[Low(res)], Length(res));
  SetLength(res,Length(res) - ord(res[High(res)]));
  decrypt := res;
end;

(*
Base64 encode : bytearray -> string
Base64 decode : string -> bytearray
*)
procedure string2bytearray(s : string; var b : bytearray);

var
  i : integer;

begin
  SetLength(b, Length(s));
  for i := 1 to Length(s) do b[Low(b) + i -1] := ord(s[i]);
end;

function bytearray2string(b : bytearray) : string;

var
  res : string;

begin
  SetString(res, PAnsiChar(@b[Low(b)]), Length(b));
  bytearray2string := res;
end;

function encode(b : bytearray) : string;

begin
  encode := Base64EncodeStr(bytearray2string(b));
end;

function decode(s : string) : bytearray;

var
  res : bytearray;

begin
  string2bytearray(Base64DecodeStr(s), res);
  decode := res;
end;

(* convert string key to bytearray key *)
function genKey(key : string; keysiz : integer) : bytearray;

var
  md : TDCP_sha256;
  res : bytearray;

begin
  md := TDCP_sha256.Create(nil);
  md.Init;
  md.UpdateStr(key);
  SetLength(res, 32);
  md.Final(res[Low(res)]);
  SetLength(res, keysiz div 8);
  md.Free;
  genKey := res;
end;

(*
encrypt: string plain + string key -> Base64 string cipher
decrypt: Base64 string cipher + string key -> string plain
*)
function encryptString(alg : TDCP_blockcipher; mode : TDCP_ciphermode; keysiz : integer; plain, key : string) : string;

var
  plain2, key2 : bytearray;

begin
  string2bytearray(plain, plain2);
  key2 := genKey(key, keysiz);
  encryptString := encode(encrypt(alg, mode, keysiz, plain2, key2));
end;

function decryptString(alg : TDCP_blockcipher; mode : TDCP_ciphermode; keysiz : integer; cipher, key : string) : string;

var
  cipher2, key2 : bytearray;

begin
  cipher2 := decode(cipher);
  key2 := genKey(key, keysiz);
  decryptString := bytearray2string(decrypt(alg, mode, keysiz, cipher2, key2));
end;

(* test *)
const
  TEXT = 'This is some random text to be used to test encryption.';
  KEY = 'This is a very long and super secret key that should really be like 4 times as long as keysize';

procedure test(lbl : string; alg : TDCP_blockcipher; mode : TDCP_ciphermode; keysiz : integer);

var
  cipher, plain : string;

begin
  writeln(lbl + ':');
  writeln('  ' + TEXT);
  cipher := encryptString(alg, mode, keysiz, TEXT, KEY);
  writeln('  ' + cipher);
  plain := decryptString(alg, mode, keysiz, cipher, KEY);
  writeln('  ' + plain);
end;

procedure test2;

begin
  if decryptString(TDCP_rijndael.Create(nil), cmCBC, 128, 'Xc252As4sZuj12tLJtDk2l60tHxu1hk4FTB0l9IK/q96cWd+Uop4lXcId1hGEmm0SN1Xjua4YqVlr3rvT+Mw4hStrnz3h8TEfdENfUMqLMY=', KEY) <> TEXT then begin
    writeln('Ooops');
    halt;
  end;
end;

begin
  test('AES 128 bit', TDCP_rijndael.Create(nil), cmCBC, 128);
  test('AES 192 bit', TDCP_rijndael.Create(nil), cmCBC, 192);
  test('AES 256 bit', TDCP_rijndael.Create(nil), cmCBC, 256);
  test('3DES 168 bit', TDCP_3des.Create(nil), cmCBC, 192);
  test('Blowfish 256 bit', TDCP_blowfish.Create(nil), cmCBC, 256);
  test('CAST5 128 bit', TDCP_cast128.Create(nil), cmCBC, 128);
  test('IDEA 128 bit', TDCP_idea.Create(nil), cmCBC, 128);
  test2();
end.

The OpenSSL library offers 2 levels of basic encryption/hashing functions:

low level
algorithm spoecific functions
high level
general EVP functions where algorithm is a function parameter

This example is using the low level AES functions to do AES encryption.

#include <stdio.h>
#include <string.h>

#include <openssl/aes.h>
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <openssl/rand.h>

/* generate random IV */
static void gen_iv(int size, unsigned char *iv)
{
    RAND_bytes(iv, size);
}

/* encrypt: binary plain + binary key -> binary cipher */
/* decrypt: binary cipher + binary key -> binary plain */
void encrypt(const unsigned char *inbuf, int inbuflen, AES_KEY *realkey, unsigned char *outbuf, int *outbuflen)
{
    unsigned char iv[AES_BLOCK_SIZE];
    unsigned char *temp;
    int padlen;
    int i;
    gen_iv(AES_BLOCK_SIZE, iv);
    memcpy(outbuf, iv, AES_BLOCK_SIZE);
    padlen = AES_BLOCK_SIZE - inbuflen % AES_BLOCK_SIZE;
    temp = malloc(inbuflen + padlen);
    memcpy(temp, inbuf, inbuflen);
    for(i = inbuflen; i < inbuflen + padlen; i++) temp[i] = padlen;
    AES_cbc_encrypt(temp, outbuf + AES_BLOCK_SIZE, inbuflen + padlen, realkey, iv, 1);
    *outbuflen = AES_BLOCK_SIZE + inbuflen + padlen;
    free(temp);
}

void decrypt(const unsigned char *inbuf, int inbuflen, AES_KEY *realkey, unsigned char *outbuf, int *outbuflen)
{
    unsigned char iv[AES_BLOCK_SIZE];
    int padlen;
    memcpy(iv, inbuf, AES_BLOCK_SIZE);
    AES_cbc_encrypt(inbuf + AES_BLOCK_SIZE, outbuf, inbuflen - AES_BLOCK_SIZE, realkey, iv, 0);
    padlen = outbuf[inbuflen - AES_BLOCK_SIZE - 1];
    *outbuflen = inbuflen - AES_BLOCK_SIZE - padlen;
}

/* Base64 encode: binary -> text */
/* Base64 decode: text -> binary */
void encode(const unsigned char *inbuf, int inbuflen, unsigned char *outbuf, int *outbuflen)
{
    EVP_ENCODE_CTX ctx;
    int len;
    EVP_EncodeInit(&ctx);
    EVP_EncodeUpdate(&ctx, outbuf, &len, inbuf, inbuflen);
    *outbuflen = len;
    EVP_EncodeFinal(&ctx, outbuf + len, &len);
    *outbuflen += len;
    if(outbuf[*outbuflen - 1] == '\n') *outbuflen -= 1;
    outbuf[*outbuflen] = '\0'; 
}

void decode(const unsigned char *inbuf, int inbuflen, unsigned char *outbuf, int *outbuflen)
{
    EVP_ENCODE_CTX ctx;
    int len;
    EVP_DecodeInit(&ctx);
    EVP_DecodeUpdate(&ctx, outbuf, &len, inbuf, inbuflen);
    *outbuflen = len;
    EVP_DecodeFinal(&ctx, outbuf + len, &len);
    *outbuflen += len;
}

/* convert text key to binary key */
static void gen_key(const char *key, int keysize, AES_KEY *realkey, int encrypt) 
{
    SHA256_CTX ctx;
    unsigned char md[32];
    SHA256_Init(&ctx);
    SHA256_Update(&ctx, key, strlen(key));
    SHA256_Final(md, &ctx);
    if(encrypt)
    {
        AES_set_encrypt_key(md, keysize, realkey);
    }
    else
    {
        AES_set_decrypt_key(md, keysize, realkey);
    }
}

/* encrypt: text plain + text key -> Base64 text cipher */
/* decrypt: Base64 text cipher + text key -> text plain */
void encrypt_encode(const char *plain, const char *key, int keysize, char *cipher)
{
    AES_KEY realkey;
    unsigned char *temp;
    int templen, cipherlen;
    gen_key(key, keysize, &realkey, 1);
    temp = malloc(AES_BLOCK_SIZE + strlen(plain) + AES_BLOCK_SIZE);
    encrypt((unsigned char *)plain, strlen(plain), &realkey, temp, &templen);
    encode(temp, templen, (unsigned char *)cipher, &cipherlen);
    free(temp);
}

void decode_decrypt(const char *cipher, const char *key, int keysize, char *plain)
{
    AES_KEY realkey;
    unsigned char *temp;
    int templen, plainlen;
    gen_key(key, keysize, &realkey, 0);
    temp = malloc(strlen(cipher));
    decode((unsigned char *)cipher, strlen(cipher), temp, &templen);
    decrypt(temp, templen, &realkey, (unsigned char *)plain, &plainlen);
    plain[plainlen] = '\0';
    free(temp);
}

/* test */
static const char *TEXT = "This is some random text to be used to test encryption.";
static const char *KEY = "This is a very long and super secret key that should really be like 4 times as long as keysize";
 
void test(char *label, int keysize)
{
    char cipher[1000];
    char plain[1000];
    printf("%s:\n", label);
    printf("  %s\n", TEXT);
    encrypt_encode(TEXT, KEY, keysize, cipher);
    printf("  %s\n", cipher);
    decode_decrypt(cipher, KEY, keysize, plain);
    printf("  %s\n", plain);
}

void test2()
{
    char temp[1000];
    decode_decrypt("Xc252As4sZuj12tLJtDk2l60tHxu1hk4FTB0l9IK/q96cWd+Uop4lXcId1hGEmm0SN1Xjua4YqVlr3rvT+Mw4hStrnz3h8TEfdENfUMqLMY=", KEY, 128, temp);
    if(strcmp(temp, TEXT) != 0)
    {
        printf("Ooops\n");
    }
}

int main()
{
    test("AES 128 bit", 128);
    test("AES 192 bit", 192);
    test("AES 256 bit", 256);
    test2();
    return 0;
}

The OpenSSL library offers 2 levels of basic encryption/hashing functions:

low level
algorithm spoecific functions
high level
general EVP functions where algorithm is a function parameter

This example is using the high level EVP functions to do AES/3DES/IDEA/Blowfish/CAST5 encryption.

#include <stdio.h>
#include <string.h>

#include <openssl/sha.h>
#include <openssl/evp.h>
#include <openssl/rand.h>

/* generate random IV */
static void gen_iv(int size, unsigned char *iv)
{
    RAND_bytes(iv, size);
}

/* encrypt: binary plain + binary key -> binary cipher */
/* decrypt: binary cipher + binary key -> binary plain */
void encrypt(const EVP_CIPHER *alg, const unsigned char *inbuf, int inbuflen, unsigned char *realkey, int keysiz, int blksiz, unsigned char *outbuf, int *outbuflen)
{
    EVP_CIPHER_CTX ctx;
    unsigned char iv[blksiz];
    int len;
    gen_iv(blksiz, iv);
    memcpy(outbuf, iv, blksiz);
    EVP_EncryptInit(&ctx, alg, realkey, iv);
    EVP_EncryptUpdate(&ctx, outbuf + blksiz, &len, inbuf, inbuflen);
    *outbuflen = blksiz + len;
    EVP_EncryptFinal(&ctx, outbuf + blksiz + len, &len);
    *outbuflen += len;
    EVP_CIPHER_CTX_free(&ctx);
}

void decrypt(const EVP_CIPHER *alg, const unsigned char *inbuf, int inbuflen, unsigned char *realkey, int keysiz, int blksiz, unsigned char *outbuf, int *outbuflen)
{
    EVP_CIPHER_CTX ctx;
    unsigned char iv[blksiz];
    int len;
    memcpy(iv, inbuf, blksiz);
    EVP_DecryptInit(&ctx, alg, realkey, iv);
    EVP_DecryptUpdate(&ctx, outbuf, &len, inbuf + blksiz, inbuflen - blksiz);
    *outbuflen = len;
    EVP_DecryptFinal(&ctx, outbuf + len, &len);
    *outbuflen += len;
    EVP_CIPHER_CTX_free(&ctx);
}

/* Base64 encode: binary -> text */
/* Base64 decode: text -> binary */
void encode(const unsigned char *inbuf, int inbuflen, unsigned char *outbuf, int *outbuflen)
{
    EVP_ENCODE_CTX ctx;
    int len;
    EVP_EncodeInit(&ctx);
    EVP_EncodeUpdate(&ctx, outbuf, &len, inbuf, inbuflen);
    *outbuflen = len;
    EVP_EncodeFinal(&ctx, outbuf + len, &len);
    *outbuflen += len;
    if(outbuf[*outbuflen - 1] == '\n') *outbuflen -= 1;
    outbuf[*outbuflen] = '\0'; 
}

void decode(const unsigned char *inbuf, int inbuflen, unsigned char *outbuf, int *outbuflen)
{
    EVP_ENCODE_CTX ctx;
    int len;
    EVP_DecodeInit(&ctx);
    EVP_DecodeUpdate(&ctx, outbuf, &len, inbuf, inbuflen);
    *outbuflen = len;
    EVP_DecodeFinal(&ctx, outbuf + len, &len);
    *outbuflen += len;
}

/* convert text key to binary key */
static void gen_key(const char *key, unsigned char *realkey) 
{
    SHA256_CTX ctx;
    SHA256_Init(&ctx);
    SHA256_Update(&ctx, key, strlen(key));
    SHA256_Final(realkey, &ctx);
}

/* encrypt: text plain + text key -> Base64 text cipher */
/* decrypt: Base64 text cipher + text key -> text plain */
void encrypt_encode(const EVP_CIPHER *alg, const char *plain, const char *key, int keysiz, int blksiz, char *cipher)
{
    unsigned char realkey[keysiz];
    unsigned char *temp;
    int templen, cipherlen;
    gen_key(key, realkey);
    temp = malloc(blksiz + strlen(plain) + blksiz);
    encrypt(alg, (unsigned char *)plain, strlen(plain), realkey, keysiz, blksiz, temp, &templen);
    encode(temp, templen, (unsigned char *)cipher, &cipherlen);
    free(temp);
}

void decode_decrypt(const EVP_CIPHER *alg, const char *cipher, const char *key, int keysiz, int blksiz, char *plain)
{
    unsigned char realkey[keysiz];
    unsigned char *temp;
    int templen, plainlen;
    gen_key(key, realkey);
    temp = malloc(strlen(cipher));
    decode((unsigned char *)cipher, strlen(cipher), temp, &templen);
    decrypt(alg, temp, templen, realkey, keysiz, blksiz, (unsigned char *)plain, &plainlen);
    plain[plainlen] = '\0';
    free(temp);
}

/* test */
static const char *TEXT = "This is some random text to be used to test encryption.";
static const char *KEY = "This is a very long and super secret key that should really be like 4 times as long as keysize";
 
void test(char *label, const EVP_CIPHER *alg, int keysize, int blksiz)
{
    char cipher[1000];
    char plain[1000];
    printf("%s:\n", label);
    printf("  %s\n", TEXT);
    encrypt_encode(alg, TEXT, KEY, keysize, blksiz, cipher);
    printf("  %s\n", cipher);
    decode_decrypt(alg, cipher, KEY, keysize, blksiz, plain);
    printf("  %s\n", plain);
}

void test2()
{
    char temp[1000];
    decode_decrypt(EVP_aes_128_cbc(), "Xc252As4sZuj12tLJtDk2l60tHxu1hk4FTB0l9IK/q96cWd+Uop4lXcId1hGEmm0SN1Xjua4YqVlr3rvT+Mw4hStrnz3h8TEfdENfUMqLMY=", KEY, 128, 16, temp);
    if(strcmp(temp, TEXT) != 0)
    {
        printf("Ooops\n");
    }
}

int main()
{
    test("AES 128 bit", EVP_aes_128_cbc(), 16, 16);
    test("AES 192 bit", EVP_aes_192_cbc(), 24, 16);
    test("AES 256 bit", EVP_aes_256_cbc(), 32, 16);
    test("3DES 168 bit", EVP_des_ede3_cbc(), 24, 8);
    test("IDEA 128 bit", EVP_idea_cbc(), 16, 8);
    test("Blowfish 256 bit", EVP_bf_cbc(), 32, 8);
    test("CAST5 128 bit", EVP_cast5_cbc(), 16, 8);
    test2();
    return 0;
}

Hashing:

Principles:

Hashing means an irreversible transformation like:

A good hash algorithm is an algorithm that has brute force as best strategy for attack:

preimage attack
given a hash value create some data that produces that hash value
collision attack
create two sets of data that produce the same hash value

Algorithms:

Well known algorithms:

Name(s) Hash size(s) Status History
MD4 128 bit Obsolete and unsecure Invented by Ron Rivest in 1990.
From mid 90's to mid 00's several vulnerabilities was found.
MD5 128 bit Obsolete and unsecure Invented by Ron Rivest in 1992.
Serious vulnerabilities was found in second half of the 00's.
RIPEMD 128/160 bit Assumed secure Invented by Hans Dobbertin, Antoon Bosselaers and Bart Preneel in 1992.
SHA-1 160 bit Obsolete and unsecure Published by the US in 1995.
Several weakneses was found in second half of the 00's and in 2017 an actual break was done.
SHA-2 224/256/384/512 bit Good Invented by NSA in 2001.
SHA-3 224/256/384/512 bit Good Keccak algorithms was invented by Guido Bertoni, Joan Daemen, Michaël Peeters and Gilles Van Assche in 2008.
In 2015 selected algorithms became the SHA-3 standard.

Advice: do not use obsolete algoithms with known vulnerabilities like MD4, MD5 and SHA-1.

Examples:

package encrypt;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

public class TestHashing {
    // hash: byte[] -> byte[]
    public static byte[] hash(String alg, byte[] content) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance(alg);
        return md.digest(content);
    }
    // Base64 encode: byte[] -> String
    private static String encode(byte[] b) {
        return Base64.getEncoder().encodeToString(b);
    }
    // hash: String -> Base64 String
    public static String hash(String alg, String content) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        return encode(hash(alg, content.getBytes("UTF-8")));
    }
    // test
    private static final String TEXT = "This is some random text to be used to test encryption.";
    private static void test(String label, String alg) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        System.out.println(label + ":");
        String hashval = hash(alg, TEXT);
        System.out.println("  " + hashval);
        String hashval2 = hash(alg, TEXT + TEXT);
        System.out.println("  " + hashval2);
    }
    private static void test2() throws NoSuchAlgorithmException, UnsupportedEncodingException {
        if(!hash("SHA-256", TEXT).equals("oJM3t4y26uLCLtiLLNP8bhZuZi24znFEdB+QnM2d0w4=")) {
            throw new RuntimeException("Ooops");
        }
    }
    public static void main(String[] args) throws Exception {
        test("SHA-2 224 bit", "SHA-224");
        test("SHA-2 256 bit", "SHA-256");
        test("SHA-2 384 bit", "SHA-384");
        test("SHA-2 512 bit", "SHA-512");
        test("SHA-3 224 bit", "SHA3-224"); // require Java 9+
        test("SHA-3 256 bit", "SHA3-256"); // require Java 9+
        test("SHA-3 384 bit", "SHA3-384"); // require Java 9+
        test("SHA-3 512 bit", "SHA3-512"); // require Java 9+
        test2();
    }
}

By switching from standard JCE provider to open source BouncyCastle JCE provider it is possible to use more less known algorithms as well.

Code snippet:

Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        test("RIPEMD 160 bit", "RIPEMD160");
using System;
using System.Security.Cryptography;
using System.Text;

namespace Encrypt
{
    public class TestHashing
    {
        // hash: byte[] -> byte[]
        public static byte[] Hash(HashAlgorithm alg, byte[] content)
        {
            return alg.ComputeHash(content);
        }
        // Base64 encode: byte[] -> string
        private static string Encode(byte[] b)
        {
            return Convert.ToBase64String(b);
        }
        // hash: string -> Base64 string
        public static string Hash(HashAlgorithm alg, string content)
        {
            return Encode(Hash(alg, Encoding.UTF8.GetBytes(content)));
        }
        // test
        private const string TEXT = "This is some random text to be used to test encryption.";
        private static void Test(string label, HashAlgorithm alg) 
        {
            Console.WriteLine(label + ":");
            string hashval = Hash(alg, TEXT);
            Console.WriteLine("  " + hashval);
            string hashval2 = Hash(alg, TEXT + TEXT);
            Console.WriteLine("  " + hashval2);
        }
        private static void Test2() 
        {
            if(Hash(new SHA256Managed(), TEXT) != "oJM3t4y26uLCLtiLLNP8bhZuZi24znFEdB+QnM2d0w4=")
            {
                throw new Exception("Ooops");
            }
        }
        public static void Main(string[] args)
        {
            // managed implementations
            Test("SHA-2 256 bit (managed)", new SHA256Managed());
            Test("SHA-2 384 bit (managed)", new SHA384Managed());
            Test("SHA-2 512 bit (managed)", new SHA512Managed());
            // CAPI implementations
            Test("SHA-2 256 bit (CAPI)", new SHA256CryptoServiceProvider());
            Test("SHA-2 384 bit (CAPI)", new SHA384CryptoServiceProvider());
            Test("SHA-2 512 bit (CAPI)", new SHA512CryptoServiceProvider());
            //
            Test2();
        }
    }
}
using System;
using System.Text;

using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;

namespace Encrypt
{
    public class TestHashing
    {
        // hash: byte[] -> byte[]
        public static byte[] Hash(IDigest alg, byte[] content)
        {
            byte[] res = new byte[alg.GetDigestSize()];
            alg.BlockUpdate(content, 0, content.Length);
            alg.DoFinal(res, 0);
            return res;
        }
        // Base64 encode: byte[] -> string
        private static string Encode(byte[] b)
        {
            return Convert.ToBase64String(b);
        }
        // hash: string -> Base64 string
        public static string Hash(IDigest alg, string content)
        {
            return Encode(Hash(alg, Encoding.UTF8.GetBytes(content)));
        }
        // test
        private const string TEXT = "This is some random text to be used to test encryption.";
        private static void Test(string label, IDigest alg) 
        {
            Console.WriteLine(label + ":");
            string hashval = Hash(alg, TEXT);
            Console.WriteLine("  " + hashval);
            string hashval2 = Hash(alg, TEXT + TEXT);
            Console.WriteLine("  " + hashval2);
        }
        private static void Test2() 
        {
            if(Hash(new Sha256Digest(), TEXT) != "oJM3t4y26uLCLtiLLNP8bhZuZi24znFEdB+QnM2d0w4=")
            {
                throw new Exception("Ooops");
            }
        }
        public static void Main(string[] args)
        {
            Test("SHA-2 224 bit", new Sha224Digest());
            Test("SHA-2 256 bit", new Sha256Digest());
            Test("SHA-2 384 bit", new Sha384Digest());
            Test("SHA-2 512 bit", new Sha512Digest());
            Test("SHA-3 224 bit", new Sha3Digest(224));
            Test("SHA-3 256 bit", new Sha3Digest(256));
            Test("SHA-3 384 bit", new Sha3Digest(384));
            Test("SHA-3 512 bit", new Sha3Digest(512));
            Test("RIPEMD 160 bit", new RipeMD160Digest());
            Test2();
        }
    }
}
Imports System
Imports System.Security.Cryptography
Imports System.Text

Namespace Encrypt
    Public Class TestHashing
        ' hash: Byte() -> Byte()
        Public Shared Function Hash(alg As HashAlgorithm, content As Byte()) As Byte()
            Return alg.ComputeHash(content)
        End Function
        ' Base64 encode: Byte() -> String
        Private Shared Function Encode(b As Byte()) As String
            Return Convert.ToBase64String(b)
        End Function
        ' hash: String -> Base64 String
        Public Shared Function Hash(alg As HashAlgorithm, content As String) As String
            Return Encode(Hash(alg, Encoding.UTF8.GetBytes(content)))
        End Function
        ' test
        Private Const TEXT As String = "This is some random text to be used to test encryption."
        Private Shared Sub Test(label As String, alg As HashAlgorithm)
            Console.WriteLine(label & ":")
            Dim hashval As String = Hash(alg, TEXT)
            Console.WriteLine("  " & hashval)
            Dim hashval2 As String = Hash(alg, TEXT & TEXT)
            Console.WriteLine("  " & hashval2)
        End Sub
        Private Shared Sub Test2()
            If Hash(New SHA256Managed(), TEXT) <> "oJM3t4y26uLCLtiLLNP8bhZuZi24znFEdB+QnM2d0w4=" Then
                Throw New Exception("Ooops")
            End If
        End Sub
        Public Shared Sub Main(args As String())
            ' managed implementations
            Test("SHA-2 256 bit (managed)", New SHA256Managed())
            Test("SHA-2 384 bit (managed)", New SHA384Managed())
            Test("SHA-2 512 bit (managed)", New SHA512Managed())
            ' CAPI implementations
            Test("SHA-2 256 bit (CAPI)", New SHA256CryptoServiceProvider())
            Test("SHA-2 384 bit (CAPI)", New SHA384CryptoServiceProvider())
            Test("SHA-2 512 bit (CAPI)", New SHA512CryptoServiceProvider())
            '
            Test2()
        End Sub
    End Class
End Namespace
<?php

// hash: binary -> binary
function myhash($alg, $content) {
    return hash($alg, $content, true);    
}

// Base64 encode: binary -> text
function encode($b) {
    return base64_encode($b);
}

// hash: text -> Base64 text 
function hash_encode($alg, $content) {
    return encode(myhash($alg, $content));
}

// test
define('TEXT', 'This is some random text to be used to test encryption.');

function test($label, $alg) {
    echo $label . ":\r\n";
    $hashval = hash_encode($alg, TEXT);
    echo '  ' . $hashval . "\r\n";
    $hashval2 = hash_encode($alg, TEXT . TEXT);
    echo '  ' . $hashval2 . "\r\n";
}

function test2() {
    if(hash_encode('SHA256', TEXT) != 'oJM3t4y26uLCLtiLLNP8bhZuZi24znFEdB+QnM2d0w4=') {
        echo 'Ooops';
    }
}

test('SHA-2 256 bit', 'SHA256');
test('SHA-2 384 bit', 'SHA384');
test('SHA-2 512 bit', 'SHA512');
test('SHA-3 256 bit', 'SHA3-256'); // require PHP 7.1+
test('SHA-3 384 bit', 'SHA3-384'); // require PHP 7.1+
test('SHA-3 512 bit', 'SHA3-512'); // require PHP 7.1+
test2();
?>

A widely used encryption package for Delphi/Lazarus is DCPcrypt. It is available from various download sites. Note that the example is tested with Delphi. DCPcrypt is available for Lazarus, but I got some errors when trying with Lazarus.

program Dhash;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  DCPbase64,
  DCPcrypt2,
  DCPsha256,
  DCPsha512,
  DCPripemd160;

type
  bytearray = array of byte;


(* hash: bytearray -> bytearray *)
function hash(alg : TDCP_hash; content : bytearray) : bytearray;

var
  res : bytearray;

begin
  alg.Init;
  alg.Update(content[Low(content)], Length(content));
  SetLength(res, alg.GetHashSize() div 8);
  alg.Final(res[Low(res)]);
  hash := res;
end;

(* Base64 encode : bytearray -> string *)
procedure string2bytearray(s : string; var b : bytearray);

var
  i : integer;

begin
  SetLength(b, Length(s));
  for i := 1 to Length(s) do b[Low(b) + i -1] := ord(s[i]);
end;

function bytearray2string(b : bytearray) : string;

var
  res : string;

begin
  SetString(res, PAnsiChar(@b[Low(b)]), Length(b));
  bytearray2string := res;
end;

function encode(b : bytearray) : string;

begin
  encode := Base64EncodeStr(bytearray2string(b));
end;

(* hash: string -> Base64 string *)
function hashString(alg : TDCP_hash; content : string) : string;

var
  content2 : bytearray;

begin
  string2bytearray(content, content2);
  hashString := encode(hash(alg, content2));
end;

(* test *)
const
  TEXT = 'This is some random text to be used to test encryption.';

procedure test(lbl : string; alg : TDCP_hash);

begin
  writeln(lbl + ':');
  writeln('  ' + hashString(alg, TEXT));
  writeln('  ' + hashString(alg, TEXT + TEXT));
end;

procedure test2;

begin
  if hashString(TDCP_sha256.Create(nil), TEXT) <> 'oJM3t4y26uLCLtiLLNP8bhZuZi24znFEdB+QnM2d0w4=' then begin
    writeln('Ooops');
    halt;
  end;
end;

begin
  test('SHA-2 256 bit', TDCP_sha256.Create(nil));
  test('SHA-2 512 bit', TDCP_sha512.Create(nil));
  test('RIPEMED 160 bit', TDCP_ripemd160.Create(nil));
  test2();
end.

The OpenSSL library offers 2 levels of basic encryption/hashing functions:

low level
algorithm spoecific functions
high level
general EVP functions where algorithm is a function parameter

This example is using the low level SHA256 functions to do SHA256 hashing.

#include <stdio.h>
#include <string.h>

#include <openssl/sha.h>
#include <openssl/evp.h>

/* hash: binary -> binary */
void hash(const unsigned char *content, int contentlen, unsigned char md[32])
{
    SHA256_CTX ctx;
    SHA256_Init(&ctx);
    SHA256_Update(&ctx, content, contentlen);
    SHA256_Final(md, &ctx);
}

/* Base64 encode: binary -> text */
void encode(const unsigned char *inbuf, int inbuflen, unsigned char *outbuf, int *outbuflen)
{
    EVP_ENCODE_CTX ctx;
    int len;
    EVP_EncodeInit(&ctx);
    EVP_EncodeUpdate(&ctx, outbuf, &len, inbuf, inbuflen);
    *outbuflen = len;
    EVP_EncodeFinal(&ctx, outbuf + len, &len);
    *outbuflen += len;
    if(outbuf[*outbuflen - 1] == '\n') *outbuflen -= 1;
    outbuf[*outbuflen] = '\0'; 
}

/* hash: text -> Base64 text */
void hash_encode(const char *content, char *md)
{
    unsigned char temp[32];
    int mdlen;
    hash((unsigned char *)content, strlen(content), temp);
    encode(temp, sizeof(temp), (unsigned char *)md, &mdlen);
    md[mdlen] = '\0';
}

/* test */
static const char *TEXT = "This is some random text to be used to test encryption.";
static const char *TEXT2 = "This is some random text to be used to test encryption.This is some random text to be used to test encryption.";
 
void test(char *label)
{
    char hashval[100];
    char hashval2[100];
    printf("%s:\n", label);
    hash_encode(TEXT, hashval);
    printf("  %s\n", hashval);
    hash_encode(TEXT2, hashval2);
    printf("  %s\n", hashval2);
}

void test2()
{
    char temp[100];
    hash_encode(TEXT, temp);
    if(strcmp(temp, "oJM3t4y26uLCLtiLLNP8bhZuZi24znFEdB+QnM2d0w4=") != 0)
    {
        printf("Ooops\n");
    }
}

int main()
{
    test("SHA 256 bit");
    test2();
    return 0;
}

The OpenSSL library offers 2 levels of basic encryption/hashing functions:

low level
algorithm spoecific functions
high level
general EVP functions where algorithm is a function parameter

This example is using the high level EVP functions to do SHA-2/RIPEMD hashing.

#include <stdio.h>
#include <string.h>

#include <openssl/sha.h>
#include <openssl/evp.h>

/* hash: binary -> binary */
void hash(const EVP_MD *alg, const unsigned char *content, int contentlen, unsigned char *md, int *mdlen)
{
    EVP_MD_CTX ctx;
    EVP_MD_CTX_init(&ctx); 
    EVP_DigestInit_ex(&ctx, alg, NULL);
    EVP_DigestUpdate(&ctx, content, contentlen);
    EVP_DigestFinal_ex(&ctx, md, (unsigned int *)mdlen);
    EVP_MD_CTX_cleanup(&ctx);
}

/* Base64 encode: binary -> text */
void encode(const unsigned char *inbuf, int inbuflen, unsigned char *outbuf, int *outbuflen)
{
    EVP_ENCODE_CTX ctx;
    int len;
    EVP_EncodeInit(&ctx);
    EVP_EncodeUpdate(&ctx, outbuf, &len, inbuf, inbuflen);
    *outbuflen = len;
    EVP_EncodeFinal(&ctx, outbuf + len, &len);
    *outbuflen += len;
    if(outbuf[*outbuflen - 1] == '\n') *outbuflen -= 1;
    outbuf[*outbuflen] = '\0'; 
}

/* hash: text -> Base64 text */
void hash_encode(const EVP_MD *alg, const char *content, char *md)
{
    unsigned char temp[100];
    int mdlen;
    int templen;
    hash(alg, (unsigned char *)content, strlen(content), temp, &templen);
    encode(temp, templen, (unsigned char *)md, &mdlen);
    md[mdlen] = '\0';
}

/* test */
static const char *TEXT = "This is some random text to be used to test encryption.";
static const char *TEXT2 = "This is some random text to be used to test encryption.This is some random text to be used to test encryption.";
 
void test(char *label, const EVP_MD *alg)
{
    char hashval[100];
    char hashval2[100];
    printf("%s:\n", label);
    hash_encode(alg, TEXT, hashval);
    printf("  %s\n", hashval);
    hash_encode(alg, TEXT2, hashval2);
    printf("  %s\n", hashval2);
}

void test2()
{
    char temp[100];
    hash_encode(EVP_sha256(), TEXT, temp);
    if(strcmp(temp, "oJM3t4y26uLCLtiLLNP8bhZuZi24znFEdB+QnM2d0w4=") != 0)
    {
        printf("Ooops\n");
    }
}

int main()
{
    test("SHA-2 224 bit", EVP_sha224());
    test("SHA-2 256 bit", EVP_sha256());
    test("SHA-2 384 bit", EVP_sha384());
    test("SHA-2 512 bit", EVP_sha512());
    test("RIPEMD 160 bit", EVP_ripemd160());
    test2();
    return 0;
}

Password hashing:

Why hash password?

Passwords should always be stored hashed never as plain text.

Everybody hope that their system never gets compromised. But the reality is that there is always a risk it could happen - vulnerabilities in internal software, vulnerabilities in external software, human errors etc..

If the system gets compromised and the attacker get hold of the user database with passwords, then it is bad if the attacker can see the actual passwords. Because it will allow the attacker to login as that user and it may allow the attacker to login to other accounts belonging to the same user, if the user has used the same or very similar passwords there (it is not good password practice to do so, but it happens all the time anyway).

The solution is to not store the plain text password but to store a hash of the password.

So instead if testing:

if entered_password = stored_passwoird then
    ...
end if

one test:

if hash(entered_password) = hash(stored_passwoird) then
    ...
end if

Note that the risk of two different passwords ending up with same hash is considered negliable.

Password should always be hashed with a random salt.

There is a problem with just hashing passwords. The same password always result in the same hash. So it is possible to generate large tables with passwords and resulting hashes. Such tables are called rainbow tables. To reverse a hashed password one simple lookup the hash in the table.

The solution is to add a salt - not use hash(password) but hash(salt concat password), where salt is a random value.

Note that salt does not need to be secret - it just need to be randdom.

Salt should always be unique per user.

If the same random salt is used for all users then it is possible to check all users for having a certain password with a single hash calculation.

The solution is to have different salts for each user, so that each user becomes a separate problem for the attacker.

Algorithms:

An obvious question is why password hashing is a special case - after all a hash algorithm can hash any data including passwords.

The problem is that because:

then brute force cracking of hashed passwords has become a risk.

10-15 years ago it was OK to use SHA-256 or SHA-512 for pasword hashing. If it needed to be a little bit more secure then one just did 10/100/1000/10000 rounds of hashing. But the world has changed. Hardware in general has become faster and Bitcoin mining has opened up for specialized SHA-256 calculation hardware.

As a result new specialized password hashing algorithms has been invented: PBKDF2, BCrypt, SCrypt and Argon2.

These algorithms are designed to be slow and take parameters that make it easy to control how slow they are.

The math:

Let us illustrate the problem with some math.

Hash speed:

Technology SHA-256 calculations per second
CPU 10 million per core
GPU 500 million per card
FPGA 1 billion
ASIC 10 trillion

Time to test all possible passwords:

Passwords to test (in regex format) 1 PC with 4 cores 1 PC with 1 high end GPU 100000 cores in cloud 1 ASIC thingy
[A-Z0-9]{6} no time no time no time no time
[A-Z0-9]{8} no time no time no time no time
[A-Z0-9]{10} 3 years 84 days no time no time
[A-Z0-9]{12} 4 thousand years 301 years 55 days 5 days
[A-Za-z0-9]{6} no time no time no time no time
[A-Za-z0-9]{8} 63 days 5 days no time no time
[A-Za-z0-9]{10} 665 years 53 years 10 days 1 day
[A-Za-z0-9]{12} 2.6 million years 205 thousand years 103 years 10 years
1 word no time no time no time no time
2 words 7 days half day no time no time
3 words 99 thousand years 8 thousand years 4 years 145 days
4 words 495 billion years 40 billion years 20 million years 2 million years

Either we need very long passwords or we need algorithms that are much slower than SHA-256.

Examples:

package encrypt;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
import java.util.Random;

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;

public class TestPasswordHashing {
    private static final String PREFIX = "pbkdf2";
    // generate salt
    private static Random rng = new SecureRandom();
    private static byte[] genSalt(int size) {
        byte[] res = new byte[size];
        rng.nextBytes(res);
        return res;
    }
    // hash: char[] + byte[] salt -> byte[]
    public static byte[] hash(String alg, char[] pw, byte[] salt, int nit, int keylen) throws NoSuchAlgorithmException, InvalidKeySpecException {
        PBEKeySpec spec = new PBEKeySpec(pw, salt, nit, keylen);
        SecretKeyFactory skf = SecretKeyFactory.getInstance(alg);
        return skf.generateSecret(spec).getEncoded();
    }
    // Base64 encode: byte[] -> String
    // Base64 decode: String -> byte[]
    private static String encode(byte[] b) {
        return Base64.getEncoder().encodeToString(b);
    }
    private static byte[] decode(String s) {
        return Base64.getDecoder().decode(s);
    }
    // hash: String -> Base64 String
    // verify: String + saved hash String-> boolean
    public static String hash(String alg, String pw, int saltlen, int nit, int keylen) throws NoSuchAlgorithmException, InvalidKeySpecException  {
        byte[] salt = genSalt(saltlen);
        return String.format("%s$%d$%d$%s$%s", PREFIX, nit, keylen, encode(salt), encode(hash(alg, pw.toCharArray(), salt, nit, keylen)));
    }
    public static boolean verify(String alg, String pw, String savedhash) throws NoSuchAlgorithmException, InvalidKeySpecException {
        String[] parts = savedhash.split("\\$");
        if(!parts[0].equals(PREFIX)) throw new RuntimeException("Unsupported algorithm: " + parts[0]);
        int nit = Integer.parseInt(parts[1]);
        int keylen = Integer.parseInt(parts[2]);
        byte[] salt = decode(parts[3]);
        return encode(hash(alg, pw.toCharArray(), salt, nit, keylen)).equals(parts[4]);
    }
    // test
    private static final String PW = "A super secret password";
    private static void test(String label, String alg, int saltlen, int nit, int keylen) throws NoSuchAlgorithmException, InvalidKeySpecException {
        System.out.println(label + ":");
        String hashval = hash(alg, PW, saltlen, nit, keylen);
        System.out.println("  " + hashval);
        boolean ok = verify(alg, PW, hashval);
        System.out.println("  " + ok);
        boolean ok2 = verify(alg, PW + "X", hashval);
        System.out.println("  " + ok2);
    }
    private static void test2() throws NoSuchAlgorithmException, InvalidKeySpecException  {
        if(!verify("PBKDF2WithHmacSHA1", PW, "pbkdf2$10000$512$OPf3qDpgi7iIyQ/Cn2XUHQ==$3T8Yhih2SEq22bnbhCucHs9TTQmsSz28O8jk2cvYh6ihZhNHBcOXsUlX2UAKWJEdmjAA+R8yMOAqtJYdSfqYwg==")) {
            throw new RuntimeException("Ooops");
        }
    }
    public static void main(String[] args) throws Exception {
        test("PBKDF2 with HMAC SHA-1", "PBKDF2WithHmacSHA1", 16, 10000, 512);
        test("PBKDF2 with HMAC SHA-512", "PBKDF2WithHmacSHA512", 16, 10000, 512);
        test2();
    }
}
using System;
using System.Security.Cryptography;
using System.Text;

namespace Encrypt
{
    public class TestPasswordHashing
    {
        private const string PREFIX = "pbkdf2";
        // generate salt
        private static RandomNumberGenerator rng = RandomNumberGenerator.Create();
        private static byte[] GenSalt(int size)
        {
            byte[] res = new byte[size];
            rng.GetBytes(res);
            return res;
        }
        // hash: byte[] + byte[] salt -> byte[]
        public static byte[] Hash(byte[] pw, byte[] salt, int nit, int keylen)
        {
            Rfc2898DeriveBytes alg = new Rfc2898DeriveBytes(pw, salt, nit);
            return alg.GetBytes(keylen / 8);
        }
        // Base64 encode: byte[] -> string
        // Base64 decode: string -> byte[]
        private static string Encode(byte[] b)
        {
            return Convert.ToBase64String(b);
        }
        private static byte[] Decode(string s)
        {
            return Convert.FromBase64String(s);
        }
        // hash: string -> Base64 string
        // verify: string + saved hash string -> bool
        public static string Hash(string pw, int saltlen, int nit, int keylen)
        {
            byte[] salt = GenSalt(saltlen);
            return String.Format("{0}${1}${2}${3}${4}", PREFIX, nit, keylen, Encode(salt), Encode(Hash(Encoding.UTF8.GetBytes(pw), salt, nit, keylen)));
        }
        public static bool Verify(string pw, string savedhash) {
            string[] parts = savedhash.Split('$');
            if(parts[0] != PREFIX) throw new Exception("Unsupported algorithm: " + parts[0]);
            int nit = int.Parse(parts[1]);
            int keylen = int.Parse(parts[2]);
            byte[] salt = Decode(parts[3]);
            return Encode(Hash(Encoding.UTF8.GetBytes(pw), salt, nit, keylen)) == parts[4];
        }
        // test
        private const string PW = "A super secret password";
        private static void Test(string label, int saltlen, int nit, int keylen) 
        {
            Console.WriteLine(label + ":");
            string hashval = Hash(PW, saltlen, nit, keylen);
            Console.WriteLine("  " + hashval);
            bool ok = Verify(PW, hashval);
            Console.WriteLine("  " + ok);
            bool ok2 = Verify(PW + "X", hashval);
            Console.WriteLine("  " + ok2);
        }
        private static void Test2() 
        {
            if(!Verify(PW, "pbkdf2$10000$512$OPf3qDpgi7iIyQ/Cn2XUHQ==$3T8Yhih2SEq22bnbhCucHs9TTQmsSz28O8jk2cvYh6ihZhNHBcOXsUlX2UAKWJEdmjAA+R8yMOAqtJYdSfqYwg=="))
            {
                throw new Exception("Ooops");
            }
        }
        public static void Main(string[] args)
        {
            Test("PBKDF2 with HMAC SHA-1", 16, 10000, 512);
            Test2();
        }
    }
}
Imports System
Imports System.Security.Cryptography
Imports System.Text

Namespace Encrypt
    Public Class TestPasswordHashing
        Private Const PREFIX As String = "pbkdf2"
        ' generate salt
        Private Shared rng As RandomNumberGenerator = RandomNumberGenerator.Create()
        Private Shared Function GenSalt(size As Integer) As Byte()
            Dim res As Byte() = New Byte(size - 1) {}
            rng.GetBytes(res)
            Return res
        End Function
        ' hash: Byte() + Byte() salt -> Byte()
        Public Shared Function Hash(pw As Byte(), salt As Byte(), nit As Integer, keylen As Integer) As Byte()
            Dim alg As New Rfc2898DeriveBytes(pw, salt, nit)
            Return alg.GetBytes(keylen \ 8)
        End Function
        ' Base64 encode: Byte() -> String
        ' Base64 decode: String -> Byte()
        Private Shared Function Encode(b As Byte()) As String
            Return Convert.ToBase64String(b)
        End Function
        Private Shared Function Decode(s As String) As Byte()
            Return Convert.FromBase64String(s)
        End Function
        ' hash: String -> Base64 String
        ' verify: String + saved hash String -> Boolean
        Public Shared Function Hash(pw As String, saltlen As Integer, nit As Integer, keylen As Integer) As String
            Dim salt As Byte() = GenSalt(saltlen)
            Return [String].Format("{0}${1}${2}${3}${4}", PREFIX, nit, keylen, Encode(salt), Encode(Hash(Encoding.UTF8.GetBytes(pw), salt, nit, keylen)))
        End Function
        Public Shared Function Verify(pw As String, savedhash As String) As Boolean
            Dim parts As String() = savedhash.Split("$"C)
            If parts(0) <> PREFIX Then
                Throw New Exception("Unsupported algorithm: " & parts(0))
            End If
            Dim nit As Integer = Integer.Parse(parts(1))
            Dim keylen As Integer = Integer.Parse(parts(2))
            Dim salt As Byte() = Decode(parts(3))
            Return Encode(Hash(Encoding.UTF8.GetBytes(pw), salt, nit, keylen)) = parts(4)
        End Function
        ' test
        Private Const PW As String = "A super secret password"
        Private Shared Sub Test(label As String, saltlen As Integer, nit As Integer, keylen As Integer)
            Console.WriteLine(label & ":")
            Dim hashval As String = Hash(PW, saltlen, nit, keylen)
            Console.WriteLine("  " & hashval)
            Dim ok As Boolean = Verify(PW, hashval)
            Console.WriteLine("  " & ok)
            Dim ok2 As Boolean = Verify(PW & "X", hashval)
            Console.WriteLine("  " & ok2)
        End Sub
        Private Shared Sub Test2()
            If Not Verify(PW, "pbkdf2$10000$512$OPf3qDpgi7iIyQ/Cn2XUHQ==$3T8Yhih2SEq22bnbhCucHs9TTQmsSz28O8jk2cvYh6ihZhNHBcOXsUlX2UAKWJEdmjAA+R8yMOAqtJYdSfqYwg==") Then
                Throw New Exception("Ooops")
            End If
        End Sub
        Public Shared Sub Main(args As String())
            Test("PBKDF2 with HMAC SHA-1", 16, 10000, 512)
            Test2()
        End Sub
    End Class
End Namespace
<?php

// generate salt
function gen_salt($size) {
    return openssl_random_pseudo_bytes($size);
}

// hash: text -> Base64 text
// verify: text + saved hash -> true/false
function myhash($alg, $pw, $opt) {
    if(array_key_exists('salt', $opt)) {
        $saltlen = $opt['salt'];
        $opt['salt'] = gen_salt($saltlen);
    }
    return password_hash($pw, $alg, $opt);
}

function verify($pw, $savedhash) {
    return password_verify($pw, $savedhash);    
}

// test
define('PW', 'A super secret password');

function test($label, $alg, $opt) {
    echo $label . ":\r\n";
    $hashval = myhash($alg, PW, $opt);
    echo '  ' . $hashval . "\r\n";
    $ok = verify(PW, $hashval);
    echo '  ' . ($ok ? 'True' : 'False') . "\r\n";
    $ok2 = verify(PW + 'X', $hashval);
    echo '  ' . ($ok2 ? 'True' : 'False') . "\r\n";
}

test('BCrypt', PASSWORD_BCRYPT, array('cost' => 15, 'salt' => 32));
test('Argon2i', PASSWORD_ARGON2I, array()); // require PHP 7.2+
?>

Asymmetric encryption:

Principles:

Encryption means a reversible transformation like:

Asymmetric encryption is called asymmetric because it uses two different keys - one for encryption and another one for decryption.

So:

Often one of they keys are called "public key" and the other key "private key" to indicate that one key is not secret and the other is secret.

Algorithms:

Well known algorithms:

Name(s) Key size(s) Status History
RSA 512/1024/2048/3072/4096 bits Good Invented by Ron Rivest, Adi Shamir and Leonard Adleman in 1977.
DSA 1024/2048/3072/4096 bits Good Invented by US in 1991.
ECC
Elliptic Curve Cryptography
256/384 bit (equivalent of 2048/3072 bit RSA) Good Invented by Neal Koblitz and Victor Miller in 1985.

Advice: do not use asymmetric encryption for more than 1 block - if longer data needs to be encrypted then encrypt the data with symmetric encryption with a random key and encrypt that key with asymmetric encryption. See later section.

Examples:

package encrypt;

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

public class TestAsymmetric {
    // encrypt: byte[] plain + byte[] private key -> byte[] cipher
    // decrypt: byte[] cipher + byte[] public key -> byte[] plain
    public static byte[] encrypt(String algchainpad, String alg, byte[] plain, byte[] serprivkey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException {
        KeyFactory kf = KeyFactory.getInstance(alg);
        PrivateKey privkey = kf.generatePrivate(new PKCS8EncodedKeySpec(serprivkey));
        Cipher c = Cipher.getInstance(algchainpad);
        c.init(Cipher.ENCRYPT_MODE, privkey);
        return c.doFinal(plain);
    }
    public static byte[] decrypt(String algchainpad, String alg, byte[] cipher, byte[] serpubkey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException {
        KeyFactory kf = KeyFactory.getInstance(alg);
        PublicKey pubkey = kf.generatePublic(new X509EncodedKeySpec(serpubkey));
        Cipher c = Cipher.getInstance(algchainpad);
        c.init(Cipher.DECRYPT_MODE, pubkey);
        return c.doFinal(cipher);
    }
    // Base64 encode: byte[] -> String
    // Base64 decode: String -> byte[]
    private static String encode(byte[] b) {
        return Base64.getEncoder().encodeToString(b);
    }
    private static byte[] decode(String s) {
        return Base64.getDecoder().decode(s);
    }
    // encrypt: String plain + byte[] private key -> Base64 String cipher
    // decrypt: Base64 String cipher + byte[] public key -> String plain
    public static String encrypt(String algchainpad, String alg, String plain, byte[] serprivkey) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException {
        return encode(encrypt(algchainpad, alg, plain.getBytes("UTF-8"), serprivkey));
    }
    public static String decrypt(String algchainpad, String alg, String cipher, byte[] serpubkey) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException {
        return new String(decrypt(algchainpad, alg, decode(cipher), serpubkey), "UTF-8");
    }
    // test
    private static final String TEXT = "This is some random text to be used to test encryption.";
    private static void test(String label, String algchainpad, String alg, int bits) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException {
        System.out.println(label + ":");
        // generate keys
        KeyPairGenerator kpg = KeyPairGenerator.getInstance(alg);
        kpg.initialize(bits);
        KeyPair kp = kpg.generateKeyPair();
        PrivateKey privkey = kp.getPrivate();
        PublicKey pubkey = kp.getPublic();
        byte[] serprivkey = privkey.getEncoded();
        byte[] serpubkey = pubkey.getEncoded();
        // real test
        System.out.println("  " + TEXT);
        String cipher = encrypt(algchainpad, alg, TEXT, serprivkey);
        System.out.println("  " + cipher);
        String plain = decrypt(algchainpad, alg, cipher, serpubkey);
        System.out.println("  " + plain);
    }
    public static void main(String[] args) throws Exception {
        test("RSA 2048 bit", "RSA/ECB/PKCS1Padding", "RSA", 2048);
        test("RSA 3072 bit", "RSA/ECB/PKCS1Padding", "RSA", 3072);
    }
}
using System;
using System.Security.Cryptography;
using System.Text;

namespace Encrypt
{
    public class TestAsymmetric
    {
        // encrypt: byte[] plain + byte[] keys -> byte[] cipher
        // decrypt: byte[] cipher + byte[] keys -> byte[] plain
        public static byte[] Encrypt(byte[] plain, byte[] keyblob)
        {
            RSACryptoServiceProvider alg = new RSACryptoServiceProvider();
            alg.ImportCspBlob(keyblob);
            return alg.Encrypt(plain, false);
        }
        public static byte[] Decrypt(byte[] cipher, byte[] keyblob)
        {
            RSACryptoServiceProvider alg = new RSACryptoServiceProvider();
            alg.ImportCspBlob(keyblob);
            return alg.Decrypt(cipher, false);
        }
        // Base64 encode: byte[] -> string
        // Base64 decode: string -> byte[]
        private static string Encode(byte[] b)
        {
            return Convert.ToBase64String(b);
        }
        private static byte[] Decode(string s)
        {
            return Convert.FromBase64String(s);
        }
        // encrypt: string plain + byte[] keys -> Base64 string cipher
        // decrypt: Base64 string cipher + byte[] keys -> string plain
        public static string Encrypt(string plain, byte[] keyblob)
        {
            return Encode(Encrypt(Encoding.UTF8.GetBytes(plain), keyblob));
        }
        public static string Decrypt(string cipher, byte[] keyblob)
        {
            return Encoding.UTF8.GetString(Decrypt(Decode(cipher), keyblob));
        }
        // test
        private const string TEXT = "This is some random text to be used to test encryption.";
        private static void Test(string label, int bits) 
        {
            Console.WriteLine(label + ":");
            // generate keys
            RSACryptoServiceProvider alg = new RSACryptoServiceProvider { KeySize = bits };
            byte[] keyblob = alg.ExportCspBlob(true);
            // real test
            Console.WriteLine("  " + TEXT);
            string cipher = Encrypt(TEXT, keyblob);
            Console.WriteLine("  " + cipher);
            string plain = Decrypt(cipher, keyblob);
            Console.WriteLine("  " + plain);
        }
        public static void Main(string[] args)
        {
            // CAPI implementations
            Test("RSA 2048 bit (CAPI)", 2048);
            Test("RSA 3072 bit (CAPI)", 3072);
            //
        }
    }
}
using System;
using System.Text;

using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.X509;

namespace Encrypt
{
    public class TestAsymmetric
    {
        // encrypt: byte[] plain + byte[] keys -> byte[] cipher
        // decrypt: byte[] cipher + byte[] keys -> byte[] plain
        public static byte[] Encrypt(byte[] plain, byte[] serprivkey)
        {
            IAsymmetricBlockCipher eng = new RsaEngine();
            AsymmetricKeyParameter privkey = PrivateKeyFactory.CreateKey(PrivateKeyInfo.GetInstance(serprivkey));
            eng.Init(true, privkey);
            return eng.ProcessBlock(plain, 0, plain.Length);
        }
        public static byte[] Decrypt(byte[] cipher, byte[] serpubkey)
        {
            IAsymmetricBlockCipher eng = new RsaEngine();
            AsymmetricKeyParameter pubkey = PublicKeyFactory.CreateKey(SubjectPublicKeyInfo.GetInstance(serpubkey));
            eng.Init(false, pubkey);
            return eng.ProcessBlock(cipher, 0, cipher.Length);
        }
        // Base64 encode: byte[] -> string
        // Base64 decode: string -> byte[]
        private static string Encode(byte[] b)
        {
            return Convert.ToBase64String(b);
        }
        private static byte[] Decode(string s)
        {
            return Convert.FromBase64String(s);
        }
        // encrypt: string plain + byte[] keys -> Base64 string cipher
        // decrypt: Base64 string cipher + byte[] keys -> string plain
        public static string Encrypt(string plain, byte[] serprivkey)
        {
            return Encode(Encrypt(Encoding.UTF8.GetBytes(plain), serprivkey));
        }
        public static string Decrypt(string cipher, byte[] serpubkey)
        {
            return Encoding.UTF8.GetString(Decrypt(Decode(cipher), serpubkey));
        }
        // test
        private const string TEXT = "This is some random text to be used to test encryption.";
        private static void Test(string label, int bits) 
        {
            Console.WriteLine(label + ":");
            // generate keys
            RsaKeyPairGenerator kpg = new RsaKeyPairGenerator();
            kpg.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), bits));
            AsymmetricCipherKeyPair kp = kpg.GenerateKeyPair();
            AsymmetricKeyParameter pubkey = kp.Public;
            AsymmetricKeyParameter privkey = kp.Private;
            byte[] serpubkey = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pubkey).GetDerEncoded();
            byte[] serprivkey = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privkey).GetDerEncoded();
            // real test
            Console.WriteLine("  " + TEXT);
            string cipher = Encrypt(TEXT, serprivkey);
            Console.WriteLine("  " + cipher);
            string plain = Decrypt(cipher, serpubkey);
            Console.WriteLine("  " + plain);
        }
        public static void Main(string[] args)
        {
            Test("RSA 2048 bit", 2048);
            Test("RSA 3072 bit", 3072);
        }
    }
}
Imports System
Imports System.Security.Cryptography
Imports System.Text

Namespace Encrypt
    Public Class TestAsymmetric
        ' encrypt: Byte() plain + Byte() keys -> Byte() cipher
        ' decrypt: Byte() cipher + Byte() keys -> Byte() plain
        Public Shared Function Encrypt(plain As Byte(), keyblob As Byte()) As Byte()
            Dim alg As New RSACryptoServiceProvider()
            alg.ImportCspBlob(keyblob)
            Return alg.Encrypt(plain, False)
        End Function
        Public Shared Function Decrypt(cipher As Byte(), keyblob As Byte()) As Byte()
            Dim alg As New RSACryptoServiceProvider()
            alg.ImportCspBlob(keyblob)
            Return alg.Decrypt(cipher, False)
        End Function
        ' Base64 encode: Byte() -> String
        ' Base64 decode: String -> Byte()
        Private Shared Function Encode(b As Byte()) As String
            Return Convert.ToBase64String(b)
        End Function
        Private Shared Function Decode(s As String) As Byte()
            Return Convert.FromBase64String(s)
        End Function
        ' encrypt: String plain + Byte() keys -> Base64 String cipher
        ' decrypt: Base64 String cipher + Byte() keys -> String plain
        Public Shared Function Encrypt(plain As String, keyblob As Byte()) As String
            Return Encode(Encrypt(Encoding.UTF8.GetBytes(plain), keyblob))
        End Function
        Public Shared Function Decrypt(cipher As String, keyblob As Byte()) As String
            Return Encoding.UTF8.GetString(Decrypt(Decode(cipher), keyblob))
        End Function
        ' test
        Private Const TEXT As String = "This is some random text to be used to test encryption."
        Private Shared Sub Test(label As String, bits As Integer)
            Console.WriteLine(label & ":")
            ' generate keys
            Dim alg As New RSACryptoServiceProvider() With { _
                .KeySize = bits _
            }
            Dim keyblob As Byte() = alg.ExportCspBlob(True)
            ' real test
            Console.WriteLine("  " & TEXT)
            Dim cipher As String = Encrypt(TEXT, keyblob)
            Console.WriteLine("  " & cipher)
            Dim plain As String = Decrypt(cipher, keyblob)
            Console.WriteLine("  " & plain)
        End Sub
        Public Shared Sub Main(args As String())
            ' CAPI implementations
            Test("RSA 2048 bit (CAPI)", 2048)
            Test("RSA 3072 bit (CAPI)", 3072)
            '
        End Sub
    End Class                    
End Namespace

The OpenSSL library offers 2 levels of basic encryption/hashing functions:

low level
algorithm spoecific functions
high level
general EVP functions where algorithm is a function parameter

This example is using the low level RSA functions to do RSA encryption.

#include <stdio.h>
#include <string.h>

#include <openssl/err.h>

#include <openssl/rsa.h>
#include <openssl/evp.h>

/* encrypt: binary plain + binary key -> binary cipher */
/* decrypt: binary cipher + binary key -> binary plain */
void encrypt(const unsigned char *inbuf, int inbuflen, RSA *key, unsigned char *outbuf, int *outbuflen)
{
    *outbuflen = RSA_private_encrypt(inbuflen, inbuf, outbuf, key, RSA_PKCS1_PADDING);
}

void decrypt(const unsigned char *inbuf, int inbuflen, RSA *key, unsigned char *outbuf, int *outbuflen)
{
    *outbuflen = RSA_public_decrypt(inbuflen, inbuf, outbuf, key, RSA_PKCS1_PADDING);
}

/* Base64 encode: binary -> text */
/* Base64 decode: text -> binary */
void encode(const unsigned char *inbuf, int inbuflen, unsigned char *outbuf, int *outbuflen)
{
    EVP_ENCODE_CTX ctx;
    int len;
    EVP_EncodeInit(&ctx);
    EVP_EncodeUpdate(&ctx, outbuf, &len, inbuf, inbuflen);
    *outbuflen = len;
    EVP_EncodeFinal(&ctx, outbuf + len, &len);
    *outbuflen += len;
    if(outbuf[*outbuflen - 1] == '\n') *outbuflen -= 1;
    outbuf[*outbuflen] = '\0'; 
}

void decode(const unsigned char *inbuf, int inbuflen, unsigned char *outbuf, int *outbuflen)
{
    EVP_ENCODE_CTX ctx;
    int len;
    EVP_DecodeInit(&ctx);
    EVP_DecodeUpdate(&ctx, outbuf, &len, inbuf, inbuflen);
    *outbuflen = len;
    EVP_DecodeFinal(&ctx, outbuf + len, &len);
    *outbuflen += len;
}

/* encrypt: text plain + text key -> Base64 text cipher */
/* decrypt: Base64 text cipher + text key -> text plain */
void encrypt_encode(const char *plain, const char *privkey, int privkeylen, char *cipher)
{
    RSA *key;
    unsigned char *temp;
    int templen, cipherlen;
    key = d2i_RSAPrivateKey(NULL, (const unsigned char **)&privkey, privkeylen);
    temp = malloc(RSA_size(key));
    encrypt((unsigned char *)plain, strlen(plain), key, temp, &templen);
    encode(temp, templen, (unsigned char *)cipher, &cipherlen);
    free(temp);
}

void decode_decrypt(const char *cipher, const char *pubkey, int pubkeylen, char *plain)
{
    RSA *key;
    unsigned char *temp;
    int templen, plainlen;
    key = d2i_RSAPublicKey(NULL, (const unsigned char **)&pubkey, pubkeylen);
    temp = malloc(strlen(cipher));
    decode((unsigned char *)cipher, strlen(cipher), temp, &templen);
    decrypt(temp, templen, key, (unsigned char *)plain, &plainlen);
    plain[plainlen] = '\0';
    free(temp);
}

/* test */
static const char *TEXT = "This is some random text to be used to test encryption.";
 
void test(char *label, int bits)
{
    RSA *keypair;
    char *privkey;
    char *pubkey;
    char *temp;
    int privkeylen;
    int pubkeylen;
    char cipher[10000];
    char plain[10000];
    printf("%s:\n", label);
    /* generate keys */
    keypair = RSA_generate_key(bits, RSA_F4, NULL, NULL);
    temp = malloc(10000);
    privkey = temp;
    privkeylen = i2d_RSAPrivateKey(keypair, (unsigned char **)&temp);
    temp = malloc(10000);
    pubkey = temp;
    pubkeylen = i2d_RSAPublicKey(keypair, (unsigned char **)&temp);
    /* real test */
    printf("  %s\n", TEXT);
    encrypt_encode(TEXT, privkey, privkeylen, cipher);
    printf("  %s\n", cipher);
    decode_decrypt(cipher, pubkey, pubkeylen, plain);
    printf("  %s\n", plain);
    free(privkey);
    free(pubkey);
}

int main()
{
    test("RSA 2048 bit", 2048);
    test("RSA 3072 bit", 3072);
    return 0;
}

The above examples are not really how asymmetric algorithms are used in practice, but they illustrate the basic technique. And the next section will explain how they are used in practice.

Combinations:

Digital signature:

Problem: how to prove that a given content comes from a given entity. Aka how to the digital equivalent of a signature.

Solution:

  1. Sender has a private key and public key
  2. Sender calculates: signature(content) = asymmetric_encrypt(hash(content), private_key)
  3. Receiver verifies: asymetric_decrypt(sent_signature, public_key) == hash(content)

Examples:

package encrypt;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class TestSigning {
    // sign: byte[] + byte[] private key -> byte[]
    // verify: byte[] + byte[] signature + byte[] public key -> boolean
    public static byte[] sign(String alghash, String alg, byte[] content, byte[] serprivkey) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException  {
        KeyFactory kf = KeyFactory.getInstance(alg);
        PrivateKey privkey = kf.generatePrivate(new PKCS8EncodedKeySpec(serprivkey));
        Signature s = Signature.getInstance(alghash);
        s.initSign(privkey);
        s.update(content);
        return s.sign();
    }
    public static boolean verify(String alghash, String alg, byte[] content, byte[] signature, byte[] serpubkey) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
        KeyFactory kf = KeyFactory.getInstance(alg);
        PublicKey pubkey = kf.generatePublic(new X509EncodedKeySpec(serpubkey));
        Signature s = Signature.getInstance(alghash);
        s.initVerify(pubkey);
        s.update(content);
        return s.verify(signature);
    }
    // Base64 encode: byte[] -> String
    // Base64 decode: String -> byte[]
    private static String encode(byte[] b) {
        return Base64.getEncoder().encodeToString(b);
    }
    private static byte[] decode(String s) {
        return Base64.getDecoder().decode(s);
    }
    // sign: String + byte[] private key -> Base64 String
    // verify: String + Base64 String signature + byte[] public key -> boolean
    public static String sign(String alghash, String alg, String content, byte[] serprivkey) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException, UnsupportedEncodingException {
        return encode(sign(alghash, alg, content.getBytes("UTF-8"), serprivkey));
    }
    public static boolean verify(String alghash, String alg, String content, String signature, byte[] serpubkey) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException, UnsupportedEncodingException {
        return verify(alghash, alg, content.getBytes("UTF-8"), decode(signature), serpubkey);
    }
    // test
    private static final String TEXT = "This is some random text to be used to test encryption.";
    private static void test(String label, String alghash, String alg, int bits) throws NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException, SignatureException, UnsupportedEncodingException {
        System.out.println(label + ":");
        // generate keys
        KeyPairGenerator kpg = KeyPairGenerator.getInstance(alg);
        kpg.initialize(bits);
        KeyPair kp = kpg.generateKeyPair();
        PrivateKey privkey = kp.getPrivate();
        PublicKey pubkey = kp.getPublic();
        byte[] serprivkey = privkey.getEncoded();
        byte[] serpubkey = pubkey.getEncoded();
        // real test
        System.out.println("  " + TEXT);
        String sig = sign(alghash, alg, TEXT, serprivkey);
        System.out.println("  " + sig);
        boolean ok = verify(alghash, alg, TEXT, sig, serpubkey);
        System.out.println("  " + ok);
        boolean ok2 = verify(alghash, alg, TEXT + "X", sig, serpubkey);
        System.out.println("  " + ok2);
    }
    public static void main(String[] args) throws Exception {
        test("SHA-1 160 bit with RSA 3072 bit", "SHA1withRSA", "RSA", 3072);
        test("SHA-2 256 bit with RSA 3072 bit", "SHA256withRSA", "RSA", 3072);
        test("SHA-2 512 bit with RSA 3072 bit", "SHA512withRSA", "RSA", 3072);
        test("SHA-1 160 bit with ECDSA 384 bit", "SHA1withECDSA", "EC", 384);
        test("SHA-2 256 bit with ECDSA 384 bit", "SHA256withECDSA", "EC", 384);
        test("SHA-2 512 bit with ECDSA 384 bit", "SHA512withECDSA", "EC", 384);
    }
}
using System;
using System.Security.Cryptography;
using System.Text;

namespace Encrypt
{
    public class TestSigning
    {
        // sign: byte[] + byte[] keys -> byte[]
        // verify: byte[] + byte[] signature + byte[] keys -> bool
        public static byte[] Sign(byte[] content, byte[] keyblob, HashAlgorithm ha)
        {
            RSACryptoServiceProvider alg = new RSACryptoServiceProvider();
            alg.ImportCspBlob(keyblob);
            return alg.SignData(content, ha);
        }
        public static bool Verify(byte[] content, byte[] signature, byte[] keyblob, HashAlgorithm ha)
        {
            RSACryptoServiceProvider alg = new RSACryptoServiceProvider();
            alg.ImportCspBlob(keyblob);
            return alg.VerifyData(content, ha, signature);
        }
        // Base64 encode: byte[] -> string
        // Base64 decode: string -> byte[]
        private static string Encode(byte[] b)
        {
            return Convert.ToBase64String(b);
        }
        private static byte[] Decode(string s)
        {
            return Convert.FromBase64String(s);
        }
        // sign: string + byte[] keys -> Base64 string
        // verify: string + Base64 string signature + byte[] keys -> bool
        public static string Sign(string content, byte[] keyblob, HashAlgorithm ha)
        {
            return Encode(Sign(Encoding.UTF8.GetBytes(content), keyblob, ha));
        }
        public static bool Verify(string content, string signature, byte[] keyblob, HashAlgorithm ha)
        {
            return Verify(Encoding.UTF8.GetBytes(content), Decode(signature), keyblob, ha);
        }
        // test
        private const string TEXT = "This is some random text to be used to test encryption.";
        private static void Test(string label, int bits, HashAlgorithm ha) 
        {
            Console.WriteLine(label + ":");
            // generate keys
            RSACryptoServiceProvider alg = new RSACryptoServiceProvider { KeySize = bits };
            byte[] keyblob = alg.ExportCspBlob(true);
            // real test
            Console.WriteLine("  " + TEXT);
            string sig = Sign(TEXT, keyblob, ha);
            Console.WriteLine("  " + sig);
            bool ok = Verify(TEXT, sig, keyblob, ha);
            Console.WriteLine("  " + ok);
            bool ok2 = Verify(TEXT + "X", sig, keyblob, ha);
            Console.WriteLine("  " + ok2);
        }
        public static void Main(string[] args)
        {
            Test("SHA-1 160 bit with RSA 3072 bit", 3072, new SHA1CryptoServiceProvider());
            Test("SHA-2 256 bit with RSA 3072 bit", 3072, new SHA256CryptoServiceProvider());
            Test("SHA-2 512 bit with RSA 3072 bit", 3072, new SHA512CryptoServiceProvider());
        }
    }
}
using System;
using System.Text;

using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.X509;

namespace Encrypt
{
    public class TestSigning
    {
        // sign: byte[] + byte[] keys -> byte[]
        // verify: byte[] + byte[] signature + byte[] keys -> bool
        public static byte[] Sign(byte[] content, byte[] serprivkey, string alg)
        {
            ISigner sign = SignerUtilities.GetSigner(alg);
            AsymmetricKeyParameter privkey = PrivateKeyFactory.CreateKey(PrivateKeyInfo.GetInstance(serprivkey));
            sign.Init(true, privkey);
            sign.BlockUpdate(content, 0, content.Length);
            return sign.GenerateSignature();
        }
        public static bool Verify(byte[] content, byte[] signature, byte[] serpubkey, string alg)
        {
            ISigner sign = SignerUtilities.GetSigner(alg);
            AsymmetricKeyParameter pubkey = PublicKeyFactory.CreateKey(SubjectPublicKeyInfo.GetInstance(serpubkey));
            sign.Init(false, pubkey);
            sign.BlockUpdate(content, 0, content.Length);
            return sign.VerifySignature(signature);
        }
        // Base64 encode: byte[] -> string
        // Base64 decode: string -> byte[]
        private static string Encode(byte[] b)
        {
            return Convert.ToBase64String(b);
        }
        private static byte[] Decode(string s)
        {
            return Convert.FromBase64String(s);
        }
        // sign: string + byte[] keys -> Base64 string
        // verify: string + Base64 string signature + byte[] keys -> bool
        public static string Sign(string content, byte[] serprivkey, string alg)
        {
            return Encode(Sign(Encoding.UTF8.GetBytes(content), serprivkey, alg));
        }
        public static bool Verify(string content, string signature, byte[] serpubkey, string alg)
        {
            return Verify(Encoding.UTF8.GetBytes(content), Decode(signature), serpubkey, alg);
        }
        // test
        private const string TEXT = "This is some random text to be used to test encryption.";
        private static void Test(string label, string alg, int bits) 
        {
            Console.WriteLine(label + ":");
            // generate keys
            RsaKeyPairGenerator kpg = new RsaKeyPairGenerator();
            kpg.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), bits));
            AsymmetricCipherKeyPair kp = kpg.GenerateKeyPair();
            AsymmetricKeyParameter pubkey = kp.Public;
            AsymmetricKeyParameter privkey = kp.Private;
            byte[] serpubkey = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pubkey).GetDerEncoded();
            byte[] serprivkey = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privkey).GetDerEncoded();
            // real test
            Console.WriteLine("  " + TEXT);
            string sig = Sign(TEXT, serprivkey, alg);
            Console.WriteLine("  " + sig);
            bool ok = Verify(TEXT, sig, serpubkey, alg);
            Console.WriteLine("  " + ok);
            bool ok2 = Verify(TEXT + "X", sig, serpubkey, alg);
            Console.WriteLine("  " + ok2);
        }
        public static void Main(string[] args)
        {
            Test("SHA-1 160 bit with RSA 3072 bit", "SHA1WITHRSA", 3072);
            Test("SHA-2 256 bit with RSA 3072 bit", "SHA256WITHRSA", 3072);
            Test("SHA-2 512 bit with RSA 3072 bit", "SHA512WITHRSA", 3072);
        }
    }
}
Imports System
Imports System.Security.Cryptography
Imports System.Text

Namespace Encrypt
    Public Class TestSigning
        ' sign: Byte() + Byte() keys -> Byte()
        ' verify: Byte() + Byte() signature + Byte() keys -> bool
        Public Shared Function Sign(content As Byte(), keyblob As Byte(), ha As HashAlgorithm) As Byte()
            Dim alg As New RSACryptoServiceProvider()
            alg.ImportCspBlob(keyblob)
            Return alg.SignData(content, ha)
        End Function
        Public Shared Function Verify(content As Byte(), signature As Byte(), keyblob As Byte(), ha As HashAlgorithm) As Boolean
            Dim alg As New RSACryptoServiceProvider()
            alg.ImportCspBlob(keyblob)
            Return alg.VerifyData(content, ha, signature)
        End Function
        ' Base64 encode: Byte() -> String
        ' Base64 decode: String -> Byte()
        Private Shared Function Encode(b As Byte()) As String
            Return Convert.ToBase64String(b)
        End Function
        Private Shared Function Decode(s As String) As Byte()
            Return Convert.FromBase64String(s)
        End Function
        ' sign: String + Byte() keys -> Base64 String
        ' verify: String + Base64 String signature + Byte() keys -> bool
        Public Shared Function Sign(content As String, keyblob As Byte(), ha As HashAlgorithm) As String
            Return Encode(Sign(Encoding.UTF8.GetBytes(content), keyblob, ha))
        End Function
        Public Shared Function Verify(content As String, signature As String, keyblob As Byte(), ha As HashAlgorithm) As Boolean
            Return Verify(Encoding.UTF8.GetBytes(content), Decode(signature), keyblob, ha)
        End Function
        ' test
        Private Const TEXT As String = "This is some random text to be used to test encryption."
        Private Shared Sub Test(label As String, bits As Integer, ha As HashAlgorithm)
            Console.WriteLine(label & ":")
            ' generate keys
            Dim alg As New RSACryptoServiceProvider() With { _
                .KeySize = bits _
            }
            Dim keyblob As Byte() = alg.ExportCspBlob(True)
            ' real test
            Console.WriteLine("  " & TEXT)
            Dim sig As String = Sign(TEXT, keyblob, ha)
            Console.WriteLine("  " & sig)
            Dim ok As Boolean = Verify(TEXT, sig, keyblob, ha)
            Console.WriteLine("  " & ok)
            Dim ok2 As Boolean = Verify(TEXT & "X", sig, keyblob, ha)
            Console.WriteLine("  " & ok2)
        End Sub
        Public Shared Sub Main(args As String())
            Test("SHA-1 160 bit with RSA 3072 bit", 3072, New SHA1CryptoServiceProvider())
            Test("SHA-2 256 bit with RSA 3072 bit", 3072, New SHA256CryptoServiceProvider())
            Test("SHA-2 512 bit with RSA 3072 bit", 3072, New SHA512CryptoServiceProvider())
        End Sub
    End Class
End Namespace

The OpenSSL library offers 2 levels of basic encryption/hashing functions:

low level
algorithm spoecific functions
high level
general EVP functions where algorithm is a function parameter

This example is using the high level EVP functions to do SHA-1/SHA-2 with RSA signing.

#include <stdio.h>
#include <string.h>

#include <openssl/err.h>

#include <openssl/evp.h>
#include <openssl/rsa.h>

// sign: binary + binary private key -> binary
// verify: binary + binary signature + binary public key -> boolean
void sign(const unsigned char *inbuf, int inbuflen, const EVP_MD *mdalg, int id, EVP_PKEY *pkey, unsigned char *sig, int *siglen, int padcode, int pad)
{
    EVP_MD_CTX mdctx;
    EVP_PKEY_CTX *pkeyctx;
    size_t len;
    EVP_MD_CTX_init(&mdctx); 
    EVP_DigestInit_ex(&mdctx, mdalg, NULL);
    pkeyctx = EVP_PKEY_CTX_new(pkey, NULL);
    EVP_PKEY_CTX_ctrl(pkeyctx,  id, -1, padcode, pad, NULL);
    EVP_DigestSignInit(&mdctx, &pkeyctx, mdalg, NULL, pkey);
    EVP_DigestSignUpdate(&mdctx, inbuf, inbuflen);
    EVP_DigestSignFinal(&mdctx, sig, &len);
    *siglen = len;
}

int verify(const unsigned char *inbuf, int inbuflen, const EVP_MD *mdalg, int id, EVP_PKEY *pkey, unsigned char *sig, int siglen, int padcode, int pad)
{
    EVP_MD_CTX mdctx;
    EVP_PKEY_CTX *pkeyctx;
    EVP_MD_CTX_init(&mdctx); 
    EVP_DigestInit_ex(&mdctx, mdalg, NULL);
    pkeyctx = EVP_PKEY_CTX_new(pkey, NULL);
    EVP_PKEY_CTX_ctrl(pkeyctx,  id, -1, padcode, pad, NULL);
    EVP_DigestVerifyInit(&mdctx, &pkeyctx, mdalg, NULL, pkey);
    EVP_DigestVerifyUpdate(&mdctx, inbuf, inbuflen);
    return EVP_DigestVerifyFinal(&mdctx, sig, siglen) == 1;
}

/* Base64 encode: binary -> text */
/* Base64 decode: text -> binary */
void encode(const unsigned char *inbuf, int inbuflen, unsigned char *outbuf, int *outbuflen)
{
    EVP_ENCODE_CTX ctx;
    int len;
    EVP_EncodeInit(&ctx);
    EVP_EncodeUpdate(&ctx, outbuf, &len, inbuf, inbuflen);
    *outbuflen = len;
    EVP_EncodeFinal(&ctx, outbuf + len, &len);
    *outbuflen += len;
    if(outbuf[*outbuflen - 1] == '\n') *outbuflen -= 1;
    outbuf[*outbuflen] = '\0'; 
}

void decode(const unsigned char *inbuf, int inbuflen, unsigned char *outbuf, int *outbuflen)
{
    EVP_ENCODE_CTX ctx;
    int len;
    EVP_DecodeInit(&ctx);
    EVP_DecodeUpdate(&ctx, outbuf, &len, inbuf, inbuflen);
    *outbuflen = len;
    EVP_DecodeFinal(&ctx, outbuf + len, &len);
    *outbuflen += len;
}

// sign: text + binary private key -> Base64 text
// verify: text + Base64 text signature + binary public key -> boolean
void sign_encode(const char *content, const EVP_MD *mdalg, int id, const char *privkey, int privkeylen, char *sig, int padcode, int pad)
{
    EVP_PKEY *pkey;
    unsigned char *temp;
    int templen, siglen;
    pkey = NULL;
    d2i_PrivateKey(id, &pkey, (const unsigned char **)&privkey, privkeylen);
    temp = malloc(EVP_PKEY_size(pkey));
    sign((unsigned char *)content, strlen(content), mdalg, id, pkey, temp, &templen, padcode, pad);
    encode(temp, templen, (unsigned char *)sig, &siglen);
    sig[siglen] = '\0';
    free(temp);
    EVP_PKEY_free(pkey);
}

int decode_verify(const char *content, const EVP_MD *mdalg, int id, const char *pubkey, int pubkeylen, char *sig, int padcode, int pad)
{
    EVP_PKEY *pkey;
    unsigned char *temp;
    int templen;
    int res;
    pkey = NULL; 
    d2i_PublicKey(id, &pkey, (const unsigned char **)&pubkey, pubkeylen);
    temp = malloc(strlen(sig));
    decode((unsigned char *)sig, strlen(sig), temp, &templen);
    res = verify((unsigned char *)content, strlen(content), mdalg, id, pkey, (unsigned char *)temp, templen, padcode, pad);
    free(temp);
    EVP_PKEY_free(pkey);
    return res;
}

static const char *TEXT = "This is some random text to be used to test encryption.";

/* test */
void test(char *label, const EVP_MD *hashalg, int id, int padcode, int pad, int bitscode, int bits)
{
    EVP_PKEY_CTX *ctx;
    EVP_PKEY *pkey;
    char *privkey;
    char *pubkey;
    char *temp;
    int privkeylen;
    int pubkeylen;
    int ok, ok2;
    char sig[10000];
    printf("%s:\n", label);
    /* generate keys */
    ctx = EVP_PKEY_CTX_new_id(id, NULL);
    EVP_PKEY_keygen_init(ctx);
    EVP_PKEY_CTX_ctrl(ctx, id, -1, padcode, pad, NULL);
    EVP_PKEY_CTX_ctrl(ctx, id, EVP_PKEY_OP_KEYGEN, bitscode, bits, NULL);
    pkey = NULL;
    EVP_PKEY_keygen(ctx, &pkey);
    temp = malloc(10000);
    privkey = temp;
    privkeylen = i2d_PrivateKey(pkey, (unsigned char **)&temp);
    temp = malloc(10000);
    pubkey = temp;
    pubkeylen = i2d_PublicKey(pkey, (unsigned char **)&temp);
    EVP_PKEY_CTX_free(ctx);
    // real test */
    printf("  %s\n", TEXT);
    sign_encode(TEXT, hashalg, id, privkey, privkeylen, sig, padcode, pad);
    printf("  %s\n", sig);
    ok = decode_verify(TEXT, hashalg, id, pubkey, pubkeylen, sig, padcode, pad);
    printf("  %d\n", ok);
    temp = malloc(strlen(TEXT) + 2);
    strcpy(temp, TEXT);
    strcat(temp, "X");
    ok2 = decode_verify(temp, hashalg, id, pubkey, pubkeylen, sig, padcode, pad);
    printf("  %d\n", ok2);
    free(temp);
    free(privkey);
    free(pubkey);
}

int main()
{
    test("SHA-1 160 bit with RSA 2048 bit", EVP_sha1(), EVP_PKEY_RSA, EVP_PKEY_CTRL_RSA_PADDING, RSA_PKCS1_PADDING, EVP_PKEY_CTRL_RSA_KEYGEN_BITS, 2048);
    test("SHA-2 256 bit with RSA 2048 bit", EVP_sha256(), EVP_PKEY_RSA, EVP_PKEY_CTRL_RSA_PADDING, RSA_PKCS1_PADDING, EVP_PKEY_CTRL_RSA_KEYGEN_BITS, 2048);
    test("SHA-2 512 bit with RSA 2048 bit", EVP_sha512(), EVP_PKEY_RSA, EVP_PKEY_CTRL_RSA_PADDING, RSA_PKCS1_PADDING, EVP_PKEY_CTRL_RSA_KEYGEN_BITS, 2048);
    test("SHA-1 160 bit with RSA 3072 bit", EVP_sha1(), EVP_PKEY_RSA, EVP_PKEY_CTRL_RSA_PADDING, RSA_PKCS1_PADDING, EVP_PKEY_CTRL_RSA_KEYGEN_BITS, 3072);
    test("SHA-2 256 bit with RSA 3072 bit", EVP_sha256(), EVP_PKEY_RSA, EVP_PKEY_CTRL_RSA_PADDING, RSA_PKCS1_PADDING, EVP_PKEY_CTRL_RSA_KEYGEN_BITS, 3072);
    test("SHA-2 512 bit with RSA 3072 bit", EVP_sha512(), EVP_PKEY_RSA, EVP_PKEY_CTRL_RSA_PADDING, RSA_PKCS1_PADDING, EVP_PKEY_CTRL_RSA_KEYGEN_BITS, 3072);
    return 0;
}

Key exchange:

Problem: how to distribute a symmetric key that can be used to encrypt large amount of data.

Solution:

  1. A has a public key A_pub_key and a private key A_priv_key
  2. B has a public key B_pub_key and a private key B_priv_key
  3. A generate a random key for symmetric encryption
  4. A generate asymetric_encrypt(asymetric_encrypt(secret_key, B_pub_key), A_priv_key) and send
  5. B receive and retrieve secret_key = asymetric_decrypt(asymetric_decrypt, A_pub_key), B_priv_key)

This is the basis of the widely used SSL/TLS protocols.

For code examples see:

Article history:

Version Date Description
1.0 March 2nd 2019 Initial version
1.1 March 24th 2019 Add more background information about password hashing
1.2 March 26th 2019 Add comments to code to clarify what it is doing
1.3 April 4th 2019 Add C OpenSSL examples
1.4 June 10th 2019 Add Delphi/Lazarus examples
1.5 March 16th Add BouncyCastle examples

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj