序列化加密的类属性

时间:2016-09-12 10:36:28

标签: .net vb.net encryption serialization

我正在编写一个存储电子邮件帐户信息的课程。某些属性(如电子邮件地址,帐户密码和服务器名称)以加密形式存储在私有变量中。

Property Setter对数据进行加密,Getter对其进行解密。

是的我知道在软件中存储加密密钥本质上是不安全的,但是当我找到替代方案时,该代码将会被更改。可能使用用户输入的密钥或将用于生成正确密钥的密码。目前,该计划仅供个人使用。

当我尝试序列化加密信息时,信息以纯文本形式发送到xml文件,因为使用Property Getter访问信息,当然这会解密数据。

我并不特别想要暴露额外的加密属性,并将未加密的属性标记为序列化程序忽略 - 它似乎不够优雅且可能不太安全。

我当然可以在将主程序传递给属性设置器之前在主程序中执行de / encryption,但是我想知道如果我想重新使用'那么这是否可以在类中完成以保持整洁。 EmailAccount'类。

此外,我希望暂时至少以纯文本形式使用xml文件,这样我才能确保en / decryption正常工作。我意识到我可以加密整个文件,但这是为了以后。

以下是' EmailAccount'的简化版本。上课..

Public Class EmailAccount

    Public Enum UseSSL
        Yes
        No
    End Enum

    Dim _imapPwd As String
    Dim _smtpPwd As String
    Dim _user As String
    Dim _imapServer As String
    Dim _smtpServer As String
    Dim _usessl As UseSSL
    Dim _imapPort As Integer
    Dim _smtpPort As Integer

    Public Property User As String
        Set(value As String)
            _user = Encrypt(value)
        End Set
        Get
            Return Decrypt(_user)
        End Get
    End Property

    Public Property ImapPassword As String
        Set(value As String)
            _imapPwd = Encrypt(value)
        End Set
        Get
            Return Decrypt(_imapPwd)
        End Get
    End Property

    Public Property ImapServer As String
        Set(value As String)
            _imapServer = Encrypt(value)
        End Set
        Get
            Return Decrypt(_imapServer)
        End Get
    End Property


    Public Sub New()
        User = "noemailset@nowhere.com"
        ImapPassword = "nopasswordfddfsg"
        ImapServer = "no.imap.server"
        SmtpPassword = "nopasswordsdfgdf"
        SmtpServer = "no.smtp.server"
        ImapPort = -1
        SmtpPort = -1
        SslState = UseSSL.Yes
    End Sub

    Private Function Encrypt(clearText As String) As String
        Dim EncryptionKey As String = "crypto key goes here"
        Dim clearBytes As Byte() = Encoding.Unicode.GetBytes(clearText)
        Using encryptor As Aes = Aes.Create()
            Dim pdb As New Rfc2898DeriveBytes(EncryptionKey, New Byte() {&H49, &H76, &H61, &H6E, &H20, &H4D,
             &H65, &H64, &H76, &H65, &H64, &H65,
             &H76})
            encryptor.Key = pdb.GetBytes(32)
            encryptor.IV = pdb.GetBytes(16)
            Using ms As New MemoryStream()
                Using cs As New CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write)
                    cs.Write(clearBytes, 0, clearBytes.Length)
                    cs.Close()
                End Using
                clearText = Convert.ToBase64String(ms.ToArray())
            End Using
        End Using
        Return clearText
    End Function

    Private Function Decrypt(cipherText As String) As String
        Dim EncryptionKey As String = "crypto key goes here"
        Dim cipherBytes As Byte() = Convert.FromBase64String(cipherText)
        Using encryptor As Aes = Aes.Create()
            Dim pdb As New Rfc2898DeriveBytes(EncryptionKey, New Byte() {&H49, &H76, &H61, &H6E, &H20, &H4D,
             &H65, &H64, &H76, &H65, &H64, &H65,
             &H76})
            encryptor.Key = pdb.GetBytes(32)
            encryptor.IV = pdb.GetBytes(16)
            Using ms As New MemoryStream()
                Using cs As New CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write)
                    cs.Write(cipherBytes, 0, cipherBytes.Length)
                    cs.Close()
                End Using
                cipherText = Encoding.Unicode.GetString(ms.ToArray())
            End Using
        End Using
        Return cipherText
    End Function


End Class

在我的主要表单中,我有以下步骤来序列化数据。

