Advice regarding encryption - asp.net

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...

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.

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

Related

Clear IntPtr target in VB.NET

I'm getting the error There is insufficient system memory in resource pool 'internal' to run this query. I've alread checked out this post: There is insufficient system memory in resource pool 'default' to run this query. on sql
However, the error occurs when I added the following code (also see ASP.NET libwebp.dll how to save WebP image to disk):
As you can see below WebPFree doesn't clear the memory reserved by the variable of type IntPtr. I'm not sure what code should be added in WebPFree and how to clear the used memory.
I also checked this post, but found no solution either.
Dim imgRequest As WebRequest = WebRequest.Create(imageURL)
Dim imgResponse As WebResponse
Dim memStream As New MemoryStream
imgResponse = imgRequest.GetResponse()
Dim streamPhoto As Stream = imgResponse.GetResponseStream()
streamPhoto.CopyTo(memStream)
memStream.Position = 0
Dim bfPhoto As BitmapFrame = ReadBitmapFrame(memStream)
Dim baResize As Byte() = ToByteArray(bfPhoto)
Dim bmp As System.Drawing.Bitmap = imageFunctions.ConvertByteArrayToBitmap(baResize)
File.WriteAllBytes(test.webp, imageFunctions.EncodeImageToWebP(bmp))
Public Class imageFunctions
<DllImport("libwebp.dll", CallingConvention:=CallingConvention.Cdecl)>
Public Shared Function WebPEncodeBGRA(ByVal rgba As IntPtr, ByVal width As Integer, ByVal height As Integer, ByVal stride As Integer, ByVal quality_factor As Single, <Out> ByRef output As IntPtr) As Integer
End Function
<DllImport("libwebp.dll", CallingConvention:=CallingConvention.Cdecl)>
Public Shared Function WebPFree(ByVal p As IntPtr) As Integer
End Function
Public Shared Function EncodeImageToWebP(ByVal img As System.Drawing.Bitmap) As Byte()
Dim bmpData As BitmapData = img.LockBits(New Drawing.Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb)
'Create a pointer for webp data
Dim webpDataSrc As IntPtr
'Store resulting webp data length after conversion
Dim webpDataLen As Integer = WebPEncodeBGRA(bmpData.Scan0, img.Width, img.Height, bmpData.Stride, 80, webpDataSrc)
'Create a managed byte array with the size you just have
Dim webpDataBin As Byte() = New Byte(webpDataLen - 1) {}
'Copy from unmanaged memory to managed byte array you created
System.Runtime.InteropServices.Marshal.Copy(webpDataSrc, webpDataBin, 0, webpDataLen)
'Free
WebPFree(webpDataSrc)
img.Dispose()
img.UnlockBits(bmpData)
Return webpDataBin
End Function
Public Shared Function ConvertByteArrayToBitmap(ByVal imageData As Byte()) As System.Drawing.Bitmap
Dim ms As New IO.MemoryStream(imageData)
Return New Drawing.Bitmap(ms)
End Function
End Class

Implementing an Image Download in VB.NET

