I've got to ask a long and involved StackOverflow question and paste a load of code but first I'll paste it here and use this as a drafting board.
The use case is using OpenSSL on a Linux server to sign a license (plain text) file with a 384 bit Elliptic Curve Digital Server Algorithm (ECDSA), the verification of the digital signature takes place on a customer's Windows desktop OS running full (Windows) .NET Framework.
The license file and a Base 64 encoded digital signature are emailed to the customer (who is not on a shared corporate network). The customer is running a C# written .NET Framework (Windows edition) application and verification of the the licence and digital signature unlocks paid-for features.
Now, I say Linux but the example server side code given below is not yet in a Linux scripting language. I'm prototyping with VBA running on Windows 8, eventually I will convert over to a Linux scripting language but bear with me for the time being.
The point is I am using OpenSSL console commands and not compiling against any OpenSSL software development kit (C++ headers etc.).
One tricky part (and perhaps is the best place to begin code review) is the digging out of the X and Y co-ordinates that form the public key from the DER file. A DER key file is a binary encoded file that uses Abstract Syntax Notation (ASN1), there are free GUI programs out there such as ASN1. Editor on the Code Project that allows easy inspection, here is a screenshot of a public key file
Luckily, OpenSSL has its own inbuilt ASN1 parser so the same details are written to the console as the following
C:\OpenSSL-Win64\bin\openssl.exe asn1parse -inform DER -in n:\ECDSA\2017-11-03T193106\ec_pubkey.der
0:d=0 hl=2 l= 118 cons: SEQUENCE
2:d=1 hl=2 l= 16 cons: SEQUENCE
4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey
13:d=2 hl=2 l= 5 prim: OBJECT :secp384r1
20:d=1 hl=2 l= 98 prim: BIT STRING
So at offset 20 there are 98 bytes which contain the X and Y co-ordinates, at byte 20 is a tag (0x03) indicating a string is following and at byte 21 is the length, 98 (any length below 127 needs only one byte). So actually the real 98 bytes of data begin at byte 22 , so I read 100 bytes in total (98+2). At byte 22 is 0x00 which is how BIT STRINGS begin (see Point 5). At byte 23 is 0x04 which indicates that both X and Y follow which is known as uncompressed form (it is possible to give the X value and compute the Y in which case use 0x02 or 0x03). After the 0x04 comes the X and Y coordinates, 48 bytes each because 8 bits in a byte and 8*48=384.
So one digs out two (X & Y) very long hexadecimal numbers as strings. The next pain comes in creating the Xml file suitable for the C# code. The key class is C#'s ECDsaCng and the method to import is FromXmlString and it expected the file to implement standard Rfc4050. The Xml file that C#'s ECDsaCng imports requires X and Y to be in decimal and not hexadecimal so we have to write another function to convert, I translated from another language taken from another Stack Overflow question.
Here is the VBA code (there's quite a lot) and you'll need to change where it will write its working files. The two code blocks to run are EntryPoint1_RunECDSAKeyGenerationBatch_RunOnce and EntryPoint2_RunHashAndSignBatch
It should be taken as read that OpenSSL has been installed, my version is at C:\OpenSSL-Win64\
Option Explicit
Option Private Module
'******* Requires Tools->References to the following libraries
'* Microsoft ActiveX Data Objects 6.1 Library C:\Program Files (x86)\Common Files\System\ado\msado15.dll
'* Microsoft Scripting Runtime C:\Windows\SysWOW64\scrrun.dll
'* Microsoft XML, v.6.0 C:\Windows\SysWOW64\msxml6.dll
'* Windows Script HostObject Model C:\Windows\SysWOW64\wshom.ocx
'* Microsoft VBScript Regular Expressions 5.5 C:\Windows\SysWOW64\vbscript.dll\3
'* Microsoft Script Control 1.0 C:\Windows\SysWOW64\msscript.ocx
Private fso As New Scripting.FileSystemObject
Private Const sOPENSSL_BIN As String = "C:\OpenSSL-Win64\bin\openssl.exe" '* installation for OpenSSL
Private msBatchDir As Variant '* hold over so we can sign multiple times
Private Property Get BatchDir() As String
'* the point of this property procedure is to interpret the contents of
'* the module level variable msBatchDir, which is a Variant and which
'* when empty signals not yet set. If not yet set then we return a default
'* for testing convenience
If IsEmpty(msBatchDir) Then
'* place your favourite default here
msBatchDir = "n:\ECDSA\2017-11-05T225657\"
End If
BatchDir = msBatchDir
End Property
Private Sub TestLoop()
Dim l
For l = 1 To 3
EntryPoint1_RunECDSAKeyGenerationBatch_RunOnce
EntryPoint2_RunHashAndSignBatch
Next l
End Sub
'* KEY ENTRY POINT 1 OF 2
Private Sub EntryPoint1_RunECDSAKeyGenerationBatch_RunOnce()
'* Notes:
'* during development I felt it convenient to start afresh whilst
'* experimenting with different flags. The best way to start afresh I
'* found was to give myself a new directory for each run
'* so here you'll find we create a directory based on timestamp
'* you'll need to change this and create your own folder
Const sROOT_DIRECTORY As String = "n:\ECDSA\"
'* create a timestamped and thus unique folder to do our work in
msBatchDir = sROOT_DIRECTORY & Format(Now(), "yyyy-MM-ddTHHnnss")
Debug.Print "Creating batch directory :" & msBatchDir
'* zap anything previous (unlikely)
If fso.FolderExists(msBatchDir) Then fso.DeleteFolder msBatchDir
fso.CreateFolder msBatchDir
Dim bReturn As Boolean
bReturn = RunECDSAKeyGenerationBatch_RunOnce(msBatchDir)
End Sub
'* KEY ENTRY POINT 2 OF 2
Private Sub EntryPoint2_RunHashAndSignBatch()
'* using the same folder as the license key creation logic
Dim sBatchDir As String
sBatchDir = BatchDir()
Dim sSignableFile As String
sSignableFile = fso.BuildPath(sBatchDir, "license.txt")
'* we write a trivial 5 bytes to a file
'* as a simple substitute for our license file
Dim bReturn As Boolean
If Not True Then
bReturn = WriteStringToFile("Hello World", sSignableFile)
Else
Dim sRandomText As String
sRandomText = RandomText2
Debug.Print sRandomText
bReturn = WriteStringToFile(sRandomText, sSignableFile)
End If
If bReturn Then
'* we need a file to contain bothe private and public keys
'* this will live on the server
Dim sKeyFile As String
sKeyFile = fso.BuildPath(sBatchDir, "ec_key.pem")
'* we need a file that contains only the public keys
'* so we can distribute to customers for digital
'* signature verification
Dim sPublicKeyFile As String
sPublicKeyFile = fso.BuildPath(sBatchDir, "ec_pubkey.pem")
'* hash file was for debugging, not stricly necessary
Dim sHashFile As String
sHashFile = fso.BuildPath(sBatchDir, "license.sha256")
'* the binary version of the license signature
'* this is an interim file because not good for emails
Dim sSignatureFile As String
sSignatureFile = fso.BuildPath(sBatchDir, "license.sig")
'* this base 64 version of license.sig allows contents to be pasted into
'* an email.
Dim sSignatureFileBase64 As String
sSignatureFileBase64 = fso.BuildPath(sBatchDir, "license.sigb64")
bReturn = RunHashAndSignBatch(sKeyFile, sPublicKeyFile, sSignableFile, sHashFile, sSignatureFile, sSignatureFileBase64)
Debug.Assert bReturn
RunCSharp "", "", ""
End If
End Sub
Private Function RandomText2() As String
'http://www.randomtext.me/api/
Dim oXHR As MSXML2.XMLHTTP60
Set oXHR = New MSXML2.XMLHTTP60
'oXHR.Open "GET", "http://www.randomtext.me/api/"
oXHR.Open "GET", "http://www.randomtext.me/api/gibberish/p-5/25-45"
'#
oXHR.send
Dim oParseJSON As Object
Set oParseJSON = ParseJSON(oXHR.responseText)
RandomText2 = VBA.CallByName(oParseJSON, "text_out", VbGet)
End Function
Function ParseJSON(ByVal sJSON As String) As Object
'* Tools->References->Microsoft Script Control 1.0 (msscript.ocx)
Static oScriptControl As MSScriptControl.ScriptControl
If oScriptControl Is Nothing Then
Set oScriptControl = New MSScriptControl.ScriptControl
oScriptControl.Language = "javascript"
End If
Dim oParsed As Object
Set ParseJSON = oScriptControl.Eval("(" & sJSON & ")")
End Function
Private Function RunECDSAKeyGenerationBatch_RunOnce(ByVal sBatchDir As String) As Boolean
'* generates paths for all our files
Dim sECDSA_KEYFILE As String
sECDSA_KEYFILE = fso.BuildPath(sBatchDir, "ec_key.pem")
'* this is a pretty print file for debugging where
'* OpenSSL outputs in a hexadecimal
Dim sECDSA_KEYFILE_Text As String
sECDSA_KEYFILE_Text = fso.BuildPath(sBatchDir, "ec_key.txt")
'* the PEM files is in a text format cut and pastable into an email
Dim sECDSA_PublicKeyPemFile As String
sECDSA_PublicKeyPemFile = fso.BuildPath(sBatchDir, "ec_pubkey.pem")
'* the DER file is required for debugging verification on the server
'* and not strictly required for production
Dim sECDSA_PublicKeyDerFile As String
sECDSA_PublicKeyDerFile = fso.BuildPath(sBatchDir, "ec_pubkey.der")
'* generate the keys by calling shelling subroutine
Dim bReturn As Boolean
bReturn = OpenSSL_GenECDSAKeys(sECDSA_KEYFILE, sECDSA_PublicKeyDerFile, sECDSA_PublicKeyPemFile)
If bReturn Then
'* only for debugging do we output the private key in plain text
If Len(Trim(sECDSA_KEYFILE_Text)) > 0 Then
'* for debugging generate the text "pretty print"
OpenSSL_PrettyPrintKey sECDSA_KEYFILE, sECDSA_KEYFILE_Text
End If
'* The ECDsaCng C# class has a FromXmlString import method
'* which consumes an Xml file conforming to standard Rfc4050
Dim sECDSA_XmlExport As String
sECDSA_XmlExport = fso.BuildPath(sBatchDir, "ec_pubkey.xml")
bReturn = ExportECDSAToXml(sECDSA_PublicKeyDerFile, sECDSA_XmlExport)
End If
RunECDSAKeyGenerationBatch_RunOnce = bReturn
End Function
Private Function RunHashAndSignBatch(ByVal sKeyFile As String, ByVal sPublicKeyFile As String, ByVal sSignableFile As String, _
ByVal sHashFile As String, ByVal sSignatureFile As String, ByVal sSignatureFileBase64 As String) As Boolean
'* the hash is for debugging, not strictly necessary
Dim bReturn As Boolean
bReturn = OpenSSL_Hash(sKeyFile, sSignableFile, sHashFile)
If bReturn Then
'* we really need the base64 signature so we can cut and paste
'* contents into an email
bReturn = OpenSSL_SignDigest(sKeyFile, sSignableFile, sSignatureFile, sSignatureFileBase64)
If bReturn Then
'* as a debugging/sanity check we verify the signature
bReturn = OpenSSL_VerifySignature(sPublicKeyFile, sSignatureFile, sSignableFile)
End If
End If
RunHashAndSignBatch = bReturn
End Function
Private Function OpenSSL_PrettyPrintKey(ByVal sEcdsaKeyFile As String, _
ByVal sEcdsaPrettyPrintFile As String) As Boolean
'* ordinarily OpenSSL spits out a binary file but if yopu want to inspect the contents
'* in a text file viewer then use this routine to create a viewable file
'* sample output ...
'Private-Key: (384 bit)
'priv:
' 54:ba:af:0d:dd:65:15:96:c6:33:ad:c8:2e:b5:3f:
' d3:c4:80:59:d3:50:9d:81:71:ea:bd:49:b5:14:cc:
' 6a:e2:d9:43:1f:48:d9:35:ec:00:fa:5d:71:be:7a:
' c6:0a:a7
'pub:
' 04:1b:aa:e5:53:22:cd:d0:d7:da:fd:da:35:46:5a:
' ed:d3:0c:b3:6c:04:ad:69:3a:a3:3e:3a:47:ea:1e:
' 9a:ca:9d:5b:7a:7d:29:f3:84:bd:b2:df:d4:79:de:
' 8e:e7:1b:68:b7:61:b7:32:54:78:ef:5f:9b:8c:4f:
' a3:73:50:cf:1b:90:7c:2e:cf:24:5e:aa:54:ad:19:
' 82:b6:26:f3:86:df:b6:d4:4a:45:01:02:76:fc:44:
' 86:58:ee:eb:6d:f3:db
'ASN1 OID: secp384r1
'NIST CURVE: P -384
Debug.Assert fso.FileExists(sOPENSSL_BIN)
Debug.Assert fso.FileExists(sEcdsaKeyFile)
If fso.FileExists(sEcdsaPrettyPrintFile) Then fso.DeleteFile sEcdsaPrettyPrintFile
Debug.Assert Not fso.FileExists(sEcdsaPrettyPrintFile)
Dim sPrettyPrintKeyCmd As String
sPrettyPrintKeyCmd = sOPENSSL_BIN & " ec -noout -text -in " & sEcdsaKeyFile & " -out " & sEcdsaPrettyPrintFile
Dim ePrettyPrintKeyStatus As WshExecStatus, sPrettyPrintKeyStdOut As String, sPrettyPrintKeyStdErr As String
ePrettyPrintKeyStatus = RunShellAndWait(sPrettyPrintKeyCmd, sPrettyPrintKeyStdOut, sPrettyPrintKeyStdErr)
If ePrettyPrintKeyStatus = WshFailed Then
Debug.Print sPrettyPrintKeyStdErr
Else
Debug.Print sPrettyPrintKeyStdOut
Debug.Assert fso.FileExists(sEcdsaPrettyPrintFile)
Debug.Print fso.OpenTextFile(sEcdsaPrettyPrintFile).ReadAll
OpenSSL_PrettyPrintKey = True
End If
'Stop
End Function
'
Private Function RunCSharp(ByVal sEcdsaKeyFile As String, _
ByVal sEcdsaPublicKeyDerFile As String, _
ByVal sEcdsaPublicKeyPemFile As String) As Boolean
'* this routine generates the key pair (private and public) file which resides on
'* the server and also the public key only file (in two formats here for debugging)
Const sCSHARP As String = "C:\Users\Simon\Documents\Visual Studio 2017\Projects\ECDSAVerifySignature\ECDSAVerifySignature\bin\Debug\ECDSAVerifySignature.exe"
Debug.Assert fso.FileExists(sCSHARP)
Dim sBatchDir As String
sBatchDir = BatchDir()
Debug.Assert fso.FolderExists(sBatchDir)
Dim sWinFile As String
sWinFile = fso.BuildPath(sBatchDir, "win.txt")
If fso.FileExists(sWinFile) Then fso.DeleteFile sWinFile
Debug.Assert Not fso.FileExists(sWinFile)
Dim sCSharpCmd As String
sCSharpCmd = sCSHARP & " " & sBatchDir
Dim eCSharpStatus As WshExecStatus, sCSharpStdOut As String, sCSharpStdErr As String
eCSharpStatus = RunShellAndWait(sCSharpCmd, sCSharpStdOut, sCSharpStdErr)
If eCSharpStatus = WshFailed Then
Debug.Print sCSharpStdErr
Else
Debug.Print sCSharpStdOut
'Debug.Assert fso.FileExists(sWinFile)
End If
End Function
Private Function OpenSSL_GenECDSAKeys(ByVal sEcdsaKeyFile As String, _
ByVal sEcdsaPublicKeyDerFile As String, _
ByVal sEcdsaPublicKeyPemFile As String) As Boolean
'* this routine generates the key pair (private and public) file which resides on
'* the server and also the public key only file (in two formats here for debugging)
Debug.Assert fso.FileExists(sOPENSSL_BIN)
If fso.FileExists(sEcdsaKeyFile) Then fso.DeleteFile sEcdsaKeyFile
Debug.Assert Not fso.FileExists(sEcdsaKeyFile)
Dim sGenKeyCmd As String
sGenKeyCmd = sOPENSSL_BIN & " ecparam -genkey -name secp384r1 -out " & sEcdsaKeyFile
Dim eGenKeyStatus As WshExecStatus, sGenKeyStdOut As String, sGenKeyStdErr As String
eGenKeyStatus = RunShellAndWait(sGenKeyCmd, sGenKeyStdOut, sGenKeyStdErr)
If eGenKeyStatus = WshFailed Then
Debug.Print sGenKeyStdErr
Else
Debug.Print sGenKeyStdOut
Debug.Assert fso.FileExists(sEcdsaKeyFile)
'Stop
If fso.FileExists(sEcdsaPublicKeyDerFile) Then fso.DeleteFile sEcdsaPublicKeyDerFile
Debug.Assert Not fso.FileExists(sEcdsaPublicKeyDerFile)
Dim sPublicKeyDerCmd As String
sPublicKeyDerCmd = sOPENSSL_BIN & " ec -pubout -outform DER -in " & sEcdsaKeyFile & " -out " & sEcdsaPublicKeyDerFile
Dim ePublicKeyDerStatus As WshExecStatus, sPublicKeyDerStdOut As String, sPublicKeyDerStdErr As String
ePublicKeyDerStatus = RunShellAndWait(sPublicKeyDerCmd, sPublicKeyDerStdOut, sPublicKeyDerStdErr)
Debug.Assert Len(Trim(sPublicKeyDerStdErr))
Debug.Assert fso.FileExists(sEcdsaPublicKeyDerFile)
Dim sPublicKeyPemCmd As String
sPublicKeyPemCmd = sOPENSSL_BIN & " ec -pubout -outform PEM -in " & sEcdsaKeyFile & " -out " & sEcdsaPublicKeyPemFile
Dim ePublicKeyPemStatus As WshExecStatus, sPublicKeyPemStdOut As String, sPublicKeyPemStdErr As String
ePublicKeyPemStatus = RunShellAndWait(sPublicKeyPemCmd, sPublicKeyPemStdOut, sPublicKeyPemStdErr)
Debug.Assert Len(Trim(sPublicKeyPemStdErr))
Debug.Assert fso.FileExists(sEcdsaPublicKeyPemFile)
If ePublicKeyPemStatus = WshFailed Then
Debug.Print sPublicKeyPemStdErr
Else
Debug.Print sPublicKeyPemStdOut
Debug.Assert fso.FileExists(sEcdsaPublicKeyDerFile)
Debug.Assert fso.FileExists(sEcdsaPublicKeyPemFile)
OpenSSL_GenECDSAKeys = True
End If
End If
End Function
Private Function OpenSSL_Hash(ByVal sEcdsaKeyFile As String, _
ByVal sFileToSign As String, _
ByVal sHashFile As String) As Boolean
'* this generates a hash file, I needed this because I'm trying to diagnose
'* a problem with some C# code which also hashes some bytes
Debug.Assert fso.FileExists(sFileToSign)
Debug.Assert fso.FileExists(sEcdsaKeyFile)
Debug.Assert fso.FileExists(sOPENSSL_BIN)
If fso.FileExists(sHashFile) Then fso.DeleteFile sHashFile
Debug.Assert Not fso.FileExists(sHashFile)
Dim sHashCmd As String
sHashCmd = sOPENSSL_BIN & " dgst -sha256 -out " & sHashFile & " " & sFileToSign
Dim eHashStatus As WshExecStatus, sHashStdOut As String, sHashStdErr As String
eHashStatus = RunShellAndWait(sHashCmd, sHashStdOut, sHashStdErr)
If eHashStatus = WshFailed Then
Debug.Print sHashStdErr
Else
Debug.Print sHashStdOut
Debug.Assert fso.FileExists(sHashFile)
Debug.Print fso.OpenTextFile(sHashFile).ReadAll
OpenSSL_Hash = True
End If
End Function
Private Function OpenSSL_SignDigest(ByVal sEcdsaKeyFile As String, _
ByVal sFileToSign As String, _
ByVal sSignatureFile As String, _
ByVal sSignatureFileBase64 As String) As Boolean
'* this code takes a signable file, hashes it to a digest and then signs the digest
'* further, it creates a base 64 version of the signature file the contents
'* of which can be cut and pasted into an email.
Debug.Assert fso.FileExists(sFileToSign)
Debug.Assert fso.FileExists(sEcdsaKeyFile)
Debug.Assert fso.FileExists(sOPENSSL_BIN)
If fso.FileExists(sSignatureFile) Then fso.DeleteFile sSignatureFile
Debug.Assert Not fso.FileExists(sSignatureFile)
Dim sSignCmd As String
sSignCmd = sOPENSSL_BIN & " dgst -sha256 -sign " & sEcdsaKeyFile & " -out " & sSignatureFile & " " & sFileToSign
Dim eSignStatus As WshExecStatus, sSignStdOut As String, sSignStdErr As String
eSignStatus = RunShellAndWait(sSignCmd, sSignStdOut, sSignStdErr)
If eSignStatus = WshFailed Then
Debug.Print sSignStdErr
Else
Debug.Print sSignStdOut
Debug.Assert fso.FileExists(sSignatureFile)
'OpenSSL_SignDigest = True
Dim sEditedSignatureFile As String
sEditedSignatureFile = VBA.Replace(sSignatureFile, ".sig", ".siged")
ExtractSignatureFromDerFile sSignatureFile, sEditedSignatureFile
Debug.Assert fso.FileExists(sEditedSignatureFile)
Dim sBase64Cmd As String
sBase64Cmd = sOPENSSL_BIN & " base64 -in " & sEditedSignatureFile & " -out " & sSignatureFileBase64
Dim eBase64Status As WshExecStatus, sBase64StdOut As String, sBase64StdErr As String
eBase64Status = RunShellAndWait(sBase64Cmd, sBase64StdOut, sBase64StdErr)
If eBase64Status = WshFailed Then
Debug.Print sBase64StdErr
Else
Debug.Print sBase64StdOut
Debug.Assert fso.FileExists(sSignatureFileBase64)
OpenSSL_SignDigest = True
End If
End If
End Function
Private Function OpenSSL_VerifySignature(ByVal sEcdsaKeyFile As String, _
ByVal sSignatureFile As String, _
ByVal sFileToSign As String) As Boolean
'* this was for debugging only because I'm trying to diagnose a C#
'* problem and was wondering if I was calling the OpenSSL commands wrong.
Debug.Assert fso.FileExists(sSignatureFile)
Debug.Assert fso.FileExists(sEcdsaKeyFile)
Debug.Assert fso.FileExists(sFileToSign)
Dim sVerifyCmd As String
sVerifyCmd = sOPENSSL_BIN & " dgst -sha256 -verify " & sEcdsaKeyFile & " -signature " & sSignatureFile & " " & sFileToSign
Dim eVerifyStatus As WshExecStatus, sVerifyStdOut As String, sVerifyStdErr As String
eVerifyStatus = RunShellAndWait(sVerifyCmd, sVerifyStdOut, sVerifyStdErr)
If StrComp(Left$(sVerifyStdOut, 11), "Verified OK") = 0 Then
Debug.Print "Verification success"
Else
Debug.Print "Verification failed"
Debug.Print "err:" & sVerifyStdErr
End If
OpenSSL_VerifySignature = True
End Function
Private Function RunShellAndWait(ByVal sCmdLine As String, _
ByRef psStdOut As String, _
ByRef psStdErr As String) As IWshRuntimeLibrary.WshExecStatus
'* This is a better Shell routine than the in built VBA.Shell function as it allows up
'* to easily capture the standard out and standard error pipes which is essential
'* for console commands
Static oShell As IWshRuntimeLibrary.WshShell
If oShell Is Nothing Then
Set oShell = New IWshRuntimeLibrary.WshShell
End If
Debug.Print sCmdLine
Dim oExec As IWshRuntimeLibrary.WshExec
Set oExec = oShell.Exec(sCmdLine)
While oExec.Status = WshRunning
DoEvents
Wend
psStdOut = oExec.StdOut.ReadAll
psStdErr = oExec.StdErr.ReadAll
RunShellAndWait = oExec.Status
Set oExec = Nothing
End Function
Private Function RegExpOffsetAndLengthFromASN1Parse(ByVal sLine As String, ByRef plOffset As Long, ByRef plLength As Long) As Boolean
'* use regular expressions to dig out numbers from ASN1 parse results
'* if always using 384 bit then we always be 20 and 98
'* but for future use we may use 512 bit or a change of curve etc.
Dim oRE As VBScript_RegExp_55.RegExp
Set oRE = New VBScript_RegExp_55.RegExp
oRE.Pattern = "(\d.):d=1 hl=2 l= (\d.) prim: BIT STRING"
Debug.Assert oRE.Test("20:d=1 hl=2 l= 98 prim: BIT STRING") '* should always be this but for future use ....
Dim oMatchCol As VBScript_RegExp_55.MatchCollection
Set oMatchCol = oRE.Execute(sLine)
Dim oMatch As VBScript_RegExp_55.Match
Set oMatch = oMatchCol.Item(0)
plOffset = oMatch.SubMatches(0)
plLength = oMatch.SubMatches(1)
RegExpOffsetAndLengthFromASN1Parse = True
End Function
Private Sub TestExtractSignatureFromDerFile()
Dim sSigFile As String
sSigFile = "N:\ecdsa\2017-11-05T225657\license.sig"
Debug.Assert fso.FileExists(sSigFile)
Dim sSigFileEd As String
sSigFileEd = "N:\ecdsa\2017-11-05T225657\license.siged"
ExtractSignatureFromDerFile sSigFile, sSigFileEd
End Sub
Private Function ExtractSignatureFromDerFile(ByVal sSignatureFile As String, ByVal sEditedSignatureFile As String)
Dim sASN1ParseSigCmd As String
sASN1ParseSigCmd = sOPENSSL_BIN & " asn1parse -inform DER -in " & sSignatureFile
Dim eASN1ParseSigStatus As WshExecStatus, sASN1ParseSigStdOut As String, sASN1ParseSigStdErr As String
eASN1ParseSigStatus = RunShellAndWait(sASN1ParseSigCmd, sASN1ParseSigStdOut, sASN1ParseSigStdErr)
Debug.Print sASN1ParseSigStdOut
Dim vOutputSplit As Variant
vOutputSplit = VBA.Split(sASN1ParseSigStdOut, vbNewLine)
'* remove the traling blank line
If Trim(vOutputSplit(UBound(vOutputSplit))) = "" Then ReDim Preserve vOutputSplit(0 To UBound(vOutputSplit) - 1)
Dim lIntegerLoop As Long: lIntegerLoop = 0
Dim alOffsets(0 To 1) As Long
Dim alLengths(0 To 1) As Long
Dim asIntegers(0 To 1) As String
Dim lTotalBytes As Long: lTotalBytes = 0
Dim vLoop As Variant
For Each vLoop In vOutputSplit
Dim sSigInt As String: sSigInt = vbNullString
Dim lOffset As Long: lOffset = 0
Dim lLength As Long: lLength = 0
If RegExpSignatureInteger(vLoop, lOffset, lLength, sSigInt) Then
alOffsets(lIntegerLoop) = lOffset
alLengths(lIntegerLoop) = lLength
asIntegers(lIntegerLoop) = sSigInt
lTotalBytes = lTotalBytes + lLength
lIntegerLoop = lIntegerLoop + 1
End If
Next vLoop
Dim abytSig() As Byte
Dim asSigHexs() As String '* for debugging
'* read in the whole file into a byte array
ReadFileAsBytes sSignatureFile, abytSig
'* for debugging create an array of hexadecimals
ByteArrayToHexStringArray abytSig, asSigHexs
For lIntegerLoop = 0 To 1
fso.CreateTextFile fso.BuildPath(BatchDir(), lIntegerLoop & "_" & alLengths(lIntegerLoop))
If alLengths(lIntegerLoop) = 49 Then
'* silly null byte
'Debug.Assert abytSig(3) = 0
'* ignore the null byte
alLengths(lIntegerLoop) = 48
alOffsets(lIntegerLoop) = alOffsets(lIntegerLoop) + 1
End If
Next
Dim abytInteger0() As Byte
CopyArraySlice abytSig, alOffsets(0) + 2, alLengths(0), abytInteger0()
Dim abytInteger1() As Byte
CopyArraySlice abytSig, alOffsets(1) + 2, alLengths(1), abytInteger1()
ReDim abytIntegers0And1(0 To alLengths(0) + alLengths(1) - 1) As Byte
Dim lConcatByteLoop As Long: lConcatByteLoop = 0
lIntegerLoop = 0
For lIntegerLoop = 0 To 1
Dim lByteLoop As Long
For lByteLoop = 0 To alLengths(lIntegerLoop) - 1
Dim byt As Byte
If lIntegerLoop = 0 Then
byt = abytInteger0(lByteLoop)
Else
byt = abytInteger1(lByteLoop)
End If
abytIntegers0And1(lConcatByteLoop) = byt
lConcatByteLoop = lConcatByteLoop + 1
Next lByteLoop
Next lIntegerLoop
'Dim lByteLoop As Long
'for lByteLoop-
'Stop
' Dim bitString() As Byte
' '* need extra 2 bytes because of leading type and length bytes
' CopyArraySlice abytes, lOffset, lLength + 2, bitString()
'
' '* some asserts which pin down structure of the bytes
' Debug.Assert bitString(0) = 3 '* TAG for BIT STRING
' Debug.Assert bitString(1) = lLength
'
'Stop
'Debug.Assert dicIntegers.Count = 2
'sIntegers(0) = dicIntegers(dicIntegers.Keys()(0))
'sIntegers(1) = dicIntegers(dicIntegers.Keys()(1))
'Debug.Assert Len(sIntegers(0)) = Len(sIntegers(1))
'Debug.Assert Len(sIntegers(0)) Mod 2 = 0
' Dim lStringLen As Long
' lStringLen = Len(sIntegers(0))
'
' Dim lByteLen As Long
' lByteLen = lStringLen / 2
'
'
' ReDim byt(0 To lTotalBytes - 1) As Byte
'
' Dim lIntegerLoop As Long
' For lIntegerLoop = 0 To 1
' Dim lCharLoop As Long
' For lCharLoop = 0 To lByteLen - 1
'
' Dim sChar2 As String
' sChar2 = Mid$(sIntegers(lIntegerLoop), (lCharLoop * 2) + 1, 2)
'
' Debug.Assert Len(sChar2) > 0
'
' Dim bChar2 As Byte
' bChar2 = Val("&h" & sChar2)
'
' byt(lCharLoop + lIntegerLoop * lByteLen) = bChar2
'
' Next lCharLoop
' Next
If fso.FileExists(sEditedSignatureFile) Then fso.DeleteFile sEditedSignatureFile
SaveBytesAsFile sEditedSignatureFile, abytIntegers0And1()
Debug.Assert fso.FileExists(sEditedSignatureFile)
End Function
Private Function RegExpSignatureInteger(ByVal sLine As String, ByRef plOffset As Long, ByRef plLength As Long, ByRef psSignatureInterger As String) As Boolean
'* use regular expressions to dig out numbers from ASN1 parse results
'* if always using 384 bit then we always be 20 and 98
'* but for future use we may use 512 bit or a change of curve etc.
Dim oRE As VBScript_RegExp_55.RegExp
Set oRE = New VBScript_RegExp_55.RegExp
oRE.Pattern = "^\s*(\d+):d=1 hl=2 l= (\d+) prim: INTEGER\s*:(.+)"
Debug.Assert oRE.Test(" 2:d=1 hl=2 l= 49 prim: INTEGER :ADFF05570D228E657E98F5455D1772F23224389641F9FA8DCC37B39816BE0C9F871BBABAF4F73ECDDB7BA6814502A85D")
Debug.Assert oRE.Test(" 53:d=1 hl=2 l= 49 prim: INTEGER :83A977122AF40623AF0C1C64CDEA761E77C534FCA267F2DA6F340CE6AB371F8419464DCC38C5F41729EB421F384A72A1")
Debug.Assert oRE.Test(" 2:d=1 hl=2 l= 48 prim: INTEGER :195848488FC6A5888D6433E5B714A45A515EE6CBF1B0C952EE35E51F6D678A2623170AE9DE2E0A29B7D47EDC8F95C264")
Debug.Assert oRE.Test(" 52:d=1 hl=2 l= 48 prim: INTEGER :4789B87BFFC7AC24DA6A9223805EC25EDD6444B483689B2CDBA73056F939C0127228F2632A387BDF6AF93B9A3EAC548E")
If oRE.Test(sLine) Then
Dim oMatchCol As VBScript_RegExp_55.MatchCollection
Set oMatchCol = oRE.Execute(sLine)
Dim oMatch As VBScript_RegExp_55.Match
Set oMatch = oMatchCol.Item(0)
plOffset = oMatch.SubMatches(0)
plLength = oMatch.SubMatches(1)
psSignatureInterger = oMatch.SubMatches(2)
RegExpSignatureInteger = True
End If
End Function
Private Function ExportECDSAToXml(ByVal sPublicKeyFile As String, ByVal sXmlFile As String) As Boolean
'* C#'s ECDsaCng class has a FromXmlString method which imports public key from a xml file Rfc4050
'* In this subroutine we use OpenSSL's asn1parse command to determine where the X and Y coordinates
'* are to be found, we dig them out and then markup an Xml file
'* sample output
'
'
'
'
'
'
'
'
'
Dim sASN1ParseCmd As String
sASN1ParseCmd = sOPENSSL_BIN & " asn1parse -inform DER -in " & sPublicKeyFile
Dim eASN1ParseStatus As WshExecStatus, sASN1ParseStdOut As String, sASN1ParseStdErr As String
eASN1ParseStatus = RunShellAndWait(sASN1ParseCmd, sASN1ParseStdOut, sASN1ParseStdErr)
Debug.Print sASN1ParseStdOut
'* sample output from standard out pipe is given blow.
'* we need to dig into the BIT STRING which is the final item
'* we need offset and length which is always 20 and 98 for 384 bit ECDSA
'* but I have written logic in case we want to upgrade to 512 or change of curve etc.
' 0:d=0 hl=2 l= 118 cons: SEQUENCE
' 2:d=1 hl=2 l= 16 cons: SEQUENCE
' 4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey
' 13:d=2 hl=2 l= 5 prim: OBJECT :secp384r1
' 20:d=1 hl=2 l= 98 prim: BIT STRING
Dim vOutputSplit As Variant
vOutputSplit = VBA.Split(sASN1ParseStdOut, vbNewLine)
'* remove the traling blank line
If Trim(vOutputSplit(UBound(vOutputSplit))) = "" Then ReDim Preserve vOutputSplit(0 To UBound(vOutputSplit) - 1)
'* final line should be the long bit string, i.e. contain 'BIT STRING'
Debug.Assert StrComp("BIT STRING", Right$(Trim(vOutputSplit(UBound(vOutputSplit))), 10)) = 0
'* use regular expression to dig out offset and length
Dim lOffset As Long, lLength As Long
RegExpOffsetAndLengthFromASN1Parse Trim(vOutputSplit(UBound(vOutputSplit))), lOffset, lLength
Dim abytes() As Byte
Dim asHexs() As String '* for debugging
'* read in the whole file into a byte array
ReadFileAsBytes sPublicKeyFile, abytes
'* for debugging create an array of hexadecimals
ByteArrayToHexStringArray abytes, asHexs
Dim bitString() As Byte
'* need extra 2 bytes because of leading type and length bytes
CopyArraySlice abytes, lOffset, lLength + 2, bitString()
'* some asserts which pin down structure of the bytes
Debug.Assert bitString(0) = 3 '* TAG for BIT STRING
Debug.Assert bitString(1) = lLength
'* From Point 5 at http://certificate.fyicenter.com/2221_View_Website_Server_Certificate_in_Google_Chrome.html
'* "ASN.1 BIT STRING value is stored with DER encoding as the value itself with an extra leading byte of 0x00. "
Debug.Assert bitString(2) = 0
'* 0x04 means by x and y values follow, i.e. uncompressed
'* (instead of just one from which the other can be derived, leading with 0x02 or 0x03)
'* https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm
Debug.Assert bitString(3) = 4
'Stop
Dim x() As Byte
Dim y() As Byte
'* slice out the 48 bits for nopth x and y
'* why 48? because 48*8=384 bits(change for 512)
CopyArraySlice bitString, 4, 48, x()
CopyArraySlice bitString, 52, 48, y()
'* convert bytes to hex string for x coord
Dim sHexX As String
sHexX = ByteArrayToHexString(x(), "")
Debug.Print "sHexX:" & sHexX
'* convert bytes to hex string for y coord
Dim sHexY As String
sHexY = ByteArrayToHexString(y(), "")
Debug.Print "sHexY:" & sHexY
'* convert hexadeciumal to plain decimal
'* as Xml file requires it
Dim sDecX As String
sDecX = HexToDecimal(sHexX)
Debug.Print "sDecX:" & sDecX
'* convert hexadeciumal to plain decimal
'* as Xml file requires it
Dim sDecY As String
sDecY = HexToDecimal(sHexY)
Debug.Print "sDecY:" & sDecY
'* create the xml file from a template
Dim dom2 As MSXML2.DOMDocument60
Set dom2 = New MSXML2.DOMDocument60
dom2.LoadXML ECDSAXml(sDecX, sDecY)
Debug.Assert dom2.parseError.ErrorCode = 0
dom2.Save sXmlFile
Debug.Print dom2.XML
Set dom2 = Nothing
Debug.Assert CreateObject("Scripting.FileSystemObject").FileExists(sXmlFile)
End Function
Private Function CopyArraySlice(ByRef abOriginal() As Byte, _
ByVal lStart As Long, ByVal lLength As Long, ByRef abNewSlice() As Byte) As Boolean
'* this copies a slice of an array of bytes to another
'* caller declares the result array and this code re-dimensions it
Debug.Assert lLength >= 1
Debug.Assert lStart >= 0
ReDim abNewSlice(0 To lLength - 1)
Dim lLoop As Long
For lLoop = 0 To lLength - 1
Dim lLoop2 As Long
lLoop2 = lLoop + lStart
abNewSlice(lLoop) = abOriginal(lLoop2)
Next lLoop
End Function
Private Function ReadFileAsBytes(ByVal sFile As String, ByRef abytes() As Byte)
'* ActiveX Data Objects has a binary load to byte array feature ....
Dim strm As ADODB.Stream
Set strm = New ADODB.Stream
With strm
.Open
.Type = adTypeBinary
.LoadFromFile sFile
abytes = .Read
.Close
End With
Set strm = Nothing
End Function
Private Function SaveBytesAsFile(ByVal sFile As String, ByRef abytes() As Byte)
Dim lFile As Long
lFile = FreeFile
Open sFile For Binary Access Write Lock Write As #lFile
Put #lFile, , abytes()
Close #lFile
'
' Dim strm As ADODB.Stream
' Set strm = New ADODB.Stream
' With strm
' .Open
' .Type = adTypeBinary
' .Write abytes
' .SaveToFile sFile
' '.LoadFromFile sFile
' 'abytes = .Read
' .Close
' End With
' Set strm = Nothing
'
End Function
Private Function ByteArrayToHexStringArray(ByRef abytes() As Byte, ByRef asHexs() As String)
'* used for debugging
ReDim asHexs(LBound(abytes) To UBound(abytes))
Dim lLoop As Long
For lLoop = LBound(abytes) To UBound(abytes)
asHexs(lLoop) = Hex$(abytes(lLoop))
Next lLoop
End Function
Private Function ByteArrayToHexString(ByRef ab() As Byte, Optional sDelimiter As String = " ") As String
'* used to create very long hex numbers as strings
'* cryptography has long numbers for keys
Dim sSegment As String
sSegment = ""
Dim lLoop As Long
For lLoop = 0 To UBound(ab)
sSegment = sSegment & sDelimiter & Right$("0" & Hex$(ab(lLoop)), 2)
Next
ByteArrayToHexString = Trim(sSegment)
End Function
Private Function HexToDecimal(ByVal sHex As String) As String
'* takes a long hexadecimal number (as a string) and converts
'* to a long decimal number (as a string)
'* translated from StackOverflow answer
'* https://stackoverflow.com/questions/16965915/convert-a-big-hex-number-string-format-to-a-decimal-number-string-format-w/46817799
'* tested against websites such as
'* http://www.statman.info/conversions/hexadecimal.php
Dim dec() As Long
ReDim dec(0 To 0) As Long
Dim lCharLoop As Long
For lCharLoop = 1 To Len(sHex)
Dim char As String * 1
char = Mid$(sHex, lCharLoop, 1)
Dim carry As Long
carry = Val("&h" & char)
Dim i As Long
For i = 0 To UBound(dec)
Dim lVal As Long
lVal = dec(i) * 16 + carry
dec(i) = lVal Mod 10
carry = lVal \ 10
Next i
While (carry > 0)
ReDim Preserve dec(0 To UBound(dec) + 1) As Long
dec(UBound(dec)) = carry Mod 10
carry = carry \ 10
Wend
Next
For lCharLoop = UBound(dec) To LBound(dec) Step -1
Dim sDecimal As String
sDecimal = sDecimal & Chr$(48 + dec(lCharLoop))
Next
HexToDecimal = sDecimal
End Function
Private Function ECDSAXml(ByVal sXValue As String, ByVal sYValue As String, Optional ByVal sCurveOid As String = "1.3.132.0.34") As String
'* sample output
'
'
'
'
'
'
'
'
'
Dim dom As MSXML2.DOMDocument60
Set dom = New MSXML2.DOMDocument60
dom.LoadXML "" & _
"" & _
"" & _
"" & _
"" & _
"" & _
""
Debug.Assert dom.parseError.ErrorCode = 0
'* namespace selection glitch requires us to declare namespace prefix
'* to use XPath even though original document does not
dom.setProperty "SelectionNamespaces", "xmlns:ec=""http://www.w3.org/2001/04/xmldsig-more#"""
Dim attrCurveId As MSXML2.IXMLDOMAttribute
Set attrCurveId = dom.SelectSingleNode("ec:ECDSAKeyValue/ec:DomainParameters/ec:NamedCurve/@URN")
Debug.Assert attrCurveId.Text = "urn:oid:" & sCurveOid ' secp384r1 – {1.3.132.0.34}
Dim attrXValue As MSXML2.IXMLDOMAttribute
Set attrXValue = dom.SelectSingleNode("ec:ECDSAKeyValue/ec:PublicKey/ec:X/@Value")
Debug.Assert Not attrXValue Is Nothing
attrXValue.Text = sXValue
Dim attrYValue As MSXML2.IXMLDOMAttribute
Set attrYValue = dom.SelectSingleNode("ec:ECDSAKeyValue/ec:PublicKey/ec:Y/@Value")
Debug.Assert Not attrYValue Is Nothing
attrYValue.Text = sYValue
ECDSAXml = PrettyPrintXml(dom)
'ECDSAXml = dom.XML
End Function
Private Function PrettyPrintXml(ByVal dom As MSXML2.DOMDocument60) As String
'* this routine inserts line breaks between elements
Dim reader As MSXML2.SAXXMLReader60
Set reader = New MSXML2.SAXXMLReader60
Dim writer As MSXML2.MXXMLWriter60
Set writer = New MSXML2.MXXMLWriter60
writer.omitXMLDeclaration = True
writer.indent = True
Set reader.contentHandler = writer
reader.Parse dom.XML
PrettyPrintXml = writer.output
End Function
Private Function WriteStringToFile(ByVal sContents As String, ByVal sFileName As String) As Boolean
'* this ought to be a one-liner but with so much other code
'* I felt it necessary to break out so I can debug and check what is happening
If fso.FileExists(sFileName) Then fso.DeleteFile sFileName
Dim txtSignableContent As Scripting.TextStream
Set txtSignableContent = fso.CreateTextFile(sFileName)
txtSignableContent.Write sContents
txtSignableContent.Close
Set txtSignableContent = Nothing
Debug.Assert fso.FileExists(sFileName)
WriteStringToFile = True
End Function
Here is the output to the VBA immediate window which illustrates the console commands and the responses for running EntryPoint1_RunECDSAKeyGenerationBatch_RunOnce.
Creating batch directory :n:\ECDSA\2017-11-03T193106
C:\OpenSSL-Win64\bin\openssl.exe ecparam -genkey -name secp384r1 -out n:\ECDSA\2017-11-03T193106\ec_key.pem
C:\OpenSSL-Win64\bin\openssl.exe ec -pubout -outform DER -in n:\ECDSA\2017-11-03T193106\ec_key.pem -out n:\ECDSA\2017-11-03T193106\ec_pubkey.der
C:\OpenSSL-Win64\bin\openssl.exe ec -pubout -outform PEM -in n:\ECDSA\2017-11-03T193106\ec_key.pem -out n:\ECDSA\2017-11-03T193106\ec_pubkey.pem
C:\OpenSSL-Win64\bin\openssl.exe ec -noout -text -in n:\ECDSA\2017-11-03T193106\ec_key.pem -out n:\ECDSA\2017-11-03T193106\ec_key.txt
Private-Key: (384 bit)
priv:
00:98:78:0d:c7:29:10:1c:9f:4d:75:b2:95:01:01:
a9:d2:36:72:0d:77:6a:5c:57:8d:51:a0:53:27:05:
9b:22:1c:c9:0a:1e:e1:27:06:92:c1:6c:2a:c4:bb:
46:91:98:f6
pub:
04:bd:4a:38:04:69:d5:ba:fa:11:27:0f:a8:ef:70:
3f:11:8d:e0:0f:e7:fd:26:ac:4d:40:32:7a:b5:9c:
97:71:c1:80:72:1b:42:25:f8:a4:49:4d:8f:89:bf:
1b:e9:6c:8c:f3:0b:02:db:89:b3:f7:92:e8:c4:a6:
ce:04:88:10:51:cc:17:0b:b8:9c:9a:a6:3d:fd:ec:
d4:99:c3:31:6b:22:1d:b6:41:fa:3c:0e:51:fe:86:
67:bb:7e:86:ce:06:6c
ASN1 OID: secp384r1
NIST CURVE: P-384
C:\OpenSSL-Win64\bin\openssl.exe asn1parse -inform DER -in n:\ECDSA\2017-11-03T193106\ec_pubkey.der
0:d=0 hl=2 l= 118 cons: SEQUENCE
2:d=1 hl=2 l= 16 cons: SEQUENCE
4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey
13:d=2 hl=2 l= 5 prim: OBJECT :secp384r1
20:d=1 hl=2 l= 98 prim: BIT STRING
sHexX:BD4A380469D5BAFA11270FA8EF703F118DE00FE7FD26AC4D40327AB59C9771C180721B4225F8A4494D8F89BF1BE96C8C
sHexY:F30B02DB89B3F792E8C4A6CE04881051CC170BB89C9AA63DFDECD499C3316B221DB641FA3C0E51FE8667BB7E86CE066C
sDecX:29134384736743232303148959866907873847020585008044539704341734517362687803911673703523083044584737202030832217844876
sDecY:37407743276271579329804703064876533532537408218368858949720169306023437854945515421210341789026319167790678153234028
Here is the VBA immediate window output for running EntryPoint2_RunHashAndSignBatch ...
C:\OpenSSL-Win64\bin\openssl.exe dgst -sha256 -out n:\ECDSA\2017-11-03T193106\license.sha256 n:\ECDSA\2017-11-03T193106\license.txt
SHA256(n:\ECDSA\2017-11-03T193106\license.txt)= 185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969
C:\OpenSSL-Win64\bin\openssl.exe dgst -sha256 -sign n:\ECDSA\2017-11-03T193106\ec_key.pem -out n:\ECDSA\2017-11-03T193106\license.sig n:\ECDSA\2017-11-03T193106\license.txt
C:\OpenSSL-Win64\bin\openssl.exe base64 -in n:\ECDSA\2017-11-03T193106\license.sig -out n:\ECDSA\2017-11-03T193106\license.sigb64
C:\OpenSSL-Win64\bin\openssl.exe dgst -sha256 -verify n:\ECDSA\2017-11-03T193106\ec_pubkey.pem -signature n:\ECDSA\2017-11-03T193106\license.sig n:\ECDSA\2017-11-03T193106\license.txt
Verification success
Next we create a C# classic console application and paste in the following code to verify the digital signature remembering that the customer will be in receipt of a base64 version of the digital signature.
using System;
using System.IO;
using System.Security.Cryptography;
using System.Xml;
namespace ECDSAVerSig
{
class Program
{
static Action feedback { get; set; }
static byte[] fileContents = null;
static byte[] signatureContents = null;
static ECDsaCng client = null;
static HashAlgorithm hashAlgo = new SHA256Managed();
static String parentDirectory = null;
static void Main(string[] args)
{
//* the following will be different for you!!!
//* and will need to match what was output by the VBA script
if (args.Length == 0)
{
parentDirectory = "n:\\ECDSA\\2017-11-06T212507\\";
}
else
{
parentDirectory = args[0];
}
feedback = Console.WriteLine; // Abstract away
if (Directory.Exists(parentDirectory))
{
feedback("Processing directory " + parentDirectory);
if (LoadSignature())
{
VerifySignature();
}
}
else
{
feedback("#ERROR: directory '" + parentDirectory + "' does not exist!");
}
}
static private Boolean VerifySignature()
{
try
{
// a byte array to store hash value
byte[] hashedData = null;
hashedData = hashAlgo.ComputeHash(fileContents);
//* the following is consistently wrong though it is my best guess
Boolean verified = client.VerifyHash(hashedData, signatureContents); //<-- Help required here StackOverflowers
feedback("Verification:" + verified);
if (verified)
{
feedback("Hooray you got this 384 bit ECDSA code working! You absolute star!");
System.IO.File.Create(Path.Combine(parentDirectory, "win.txt"));
}
else
{
feedback("Oh dear, still does not work. Please keep twiddling.");
System.IO.File.Create(Path.Combine(parentDirectory, "lose.txt"));
}
return true;
}
catch (XmlException ex)
{
feedback("Problem with verification (Xml parse error):" + ex.ToString());
return false;
}
catch (Exception ex)
{
feedback("Problem with verification :" + ex.ToString());
return false;
}
}
static private Boolean LoadSignature()
{
client = new ECDsaCng();
try
{
System.Xml.XmlDocument dom = new System.Xml.XmlDocument();
dom.Load(Path.Combine(parentDirectory, "ec_pubkey.xml"));
string xml = dom.OuterXml;
feedback(xml);
client.FromXmlString(xml, ECKeyXmlFormat.Rfc4050);
fileContents = System.IO.File.ReadAllBytes(Path.Combine(parentDirectory, "license.txt"));
string base64SignatureContents = System.IO.File.ReadAllText(Path.Combine(parentDirectory, "license.sigB64"));
signatureContents = Convert.FromBase64String(base64SignatureContents);
byte[] hashedData = hashAlgo.ComputeHash(fileContents);
return true;
}
catch (XmlException ex)
{
feedback("Problem with reading digital signature (Xml parse error):" + ex.ToString());
return false;
}
catch (Exception ex)
{
feedback("Problem with reading digital signature:" + ex.ToString());
return false;
}
}
}
}
I have triple checked this code. I've made the license file a very short "Hello" and checked bytes and encoding. I do not know what next to do. Please assist. Thanks in advance