Attribute VB_Name = "basSignExample"
Option Explicit
' This example shows how you could use the RSA_Raw functions
' to create and verify a digital signatue of a short message
' using a 512-bit RSA key. Please read the comments below.
' Last updated:
' $Date: 2006-06-27 04:20:00 $
' Include the module basCrPKI in your project
' from the CryptoSys PKI toolkit
' ******************************************************************
' COMMENTS ON THIS CODE
'
' 1. This code is given as an example of how you could use RSA to
' create a digital signature of a short message directly.
'
' 2. In practice, a message digest hash of the longer message is
' computed first and this shorter hash value is "encrypted" in the
' RSA block.
'
' 3. The methods used here to create and read the RSA block
' can be done much faster using the RSA_EncodeMsg and RSA_DecodeMsg
' functions.
'
' 4. In practice, too, the public key is usually provided in a
' public key certificate file (X.509 file).
'
' 5. In an earlier version we hinted that the key strings could be
' hardcoded. We lied. ALWAYS read in the private and public key
' values from the files each time you use them.
'
' 6. Treat the private key string as the very important secret that
' it is. NEVER print it out or save it to disk. Always use the
' WIPE_String function immediately you have finished with it.
' ******************************************************************
Public Function TestSign()
Dim strPlain As String
Dim lngRet As Long
Dim nBlockLen As Long
Dim nBytes As Long
Dim nPad As Long
Dim sPublicKeyFile As String
Dim sPrivateKeyFile As String
Dim strPublicKey As String
Dim strPrivateKey As String
Dim strPassword As String
Dim abBlock() As Byte
Dim abPlain() As Byte
Dim i As Integer
Dim iOffset As Integer
Dim abResult() As Byte
Dim strResult As String
' Some keys we prepared earlier (see CreateTestKeys)
sPublicKeyFile = App.Path & "\" & "ourkeypub.bin"
sPrivateKeyFile = App.Path & "\" & "ourkeypri.bin"
' This is just a demo, OK. NEVER hard-code real passwords.
strPassword = "password"
' (1) In this first part of the example, we are the issuing authority
' We have the private key and its password (hopefully a better one than 'password')
' Read in the private key as base64 string
' NB There is a deliberate 500ms pause in this function
strPrivateKey = rsaReadPrivateKey(sPrivateKeyFile, strPassword)
' Check for error
If Len(strPrivateKey) <= 0 Then
MsgBox "Could not read private key file " & sPrivateKeyFile, vbCritical
Exit Function
End If
' Check its length - it should be 64 bytes (512 bits)
nBlockLen = RSA_KeyBytes(strPrivateKey)
If nBlockLen <= 0 Then '(you could be more specific here and check for the exact length)
MsgBox "Error with private key length", vbCritical
Exit Function
End If
Debug.Print "Private key is " & nBlockLen & " bytes long (" & RSA_KeyBits(strPrivateKey) & " bits)"
' Get the string of characters we want to sign (max 61 bytes long for a 512-bit key)
strPlain = "What we want to sign...!"
Debug.Print "DATA=" & strPlain
' Convert to bytes
abPlain = StrConv(strPlain, vbFromUnicode)
' Show in hex
Debug.Print "DATA=" & cnvHexStrFromBytes(abPlain)
' Check exact length in bytes
nBytes = UBound(abPlain) - LBound(abPlain) + 1
' Construct encryption block of exactly 128 bytes
ReDim abBlock(nBlockLen - 1)
' Required encryption block - this is a simplified version of PKCS#1
'|<-------------------(64 bytes)----------------->|
'+--+--+---------+--+-----------------------------+
'|00|01|FFFF...FF|00| DATA TO SIGN |
'+--+--+---------+--+-----------------------------+
' First byte is always 00
abBlock(0) = 0
' Second byte is always 01
abBlock(1) = 1
' Then pad with 0xFF
nPad = nBlockLen - 3 - nBytes
If nPad < 0 Then
MsgBox "Data is too long/ key is too short"
Exit Function
End If
iOffset = 2
For i = 0 To nPad - 1
abBlock(iOffset + i) = &HFF
Next
' Separating NUL
iOffset = iOffset + nPad
abBlock(iOffset) = 0
' Then the data to be signed
iOffset = iOffset + 1
For i = 0 To nBytes - 1
abBlock(iOffset + i) = abPlain(i)
Next
' Show the block in hex
Debug.Print "BLCK=" & cnvHexStrFromBytes(abBlock)
' Now we use the RSA function to encrypt the input block using the private key
' this creates a `signature'
lngRet = RSA_RawPrivate(abBlock(0), nBlockLen, strPrivateKey, 0)
Debug.Print "RSA_RawPrivate returns " & lngRet
If lngRet <> 0 Then
Debug.Print pkiGetLastError()
Else
' Display our results in hex format
Debug.Print "ENCR=" & cnvHexStrFromBytes(abBlock)
End If
' We send our `signature' as the 64 bytes now in abBlock
' Sending in hex or base64 format is usually simpler.
' -------
' (2) In this second part of the example, we are the person authenticating the data
' We only have the public key, which can be made freely available.
' No one can use the public key to create the signature
' Read in the public key as base64 string
strPublicKey = rsaReadPublicKey(sPublicKeyFile)
' Check for error
If Len(strPublicKey) = 0 Then
MsgBox "Could not read public key file " & sPublicKeyFile, vbCritical
Exit Function
End If
' Check its length - it should be 64 bytes (512 bits)
nBlockLen = RSA_KeyBytes(strPublicKey)
If nBlockLen <= 0 Then
MsgBox "Error with public key length", vbCritical
Exit Function
End If
Debug.Print "Public key is " & nBlockLen & " bytes long (" & RSA_KeyBits(strPublicKey) & " bits)"
' [Do not hardcode this key - always read it in from the public key file or certificate]
' Given the encrypted data in abBlock,
' we use the RSA function to decrypt the input block using the public key
lngRet = RSA_RawPublic(abBlock(0), nBlockLen, strPublicKey, 0)
Debug.Print "RSA_RawPublic returns " & lngRet
If lngRet <> 0 Then
Debug.Print pkiGetLastError()
Else
' Display our results in hex format
Debug.Print "DECR=" & cnvHexStrFromBytes(abBlock)
End If
' Extract the plaintext from the resulting block
' And check the first two bytes are what we expect
If abBlock(0) <> 0 Then
MsgBox "Decryption error"
Exit Function
End If
If abBlock(1) <> 1 Then
MsgBox "Decryption error"
Exit Function
End If
' If we've got this far, we have almost certainly succeeded
' but we'll keep checking - we should have all 0xff's until we find a 0
For i = 2 To nBlockLen - 1
If abBlock(i) = 0 Then
' Found start of real data we want
Exit For
ElseIf abBlock(i) <> &HFF Then
' Unexpected character
MsgBox "Decryption error"
Exit Function
End If
Next
' Copy result we want
nBytes = nBlockLen - i - 1
ReDim abResult(nBytes - 1)
For i = i + 1 To nBlockLen - 1
abResult(i - nBlockLen + nBytes) = abBlock(i)
Next
' Convert to a string
strResult = StrConv(abResult, vbUnicode)
Debug.Print strResult
End Function