CryptoSys Home > PKI > Ferguson-Schneier RSA Encryption

Ferguson-Schneier RSA Encryption


This page gives example code in Visual Basic (VB6 and VB.NET/VB200x) using CryptoSys PKI to do RSA encryption in the manner suggested by Ferguson and Schneier in their book Practical Cryptography (Wiley, 2003). The algorithm is described on our RSA Algorithm page.

We use the 1024-bit RSA key pair belonging to Bob from RFC 4134 Examples of S/MIME Messages. Bob's public key is in his X.509 certificate and his private key is in an encrypted PKCS#8 file. The complete files are in the download below. Yes, we know this public key does not have an exponent of 5 as suggested, but you get the idea. The password for the encrypted private key is, as always, "password".

We take a simple text message of 60 bytes "Hello, world! This is a secret message. For Bob's eyes only.", derive a content encryption key (CEK) from a randomly-generated value, encrypt this using RSA, and use the CEK to encrypt the text message using conventional AES-128 (this is much faster than RSA).

We pass the information to the recipient, Bob, as a concatenated string of hex characters; we could have used base64 which would be slightly shorter, but hex characters are easier to manipulate for the demo. You could, of course, pass the information in binary form, or wrap it up in ASN.1 encoding, or XML. It's your choice to agree with the recipient so long as they know how to interpret it.

To decrypt we follow the reverse process. This is just a demonstration program outlining the technique. Obviously in practice you'd split it up into sub-routines with suitable parameters (and add better error handling).

VB6/VBA

