System.ArgumentNullException error when attemtping to change string source from InputBox to an existing variable

41 Views Asked by At

Summary

I am creating a VB project that requires a user to enter an API key. I do not want to store the API key in plaintext for security purposes, so I am attempting to encrypt it. When changing the source of the "password" from an InputBox to a earlier declared variable, I get an error of System.ArgumentNullException: 'String reference not set to an instance of a String. Parameter name: s', and the execution fails.

Context

For security reasons, I don't want the API key to be stored in Plaintext. Instead, I would like the user to enter it once during the application's initial launch, where it converts the plaintext API key into an encrypted file. To encrypt the file, I don't want the user to enter a password, instead, I have combined a few local variables (User name, MAC address, Install location and Host name.) to serve as a password instead. This means the user shouldn't need to enter it on launch (it will read the encrypted API key from the local file), but if any of the variables change (install location, local username, etc.) then it will error, and the user will need to re-enter the API key.

When I try to change the code from having to type in the password, to using the userKey variable, the program no longer launches correctly. Dim password As String = InputBox("Enter the password:") works fine, but Dim password As String = userKey leads to the above error, despite userKey being defined as a string, it refuses to accept it.

Outcome

I would like to have it so the user only needs to enter the API key once the application is first ever launched. It then takes the machine's data to essentially create a UUID, which is what is used to encrypt the plaintext API key entered by the user. I do not want the user to have to set or remember a password to decrypt the API key. It should be automated, and throw an error if they attempt to login where the automated data doesn't allow successful decryption. It'll wipe the API key and ask the user to re-enter it.

Currently, the code works, but the user has to enter their chosen password each time, which is what I am trying to avoid. I am wanting to fully automate this using the UUID I have generated as userKey which is generated when the form is loaded.

My Code

Please see the current code below. It should be in a working state, but I am unsure as to how I can have it so Dim password as String = userKey doesn't get an error when not having the user actualy typing it.

Conclusion

I am new to VB, so this is a new experiment for me to try and learn VB. Please be gentle - I'd appreciate any tips or tricks on how to best achieve this task and similar. Thanks!

Imports System.IO
Imports Newtonsoft.Json
Imports System.Security.Cryptography
Imports System.Net.NetworkInformation
Imports System.Management
Imports System.Text


Public Class Form1
    Public userKey As String
    Private posts As List(Of Post)
    Public Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        Dim sHostName As String
        Dim sUserName As String
        Dim mac As String
        Dim exePath As String = Application.ExecutablePath()
        mac = getMacAddress()
        ' Get Host Name / Get Computer Name    
        sHostName = Environ$("computername")
        ' Get Current User Name    
        sUserName = Environ$("username")

        Dim userKey As String
        userKey = sUserName & mac & exePath & sHostName

        Label5.Text = userKey

    End Sub

    Private Sub Button5_Click(sender As Object, e As EventArgs) Handles Button5.Click
        Dim plainText As String = InputBox("Enter the plain text:")
        Dim password As String = InputBox("Enter the password:")

        Dim wrapper As New Simple3Des(password)
        Dim cipherText As String = wrapper.EncryptData(plainText)

        MsgBox("The cipher text is: " & cipherText)
        My.Computer.FileSystem.WriteAllText("cipherText.txt", cipherText, False)
    End Sub

    Private Sub Button6_Click(sender As Object, e As EventArgs) Handles Button6.Click
        Dim cipherText As String = My.Computer.FileSystem.ReadAllText("cipherText.txt")
        Dim password As String = InputBox("Enter the password:")
        Dim wrapper As New Simple3Des(password)

        ' DecryptData throws if the wrong password is used.
        Try
            Dim plainText As String = wrapper.DecryptData(cipherText)
            MsgBox("The plain text is: " & plainText)
        Catch ex As System.Security.Cryptography.CryptographicException
            MsgBox("The data could not be decrypted with the password.")
        End Try
    End Sub

    Function getMacAddress()
        Dim nics() As NetworkInterface = NetworkInterface.GetAllNetworkInterfaces()
        Return nics(1).GetPhysicalAddress.ToString
    End Function


End Class

Public NotInheritable Class Simple3Des
    Private TripleDes As New TripleDESCryptoServiceProvider

    Private Function TruncateHash(
    ByVal key As String,
    ByVal length As Integer) As Byte()

        Dim sha1 As New SHA1CryptoServiceProvider

        ' Hash the key.
        Dim keyBytes() As Byte =
            System.Text.Encoding.Unicode.GetBytes(key)
        Dim hash() As Byte = sha1.ComputeHash(keyBytes)

        ' Truncate or pad the hash.
        ReDim Preserve hash(length - 1)
        Return hash
    End Function

    Sub New(ByVal key As String)
        ' Initialize the crypto provider.
        TripleDes.Key = TruncateHash(key, TripleDes.KeySize \ 8)
        TripleDes.IV = TruncateHash("", TripleDes.BlockSize \ 8)
    End Sub

    Public Function EncryptData(
    ByVal plaintext As String) As String

        ' Convert the plaintext string to a byte array.
        Dim plaintextBytes() As Byte =
            System.Text.Encoding.Unicode.GetBytes(plaintext)

        ' Create the stream.
        Dim ms As New System.IO.MemoryStream
        ' Create the encoder to write to the stream.
        Dim encStream As New CryptoStream(ms,
            TripleDes.CreateEncryptor(),
            System.Security.Cryptography.CryptoStreamMode.Write)

        ' Use the crypto stream to write the byte array to the stream.
        encStream.Write(plaintextBytes, 0, plaintextBytes.Length)
        encStream.FlushFinalBlock()

        ' Convert the encrypted stream to a printable string.
        Return Convert.ToBase64String(ms.ToArray)
    End Function
    Public Function DecryptData(
    ByVal encryptedtext As String) As String

        ' Convert the encrypted text string to a byte array.
        Dim encryptedBytes() As Byte = Convert.FromBase64String(encryptedtext)

        ' Create the stream.
        Dim ms As New System.IO.MemoryStream
        ' Create the decoder to write to the stream.
        Dim decStream As New CryptoStream(ms,
            TripleDes.CreateDecryptor(),
            System.Security.Cryptography.CryptoStreamMode.Write)

        ' Use the crypto stream to write the byte array to the stream.
        decStream.Write(encryptedBytes, 0, encryptedBytes.Length)
        decStream.FlushFinalBlock()

        ' Convert the plaintext stream to a string.
        Return System.Text.Encoding.Unicode.GetString(ms.ToArray)
    End Function
End Class
0

There are 0 best solutions below