VB.Net中的简单加密/解密

6
我正在尝试在VB.Net中加密/解密字符串。我按照这里给出的示例编写了以下代码。页面上有一个文本框,一个“加密”按钮和一个“解密”按钮。输入一些内容(例如“hello world”),然后单击“加密”按钮,就会在文本框中看到已加密的版本。单击“解密”按钮将返回原始字符串。但是,在尝试加密时,当我尝试“FlushFinalBlock”时,会出现错误:“要加密的数据的长度无效”。由于示例只涉及加密而不是解密,因此“解密”部分完全是凭感觉编写的。虽然我确信它是错误的,但由于我无法使“加密”正常工作,因此还没有测试它。请问有人能告诉我为什么这样做行不通吗?
Imports System.Data.SqlClient
Imports System.IO
Imports System.Security.Cryptography

Public Class Form1

  Private cryptObj As RijndaelManaged
  Private KEY_128 As Byte() = {42, 1, 52, 67, 231, 13, 94, 101, 123, 6, 0, 12, 32, 91, 4, 111, 31, 70, 21, 141, 123, 142, 234, 82, 95, 129, 187, 162, 12, 55, 98, 23}
  Private IV_128 As Byte() = {234, 12, 52, 44, 214, 222, 200, 109, 2, 98, 45, 76, 88, 53, 23, 78}
  Private enc As System.Text.UTF8Encoding = New System.Text.UTF8Encoding()

  Private Sub btnEncrypt_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEncrypt.Click
    Dim sPlainText As String = Me.TextBox1.Text
    If Not String.IsNullOrEmpty(sPlainText) Then
      Dim bPlainText As Byte() = Me.enc.GetBytes(Me.TextBox1.Text)
      Dim ms As MemoryStream = New MemoryStream()
      Dim cs As CryptoStream = New CryptoStream(ms, cryptObj.CreateEncryptor(), CryptoStreamMode.Write)
      cs.Write(bPlainText, 0, sPlainText.Length)
      cs.FlushFinalBlock()
      Me.TextBox1.Text = Me.enc.GetString(ms.ToArray())
    End If
  End Sub

  Private Sub btnDecrypt_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDecrypt.Click
    Dim sCipherText = Me.TextBox1.Text
    Dim ms As MemoryStream = New MemoryStream()
    Dim cs As CryptoStream = New CryptoStream(ms, cryptObj.CreateDecryptor(), CryptoStreamMode.Read)
    cs.Read(Me.enc.GetBytes(sCipherText), 0, sCipherText.Length)
    Me.TextBox1.Text = Me.enc.GetString(ms.ToArray())
  End Sub

  Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    Me.cryptObj = New RijndaelManaged()
    Me.cryptObj.BlockSize = 128
    Me.cryptObj.KeySize = 128
    Me.cryptObj.Mode = CipherMode.ECB
    Me.cryptObj.Padding = PaddingMode.None
    Me.cryptObj.Key = KEY_128
    Me.cryptObj.IV = IV_128
  End Sub

End Class
4个回答

12

最终我在这里找到了答案:

http://www.obviex.com/samples/Encryption.aspx

他的示例看起来有些复杂。我相信它代表了一个更一般和灵活的情况,但我能够省去“saltPhrase”,“initVector”的使用,“PasswordDeriveBytes”,后者似乎已经过时了,但我也避免了它难以命名的替代品:Rfc2898DeriveBytes。

以下内容可以让您输入任意长度的字符串,对其进行加密和重新解密。

Imports System.Data.SqlClient
Imports System.IO
Imports System.Security.Cryptography

Public Class Form1

  Private enc As System.Text.UTF8Encoding
  Private encryptor As ICryptoTransform
  Private decryptor As ICryptoTransform

  Private Sub btnEncrypt_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEncrypt.Click
    Dim sPlainText As String = Me.TextBox1.Text
    If Not String.IsNullOrEmpty(sPlainText) Then
      Dim memoryStream As MemoryStream = New MemoryStream()
      Dim cryptoStream As CryptoStream = New CryptoStream(memoryStream, Me.encryptor, CryptoStreamMode.Write)
      cryptoStream.Write(Me.enc.GetBytes(sPlainText), 0, sPlainText.Length)
      cryptoStream.FlushFinalBlock()
      Me.TextBox1.Text = Convert.ToBase64String(memoryStream.ToArray())
      memoryStream.Close()
      cryptoStream.Close()
    End If
  End Sub

  Private Sub btnDecrypt_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDecrypt.Click
    Dim cypherTextBytes As Byte() = Convert.FromBase64String(Me.TextBox1.Text)
    Dim memoryStream As MemoryStream = New MemoryStream(cypherTextBytes)
    Dim cryptoStream As CryptoStream = New CryptoStream(memoryStream, Me.decryptor, CryptoStreamMode.Read)
    Dim plainTextBytes(cypherTextBytes.Length) As Byte
    Dim decryptedByteCount As Integer = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length)
    memoryStream.Close()
    cryptoStream.Close()
    Me.TextBox1.Text = Me.enc.GetString(plainTextBytes, 0, decryptedByteCount)
  End Sub

  Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    Dim KEY_128 As Byte() = {42, 1, 52, 67, 231, 13, 94, 101, 123, 6, 0, 12, 32, 91, 4, 111, 31, 70, 21, 141, 123, 142, 234, 82, 95, 129, 187, 162, 12, 55, 98, 23}
    Dim IV_128 As Byte() = {234, 12, 52, 44, 214, 222, 200, 109, 2, 98, 45, 76, 88, 53, 23, 78}
    Dim symmetricKey As RijndaelManaged = New RijndaelManaged()
    symmetricKey.Mode = CipherMode.CBC

    Me.enc = New System.Text.UTF8Encoding
    Me.encryptor = symmetricKey.CreateEncryptor(KEY_128, IV_128)
    Me.decryptor = symmetricKey.CreateDecryptor(KEY_128, IV_128)
  End Sub