Public Sub Ferguson_Schneier_Encrypt()
' Ref: Niels Ferguson and Bruce Schneier, Practical Cryptography, Wiley, 2003
    Dim nRet As Long
    Dim strPublicKey As String
    Dim strCert As String
    Dim nKeyBytes As Long
    Dim abR() As Byte
    Dim abC() As Byte
    Dim abCEK() As Byte
    Dim abData() As Byte
    Dim abMessage() As Byte
    Dim strMessage As String
    Dim abIV() As Byte
    Dim nMsgLen As Long
    Dim nDataLen As Long
    Dim strTransmission As String
    Dim strCorrect As String
    
    ' 1. ENCRYPTION
    ' INPUT:
    strCert = "BobRSASignByCarl.cer"
    strMessage = "Hello, world! This is a secret message. For Bob's eyes only."
    strCorrect = strMessage
    
    Debug.Print "ENCRYPTING..."
    Debug.Print "m='" & strMessage & "'"
    
    ' 1.1 Generate content encryption key, CEK, and RSA-encrypted CEK, c.
    
    ' 1.1.1 Read in Bob's public key to internal string
    strPublicKey = rsaGetPublicKeyFromCert(strCert)
    Debug.Print "Public key length k=|n|=" & RSA_KeyBits(strPublicKey) & " bits"
    ' How many bytes in the key?
    nKeyBytes = RSA_KeyBytes(strPublicKey)
    If nKeyBytes <= 0 Then Exit Sub    ' CATCH ERROR
    ' 1.1.2 Generate a random number r of the same length
    ReDim abR(nKeyBytes - 1)
    nRet = RNG_Bytes(abR(0), nKeyBytes, "", 0)
    ' and make sure it is less than n by making the two most-significant byte zero
    ' (we will use this as a check later)
    abR(0) = 0
    abR(1) = 0
    Debug.Print "r=" & cnvHexStrFromBytes(abR)
    ' 1.1.3 Compute CEK by hashing r: CEK = Hash(r)
    abCEK = Hash256Double(abR)
    Debug.Print "CEK=" & cnvHexStrFromBytes(abCEK)
    ' 1.1.4 Compute c = r^e mod n
    abC = abR
    nRet = RSA_RawPublic(abC(0), nKeyBytes, strPublicKey, 0)
    If nRet <> 0 Then Exit Sub    ' CATCH ERROR
    Debug.Print "c=" & cnvHexStrFromBytes(abC)
    
    ' 1.2 Encrypt message using symmetrical AES-128 block cipher in CBC mode
    
    ' 1.2.1 Convert the message string to an array of bytes
    abMessage = StrConv(strMessage, vbFromUnicode)
    nMsgLen = UBound(abMessage) + 1
    Debug.Print "m= " & cnvHexStrFromBytes(abMessage)
    ' 1.2.2 Pad this to an exact multiple of the AES block size
    nDataLen = PAD_BytesBlock(vbNull, 0, abMessage(0), nMsgLen, PKI_BLK_AES_BYTES, 0)
    If nDataLen <= 0 Then Exit Sub    ' CATCH ERROR
    ReDim abData(nDataLen - 1)
    nDataLen = PAD_BytesBlock(abData(0), nDataLen, abMessage(0), nMsgLen, PKI_BLK_AES_BYTES, 0)
    Debug.Print "PT=" & cnvHexStrFromBytes(abData)
    
    ' 1.2.3 Generate a random IV of same size as AES block (16 bytes)
    ReDim abIV(PKI_BLK_AES_BYTES - 1)
    nRet = RNG_Bytes(abIV(0), PKI_BLK_AES_BYTES, "", 0)
    Debug.Print "IV=" & cnvHexStrFromBytes(abIV)
    
    ' 1.2.4 Encrypt the plain text using AES-128 with the content encryption key: CT=Encrypt(CEK, m)
    nRet = CIPHER_Bytes(ENCRYPT, abData(0), abData(0), nDataLen, abCEK(0), abIV(0), "aes128-cbc", 0)
    If nRet <> 0 Then Exit Sub    ' CATCH ERROR
    Debug.Print "CT=" & cnvHexStrFromBytes(abData)
    
    ' 1.3 Send transmission to recipient
    
    ' We need to transmit c || IV || CT to Bob. We'll send a concatenated hex string
    strTransmission = cnvHexStrFromBytes(abC) & cnvHexStrFromBytes(abIV) & cnvHexStrFromBytes(abData)
    Debug.Print
    Debug.Print "TRANSMIT=" & strTransmission
    Debug.Print
    
    ' 2. DECRYPTION
    
    Dim strKeyFile As String
    Dim strPassword As String
    Dim strPrivateKey As String
    Dim strC_Hex As String
    Dim strIV_Hex As String
    Dim strCT_Hex As String
    Dim nLen As Long
    Dim nOffset As Long
    
    ' INPUT:
    strKeyFile = "BobPrivRSAEncrypt.epk"    ' Bob's encrypted private key file
    strPassword = "password"
    Debug.Print "DECRYPTING..."
    
    ' 2.1 Read in private key
    strPrivateKey = rsaReadPrivateKey(strKeyFile, strPassword)
    Debug.Print "Private key length k=|n|=" & RSA_KeyBits(strPrivateKey) & " bits"
    ' and get its size in bytes
    nKeyBytes = RSA_KeyBytes(strPublicKey)
    If nKeyBytes <= 0 Then Exit Sub    ' CATCH ERROR
    ' We are done with password, so wipe it securely (and don't hard-code it like we did!)
    strPassword = wipeString(strPassword)
    
    ' 2.2 Parse input string: c || IV || CT
    ' The first nKeyBytes are c, two hex digits per byte
    nOffset = 1
    nLen = nKeyBytes * 2
    strC_Hex = Mid(strTransmission, nOffset, nLen)
    ' The next 16 bytes are the IV
    nOffset = nOffset + nLen
    nLen = PKI_BLK_AES_BYTES * 2
    strIV_Hex = Mid(strTransmission, nOffset, nLen)
    ' And the remainder is the ciphertext, CT
    nOffset = nOffset + nLen
    strCT_Hex = Mid(strTransmission, nOffset)
    ' Convert these to byte arrays
    abC = cnvBytesFromHexStr(strC_Hex)
    Debug.Print "c=" & cnvHexStrFromBytes(abC)
    abIV = cnvBytesFromHexStr(strIV_Hex)
    Debug.Print "IV=" & cnvHexStrFromBytes(abIV)
    abData = cnvBytesFromHexStr(strCT_Hex)
    Debug.Print "CT=" & cnvHexStrFromBytes(abData)
    
    ' 2.3 Decrypt c to obtain r
    abR = abC
    nRet = RSA_RawPrivate(abR(0), nKeyBytes, strPrivateKey, 0)
    If nRet <> 0 Then Exit Sub    ' CATCH ERROR
    Debug.Print "r=" & cnvHexStrFromBytes(abR)
    ' We are done with private key string, so wipe it securely
    strPrivateKey = wipeString(strPrivateKey)
    
    ' 2.3.1 Check that first two bytes are zero (1/65536 chance of false positive)
    If abR(0) <> 0 Or abR(1) <> 0 Then Exit Sub    ' CATCH ERROR
    
    ' 2.4 Obtain CEK by hashing r
    abCEK = Hash256Double(abR)
    Debug.Print "CEK=" & cnvHexStrFromBytes(abCEK)
    
    ' 2.5 Decrypt the plain text using AES-128 with the content encryption key: m=Decrypt(CEK, CT)
    nRet = CIPHER_Bytes(DECRYPT, abData(0), abData(0), nDataLen, abCEK(0), abIV(0), "aes128-cbc", 0)
    If nRet <> 0 Then Exit Sub    ' CATCH ERROR
    Debug.Print "PT=" & cnvHexStrFromBytes(abData)
    
    ' 2.5.1 Strip the padding
    nMsgLen = PAD_UnpadBytes(abData(0), nDataLen, abData(0), nDataLen, PKI_BLK_AES_BYTES, 0)
    If (nMsgLen <= 0) Then Exit Sub
    ReDim Preserve abData(nMsgLen - 1)
    Debug.Print "m =" & cnvHexStrFromBytes(abData)
    
    ' 2.5.2 Convert byte array back to a string
    strMessage = StrConv(abData, vbUnicode)
    Debug.Print "m='" & strMessage & "'"
    
    If strMessage <> strCorrect Then Exit Sub   ' CATCH ERROR
    Debug.Print "OK, all done successfully"
  