''' <summary>
''' Convert a class state into XML
''' </summary>
''' <typeparam name="T">The type of object</typeparam>
''' <param name="obj">The object to serilize</param>
''' <param name="FilePath">The path to the XML</param>
Public Shared Sub Serialize(Of T)(ByVal obj As T, Filepath As String)
    Dim XmlBuddy As New System.Xml.Serialization.XmlSerializer(GetType(T))
    Dim MySettings As New System.Xml.XmlWriterSettings()
    MySettings.Indent = True
    MySettings.CloseOutput = True
    Dim MyWriter As System.Xml.XmlWriter = System.Xml.XmlWriter.Create(Filepath, MySettings)
    XmlBuddy.Serialize(MyWriter, obj)
    MyWriter.Flush()
    MyWriter.Close()
End Sub

1 个答案:

答案 0 :(得分:0)

您可以在加密中执行相同的操作,并在序列化程序流中使用CryptoStream。这使用BinaryFormatter

Private Sub SaveEmails(data As List(Of EmailAccount), password As String,
                   Filepath As String)
    ' ToDo: further hashing, maybe
    Dim key As Byte() = GetPasswordHash(passW, 32, 62700)
    Dim iv(15) As Byte
    Using rng As New RNGCryptoServiceProvider
        rng.GetNonZeroBytes(iv)
    End Using

    ' ToDo: Try/Catch
    If File.Exists(Filepath) Then File.Delete(Filepath)

    Using rij = Rijndael.Create()
        rij.Padding = PaddingMode.ISO10126

        Using cryptor As ICryptoTransform = rij.CreateEncryptor(key, iv),
            fs As New FileStream(Filepath, FileMode.CreateNew, FileAccess.Write)
            ' write the iv to bare stream
            fs.Write(iv, 0, iv.Length)

            Using cs = New CryptoStream(fs, cryptor, CryptoStreamMode.Write)
                Dim bf = New BinaryFormatter
                bf.Serialize(cs, data)
                ' may not be needed - doesnt hurt
                cs.FlushFinalBlock()
            End Using
        End Using
    End Using
End Sub

Private Function LoadEmails(passW As String, 
                        Filepath As String) As List(Of EmailAccount)
    Dim iv(15) As Byte
    Dim key As Byte() = GetPasswordHash(passW, 32, 62700)

    Dim emails As List(Of EmailAccount)

    Using rijAlg = Rijndael.Create()
        rijAlg.Padding = PaddingMode.ISO10126

        ' to do: check if exists
        Using fs As New FileStream(Filepath, FileMode.Open)
            ' read the IV first
            fs.Read(iv, 0, iv.Length)
            ' USING encryptor AND CryptoStream
            Using encryptor As ICryptoTransform = rijAlg.CreateDecryptor(key, iv),
                cs As New CryptoStream(fs, encryptor, CryptoStreamMode.Read)
                Dim bf As New BinaryFormatter
                emails = CType(bf.Deserialize(cs), List(Of EmailAccount))
            End Using
        End Using
    End Using

    Return emails
End Function

Private Function GetPasswordHash(pw As String, count As Int32, iter As Int32) As Byte()
    Dim salt As Byte() = {&H49, &H76, &H61, &H6E, &H20, &H4D,
                          &H65, &H64, &H76, &H65, &H64, &H65, &H76}

    Using pbkdf As New Rfc2898DeriveBytes(pw, salt,
                                          iter)
        Return pbkdf.GetBytes(count)
    End Using
End Function

一个区别是使用随机IV并将其写入基本流并从基本流中读取,这样每次保存文件时都可以使用新的文件,而不必将其保存在任何地方。这些可以是Crypto类的共享方法来创建CryptoStream,以便代码可以单独处理。

密码也有点不同。那个hasher是分开的,所以如果你改变迭代或方法,加密器和解密器最终不会使用不同的方法。

用法,测试:

Dim EmailAccts As New List(Of EmailAccount)
EmailAccts.Add(New EmailAccount With {.User = "Ziggy@ziggysplace.net", .SMTPPass = "secret!pass",
                                      .UseSSL = True, .SMTPServer = "yadayada", .SMTPPort = -42})
EmailAccts.Add(New EmailAccount With {.User = "david@somewhere.com", .SMTPPass = "$tack!overflow$",
                                      .UseSSL = False, .SMTPServer = "blahblah", .SMTPPort = 314})

Dim password As String = "AWeak!pa$$WorD!"

SaveEmails(EmailAccts, password, "C:\Temp\secretemails.bin")
Dim newEmails = LoadEmails(password, "C:\Temp\secretemails.bin")

' is everyone ok?
For Each email In newEmails
    Console.WriteLine("{0}  ... {1}", email.User, email.SMTPPass)
Next

Mine只是一类简单的自动属性,因为加密不被使用。每个人都活了下来:

  

Ziggy@ziggysplace.net ...秘密!通过
  david@somewhere.com ... $ tack!overflow $