I am trying to implement an image download in vb.net where the client clicks a button and the browser downloads an image for the client from a file in the server.
I tried this:
Private Sub DownloadImage(ByVal ImageURL As String)
Dim Buffer(6000000) As Byte
Response.Clear()
Response.AddHeader("content-disposition", "attachment;filename=" & ImageURL & ".jpg")
Response.ContentType = "image/jpeg"
Response.BinaryWrite(Buffer)
Response.Flush()
Response.End()
End Sub
But this has 2 problems. First, it doesn't actually show up the image when I download it so it doesn't work properly. Secondly I have to manually enter the size of the image to determine the size of the byte array.
Please help me out, I couldn't find any resources on internet and couldn't get my head around it.
Thanks!
This is a sample image handler I've used for several years. It's probably got some room for improvement, but it is intended to be used as the src attribute for an img tag.
<img src="Thumbs.ashx?img=/ImagePath/BigImage.jpg&max=100" />
It's a direct copy-paste of my working code and it's got some references to private code - but it should give you a good start. It will create a thumbnail image so it doesn't have to be regenerated each time, if the thumbnail has already been generated. Hope this helps...
<%# WebHandler Language="VB" Class="GetImage" %>
Imports Web.Utilities
Imports Common
Imports System
Imports System.Drawing
Imports System.Web
Imports System.IO
''' <summary>
''' Resizes the image supplied in the "img" querystring parameter and writes it to the OutputStream in jpg format.
''' The default dimension is a max of 90 pixels (H or W; whichever is larger).
''' The "max" querystring parameter can alter the maximum dimension by supplying a new dimension as an integer.
''' If the file name contains an ampersand, it should be replaced with [amp].
''' </summary>
''' <remarks></remarks>
Public Class GetImage : Implements IHttpHandler
Private thumbFolder As String = "/images/Thumbs/"
Private trapCount As Integer = 0
Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
'General.PreventClientCaching()
'context.Response.Cache.SetCacheability(HttpCacheability.NoCache)
Dim maxSize As Integer = 90
Dim img As String = context.Request.QueryString("img")
Dim strMax As String = context.Request.QueryString("max")
If Not strMax.IsNullOrEmpty AndAlso IsNumeric(strMax) Then
maxSize = CInt(strMax)
End If
If maxSize > 1000 Then
maxSize = 1000
End If
If img.IsNullOrEmpty Then
img = "/images/PicUnavailable.jpg"
End If
Try
Dim uri As New System.Uri(img, UriKind.RelativeOrAbsolute)
img = uri.AbsolutePath
Catch ex As Exception
End Try
General.SetContentType(General.FileType.Jpg)
img = img.Replace("[amp]", "&")
ServeImage(context, img, maxSize)
End Sub
Private Sub ServeImage(ByVal context As HttpContext, ByVal imagePath As String, ByVal maximumSize As Integer)
Dim imgFile As Image = Nothing
Dim existingThumb As String = imagePath.Replace("/", "{s}") & "_" & maximumSize.ToString & ".jpg"
Dim doPurge As Boolean
Boolean.TryParse(context.Request.QueryString("purge"), doPurge)
' Check to see if the thumbnail has already been generated and it's older than the source.
' If it is, delete it.
If File.Exists(context.Server.MapPath(thumbFolder & existingThumb)) Then
If doPurge Then
File.Delete(context.Server.MapPath(thumbFolder & existingThumb))
Else
If File.GetLastWriteTime(context.Server.MapPath(thumbFolder & existingThumb)) < File.GetLastWriteTime(context.Server.MapPath(imagePath)) Then
Try
File.Delete(context.Server.MapPath(thumbFolder & existingThumb))
Catch ex As Exception
End Try
End If
End If
End If
' If the thumbnail already exists, write the byte array to the output stream
If File.Exists(context.Server.MapPath(thumbFolder & existingThumb)) Then
Dim fs As FileStream = Nothing
Try
fs = New FileStream(context.Server.MapPath(thumbFolder & existingThumb), IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read)
Dim imgLen As Long = fs.Length()
Dim imgData(imgLen) As Byte
fs.Read(imgData, 0, Integer.Parse(imgLen.ToString()))
context.Response.OutputStream.Write(imgData, 0, Integer.Parse(imgLen.ToString()))
Catch ex2 As UnauthorizedAccessException
'context.Server.Transfer(img)
Throw
Catch exIO As IOException
'context.Server.Transfer(img)
Throw
Finally
If Not fs Is Nothing Then
fs.Dispose()
fs = Nothing
End If
End Try
' the file doesn't exist, so render it to the output stream and save it to the thumbnail
Else
Try
imgFile = Image.FromFile(context.Server.MapPath(imagePath))
Dim maxDim As Integer = maximumSize
Dim maxH As Integer = maximumSize
Dim maxW As Integer = maximumSize
'If img.Height > maxH OrElse img.Width > maxW Then
Dim thumb As Image
Dim gfx As Graphics
Dim rect As Rectangle
If imgFile.Height >= imgFile.Width Then 'portrait or square
Dim newW As Integer
newW = CInt((maxH / imgFile.Height) * imgFile.Width)
thumb = New Bitmap(newW, maxDim)
gfx = Graphics.FromImage(thumb)
gfx.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
gfx.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
gfx.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
rect = New Rectangle(0, 0, newW, maxDim)
Else 'landscape
Dim newH As Integer
newH = CInt((maxW / imgFile.Width) * imgFile.Height)
thumb = New Bitmap(maxDim, newH)
gfx = Graphics.FromImage(thumb)
gfx.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
gfx.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
gfx.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
rect = New Rectangle(0, 0, maxDim, newH)
End If
gfx.DrawImage(imgFile, rect)
thumb.Save(context.Response.OutputStream, Drawing.Imaging.ImageFormat.Jpeg)
thumb.Save(context.Server.MapPath(thumbFolder & existingThumb), Drawing.Imaging.ImageFormat.Jpeg)
gfx.Dispose()
thumb.Dispose()
gfx = Nothing
thumb = Nothing
Catch ex As Exception
Dim fs As FileStream = Nothing
Try
fs = New FileStream(context.Server.MapPath(imagePath), IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read)
Dim imgLen As Long = fs.Length()
Dim imgData(imgLen) As Byte
fs.Read(imgData, 0, Integer.Parse(imgLen.ToString()))
context.Response.OutputStream.Write(imgData, 0, Integer.Parse(imgLen.ToString()))
Catch ex2 As UnauthorizedAccessException
'context.Server.Transfer(img)
Throw
Catch exIO As IOException
'context.Server.Transfer(img)
If trapCount > 0 Then
Throw
End If
trapCount += 1
ServeImage(context, "/images/PicUnavailable.jpg", maximumSize)
Finally
If Not fs Is Nothing Then
fs.Dispose()
fs = Nothing
End If
End Try
Finally
If Not imgFile Is Nothing Then
imgFile.Dispose()
imgFile = Nothing
End If
End Try
End If
End Sub
Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class

ASP.NET || Is this a good way to hash passwords?

I'm trying stuff to hash password for my website and I've been experimenting a bit and I've gotten a result. Now I'm asking myself if this is actually a good way to hash my passwords.
My Main code:
Imports System.Security.Cryptography
Partial Class _Default
Inherits System.Web.UI.Page
Protected Sub Button1_Click(sender As Object, e As System.EventArgs) Handles Button1.Click
Dim strWoordOmTeHashen As String
Dim strSalt1, strSalt2, strSalt3 As String
Dim random As New Random
Dim arrSalt1(255), arrSalt2(255), arrSalt3(255) As String
For i = 0 To 255
arrSalt1(i) = random.Next(1, 26).ToString
arrSalt2(i) = random.Next(1, 26).ToString
arrSalt3(i) = random.Next(1, 26).ToString
Next
For i = 0 To 255
arrSalt1(i) = VeranderGetalNaarLetter.VeranderGetalNaarLetter(CInt(arrSalt1(i)))
arrSalt2(i) = VeranderGetalNaarLetter.VeranderGetalNaarLetter(CInt(arrSalt2(i)))
arrSalt3(i) = VeranderGetalNaarLetter.VeranderGetalNaarLetter(CInt(arrSalt3(i)))
Next
For i = 0 To 255
strSalt1 &= arrSalt1(i)
strSalt2 &= arrSalt2(i)
strSalt3 &= arrSalt3(i)
Next
strWoordOmTeHashen = strSalt1 & strSalt2 & txtWoord.Text & strSalt3
'Sha512 zoder salt
Dim sham As New SHA512Managed
Dim result As Byte()
Dim data As Byte()
Dim hexstring As String
data = ASCIIEncoding.ASCII.GetBytes(strWoordOmTeHashen)
result = sham.ComputeHash(data)
For i = 0 To UBound(result)
hexstring &= Hex(result(i)).ToLower
Next
TextBox1.Text = hexstring
End Sub
End Class
You might notice that I'm calling a function. I'm calling this function:
Public Class VeranderGetalNaarLetter
Public Shared Function VeranderGetalNaarLetter(intSalt As Integer) As String
Dim strAlfabet As String = "!abcdefghijklmnopqrstuvwxyz"
Dim strLetter As String
strLetter = strAlfabet.Substring(intSalt, 1)
Return strLetter
End Function
End Class
Any comment is welcome. I'm hoping to get comments to improve my programming a bit.
Thanks in advance :)
While this is not wrong it is not best practice either. Hashing passwords is very tedious and sometimes re-inventing the wheel is just not worth it. If you need to hash your password then you should use an already existing library. Please take a look at BCrypt http://bcrypt.codeplex.com/
This is the code I normally use to hash stuff, it's a really simple function actually :)
Function hash(text As String) As String
Dim encoder As New System.Text.UnicodeEncoding
Dim sha256 As New System.Security.Cryptography.SHA256CryptoServiceProvider
Return Convert.ToBase64String(sha256.ComputeHash(encoder.GetBytes(text)))
End Function
Good luck!

InvalidCastException when reading a BLOB object (PDF File) from an SQL Database

I'm having problems with an Invalid Cast Exception when I try and read a PDF from a database as a BLOB. I am able to write the files into the database no problems at all, however, when I try to retrieve them I just get InvalidCastException.
Here is the code I'm using:
Protected Sub btnPDF_Click(sender As Object, e As EventArgs) Handles btnPDF.Click
' Request.QueryString["docid"].ToString();
Dim docuid As String = "b39a443d-ccfd-47f4-b333-f12cd94683d6"
'Connection and Parameters
Dim param As SqlParameter = Nothing
Dim conn As SqlConnection = New SqlConnection(
ConfigurationManager.ConnectionStrings("menu").ToString())
Dim cmd As New SqlCommand("sp_getdoc", conn)
cmd.CommandType = CommandType.StoredProcedure
param = New SqlParameter("#docuid", SqlDbType.NVarChar, 100)
param.Direction = ParameterDirection.Input
param.Value = docuid
cmd.Parameters.Add(param)
'Open connection and fetch the data with reader
conn.Open()
Dim reader As SqlDataReader =
cmd.ExecuteReader(CommandBehavior.CloseConnection)
If reader.HasRows Then
reader.Read()
'
Dim doctype As String = reader("doctype").ToString()
Dim docname As String = reader("docname").ToString()
'
Response.Buffer = False
Response.ClearHeaders()
Response.ContentType = doctype
Response.AddHeader("Content-Disposition",
"attachment; filename=" + docname)
'
'Code for streaming the object while writing
Const ChunkSize As Integer = 1024
Dim buffer() As Byte = New Byte(ChunkSize) {}
Dim binary(reader("document")) As Byte
Dim ms As New MemoryStream(binary)
Dim SizeToWrite As Integer = ChunkSize
For i As Integer = 0 To binary.GetUpperBound(0) - 1 Step i = i + ChunkSize
If Not Response.IsClientConnected Then
Return
End If
If i + ChunkSize >= binary.Length Then
SizeToWrite = binary.Length - i
End If
Dim chunk(SizeToWrite) As Byte
ms.Read(chunk, 0, SizeToWrite)
Response.BinaryWrite(chunk)
Response.Flush()
Next
Response.Close()
End If
End Sub
I am encountering the problem specifically on the following line:
Dim binary(reader("document")) As Byte
It seems to think that binary is being passed an Integer. Is this something to do with the SQLReader? I'm not really sure at this point what the problem is.
Any help would be greatly appreciated.
Many Thanks,
Richard E Logan-Baker
*UPDATE*
I have since worked out the problem that I'm getting by changing the lines to:
Dim blob() As Byte
blob = reader.Item("document")
However, the PDF does not display inside firefox, and when I save the file (even though my DB is only 2MB~) it is quite happy at downloading over 40MB of data! Also, the file size reports as unknown. I am really stuck now.
*UPDATE*
I've now got the PDF to open in the browser, but there is no data being displayed and Adobe Acrobat says it has problems extracting the text/fonts from the file and that the PDF is broken somehow.
Here is my updated code now:
Protected Sub btnPDF_Click(sender As Object, e As EventArgs) Handles btnPDF.Click
' Request.QueryString["docid"].ToString();
Dim docuid As String = "ba32bf45-1b5c-451a-969c-290dc2cf9073"
'Connection and Parameters
Dim param As SqlParameter = Nothing
Dim conn As SqlConnection = New SqlConnection(
ConfigurationManager.ConnectionStrings("menu").ToString())
Dim cmd As New SqlCommand("sp_getdoc", conn)
cmd.CommandType = CommandType.StoredProcedure
param = New SqlParameter("#docuid", SqlDbType.NVarChar, 100)
param.Direction = ParameterDirection.Input
param.Value = docuid
cmd.Parameters.Add(param)
'Open connection and fetch the data with reader
conn.Open()
Dim reader As SqlDataReader =
cmd.ExecuteReader(CommandBehavior.CloseConnection)
If reader.HasRows Then
reader.Read()
'
Dim doctype As String = reader("doctype").ToString()
Dim docname As String = reader("docname").ToString()
'
Response.Buffer = False
Response.ClearHeaders()
Response.ContentType = doctype
Response.AddHeader("Content-Disposition",
"attachment; filename=" + docname)
'
'Code for streaming the object while writing
Const ChunkSize As Integer = 1024
Dim buffer() As Byte = New Byte(ChunkSize) {}
Dim blob() As Byte
blob = reader.Item("document")
Dim ms As New MemoryStream(blob)
Dim SizeToWrite As Integer = ChunkSize
For i As Integer = 0 To blob.GetUpperBound(0) - 1
If Not Response.IsClientConnected Then
Return
End If
If i + ChunkSize >= blob.Length Then
SizeToWrite = blob.Length - i
End If
Dim chunk(SizeToWrite) As Byte
ms.Read(chunk, 0, SizeToWrite)
Response.BinaryWrite(chunk)
Response.Flush()
i = i + ChunkSize
Next i
Response.Close()
End If
End Sub
I think your problem is coming from the way you are incrementing "i" inside the loop. After you increment it by the ChunkSize at the end of your For...Next statement, the "Next i" statement will increment it by 1 again. Try changing that line to:
i = i + ChunkSize - 1
Or alternatively you could add a "Step ChunkSize" at the end of the For statement and remove the line I'm referring to.

CryptoStream.FlushFinalBlock throwing input data is not a complete block exception

I use the following two methods to encrypt and decrypt strings:
'Encrypts string. Returns encrypted byte array.
Public Function Encrypt(ByVal str As String) As Byte()
Dim inputInBytes() As Byte = Encoding.Unicode.GetBytes(str)
Dim laesProvider As New AesCryptoServiceProvider()
laesProvider.Key = _key
laesProvider.Mode = CipherMode.CBC
laesProvider.IV = _IV
laesProvider.Padding = PaddingMode.PKCS7
Dim lencryptor As ICryptoTransform = laesProvider.CreateEncryptor
Dim encryptedStream As New MemoryStream
Dim cryptStream As CryptoStream = New CryptoStream(encryptedStream, lencryptor, CryptoStreamMode.Write)
cryptStream.Write(inputInBytes, 0, inputInBytes.Length)
cryptStream.FlushFinalBlock()
encryptedStream.Position = 0
Dim result(encryptedStream.Length - 1) As Byte
encryptedStream.Read(result, 0, encryptedStream.Length)
cryptStream.Close()
Return result
End Function
'Decrypts bytearray. Returns string.
Public Function DecryptToStr(ByVal inputInBytes() As Byte) As String
Dim laesProvider As New AesCryptoServiceProvider()
laesProvider.Key = _key
laesProvider.Mode = CipherMode.CBC
laesProvider.IV = _IV
laesProvider.Padding = PaddingMode.PKCS7
Dim ldecryptor As ICryptoTransform = laesProvider.CreateDecryptor
' Provide a memory stream to decrypt information into
Dim decryptedStream As MemoryStream = New MemoryStream()
Dim cryptStream As CryptoStream = New CryptoStream(decryptedStream, ldecryptor, CryptoStreamMode.Write)
cryptStream.Write(inputInBytes, 0, inputInBytes.Length)
cryptStream.FlushFinalBlock() '#### This is where the exception is thrown ####
decryptedStream.Position = 0
' Read the memory stream and convert it back into a string
Dim result(decryptedStream.Length - 1) As Byte
decryptedStream.Read(result, 0, decryptedStream.Length)
cryptStream.Close()
Return Encoding.Unicode.GetString(result)
End Function
The error occurs when attempting to decrypt certain length strings. When the string is a social security # (11 chars including dashes) then is throws "The input data is not a complete block" CryptographicException. If I pass in for example a string that is exactly 8 characters long, then everything works as expected. I thought that the PKCS7 padding would take care of the various lengths. I'm sure that I'm missing something simple, but after hours of googling, the answer eludes me.
The issue wasn't in the encryption method, it was in the length of the varbinary set in the database where it was being stored. So the encrypted data was being truncated.

Resources