End Sub

Public Function Hash256Double(abInput() As Byte) As Variant
' Computes SHA256(SHA256(input))
    Dim nRet As Long
    Dim nInputLen As Long
    Dim abDigest() As Byte
    ReDim abDigest(PKI_SHA256_BYTES - 1)
    
    nInputLen = UBound(abInput) + 1
    nRet = HASH_Bytes(abDigest(0), PKI_SHA256_BYTES, abInput(0), nInputLen, PKI_HASH_SHA256)
    nRet = HASH_Bytes(abDigest(0), PKI_SHA256_BYTES, abDigest(0), PKI_SHA256_BYTES, PKI_HASH_SHA256)
    Hash256Double = abDigest
    
End Function

Output

Note that the output will be different each time the program is run because of the random values used in the algorithm. This is by design.

ENCRYPTING...
m='Hello, world! This is a secret message. For Bob's eyes only.'
Public key length k=|n|=1024 bits
r=0000481DD2EEB162F18BE26BF64BB980FF99134C39F1443823811D837370F8FAE4364C3171CEE355082118BC9EF97779B9DEB6FBAC33E2DCB4F7B6AB5299B1661AD5CDEF8C76B72A1F0D85A1079B96F7C293C029AFD2442FB65AFB370FE7A971A235C3BDD866C8242C15BB4B823696FF51681C012905101F1D643A4AC057DECE
CEK=6670B591275D5B9946741A1BFA3755068831A4B696AF7DA39FBAF208D99ADF8C
c=0BF2770071E690C2327729978037F9BE5513A594A6F9CD25C4834CF51C4277414991EC3871B0183FB183636A28B4294D48766E6AC69061A84B4D5E192143CFBEC3ABFEE56501F7D1A2C544A84077DD3C8AC8B390A34214FDBFC8964CC10252E4EA6936F8430BCC0355A67C33C0307F1D3E4A9459A323745DB64B541A61CB9977
m= 48656C6C6F2C20776F726C64212054686973206973206120736563726574206D6573736167652E20466F7220426F6227732065796573206F6E6C792E
PT=48656C6C6F2C20776F726C64212054686973206973206120736563726574206D6573736167652E20466F7220426F6227732065796573206F6E6C792E04040404
IV=84D8A4C56B5EA779A42D358081F940A2
CT=8487F05119052DA340612DA5F70E5CD590854A5C568405DB14EF7FB09939D5E64CC2180BD8923E6785079040D815709FCF9DB188BBB71FCA3D5943EB10B25C78

