' Module: RSAES_OAEP_example.bas
' The function `rsaes_oaep_encrypt_example' reproduces the
' WORKED-OUT EXAMPLE FOR RSAES-OAEP from pkcs-1v2-1-vec.zip in the file `oaep-int.txt'
' using functions from the CryptoSys PKI Toolkit <www.cryptosys.net/pki/>
' The function `rsaes_oaep_decrypt_example' below shows how to decrypt the same data.
' The public and private key data have been converted to DER-encoded binary files
' rsa1024pub.bin and rsa1024pri.bin.
' We have created an encrypted version of the private key, rsa1024epk.bin, with password "password"
' (see the function `encryptPrivateKeyInfo' below)
' Ref: pkcs-1v2-1-vec.zip
' Available from: <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
' Also: PKCS #1: RSA Cryptography Specifications Version 2.1 (PKCS-1v2-1)
' RSAEP = RSA Encryption Primitive
' OAEP = Optimal Asymmetric Encryption Primitive
'***************** COPYRIGHT NOTICE *******************
' This code was originally written by David Ireland and
' is copyright (C) 2005 DI Management Services Pty Ltd
' <www.di-mgt.com.au>.
' Provided "as is". No warranties. Use at own risk.
' It is not to be altered or distributed,
' except as part of an application.
' You are free to use it in any application,
' provided this copyright notice is left unchanged.
'************** END OF COPYRIGHT NOTICE ***************
Option Explicit
' IMPORTANT: change this path to suit your system
Private Const TEST_KEY_PATH As String = "C:\Test\"
Public Function rsaes_oaep_encrypt_example()
Dim lngRet As Long
Dim strMessage As String
Dim strSeed As String
Dim abMessage() As Byte
Dim abSeed() As Byte
Dim abDB() As Byte
Dim abLHash() As Byte
Dim abDbMask() As Byte
Dim abMaskedDB() As Byte
Dim abSeedMask() As Byte
Dim abMaskedSeed() As Byte
Dim abBlock() As Byte
' All lengths are in octets (i.e. 8-bit bytes)
Dim nhLen As Long ' length of hash = 20 for SHA-1
Dim nkLen As Long ' length, k, of RSA key modulus (= 1024/8 = 128 here)
Dim nmLen As Long ' length of message, M
Dim npsLen As Long ' length of padding string, PS
Dim ndbLen As Long ' length of data block, DB
Dim i As Long
Dim iOffset As Long
Dim strPublicKey As String
' We are given the following hex data
strMessage = "d4 36 e9 95 69 fd 32 a7 c8 a0 5b bc 90 d3 2c 49"
strSeed = "aa fd 12 f6 59 ca e6 34 89 b4 79 e5 07 6d de c2 f0 6c b5 8f"
' --in practice, we'd be generating a new random seed each time
' --i.e.
' ReDim abSeed(19)
' Call RNG_Bytes(abSeed(0), 20, "", 0)
' --see rsaes_oaep_Encrypt()
' Convert the given hex data into byte arrays.
' --note that spaces and other non-hex chars are ignored
abMessage = cnvBytesFromHexStr(strMessage)
abSeed = cnvBytesFromHexStr(strSeed)
' Compute Hash(L, the empty string) in byte array format
' --we know the resulting digest is going to be 20 bytes long
nhLen = SHA1_BYTE_LEN
ReDim abLHash(nhLen - 1)
lngRet = HASH_Bytes(abLHash(0), nhLen, 0, 0, PKI_HASH_SHA1)
Debug.Print "lHash: " & cnvHexStrFromBytes(abLHash)
' Compute lengths
nmLen = BytesLength(abMessage)
nkLen = 1024 / 8 ' We hard code in this example
ndbLen = nkLen - 1 - nhLen
npsLen = ndbLen - nmLen - nhLen - 1
' Catch error
If npsLen < 0 Then
MsgBox "Message is too long", vbCritical
Exit Function
End If
' Construct DB = lHash || Padding || M
ReDim abDB(ndbLen - 1)
iOffset = 0
' Copy lHash into DB
For i = 0 To nhLen - 1
abDB(iOffset) = abLHash(i)
iOffset = iOffset + 1
Next
' Followed by npsLen zero bytes
' --we don't have to do this as the bytes should already be zero
For i = 0 To npsLen - 1
abDB(iOffset) = 0
iOffset = iOffset + 1
Next
' Followed by a single 0x01 byte
abDB(iOffset) = &H1
iOffset = iOffset + 1
' And then the message itself
For i = 0 To nmLen - 1
abDB(iOffset) = abMessage(i)
iOffset = iOffset + 1
Next
Debug.Print "DB: " & cnvHexStrFromBytes(abDB)
' Compute dbMask = MGF(seed, length(DB))
abDbMask = MGF1(abSeed, ndbLen)
Debug.Print "dbMask: " & cnvHexStrFromBytes(abDbMask)
' Compute maskedDB = DB XOR dbMask
abMaskedDB = XorBytes(abDB, abDbMask)
Debug.Print "maskedDB: " & cnvHexStrFromBytes(abMaskedDB)
' Compute seedMask = MGF(maskedDB, length(seed))
abSeedMask = MGF1(abMaskedDB, nhLen)
Debug.Print "dbSeedMask: " & cnvHexStrFromBytes(abSeedMask)
' Compute maskedSeed = seed XOR seedMask
abMaskedSeed = XorBytes(abSeed, abSeedMask)
Debug.Print "maskedSeed: " & cnvHexStrFromBytes(abMaskedSeed)
' Build EM = 00 || maskedSeed || maskedDB
' --be careful, EM is different in the examples and in PKCS#1
' --in PKCS#1, EM does not include the leading zero.
ReDim abBlock(0)
abBlock(0) = 0
abBlock = AppendBytes(abBlock, abMaskedSeed)
abBlock = AppendBytes(abBlock, abMaskedDB)
Debug.Print "EM: " & cnvHexStrFromBytes(abBlock)
' Encrypt using RSA public key
' Get key from the file
' --in practice we would have read this at the start and found out nkLen then
' --rather than hard-coding it in as we did above for this example
strPublicKey = rsaReadPublicKey(TEST_KEY_PATH & "rsa1024pub.bin")
If Len(strPublicKey) = 0 Then
MsgBox "Cannot read public key", vbCritical
Exit Function
End If
' Check the length is OK
If RSA_KeyBytes(strPublicKey) <> nkLen Then
MsgBox "Key length does not match", vbCritical
Exit Function
End If
' Encrypt
lngRet = RSA_RawPublic(abBlock(0), nkLen, strPublicKey, 0)
Debug.Print "CT: " & cnvHexStrFromBytes(abBlock)
End Function
Public Function rsaes_oaep_decrypt_example()
' This decrypts the ciphertext from the previous example
Dim lngRet As Long
Dim abMessage() As Byte
Dim abSeed() As Byte
Dim abDB() As Byte
Dim abLHash() As Byte
Dim abPHash() As Byte
Dim abDbMask() As Byte
Dim abMaskedDB() As Byte
Dim abSeedMask() As Byte
Dim abMaskedSeed() As Byte
Dim abBlock() As Byte
Dim nhLen As Long ' length of hash = 20 for SHA-1
Dim nkLen As Long ' length, k, of RSA key modulus (= 1024/8 = 128 here)
Dim nmLen As Long ' length of message, M
Dim npsLen As Long ' length of padding string, PS
Dim ndbLen As Long ' length of data block, DB
Dim i As Long
Dim iOffset As Long
Dim strPrivateKey As String
' We are given the ciphertext in hex format, so convert to bytes
abBlock = cnvBytesFromHexStr( _
"1253E04DC0A5397BB44A7AB87E9BF2A039A33D1E996FC82A94CCD30074C95DF763722017069E5268DA5D1C0B4F872CF653C11DF82314A67968DFEAE28DEF04BB6D84B1C31D654A1970E5783BD6EB96A024C2CA2F4A90FE9F2EF5C9C140E5BB48DA9536AD8700C84FC9130ADEA74E558D51A74DDF85D8B50DE96838D6063E0955")
Debug.Print "CT: " & cnvHexStrFromBytes(abBlock)
' Read in the private key
strPrivateKey = rsaReadPrivateKey(TEST_KEY_PATH & "rsa1024epk.bin", "password")
If Len(strPrivateKey) = 0 Then
MsgBox "Cannot read private key", vbCritical
Exit Function
End If
nkLen = RSA_KeyBytes(strPrivateKey)
Debug.Print "RSA key is " & nkLen & " bytes long (" & RSA_KeyBits(strPrivateKey) & ") bits"
' Decrypt
lngRet = RSA_RawPrivate(abBlock(0), nkLen, strPrivateKey, 0)
' We are done with the private key, so erase it
Call WIPE_String(strPrivateKey, Len(strPrivateKey))
strPrivateKey = ""
If lngRet <> 0 Then
MsgBox "Decryption error", vbCritical
Exit Function
End If
Debug.Print "EM: " & cnvHexStrFromBytes(abBlock)
' EM = 00 || maskedSeed || maskedDB
' so check leading byte is zero and then split up
If abBlock(0) <> 0 Then
MsgBox "Decryption error", vbCritical
Exit Function
End If
' Now we decode according to EME-OAEP-DECODE
nhLen = SHA1_BYTE_LEN
ndbLen = nkLen - nhLen - 1
ReDim abMaskedSeed(nhLen - 1)
ReDim abMaskedDB(ndbLen - 1)
iOffset = 1
For i = 0 To nhLen - 1
abMaskedSeed(i) = abBlock(iOffset)
iOffset = iOffset + 1
Next
Debug.Print "maskedSeed: " & cnvHexStrFromBytes(abMaskedSeed)
For i = 0 To ndbLen - 1
abMaskedDB(i) = abBlock(iOffset)
iOffset = iOffset + 1
Next
Debug.Print "maskedDB: " & cnvHexStrFromBytes(abMaskedDB)
' Let seedMask = MGF(maskedDB, hLen)
abSeedMask = MGF1(abMaskedDB, nhLen)
Debug.Print "seedMask: " & cnvHexStrFromBytes(abSeedMask)
' Let seed = maskedSeed \xor seedMask
abSeed = XorBytes(abMaskedSeed, abSeedMask)
Debug.Print "seed: " & cnvHexStrFromBytes(abSeed)
' Let dbMask = MGF(seed, ||EM|| - hLen)
abDbMask = MGF1(abSeed, ndbLen)
Debug.Print "dbMask: " & cnvHexStrFromBytes(abDbMask)
' Let DB = maskedDB \xor dbMask
abDB = XorBytes(abMaskedDB, abDbMask)
Debug.Print "DB: " & cnvHexStrFromBytes(abDB)
' Let pHash = Hash(P), an octet string of length hLen
' where P is the empty string in this implementation
ReDim abPHash(nhLen - 1)
lngRet = HASH_Bytes(abPHash(0), nhLen, 0, 0, PKI_HASH_SHA1)
Debug.Print "pHash: " & cnvHexStrFromBytes(abPHash)
' Separate DB = pHash' || PS || 01 || M
' If there is no 01 octet to separate PS from M, output "decoding error" and stop.
' If pHash' does not equal pHash, output "decoding error" and stop.
ReDim abLHash(nhLen - 1)
For i = 0 To nhLen - 1
If abDB(i) <> abPHash(i) Then
MsgBox "Decryption error", vbCritical
Exit Function
End If
Next
' Work through the zero bytes of PS, if any, to find the 01 byte
For i = nhLen To ndbLen - 1
If abDB(i) <> 0 Then
Exit For
End If
Next
If i >= ndbLen Then ' Too long - no message at all
MsgBox "Decryption error", vbCritical
Exit Function
End If
If abDB(i) <> &H1 Then
MsgBox "Decryption error", vbCritical
Exit Function
End If
' The remainder of DB is M, the message
iOffset = i + 1
nmLen = ndbLen - iOffset
Debug.Print "M is " & nmLen & " bytes long"
ReDim abMessage(nmLen - 1)
For i = 0 To nmLen - 1
abMessage(i) = abDB(iOffset + i)
Next
' Output M.
Debug.Print "M: " & cnvHexStrFromBytes(abMessage)
End Function
'******************************************************
' HOW TO ENCRYPT AN UNENCRYPTED PRIVATE KEY INFO FILE *
'******************************************************
Public Function encryptPrivateKeyInfo()
' Converts the unencrypted pkcs-8 PrivateKeyInfo data file into encrypted pkcs-8 format
Dim strPrivateKey64 As String
Dim strPRIFile As String
Dim strEPKFile As String
Dim strPassword As String
Dim nKeyLen As Long
Dim lngRet As Long
strPRIFile = TEST_KEY_PATH & "rsa1024pri.bin"
strEPKFile = TEST_KEY_PATH & "rsa1024epk.bin"
strPassword = "password"
' First read in as a string from the unencrypted file
' How long is PrivateKey string?
nKeyLen = RSA_ReadPrivateKeyInfo("", 0, strPRIFile, 0)
If nKeyLen <= 0 Then
MsgBox "Unable to read private key info", vbCritical
Exit Function
End If
Debug.Print "Private key string is " & nKeyLen & " characters long"
' Pre-dimension the string to receive data
strPrivateKey64 = String(nKeyLen, " ")
' Read it in
lngRet = RSA_ReadPrivateKeyInfo(strPrivateKey64, nKeyLen, strPRIFile, 0)
' Then save in encrypted form
lngRet = RSA_SaveEncPrivateKey(strEPKFile, strPrivateKey64, 1000, strPassword, 0)
End Function