Imports CryptoSysPKI

Module XML_UTF8chars

    ' $Id: XML_UTF8chars.vb $
    ' $Date: 2010-12-04 17:29 $
    ' $Author: dai $

    ' This module uses functions from the CryptoSys (tm) PKI Toolkit available from
    ' <www.cryptosys.net/pki/>.
    ' Add a reference to the library file `diCrSysPKINet.dll`

    ' *************************** COPYRIGHT NOTICE ******************************
    ' This code was originally written by David Ireland and is copyright
    ' (C) 2010 DI Management Services Pty Ltd <www.di-mgt.com.au>.
    ' Provided "as is". No warranties. Use at your own risk. You must make your
    ' own assessment of its accuracy and suitability for your own purposes.
    ' 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 **************************

    Sub Main()
        Call XML_UTF8chars_Compute_Digest()
        Call XML_UTF8chars_MakeSignature()
        Call XML_UTF8chars_GetXMLPublicKeyFromCert()
        Call XML_UTF8chars_GetXMLPublicKeyFromPrivate()
    End Sub

    Public Function XML_UTF8chars_Compute_Digest() As String
        Dim strData As String
        Dim abData() As Byte
        Dim strDigest As String
        Dim strSig64 As String

        ' Input as (would be) read from original file (with LATIN SMALL LETTER N WITH TILDE)
        strData = "<Book xml:id=""F01"">" & vbCrLf & _
            "<FirstName>BruceƱ</FirstName>" & vbCrLf & _
            "</Book>"
        Console.WriteLine(strData)
        Console.WriteLine("ORIG= " & Cnv.ToHex(System.Text.Encoding.GetEncoding("iso-8859-1").GetBytes(strData)))
        ' Line breaks are normalized to "#xA": i.e. convert CR-LF pairs to LF
        strData = strData.Replace(vbCrLf, vbLf)
        ' Convert to UTF-8
        abData = System.Text.Encoding.UTF8.GetBytes(strData)
        ' Display input as sequence of bytes in hex form
        Console.WriteLine("INPUT=" & Cnv.ToHex(abData))
        ' Form SHA-1 digest of input
        strDigest = Hash.HexFromBytes(abData, HashAlgorithm.Sha1)
        Console.WriteLine("DIGEST(hex)=" & strDigest)
        ' Encode in base64
        strSig64 = Cnv.Base64FromHex(strDigest)
        ' Return base64-encoded digest...
        Console.WriteLine("DIGEST(base64)=" & strSig64)
        Return strSig64
    End Function

    Public Function XML_UTF8chars_MakeSignature() As String
        Dim strData As String
        Dim abData() As Byte
        Dim abBlock() As Byte
        Dim strSig As String
        Dim nBlkLen As Long
        Dim strInternalKey As String
        Dim strKeyFile As String
        Dim strPassword As String

        strData = "<SignedInfo xmlns=""http://www.w3.org/2000/09/xmldsig#"">" & vbCrLf & _
            "<CanonicalizationMethod Algorithm=""http://www.w3.org/TR/2001/REC-xml-c14n-20010315""></CanonicalizationMethod>" & vbCrLf & _
            "<SignatureMethod Algorithm=""http://www.w3.org/2000/09/xmldsig#rsa-sha1""></SignatureMethod>" & vbCrLf & _
            "<Reference URI=""#F01"">" & vbCrLf & _
            "<Transforms>" & vbCrLf & _
            "<Transform Algorithm=""http://www.w3.org/TR/2001/REC-xml-c14n-20010315""></Transform>" & vbCrLf & _
            "</Transforms>" & vbCrLf & _
            "<DigestMethod Algorithm=""http://www.w3.org/2000/09/xmldsig#sha1""></DigestMethod>" & vbCrLf & _
            "<DigestValue>/xrjkAVvCuoE3I5tudGdIyU5Gh0=</DigestValue>" & vbCrLf & _
            "</Reference>" & vbCrLf & _
            "</SignedInfo>"
        ' Convert CR-LF to LF
        strData = strData.Replace(vbCrLf, vbLf)
        ' No non-ASCII characters in input, so convert directly to byte array
        abData = System.Text.Encoding.Default.GetBytes(strData)
        Console.WriteLine("Data length = " & abData.Length & " bytes")

        ' Read in the private key from the key file (encrypted "internal" format)
        strKeyFile = "AlicePrivRSASign.epk"
        strPassword = "password"
        strInternalKey = Rsa.ReadEncPrivateKey(strKeyFile, strPassword).ToString
        ' How long is it?
        nBlkLen = Rsa.KeyBytes(strInternalKey)
        Console.WriteLine("Key length = " & nBlkLen & " bytes")
        If nBlkLen <= 0 Then
            Return "" ' CATCH ERROR
        End If
        ' Encode for PKCS1v1.5 signature 
        abBlock = Rsa.EncodeMsgForSignature(nBlkLen, abData, HashAlgorithm.Sha1)
        Console.WriteLine("BLK=" & Cnv.ToHex(abBlock))
        ' Sign using RSA private key
        abBlock = Rsa.RawPrivate(abBlock, strInternalKey)
        Console.WriteLine("SIG=" & Cnv.ToHex(abBlock))
        ' Convert to base64 string
        strSig = Cnv.ToBase64(abBlock)
        ' Return the signature value in base64 form
        Console.WriteLine("SIG=" & strSig)
        Return strSig
    End Function

    Public Function XML_UTF8chars_GetXMLPublicKeyFromCert() As String
        ' How to get the XML <RSAKeyValue> field from an X.509 certificate file
        Dim strInternalKey As String
        Dim strCertFile As String
        Dim strXml As String

        ' Read in the public key from the X.509 certificate file as an "internal" string
        strCertFile = "AliceRSASignByCarl.pem.cer"
        strInternalKey = Rsa.GetPublicKeyFromCert(strCertFile).ToString

        ' Output the public key in XML form
        strXml = Rsa.ToXMLString(strInternalKey, 0)
        If strXml.Length <= 0 Then Return "" 'CATCH ERROR
        Console.WriteLine(strXml)
        Return strXml

    End Function

    Public Function XML_UTF8chars_GetXMLPublicKeyFromPrivate() As String
        ' How to get the XML <RSAKeyValue> field from a PKCS-8 private key file
        ' DANGER!!!
        ' The RSA private key contains the public key, too, as a sub-set.
        ' So we can extract the public key in XML <RsaKeyValue> form.
        ' But be careful doing this trick. You may accidentally give away your private key!!!

        Dim strInternalKey As String
        Dim strKeyFile As String
        Dim strPassword As String
        Dim strXml As String

        ' Read in the private key from the key file (encrypted "internal" format)
        strKeyFile = "AlicePrivRSASign.epk"
        strPassword = "password"
        strInternalKey = Rsa.ReadEncPrivateKey(strKeyFile, strPassword).ToString

        ' Output the public key in XML form
        ' DANGER: Use the EXCLPRIVATE option or you will output your private key!!!
        strXml = Rsa.ToXMLString(strInternalKey, Rsa.XmlOptions.ExcludePrivateParams)
        ' CHECK: this should only contain <Modulus> and <Exponent> fields.
        ' If you see a <D> or <P> or <Q> field, you have made a mistake.
        Console.WriteLine(strXml)
        Return strXml

    End Function


End Module