TRANSMIT=0BF2770071E690C2327729978037F9BE5513A594A6F9CD25C4834CF51C4277414991EC3871B0183FB183636A28B4294D48766E6AC69061A84B4D5E192143CFBEC3ABFEE56501F7D1A2C544A84077DD3C8AC8B390A34214FDBFC8964CC10252E4EA6936F8430BCC0355A67C33C0307F1D3E4A9459A323745DB64B541A61CB997784D8A4C56B5EA779A42D358081F940A28487F05119052DA340612DA5F70E5CD590854A5C568405DB14EF7FB09939D5E64CC2180BD8923E6785079040D815709FCF9DB188BBB71FCA3D5943EB10B25C78

DECRYPTING...
Private key length k=|n|=1024 bits
c=0BF2770071E690C2327729978037F9BE5513A594A6F9CD25C4834CF51C4277414991EC3871B0183FB183636A28B4294D48766E6AC69061A84B4D5E192143CFBEC3ABFEE56501F7D1A2C544A84077DD3C8AC8B390A34214FDBFC8964CC10252E4EA6936F8430BCC0355A67C33C0307F1D3E4A9459A323745DB64B541A61CB9977
IV=84D8A4C56B5EA779A42D358081F940A2
CT=8487F05119052DA340612DA5F70E5CD590854A5C568405DB14EF7FB09939D5E64CC2180BD8923E6785079040D815709FCF9DB188BBB71FCA3D5943EB10B25C78
r=0000481DD2EEB162F18BE26BF64BB980FF99134C39F1443823811D837370F8FAE4364C3171CEE355082118BC9EF97779B9DEB6FBAC33E2DCB4F7B6AB5299B1661AD5CDEF8C76B72A1F0D85A1079B96F7C293C029AFD2442FB65AFB370FE7A971A235C3BDD866C8242C15BB4B823696FF51681C012905101F1D643A4AC057DECE
CEK=6670B591275D5B9946741A1BFA3755068831A4B696AF7DA39FBAF208D99ADF8C
PT=48656C6C6F2C20776F726C64212054686973206973206120736563726574206D6573736167652E20466F7220426F6227732065796573206F6E6C792E04040404
m =48656C6C6F2C20776F726C64212054686973206973206120736563726574206D6573736167652E20466F7220426F6227732065796573206F6E6C792E
m='Hello, world! This is a secret message. For Bob's eyes only.'
OK, all done successfully

VB.NET/VB200x

Imports CryptoSysPKI
Imports System.Text