End Class

8

这是一个基于我的NextLevelEncryption库的加密类示例。

Public Class Encryption
''' <summary>
''' Encrypt text using AES Algorithm
''' </summary>
''' <param name="text">Text to encrypt</param>
''' <param name="password">Password with which to encrypt</param>
''' <returns>Returns encrypted text</returns>
''' <remarks></remarks>
Public Shared Function Encrypt(text As String, password As String) As String
    Dim AES As New System.Security.Cryptography.RijndaelManaged
    Dim Hash_AES As New System.Security.Cryptography.MD5CryptoServiceProvider
    Dim encrypted As String = ""
    Dim hash(31) As Byte
        Dim temp As Byte() = Hash_AES.ComputeHash(System.Text.ASCIIEncoding.ASCII.GetBytes(password))
        Array.Copy(temp, 0, hash, 0, 16)
        Array.Copy(temp, 0, hash, 15, 16)
        AES.Key = hash
        AES.Mode = Security.Cryptography.CipherMode.ECB
        Dim DESEncrypter As System.Security.Cryptography.ICryptoTransform = AES.CreateEncryptor
        Dim Buffer As Byte() = System.Text.ASCIIEncoding.ASCII.GetBytes(text)
        encrypted = Convert.ToBase64String(DESEncrypter.TransformFinalBlock(Buffer, 0, Buffer.Length))
        Return encrypted
End Function

''' <summary>
''' Decrypt text using AES Algorithm
''' </summary>
''' <param name="text">Text to decrypt</param>
''' <param name="password">Password with which to decrypt</param>
''' <returns>Returns decrypted text</returns>
''' <remarks></remarks>
Public Shared Function Decrypt(text As String, password As String) As String
    Dim AES As New System.Security.Cryptography.RijndaelManaged
    Dim Hash_AES As New System.Security.Cryptography.MD5CryptoServiceProvider
    Dim decrypted As String = ""
    Dim hash(31) As Byte
        Dim temp As Byte() = Hash_AES.ComputeHash(System.Text.ASCIIEncoding.ASCII.GetBytes(password))
        Array.Copy(temp, 0, hash, 0, 16)
        Array.Copy(temp, 0, hash, 15, 16)
        AES.Key = hash
        AES.Mode = Security.Cryptography.CipherMode.ECB
        Dim DESDecrypter As System.Security.Cryptography.ICryptoTransform = AES.CreateDecryptor
        Dim Buffer As Byte() = Convert.FromBase64String(text)
        decrypted = System.Text.ASCIIEncoding.ASCII.GetString(DESDecrypter.TransformFinalBlock(Buffer, 0, Buffer.Length))
    Return decrypted
End Function
End Class

要使用它,您只需要调用函数Encrypt("要加密的文本","您的密码")Decrypt("要解密的文本","您的密码")
欲了解更多信息,请访问https://nextlevelencryption.codeplex.com/

2
我发现的问题在于你的加密代码中这一行:
``` Me.TextBox1.Text = Me.enc.GetString(ms.ToArray()) ```
问题在于它假设你的字节数组已经是一个 UTF-8 字符串,只是被切成了一个字节数组,但实际上它应该是随机的字节,并且很可能包含不可打印的字符。它不是有效的 utf-8 数据。
相反,你需要使用 Convert.ToBase64String() 函数对该字节数组进行 base-64 编码。然后,你的解密需要正确地将该 base 64 字符串转换回字节数组,使用 Convert.FromBase64String() 方法。

这可能是一个问题,但我认为它并没有解决问题。目前我只是尝试从键盘输入编码到文本框中的文本,这将是一个有效的UTF-8字符串。在“cs.FlushFinalBlock”处仍然会出现“加密数据的长度无效”的错误。 - nttaylor

1

