views:

193

answers:

2

Hi there,

I have a class called tdes which looks like this:

Imports Microsoft.VisualBasic
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Security.Cryptography
Imports System.IO

Namespace security
    Public Class tdes
        Private des As New TripleDESCryptoServiceProvider()
        Private utf8 As New UTF8Encoding()

    Private keyValue As Byte()
    Private iVValue As Byte()

    Public Property Key() As Byte()
        Get
            Return keyValue
        End Get
        Set(ByVal value As Byte())
            keyValue = value
        End Set
    End Property

    Public Property iV() As Byte()
        Get
            Return iVValue
        End Get
        Set(ByVal value As Byte())
            iVValue = value
        End Set
    End Property

    Public Sub New(ByVal key As Byte(), ByVal iV As Byte())
        Me.keyValue = key
        Me.iVValue = iV
    End Sub

    Public Function ByteDecrypt(ByVal bytes As Byte()) As String
        Dim output As Byte()
        output = Transform(bytes, des.CreateDecryptor(Me.keyValue, Me.iVValue))
        'Return Convert.ToBase64String(output)
        Return utf8.GetString(output)
    End Function

    Public Function ByteEncrypt(ByVal bytes As Byte()) As Byte()
        Return Transform(bytes, des.CreateEncryptor(Me.keyValue, Me.iVValue))
    End Function

    Public Function StringDecrypt(ByVal text As String) As String
        Dim input As Byte() = Convert.FromBase64String(text)
        Dim output As Byte() = Transform(input, des.CreateDecryptor(Me.keyValue, Me.iVValue))
        Return utf8.GetString(output)
    End Function

    Public Function StringEncrypt(ByVal text As String) As String
        Dim input As Byte() = utf8.GetBytes(text)
        Dim output As Byte() = Transform(input, des.CreateEncryptor(Me.keyValue, Me.iVValue))
        Return Convert.ToBase64String(output)
    End Function

    Public Function StringEncryptByte(ByVal text As String) As Byte()
        Dim input As Byte() = utf8.GetBytes(text)
        Dim output As Byte() = Transform(input, des.CreateEncryptor(Me.keyValue, Me.iVValue))
        'Return Convert.ToBase64String(output)
        Return output
    End Function

    Private Function Transform(ByVal input As Byte(), ByVal cryptoTransform As ICryptoTransform) As Byte()
        ' Create the necessary streams
        Dim memory As New MemoryStream()
        Dim stream As New CryptoStream(memory, cryptoTransform, CryptoStreamMode.Write)

        ' Transform the bytes as requesed
        stream.Write(input, 0, input.Length)
        stream.FlushFinalBlock()

        ' Read the memory stream and convert it back into byte array
        memory.Position = 0
        Dim result As Byte() = New Byte(memory.Length - 1) {}
        memory.Read(result, 0, result.Length)

        ' Clean up
        memory.Close()
        stream.Close()

        ' Return result
        Return result
    End Function

End Class

End Namespace

And it works well encrypting and decrypting things. I want to encrypt some existing passwords in a database table, so I've run a little script on them which encrypt them like so (lots missing for brevity):

Dim key As Byte() = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}
    Dim iv As Byte() = {8, 7, 6, 5, 4, 3, 2, 1}
    Dim enc As New security.tdes(key, iv)
    Dim i As Integer = 0

    Using oConn As New SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings("pitstopConnectionString").ConnectionString)
        Using cmd As New SqlCommand("doUpdatePasswords", oConn)
            cmd.CommandType = CommandType.StoredProcedure
            cmd.Parameters.AddWithValue("@userid", userid)
            cmd.Parameters.AddWithValue("@newpassword", enc.StringEncryptByte(currentPassword))
            oConn.Open()
            Try
                i = cmd.ExecuteNonQuery()
            Catch ex As Exception
                Exit Sub
            End Try
        End Using
    End Using

This has successfully encrypted all my passwords for all the records in the table. Now, when I allow someone to login, I want to compare the two values (what was typed in, to what the stored password is) and I assumed the best way would be to decrypt the stored and compare using strings? Like so:

If (enc.ByteDecrypt(pwdenc).ToString() = pitstop.doMakeSafeForSQL(txtPassword.Text.ToString.ToLower)) Then

However, I get all sorts of bizarre errors; Unable to cast object of type 'System.String' to type 'System.Byte[]'. is one, Invalid character in a Base-64 string. is another based on how I call the password in the proceeding SQLDataReader:

 Using oConn As New SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings("pitstopConnectionString").ConnectionString)
        Using cmd As New SqlCommand("doGetEncryptedForLogin", oConn)
            cmd.CommandType = CommandType.StoredProcedure
            cmd.Parameters.AddWithValue("@email", pitstop.doMakeSafeForSQL(txtEmail.Text.ToString.ToLower))
            oConn.Open()
            Using dr As SqlDataReader = cmd.ExecuteReader()
                If dr.HasRows() = True Then
                    While dr.Read()
                        pwdenc = dr("password_b")
                    End While
                Else
                    pitstop.doLogIt("Denied login for [" & txtEmail.Text & "]")
                    litError.Text = "The details you have provided are incorrect. Please try again."
                    pnlDenied.Visible = True
                    Exit Sub
                End If
                dr.Close()
            End Using
        End Using
    End Using

Help or advice welcomed, as I'm stumped here...

A: 

Since noone else seems to answer, I'll give you my own two cents. I remember having experienced a similiar situation when I started "playing" with encryption, more than three years ago; in my case the problem was somehow related with the Base64 conversion, though I cannot remember the exact details now. I suggest you going through a calm and patient debugging session, tracking down all the values of the strings, the byte arrays before and after crypting and decrypting. I understand it's not a great help, but I hope at least can direct you in the right direction.

Turro
Thanks Turro. Always nice to have an answer regardless. I have solved the problem myself and I will post an answer as an assistance to others shortly. Thanks again though. +1 for the effort.
Chris Laythorpe
A: 

I fixed this error by replacing my 'encryption' class with Jeff Atwood's (www.codinghorror.com) fantastic encryption tools.

I did want to salt the passwords and store the salt, but I run out of time to implement that and will have to return to it. In the meantime, I've used this code as my encrypt and decrypt functions - I hope this is of some assistance to someone:

    Dim key As String = System.Configuration.ConfigurationManager.AppSettings("key").ToString()
    Dim salty As String = pitstop.doMakeSafeForSQL(txtEmail.Text.ToString().ToLower())
    Dim p As Encryption.Symmetric.Provider = Encryption.Symmetric.Provider.TripleDES
    Dim sym As New Encryption.Symmetric(p)
    sym.Key.Text = key

    Dim encryptedData As Encryption.Data
    encryptedData = sym.Encrypt(New Encryption.Data(pitstop.doMakeSafeForSQL(txtPassword.Text.ToString().ToLower())))

Decryption:

    Dim decryptedData As Encryption.Data
    Dim pr As Encryption.Symmetric.Provider = Encryption.Symmetric.Provider.TripleDES
    Dim sym2 As New Encryption.Symmetric(pr)
    sym2.Key.Text = System.Configuration.ConfigurationManager.AppSettings("key").ToString()

    Try
        decryptedData = sym2.Decrypt(encryptedData)
    Catch ex As Exception
// removed for brevity //
        End Try

If anyone has any articles or advice on how to create, store and retrieve salted passwords, I'd love to see it.

Chris

Chris Laythorpe