Module RSA_FergusonSchneier

    Sub Main()
        Call Ferguson_Schneier_Encrypt()
    End Sub

    Public Sub Ferguson_Schneier_Encrypt()
        ' Ref: Niels Ferguson and Bruce Schneier, Practical Cryptography, Wiley, 2003
        Dim strPublicKey As String
        Dim strCert As String
        Dim nKeyBytes As Integer
        Dim abR() As Byte
        Dim abC() As Byte
        Dim abCEK() As Byte
        Dim abData() As Byte
        Dim abMessage() As Byte
        Dim strMessage As String
        Dim abIV() As Byte
        Dim nMsgLen As Integer
        Dim strTransmission As String
        Dim strCorrect As String

        ' 1. ENCRYPTION
        ' INPUT:
        strCert = "BobRSASignByCarl.cer"
        strMessage = "Hello, world! This is a secret message. For Bob's eyes only."
        strCorrect = strMessage

        Console.WriteLine("ENCRYPTING...")
        Console.WriteLine("m='" & strMessage & "'")

        ' 1.1 Generate content encryption key, CEK, and RSA-encrypted CEK, c.

        ' 1.1.1 Read in Bob's public key to internal string
        strPublicKey = Rsa.GetPublicKeyFromCert(strCert).ToString
        Console.WriteLine("Public key length k=|n|=" & Rsa.KeyBits(strPublicKey) & " bits")
        ' How many bytes in the key?
        nKeyBytes = Rsa.KeyBytes(strPublicKey)
        If nKeyBytes <= 0 Then Exit Sub ' CATCH ERROR
        ' 1.1.2 Generate a random number r of the same length
        abR = Rng.Bytes(nKeyBytes)
        ' and make sure it is less than n by making the two most-significant byte zero
        ' (we will use this as a check later)
        abR(0) = 0
        abR(1) = 0
        Console.WriteLine("r=" & Cnv.ToHex(abR))
        ' 1.1.3 Compute CEK by hashing r: CEK = Hash(r)
        abCEK = Hash256Double(abR)
        Console.WriteLine("CEK=" & Cnv.ToHex(abCEK))
        ' 1.1.4 Compute c = r^e mod n
        abC = Rsa.RawPublic(abR, strPublicKey)
        If abC.Length = 0 Then Exit Sub ' CATCH ERROR
        Console.WriteLine("c=" & Cnv.ToHex(abC))

        ' 1.2 Encrypt message using symmetrical AES-128 block cipher in CBC mode

        ' 1.2.1 Convert the message string to an array of bytes
        abMessage = System.Text.Encoding.Default.GetBytes(strMessage)
        nMsgLen = UBound(abMessage) + 1
        Console.WriteLine("m= " & Cnv.ToHex(abMessage))
        ' 1.2.2 Pad this to an exact multiple of the AES block size
        abData = Cipher.Pad(abMessage, CipherAlgorithm.Aes128)
        Console.WriteLine("PT=" & Cnv.ToHex(abData))

        ' 1.2.3 Generate a random IV of same size as AES block (16 bytes)
        abIV = Rng.Bytes(16)
        Console.WriteLine("IV=" & Cnv.ToHex(abIV))

        ' 1.2.4 Encrypt the plain text using AES-128 with the content encryption key: CT=Encrypt(CEK, m)
        abData = Cipher.Encrypt(abData, abCEK, abIV, CipherAlgorithm.Aes128, Mode.CBC)
        If abData.Length = 0 Then Exit Sub ' CATCH ERROR
        Console.WriteLine("CT=" & Cnv.ToHex(abData))

        ' 1.3 Send transmission to recipient

        ' We need to transmit c || IV || CT to Bob. We'll send a concatenated hex string
        strTransmission = Cnv.ToHex(abC) & Cnv.ToHex(abIV) & Cnv.ToHex(abData)
        Console.WriteLine()
        Console.WriteLine("TRANSMIT=" & strTransmission)
        Console.WriteLine()

        ' 2. DECRYPTION

        Dim strKeyFile As String
        Dim sbPassword As StringBuilder
        Dim sbPrivateKey As StringBuilder
        Dim strC_Hex As String
        Dim strIV_Hex As String
        Dim strCT_Hex As String
        Dim nLen As Integer
        Dim nOffset As Integer

        ' INPUT:
        strKeyFile = "BobPrivRSAEncrypt.epk"    ' Bob's encrypted private key file
        sbPassword = New StringBuilder("password")
        Console.WriteLine("DECRYPTING...")

        ' 2.1 Read in private key
        sbPrivateKey = Rsa.ReadEncPrivateKey(strKeyFile, sbPassword.ToString)
        Console.WriteLine("Private key length k=|n|=" & Rsa.KeyBits(sbPrivateKey.ToString) & " bits")
        ' and get its size in bytes
        nKeyBytes = Rsa.KeyBytes(sbPrivateKey.ToString)
        If nKeyBytes <= 0 Then Exit Sub ' CATCH ERROR
        ' We are done with password, so wipe it securely (and don't hard-code it like we did!)
        Wipe.String(sbPassword)

        ' 2.2 Parse input string: c || IV || CT
        ' The first nKeyBytes are c, two hex digits per byte
        nOffset = 1
        nLen = nKeyBytes * 2
        strC_Hex = Mid(strTransmission, nOffset, nLen)
        ' The next 16 bytes are the IV
        nOffset = nOffset + nLen
        nLen = 16 * 2
        strIV_Hex = Mid(strTransmission, nOffset, nLen)
        ' And the remainder is the ciphertext, CT
        nOffset = nOffset + nLen
        strCT_Hex = Mid(strTransmission, nOffset)
        ' Convert these to byte arrays
        abC = Cnv.FromHex(strC_Hex)
        Console.WriteLine("c=" & Cnv.ToHex(abC))
        abIV = Cnv.FromHex(strIV_Hex)
        Console.WriteLine("IV=" & Cnv.ToHex(abIV))
        abData = Cnv.FromHex(strCT_Hex)
        Console.WriteLine("CT=" & Cnv.ToHex(abData))

        ' 2.3 Decrypt c to obtain r
        abR = Rsa.RawPrivate(abC, sbPrivateKey.ToString)
        If abR.Length = 0 Then Exit Sub ' CATCH ERROR
        Console.WriteLine("r=" & Cnv.ToHex(abR))
        ' We are done with private key string, so wipe it securely
        Wipe.String(sbPrivateKey)

        ' 2.3.1 Check that first two bytes are zero (1/65536 chance of false positive)
        If abR(0) <> 0 Or abR(1) <> 0 Then Exit Sub ' CATCH ERROR

        ' 2.4 Obtain CEK by hashing r
        abCEK = Hash256Double(abR)
        Console.WriteLine("CEK=" & Cnv.ToHex(abCEK))

        ' 2.5 Decrypt the plain text using AES-128 with the content encryption key: m=Decrypt(CEK, CT)
        abData = Cipher.Decrypt(abData, abCEK, abIV, CipherAlgorithm.Aes128, Mode.CBC)
        If abData.Length = 0 Then Exit Sub ' CATCH ERROR
        Console.WriteLine("PT=" & Cnv.ToHex(abData))

        ' 2.5.1 Strip the padding
        abData = Cipher.Unpad(abData, CipherAlgorithm.Aes128)
        If abData.Length = 0 Then Exit Sub
        Console.WriteLine("m =" & Cnv.ToHex(abData))

        ' 2.5.2 Convert byte array back to a string
        strMessage = System.Text.Encoding.Default.GetString(abData)
        Console.WriteLine("m='" & strMessage & "'")

        If strMessage <> strCorrect Then Exit Sub ' CATCH ERROR
        Console.WriteLine("OK, all done successfully")
    End Sub

    Public Function Hash256Double(ByVal abInput() As Byte) As Object
        ' Computes SHA256(SHA256(input))
        Dim abDigest() As Byte

        abDigest = Hash.BytesFromBytes(abInput, HashAlgorithm.Sha256)
        abDigest = Hash.BytesFromBytes(abDigest, HashAlgorithm.Sha256)
        Hash256Double = abDigest
    End Function

End Module

Download source code

Here is the above source code and Bob's key material: zipped, 6 kB.

Contact us

To contact us or comment on this page, please send us a message.

[Go to top]

This page first published by DI Management 20 November 2010. Last updated 14 October 2012. Reformatted for HTML5 on 12 June 2020.