你的加密看起来大部分都是正确的,但我不确定UTF8编码或其他加密对象上的设置是否会影响你。这是我们使用的加密方法的核心,稍微根据你的代码进行了调整:

' Return the encrypted bytes from the memory stream.
Dim aoBytes As Byte() = Nothing

' Declare the RijndaelManaged object used to encrypt the data.
Using oEncryptor As New RijndaelManaged
    Try
        ' Initialize the encryptor with the specified key and initialization vector
        oEncryptor.Key = KEY_128
        oEncryptor.IV = IV_128

        ' Declare the streams used to encrypt to an in memory array of bytes.
        Using msEncrypt As New MemoryStream
            ' Create the streams used for encryption.
            Using csEncrypt As New CryptoStream(msEncrypt, oEncryptor.CreateEncryptor(), CryptoStreamMode.Write)
                Using swEncrypt As New StreamWriter(csEncrypt)
                    ' Write all data to the stream.
                    swEncrypt.Write(Me.TextBox1.Text)
                End Using

                ' Retrieve the bytes
                aoBytes = msEncrypt.ToArray()
            End Using

        End Using
    Finally
        ' Clear the RijndaelManaged object.
        If oEncryptor IsNot Nothing Then
            oEncryptor.Clear()
        End If
    End Try
End Using

If aoBytes IsNot Nothing Then
    Me.TextBox1.Text = System.Convert.ToBase64String(aoBytes)
Else
    Me.TextBox1.Text = String.Empty
End If

解密如下:

Dim sDecryptedValue As String = ""

' Declare the RijndaelManaged object used to encrypt the data.
Using oDecryptor As New RijndaelManaged
    Try
        ' Initialize the encryptor with the specified key and a default initialization vector
        oDecryptor.Key = KEY_128
        oDecryptor.IV = IV_128

        Using msDecrypt As New MemoryStream(System.Convert.FromBase64String(Me.TextBox1.Text))
            ' Create the streams used for encryption.
            Using csDecrypt As New CryptoStream(msDecrypt, oDecryptor.CreateDecryptor(), CryptoStreamMode.Read)
                Using srDecrypt As New StreamReader(csDecrypt)
                    ' Write all data to the stream.
                    sDecryptedValue = srDecrypt.ReadToEnd()
                End Using
            End Using
        End Using
    Finally
        ' Clear the RijndaelManaged object.
        If oDecryptor IsNot Nothing Then
            oDecryptor.Clear()
        End If
    End Try
End Using

Me.TextBox1.Text = sDecryptedValue

一个小的区别是我们接受来自调用者的字符串密钥和初始向量,并按以下方式清理它们。

初始化向量清理:

If sInitializationVector.Length > 16 Then
    ' Trim the IV if it is too long
    sInitializationVector = sInitializationVector.Substring(0, 16)
ElseIf sInitializationVector.Length < 16 Then
    ' Pad the IV if it is too short
    sInitializationVector = sInitializationVector.PadRight(16)
End If

oDecryptor.IV = System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(sInitializationVector)

加密密钥清理:

oDecryptor.Key = GetLegalEncryptionKey(sKey, oDecryptor)

Public Function GetLegalEncryptionKey(ByVal sKey As String, ByVal oEncryptor As RijndaelManaged) As Byte()

Dim sTemp As String

If oEncryptor.LegalKeySizes.Length > 0 Then
    Dim wSize As Integer
    ' key sizes are in bits

    With oEncryptor.LegalKeySizes(0)
        wSize = .MinSize
        Do While sKey.Length * 8 > wSize AndAlso .SkipSize > 0 AndAlso wSize < .MaxSize
            wSize += oEncryptor.LegalKeySizes(0).SkipSize
        Loop
    End With
    Dim wTotalChars As Integer

    wTotalChars = CInt(wSize / 8)
    If sKey.Length > wTotalChars Then
        sTemp = sKey.Substring(0, wTotalChars)
    Else
        sTemp = sKey.PadRight(wTotalChars, " "c)
    End If
Else
    sTemp = sKey
End If

' convert the secret key to byte array
Return System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(sTemp)

结束函数


我尝试使用类似于您上面的代码进行加密。 Dim sw As StreamWriter = New StreamWriter(cs); sw.Write(Me.TextBox1.Text); Dim aoBytes As Byte() = ms.ToArray(); Me.TextBox1.Text = Convert.ToBase64String(aoBytes) 但结果是它清空了文本字段。 "aoBytes" 的长度为0。我确定我做错了什么,但问题的一部分在于所有这些流和编写器飞来飞去,我几乎是在黑暗中射击,对这些东西实际上在做什么没有太多的了解。 - nttaylor
问题可能出在您的密钥和初始化向量上。尝试将它们更改为字符串,并使用我提供的可选代码来查看是否有效。如果需要,我可以修改答案以准确显示它们的位置。 - competent_tech

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接