【发布时间】:2016-07-18 14:05:58
【问题描述】:
这是一个常见问题,但建议的解决方案 here 和 here 对我不起作用,而且它们不涉及数据库。
我希望代码示例“失败代码(WITH 数据库)”能够正常工作。
这是我的加密/解密代码(从这里复制:https://msdn.microsoft.com/en-us/library/system.security.cryptography.aes.aspx?cs-save-lang=1&cs-lang=vb&f=255&MSPPError=-2147217396#code-snippet-2):
Shared Function EncryptStringToBytes_Aes(ByVal plainText As String, ByVal Key() As Byte, ByVal IV() As Byte) As Byte()
' Check arguments.
If plainText Is Nothing OrElse plainText.Length <= 0 Then
Throw New ArgumentNullException("plainText")
End If
If Key Is Nothing OrElse Key.Length <= 0 Then
Throw New ArgumentNullException("Key")
End If
If IV Is Nothing OrElse IV.Length <= 0 Then
Throw New ArgumentNullException("Key")
End If
Dim encrypted() As Byte
' Create an Aes object
' with the specified key and IV.
Using aesAlg As Aes = Aes.Create()
aesAlg.Key = Key
aesAlg.IV = IV
' Create a decrytor to perform the stream transform.
Dim encryptor As ICryptoTransform = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV)
' Create the streams used for encryption.
Using msEncrypt As New MemoryStream()
Using csEncrypt As New CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)
Using swEncrypt As New StreamWriter(csEncrypt)
'Write all data to the stream.
swEncrypt.Write(plainText)
End Using
encrypted = msEncrypt.ToArray()
End Using
End Using
End Using
' Return the encrypted bytes from the memory stream.
Return encrypted
End Function 'EncryptStringToBytes_Aes
Shared Function DecryptStringFromBytes_Aes(ByVal cipherText() As Byte, ByVal Key() As Byte, ByVal IV() As Byte) As String
' Check arguments.
If cipherText Is Nothing OrElse cipherText.Length <= 0 Then
Throw New ArgumentNullException("cipherText")
End If
If Key Is Nothing OrElse Key.Length <= 0 Then
Throw New ArgumentNullException("Key")
End If
If IV Is Nothing OrElse IV.Length <= 0 Then
Throw New ArgumentNullException("Key")
End If
' Declare the string used to hold
' the decrypted text.
Dim plaintext As String = Nothing
' Create an Aes object
' with the specified key and IV.
Using aesAlg As Aes = Aes.Create()
aesAlg.Key = Key
aesAlg.IV = IV
' Create a decrytor to perform the stream transform.
Dim decryptor As ICryptoTransform = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV)
' Create the streams used for decryption.
Using msDecrypt As New MemoryStream(cipherText)
Using csDecrypt As New CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)
Using srDecrypt As New StreamReader(csDecrypt)
' Read the decrypted bytes from the decrypting stream
' and place them in a string.
plaintext = srDecrypt.ReadToEnd()
End Using <= PADDING ERROR THROWN HERE
End Using
'cipherText = msDecrypt.ToArray() 'added by me
End Using
End Using
Return plaintext
End Function 'DecryptStringFromBytes_Aes
工作示例(无数据库)
Dim original As String = "Here is some data to encrypt!"
Dim key As Rfc2898DeriveBytes = New Rfc2898DeriveBytes(_sharedSecret, _salt)
Try
' Create a new instance of the Aes
' class. This generates a new key and initialization
' vector (IV).
Using myAes As Aes = Aes.Create()
myAes.Key = key.GetBytes(myAes.KeySize / 8)
myAes.IV = key.GetBytes(myAes.BlockSize / 8)
' Encrypt the string to an array of bytes.
Dim encrypted As Byte() = EncryptStringToBytes_Aes(original, myAes.Key, myAes.IV)
' Decrypt the bytes to a string.
Dim roundtrip As String = DecryptStringFromBytes_Aes(encrypted, myAes.Key, myAes.IV)
'Display the original data and the decrypted data.
ltStatus.Text = String.Format("Original: {0}", original)
ltStatus.Text += String.Format("Round Trip: {0}", roundtrip)
End Using
Catch ex As Exception
Console.WriteLine("Error: {0}", ex.Message)
End Try
失败代码(WITH 数据库)
Dim _salt As Byte() = Encoding.ASCII.GetBytes("o6806642kbM7c5")
Dim _sharedSecret As String = "abcd"
Dim original As String = "Here is some data to encrypt!"
Dim key As Rfc2898DeriveBytes = New Rfc2898DeriveBytes(_sharedSecret, _salt)
Dim encrypted As Byte()
Try
Using myAes As Aes = Aes.Create()
myAes.Key = key.GetBytes(myAes.KeySize / 8)
myAes.IV = key.GetBytes(myAes.BlockSize / 8)
myAes.Padding = PaddingMode.PKCS7
encrypted = EncryptStringToBytes_Aes(original, myAes.Key, myAes.IV)
End Using
Catch ex As Exception
Console.WriteLine("Error: {0}", ex.Message)
End Try
'save to DB
Dim myConnection As SqlConnection = GetConnection()
Dim cmd As New SqlCommand("UPDATE banks set bank_name=@bankname WHERE id=1", myConnection)
cmd.Parameters.Add(New SqlParameter("@bankname", encrypted))
Try
myConnection.Open()
cmd.ExecuteScalar()
Catch ex As Exception
GlobalFunctions.LogError("banks:INSERT encrypted", ex.Message, LogLevel.Normal)
Finally
myConnection.Close()
End Try
'retreive from db
Dim decrypted As String = ""
myConnection = GetConnection()
cmd = New SqlCommand("SELECT bank_name FROM banks where id=1", myConnection)
Dim reader As SqlDataReader
Try
myConnection.Open()
reader = cmd.ExecuteReader
If reader.Read Then
Using myAes As Aes = Aes.Create()
myAes.Key = key.GetBytes(myAes.KeySize / 8)
myAes.IV = key.GetBytes(myAes.BlockSize / 8)
myAes.Padding = PaddingMode.PKCS7
decrypted = DecryptStringFromBytes_Aes(reader("bank_name"), myAes.Key, myAes.IV)
End Using
Else
GlobalFunctions.LogError("banks:nothing to be read?!?", LogLevel.Normal)
End If
Catch ex As Exception
GlobalFunctions.LogError("banks:SELECT encrypted.", ex.Message, LogLevel.Normal)
Finally
myConnection.Close()
End Try
但是这里出了点问题:
二进制值已成功添加到我的 MSSQL 数据库中 varbinary(MAX) 类型的字段 bank_name 中。 (我也尝试了较小的字段,例如varbinary(50))
但是,当我在从数据库检索后尝试解密此字段时,我收到错误 Padding is invalid and cannot be removed. 请参阅上面代码“失败代码(带有数据库)”中带有注释“
我检查了here 和here。而且我没有传递一个空字符串
我也尝试添加cipherText = msDecrypt.ToArray(),但在命中此行之前已经发生错误。
更新 2
我的转储值是:
ReportError 将值存储在数据库中的文本字段中,报告的值是:
myAes.键入
00000000 95 0C 95 EA 1D 40 0C FB 1D 3F B7 FB 73 FB 3F EA ����������������
00000010 40 62 51 62 51 EA 62 73 B7 2E 1D C8 1D 51 51 95 ����������������
myAes.IV 在
00000000 51 A6 84 73 95 C8 2E 62 84 C8 0C 62 C8 2E 1D 84 ����������������
加密
00000000 FB FB B7 73 D9 51 A6 2E 95 73 62 73 3F 84 A6 40 ����������������
00000010 B7 62 84 2E 51 95 EA 1D 51 A6 EA 2E 51 A6 51 95 ����������������
myAes.Key out
00000000 51 1D 73 40 EA A6 73 EA FB 73 73 A6 0C A6 D9 1D ����������������
00000010 2E 3F FB 2E 73 A6 A6 0C A6 C8 95 0C D9 1D B7 73 ����������������
myAes.IV 输出
00000000 B7 95 51 73 B7 D9 95 EA 0C C8 95 95 0C 84 40 62 ����������������
加密
00000000 FB FB B7 73 D9 51 A6 2E 95 73 62 73 3F 84 A6 40 ����������������
00000010 B7 62 84 2E 51 95 EA 1D 51 A6 EA 2E 51 A6 51 95 ����������������
银行:SELECT 已加密。内边距无效,无法移除。
这是我现在使用的完整代码:
Imports System.Security.Cryptography
Imports System.Data.SqlClient
Imports System.Text
Namespace HexDump
Class Utils
Public Shared Function HexDump(bytes As Byte(), Optional bytesPerLine As Integer = 16) As String
If bytes Is Nothing Then
Return "<null>"
End If
Dim bytesLength As Integer = bytes.Length
Dim HexChars As Char() = "0123456789ABCDEF".ToCharArray()
' 8 characters for the address
Dim firstHexColumn As Integer = 8 + 3
' 3 spaces
' - 2 digit for the hexadecimal value and 1 space
' - 1 extra space every 8 characters from the 9th
Dim firstCharColumn As Integer = firstHexColumn + bytesPerLine * 3 + (bytesPerLine - 1) / 8 + 2
' 2 spaces
' - characters to show the ascii value
Dim lineLength As Integer = firstCharColumn + bytesPerLine + Environment.NewLine.Length
' Carriage return and line feed (should normally be 2)
Dim line As Char() = (New [String](" "c, lineLength - Environment.NewLine.Length) + Environment.NewLine).ToCharArray()
Dim expectedLines As Integer = (bytesLength + bytesPerLine - 1) / bytesPerLine
Dim result As New StringBuilder(expectedLines * lineLength)
Dim i As Integer = 0
While i < bytesLength
line(0) = HexChars((i >> 28) And &HF)
line(1) = HexChars((i >> 24) And &HF)
line(2) = HexChars((i >> 20) And &HF)
line(3) = HexChars((i >> 16) And &HF)
line(4) = HexChars((i >> 12) And &HF)
line(5) = HexChars((i >> 8) And &HF)
line(6) = HexChars((i >> 4) And &HF)
line(7) = HexChars((i >> 0) And &HF)
Dim hexColumn As Integer = firstHexColumn
Dim charColumn As Integer = firstCharColumn
For j As Integer = 0 To bytesPerLine - 1
If j > 0 AndAlso (j And 7) = 0 Then
hexColumn += 1
End If
If i + j >= bytesLength Then
line(hexColumn) = " "c
line(hexColumn + 1) = " "c
line(charColumn) = " "c
Else
'Dim b As Byte = bytes(i + j)
'line(hexColumn) = HexChars((b >> 4) And &HF)
'line(hexColumn + 1) = HexChars(b And &HF)
'line(charColumn) = (If(b < 32, "·"c, CChar(b)))
Dim b As Byte = bytes((i + j))
line(hexColumn) = HexChars(((b + 4) _
And 15))
line((hexColumn + 1)) = HexChars((b And 15))
line(charColumn) = Microsoft.VisualBasic.ChrW(65533)
End If
hexColumn += 3
charColumn += 1
Next
result.Append(line)
i += bytesPerLine
End While
Return result.ToString()
End Function
End Class
End Namespace
Public Class banks_financial
Inherits System.Web.UI.Page
Private _lang As String
Private _registryId As Integer
Private _salt As Byte() = Encoding.ASCII.GetBytes("o6806642kbM7c5")
Private _sharedSecret As String = "abcd"
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim original As String = "Here is some data to encrypt!"
Dim key As Rfc2898DeriveBytes = New Rfc2898DeriveBytes(_sharedSecret, _salt)
Dim encrypted As Byte()
Try
Using myAes As Aes = Aes.Create()
myAes.Key = key.GetBytes(myAes.KeySize / 8)
myAes.IV = key.GetBytes(myAes.BlockSize / 8)
myAes.Padding = PaddingMode.PKCS7
encrypted = EncryptStringToBytes_Aes(original, myAes.Key, myAes.IV)
ReportError("myAes.Key in", HexDump.Utils.HexDump(myAes.Key))
ReportError("myAes.IV in", HexDump.Utils.HexDump(myAes.IV))
ReportError("encrypted in", HexDump.Utils.HexDump(encrypted))
End Using
Catch ex As Exception
Console.WriteLine("Error: {0}", ex.Message)
End Try
'save to DB
Dim myConnection As SqlConnection = GetConnection()
Dim cmd As New SqlCommand("UPDATE banks set bank_name=@bankname WHERE id=1", myConnection)
cmd.Parameters.Add(New SqlParameter("@bankname", encrypted))
Try
myConnection.Open()
cmd.ExecuteScalar()
Catch ex As Exception
GlobalFunctions.ReportError("banks:INSERT encrypted", ex.Message, LogLevel.Normal)
Finally
myConnection.Close()
End Try
'retreive from db
Dim decrypted As String = ""
myConnection = GetConnection()
cmd = New SqlCommand("SELECT bank_name FROM banks where id=1", myConnection)
Dim reader As SqlDataReader
Try
myConnection.Open()
reader = cmd.ExecuteReader
If reader.Read Then
Using myAes As Aes = Aes.Create()
myAes.Key = key.GetBytes(myAes.KeySize / 8)
myAes.IV = key.GetBytes(myAes.BlockSize / 8)
myAes.Padding = PaddingMode.PKCS7
ReportError("myAes.Key out", HexDump.Utils.HexDump(myAes.Key))
ReportError("myAes.IV out", HexDump.Utils.HexDump(myAes.IV))
ReportError("encrypted out", HexDump.Utils.HexDump(reader("bank_name")))
decrypted = DecryptStringFromBytes_Aes(reader("bank_name"), myAes.Key, myAes.IV)
ReportError("decrypted", decrypted)
End Using
Else
GlobalFunctions.ReportError("banks:nothing to be read?!?", LogLevel.Normal)
End If
Catch ex As Exception
GlobalFunctions.ReportError("banks:SELECT encrypted.", ex.Message, LogLevel.Normal)
Finally
myConnection.Close()
End Try
ltStatus.Text = GetMessageStatus(decrypted, MsgType.ok)
End Class
【问题讨论】:
-
您将
PBKDF2用于PW 有什么原因吗?那些的盐应该不同,每种加密的 IV 也应该不同。一遍又一遍地使用相同的东西会破坏目的(破解一个,它们都被破解了)。使用不同的意味着将它们保存在某个地方。 -
嗨,谢谢。我假设你的意思是
PKCS7?不是真的,我在另一篇文章中看到了它的推荐。我可以完全省略填充还是你会推荐什么?此外,在现实生活场景中,每个用户的盐值会有所不同(可能是他们的用户 ID 或帐户创建日期)。 -
不,
Rfc2898DeriveBytes用于基于密码的密钥派生功能 或 PBKDF。您构建它的方式。正确地做这意味着还为每个项目保存一个唯一的 IV 和盐。我试图弄清楚有多少试验和错误残余与实际需要。 -
只有一个应用程序读取和写入数据库,还是其他位置的其他应用程序实例读取信息?
-
确实只有一个应用程序读写数据库。
标签: asp.net vb.net encryption aes sql-server-2014-express