I created a SSRS Report which contains a few Code128 Barcodes. The Barcodes are generated using the latest zxing.net library. I would like to include tabs (char(9)) in an Code128 Barcode. But it fail with the following exception message:
System.ArgumentException: Bad character in input:
Needless to say that it works like a charm without the tabulator character.
The GetBarCodeHorizontal is used in the report to generate the barcodes. However, for testing purpose i wrapped it into a visual studio vb project:
Class MainWindow
Public Function GetBarCodeHorizontal(ByVal s As String, ByVal width As Integer) As Byte()
Dim writer As New ZXing.BarcodeWriter()
Dim ms As System.IO.MemoryStream = New System.IO.MemoryStream()
writer.Format = ZXing.BarcodeFormat.CODE_128
writer.Options = New ZXing.Common.EncodingOptions
writer.Options.Width = width
writer.Options.Height = 60
writer.Options.PureBarcode = False
'writer.Options.Hints.Add(ZXing.EncodeHintType.CHARACTER_SET, "UTF-8")
Dim bmp As System.Drawing.Bitmap = writer.Write(s)
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png)
Dim imagedata As Byte()
imagedata = ms.GetBuffer()
Return imagedata
End Function
Private Sub MainWindow_OnLoaded(sender As Object, e As RoutedEventArgs)
Try
Dim barCodeHorizontal = GetBarCodeHorizontal("3999999 80 1XXXXXX8 r1XX3", 200)
Catch ex As Exception
Console.WriteLine(ex)
End Try
End Sub
End Class
Questions:
How can i solve this problem?
Is this a limitation of the zxing library?
Is there any suitable workaround (or maybe even another library)?
I ended up with another (free) library which turns out to work very well.
There is also a tutorial how to embed barcodes into SSRS for this specific library.
For those of interest here is my code to create the barcodes:
Public Function GetBarcode(ByVal text As String, ByVal barcodeWidth As Integer, ByVal barcodeHeight As Integer) As Byte()
Dim b As System.Drawing.Bitmap
Dim bar As New BarcodeLib.Barcode
bar.Alignment = BarcodeLib.AlignmentPositions.CENTER
bar.IncludeLabel = False
b = bar.Encode(BarcodeLib.TYPE.CODE128, text, barcodeWidth, barcodeHeight)
Dim bitmapData As Byte() = Nothing
Using ms As New System.IO.MemoryStream()
b.Save(ms, System.Drawing.Imaging.ImageFormat.Png)
bitmapData = ms.ToArray()
End Using
Return bitmapData
End Function
The barcode data comes directly from the query as shown here:
SELECT MilkrunID, Code, Quantity, Batch, PickLocation, Code + CHAR(9) + CAST(Quantity AS NVARCHAR(20)) + CHAR(9) + Batch + CHAR(9) + PickLocation AS Barcode
FROM Tbl_ReportData_ProductionReplenishment_MilkrunSummary
char(9) creates a Tab.
Related
I am able to save Images to Access database table successfully using memory Stream. The Column in which I am saving the Images has datatype "OLEObject". When I open the table I see "Long binary data" value in this Image Column. Problem is that when I am creating any Report in this database, I am not able to see the image.
Whereas when I opened the Image file in MSPaint and Ctrl+A and then Ctrl+C to copy all the Graphics and then pasted it in the Access Database column, then it was pasted successfully. and Now shows value as "Bitmap image". When I double click on the image column value , it opens the image in MSPaint.And also works in Report. I would like to know how to make the image save directly as type "Bitmap image" ? I am using VB .Net Framework 4.0, Visual Studio 2012.
The MS Access OLE field structure is not documented in any literature I could find.
The class AccessOLEBitmapConverter shown below is the result of a forensic hack based on an article referenced (How To Retrieve Bitmap from Access and Display It in Web Page ) in the source code of the System.Drawing.ImageConverter Class for the method GetBitmapStream.
Based on a bit of hacking I discovered that the field comprises two structures: 1) a header structure, and 2) an EmbeddedObject structure. After a bit more hacking, I discovered that the Presentation field of the EmbeddedObject structure can be replaced with a sequence of 12 bytes as long as the name field in the header structure is populated with a sequence of six bytes ({33, 0, 255, 255, 255, 255}). Please refer to the code for further information.
Public Class AccessOLEBitmapConverter
Const nullByte As Byte = 0
Const OLEVersion As Int32 = 1281
Private Shared arrayInvalid As String = "Source array is invalid"
Public Shared Function ToByteArray(sourceBitmap As Bitmap) As Byte()
Dim ret As Byte() = Nothing
Using ms As New IO.MemoryStream(5000)
EmbeddedObjectHeader.Write(ms)
EmbeddedObject.Write(ms, sourceBitmap)
ret = ms.ToArray()
End Using
Return ret
End Function
Public Shared Function ToBitmap(bytes As Byte()) As Bitmap
Dim ret As Bitmap = Nothing
If bytes Is Nothing Then Throw New ArgumentNullException("Argument ""bytes"" cannot be null")
If bytes.Length < 14 Then Throw New ArgumentOutOfRangeException(arrayInvalid)
Using ms As New IO.MemoryStream(bytes, False),
br As New IO.BinaryReader(ms, System.Text.Encoding.ASCII)
Dim signature As Int16 = br.ReadInt16()
If signature <> &H1C15 Then Throw New ArgumentOutOfRangeException(arrayInvalid)
Dim headersize As Int16 = br.ReadInt16() ' 47
If bytes.Length < headersize Then Throw New ArgumentOutOfRangeException(arrayInvalid)
ms.Position = headersize
' Start ObjectHeader
If ms.Position = bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid)
Dim OLEVersion As UInt32 = br.ReadUInt32
If ms.Position = bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid)
Dim FormatID As UInt32 = br.ReadUInt32
If ms.Position = bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid)
Dim ClassName As String = LengthPrefixedAnsiString.Read(br)
If ms.Position = bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid)
Dim TopicName As String = LengthPrefixedAnsiString.Read(br)
If ms.Position = bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid)
Dim ItemName As String = LengthPrefixedAnsiString.Read(br)
' End ObjectHeader
If ms.Position = bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid)
Dim NativeDataSize As Int32 = br.ReadInt32
If (ms.Position + NativeDataSize) > bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid)
Dim NativeData As Byte() = br.ReadBytes(NativeDataSize)
Dim msImage As New IO.MemoryStream(NativeData)
ret = CType(Image.FromStream(msImage), Bitmap)
End Using
Return ret
End Function
Private Class EmbeddedObjectHeader
' ref: How To Retrieve Bitmap from Access and Display It in Web Page
' https://support.microsoft.com/en-us/kb/175261
Friend Shared Sub Write(ms As System.IO.MemoryStream)
Const signature As Int16 = &H1C15
Const headersize As Int16 = 47S
Const objectType As Int16 = 2S
Const nameLen As Int16 = 0S
Const classLen As Int16 = 13S
Const nameOffset As Int16 = 14S
Const classOffset As Int16 = 20S
Const classType As String = "Bitmap Image"
Const hdrClassName As String = "Paint.Picture"
Using bw As New IO.BinaryWriter(ms, System.Text.Encoding.ASCII, True)
With bw
.Write(signature)
.Write(headersize)
.Write(objectType)
.Write(nameLen)
.Write(classLen)
.Write(nameOffset)
.Write(classOffset)
' Even though this offset is declared as being for the 'name' field and
' the 'name' field always has a zero length, these six bytes must be present
' to allow the resultant byte array to be identified as a BitMap Image by Access
ms.Position = nameOffset
.Write(New Byte() {33, 0, 255, 255, 255, 255})
ms.Position = classOffset
.Write(classType.ToCharArray())
.Write(nullByte)
.Write(hdrClassName.ToCharArray())
.Write(nullByte)
End With
End Using
End Sub
End Class ' EmbeddedObjectHeader
Private Class EmbeddedObject
' ref: https://msdn.microsoft.com/en-us/library/dd942053.aspx
'Header (variable): This MUST be an ObjectHeader (section 2.2.4).
' The FormatID field of the Header MUST be set to 0x00000002.
'NativeDataSize (4 bytes): This MUST be set to the size of the NativeData field, in bytes.
'NativeData (variable): This must be an array of bytes that contains the native data.
'Presentation (variable): This MUST be a MetaFilePresentationObject (section 2.2.2.1),
' a BitmapPresentationObject (section 2.2.2.2), a DIBPresentationObject (section 2.2.2.3),
' a StandardClipboardFormatPresentationObject (section 2.2.3.2), or
' a RegisteredClipboardFormatPresentationObject (section 2.2.3.3).
Friend Shared Sub Write(ms As System.IO.Stream, sourceBitmap As Bitmap)
Using bw As New IO.BinaryWriter(ms, System.Text.Encoding.ASCII, True)
With bw
ObjectHeader.Write(ms)
' Determine and write the NativeDataSize and NativeData fields
Using imgStream As New IO.MemoryStream(20000)
sourceBitmap.Save(imgStream, System.Drawing.Imaging.ImageFormat.Bmp)
Dim NativeDataSize As Int32 = CInt(imgStream.Length)
.Write(NativeDataSize)
.Write(imgStream.ToArray)
End Using
' At this point the 'Presentation' variable should be written.
' However, Bitmap files copied from Windows Explorer and pasted into
' the MS Access OLE field have only 12 bytes written and this allows for
' a much smaller size to be stored as the 'Presentation' variable appears to
' duplicate the NativeData field. Adding the Bitmap via the 'Insert Object'
' dialog creates a storage nearly twice that of this method.
' The first 4 bytes correspond to the integer value for OLEVersion.
.Write(OLEVersion)
' The next 4 bytes are always zero.
.Write(0I)
' The next byte (position 8) appears variable and its value does not appear
' to impact using the 'BitMap Image' in Access. So write a zero.
.Write(nullByte)
' The final three bytes appear to be constant {173, 5, 254}
.Write(New Byte() {173, 5, 254})
.Flush()
End With
End Using
End Sub
End Class 'EmbeddedObject
Private Class ObjectHeader
Friend Shared Sub Write(ms As System.IO.Stream)
Const FormatID As Int32 = 2
Const ClassName As String = "PBrush"
Const TopicName As String = ""
Const ItemName As String = ""
Using bw As New IO.BinaryWriter(ms, System.Text.Encoding.ASCII, True)
With bw
.Write(OLEVersion)
.Write(FormatID)
LengthPrefixedAnsiString.Write(bw, ClassName)
LengthPrefixedAnsiString.Write(bw, TopicName)
LengthPrefixedAnsiString.Write(bw, ItemName)
End With
End Using
End Sub
End Class ' ObjectHeader
Private Class LengthPrefixedAnsiString
' ref : https://msdn.microsoft.com/en-us/library/dd942554.aspx
' This structure specifies a null-terminated American National Standards Institute (ANSI) character set string.
' Length (4 bytes): This MUST be set to the number of ANSI characters in the String field,
' including the terminating null character. Length MUST be set to 0x00000000 to indicate an empty string.
' String (variable): This MUST be a null-terminated ANSI string.
Const nullChar As Byte = 0
Friend Shared Function Read(br As IO.BinaryReader) As String
Dim ret As String = String.Empty
Dim length As Int32 = br.ReadInt32
If length > 0 Then
Dim chars As Char() = br.ReadChars(length)
ret = New String(chars)
End If
Return ret
End Function
Friend Shared Sub Write(bw As IO.BinaryWriter, val As String)
If val.Length = 0 Then
bw.Write(0I)
Else
bw.Write(val.Length + 1)
bw.Write(val.ToCharArray)
bw.Write(nullChar)
End If
End Sub
End Class 'LengthPrefixedAnsiString
End Class
Example usage:
' To obtain a Byte() to store a Bitmap to MS Access
Dim bm As Bitmap = CType(Image.FromFile("SomeBitmapFile.bmp"), Bitmap)
Dim bytesToStoreInAccess As Byte() = AccessOLEBitmapConverter.ToByteArray(bm)
' To retrieve a Bitmap from an Access Bitmap Image field.
Dim bytesFromBitmapImageField As Byte() ' set this equal to the field data
Dim bmRetrieved As Bitmap = AccessOLEBitmapConverter.ToBitmap(bytesFromBitmapImageField)
This code has been successfully tested using MS Access 2007 on a Windows 10 machine to store a Bitmap to an Access OLE field to yield a MS Access Bitmap Image that can be shown in an Access report.
I'm basically writing a custom Error Logging Form for one of my applications because users cannot be trusted to report the errors to me.
I am obtaining the Form Name using the 'MethodBase' Object and then getting the DeclaringType Name.
Dim st As StackTrace = New StackTrace()
Dim sf As StackFrame = st.GetFrame(1)
Dim mb As MethodBase = sf.GetMethod()
Dim dt As String = mb.DeclaringType.Name
How can I then use this to obtain the Form Object so I can pass this to my 'screenshot method' that screenshots the particular form referenced.
Public Sub SaveAsImage(frm As Form)
'Dim fileName As String = "sth.png"
'define fileName
Dim format As ImageFormat = ImageFormat.Png
Dim image = New Bitmap(frm.Width, frm.Height)
Using g As Graphics = Graphics.FromImage(image)
g.CopyFromScreen(frm.Location, New Point(0, 0), frm.Size)
End Using
image.Save(_LogPath & Date.Now.ToString("ddMMyyyy") & ".png", format)
End Sub
I posted the same solution to a similar question. Try this:
Dim frm = Application.OpenForms.Item(dt)
I am trying to do following things in Windows Forms
1) Read a PDF in Windows Forms
2) Get the Images with Text in it
3) Color / fill the Image
4) save everything to a new file
I have tried Problem with PdfTextExtractor in itext!
But It didn't help.
Here is the code I've tried:
Public Shared Sub ExtractImagesFromPDF(sourcePdf As String, outputPath As String)
'NOTE: This will only get the first image it finds per page.'
Dim pdf As New PdfReader(sourcePdf)
Dim raf As RandomAccessFileOrArray = New iTextSharp.text.pdf.RandomAccessFileOrArray(sourcePdf)
Try
For pageNumber As Integer = 1 To pdf.NumberOfPages
Dim pg As PdfDictionary = pdf.GetPageN(pageNumber)
' recursively search pages, forms and groups for images.'
Dim obj As PdfObject = FindImageInPDFDictionary(pg)
If obj IsNot Nothing Then
Dim XrefIndex As Integer = Convert.ToInt32(DirectCast(obj, PRIndirectReference).Number.ToString(System.Globalization.CultureInfo.InvariantCulture))
Dim pdfObj As PdfObject = pdf.GetPdfObject(XrefIndex)
Dim pdfStrem As PdfStream = DirectCast(pdfObj, PdfStream)
Dim bytes As Byte() = PdfReader.GetStreamBytesRaw(DirectCast(pdfStrem, PRStream))
If (bytes IsNot Nothing) Then
Using memStream As New System.IO.MemoryStream(bytes)
memStream.Position = 0
Dim img As System.Drawing.Image = System.Drawing.Image.FromStream(memStream)
' must save the file while stream is open.'
If Not Directory.Exists(outputPath) Then
Directory.CreateDirectory(outputPath)
End If
Dim path__1 As String = Path.Combine(outputPath, [String].Format("{0}.jpg", pageNumber))
Dim parms As New System.Drawing.Imaging.EncoderParameters(1)
parms.Param(0) = New System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Compression, 0)
'Dim jpegEncoder As System.Drawing.Imaging.ImageCodecInfo = iTextSharp.text.Utilities.GetImageEncoder("JPEG")'
img.Save(path__1) 'jpegEncoder, parms'
End Using
End If
End If
Next
Catch
Throw
Finally
pdf.Close()
raf.Close()
End Try
End Sub
Now, the actual purpose of this is to get something like this
If this is the actual PDF, I will have to check if there any any items in that bin(by Text in that box)
If there are items then I have to color it like below
Can someone help me with this
The PDF can be retrieved here.
I'm using this code to encrypt/decrypt files:
Public Shared Sub encryptordecryptfile(ByVal strinputfile As String, _
ByVal stroutputfile As String, _
ByVal bytkey() As Byte, _
ByVal bytiv() As Byte, _
ByVal direction As CryptoAction)
Try
fsInput = New System.IO.FileStream(strinputfile, FileMode.Open, FileAccess.Read)
fsOutput = New System.IO.FileStream(stroutputfile, FileMode.OpenOrCreate, FileAccess.Write)
fsOutput.SetLength(0)
Dim bytbuffer(4096) As Byte
Dim lngbytesprocessed As Long = 0
Dim lngfilelength As Long = fsInput.Length
Dim intbytesincurrentblock As Integer
Dim cscryptostream As CryptoStream
Dim csprijndael As New System.Security.Cryptography.RijndaelManaged
Select Case direction
Case CryptoAction.ActionEncrypt
cscryptostream = New CryptoStream(fsOutput, _
csprijndael.CreateEncryptor(bytkey, bytiv), _
CryptoStreamMode.Write)
Case CryptoAction.ActionDecrypt
cscryptostream = New CryptoStream(fsOutput, _
csprijndael.CreateDecryptor(bytkey, bytiv), _
CryptoStreamMode.Write)
End Select
While lngbytesprocessed < lngfilelength
intbytesincurrentblock = fsInput.Read(bytbuffer, 0, 4096)
cscryptostream.Write(bytbuffer, 0, intbytesincurrentblock)
lngbytesprocessed = lngbytesprocessed + CLng(intbytesincurrentblock)
End While
cscryptostream.Close()
fsInput.Close()
fsOutput.Close()
Catch ex As Exception
End Try
End Sub
Is I need to get the percentage of this process being done as an integer. I am going to use a background worker, so I need to call for this sub from the background worker and be able to keep refreshing a progress bar that the background worker reports to. Is this possible?
Thanks in advance.
There are a couple of things you can do to make your cryptor more efficient and other issues:
A method like encryptordecryptfile which then requires a "mode" argument to know which action to take means it really might be better off as 2 methods
The way you are going, you will be raising a blizzard of ProgressChanged events which the ProgressBar wont be able to keep up with given the animation. A 700K file will result in 170 or so progress reports of tiny amounts
Some of the crypto steps can be incorporated
You have a lot of things not being disposed of; you could run out of resources if you run a number of files thru it in a loop.
It might be worth noting that you can replace the entire While block with fsInput.CopyTo(cscryptostream) to process the file all at once. This doesnt allow progress reporting though. Its also not any faster.
Rather than a BackgroundWorker (which will work fine), you might want to implement it as a Task. The reason for this is that all those variables need to make their way from something like a button click to the DoWork event where your method is actually called. Rather than using global variables or a class to hold them, a Task works a bit more directly (but does involve one extra step when reporting progress). First, a revised EncryptFile method:
Private Sub EncryptFile(inFile As String,
outFile As String,
pass As String,
Optional reporter As ProgressReportDelegate = Nothing)
Const BLOCKSIZE = 4096
Dim percentDone As Integer = 0
Dim totalBytes As Int64 = 0
Dim buffSize As Int32
' Note A
Dim key = GetHashedBytes(pass)
Dim iv = GetRandomBytes(16)
Dim cryptor As ICryptoTransform
' Note B
Using fsIn As New FileStream(inFile, FileMode.Open, FileAccess.Read),
fsOut As New FileStream(outFile, FileMode.OpenOrCreate, FileAccess.Write)
fsOut.SetLength(0)
' Note C
'ToDo: work out optimal block size for Lg vs Sm files
If fsIn.Length > (2 * BLOCKSIZE) Then
' use buffer size to limit to 20 progress reports
buffSize = CInt(fsIn.Length \ 20)
' to multiple of 4096
buffSize = CInt(((buffSize + BLOCKSIZE - 1) / BLOCKSIZE) * BLOCKSIZE)
' optional, limit to some max size like 256k?
'buffSize = Math.Min(buffSize, BLOCK256K)
Else
buffSize = BLOCKSIZE
End If
Dim buffer(buffSize-1) As Byte
' Note D
' write the IV to "naked" fs
fsOut.Write(iv, 0, iv.Length)
Using rij = Rijndael.Create()
rij.Padding = PaddingMode.ISO10126
Try
cryptor = rij.CreateEncryptor(key, iv)
Using cs As New CryptoStream(fsOut, cryptor, CryptoStreamMode.Write)
Dim bytesRead As Int32
Do Until fsIn.Position = fsIn.Length
bytesRead = fsIn.Read(buffer, 0, buffSize)
cs.Write(buffer, 0, bytesRead)
If reporter IsNot Nothing Then
totalBytes += bytesRead
percentDone = CInt(Math.Floor((totalBytes / fsIn.Length) * 100))
reporter(percentDone)
End If
Loop
End Using
Catch crEx As CryptographicException
' ToDo: Set breakpoint and inspect message
Catch ex As Exception
' ToDo: Set breakpoint and inspect message
End Try
End Using
End Using
End Sub
Note A
One of the standard crypto tasks it could handle is creating the Key and IV arrays for you. These are pretty simple and could be shared/static members.
Public Shared Function GetHashedBytes(data As String) As Byte()
Dim hBytes As Byte()
' or SHA512Managed
Using hash As HashAlgorithm = New SHA256Managed()
' convert data to bytes:
Dim dBytes = Encoding.UTF8.GetBytes(data)
' hash the result:
hBytes = hash.ComputeHash(dBytes)
End Using
Return hBytes
End Function
Public Shared Function GetRandomBytes(size As Integer) As Byte()
Dim data(size - 1) As Byte
Using rng As New RNGCryptoServiceProvider
' fill the array
rng.GetBytes(data)
End Using
Return data
End Function
As will be seen later, you can store the IV in the encrypted file rather than saving and managing it in code.
Note B
Using blocks close and dispose of resources for you. Basically, if something has a Dispose method, then you should wrap it in a Using block.
Note C
You dont want to report progress for every block read, that will just overwhelm the ProgressBar. Rather than another variable to keep track of when the progress has changed by some amount, this code starts by creating a buffer size which is 5% of the input file size so there will be about 20 reports (every 5%).
As the comments indicate, you may want to add some code to set minimum/maximum buffer size. Doing so would change the progress report frequency.
Note D
You can write the IV() to the filestream before you wrap it in the CryptoStream (and of course read it back first when Decrypting). This prevents you from having to store the IV.
The last part is kicking this off as a Task:
Dim t As Task
t = Task.Run(Sub() EncryptFile(inFile, oFile, "MyWeakPassword",
AddressOf ReportProgress))
...
What a BGW does is execute the work on one thread, but progress is reported on the UI thread. As a Task, all we need to do is use Invoke:
Delegate Sub ProgressReportDelegate(value As Int32)
Private Sub ReportProgress(v As Int32)
If progBar.InvokeRequired Then
progBar.Invoke(Sub() progBar.Value = v)
Else
progBar.Value = v
progBar.Invalidate()
End If
End Sub
The Encryptor will work either directly or as a Task. For small files, you can omit the progress report entirely:
' small file, no progress report:
EncryptFile(ifile, oFile, "MyWeakPassword")
' report progress, but run on UI thread
EncryptFile(ifile, oFile, "MyWeakPassword",
AddressOf ReportProgress)
' run as task
Dim t As Task
t = Task.Run(Sub() EncryptFile(ifile, oFile, "MyWeakPassword",
AddressOf ReportProgress))
...and if you had a list of files to do, you could run them all at once and perhaps report total progress.
I'm aware of the other thread on this issue (AES decryption error " The input data is not a complete block." Error vb.net), but I'm either not implementing the solutions offered there correctly, or something about my particular variant of this issue isn't covered by those solutions. In any event I'm getting the incomplete block error from the following code
Private GD As System.Security.Cryptography.Aes = System.Security.Cryptography.Aes.Create
Private PDB As New System.Security.Cryptography.Rfc2898DeriveBytes(EK, New Byte() {&H49, &H76, &H61, &H6E, &H20, &H4D, &H65, &H64, &H76, &H65, &H64, &H65, &H76})
Public Function Decrypt(ByVal val As String) As String
Dim ret As String = Nothing
Dim TTB As New System.Text.UTF8Encoding
Try
Dim input() As Byte = TTB.GetBytes(val)
Using ms As New System.IO.MemoryStream(input)
Using cs As New System.Security.Cryptography.CryptoStream(ms, GD.CreateDecryptor(PDB.GetBytes(32), PDB.GetBytes(16)), Security.Cryptography.CryptoStreamMode.Read)
Using sr As New System.IO.StreamReader(cs)
ret = sr.ReadToEnd()
End Using
End Using
End Using
input = nothing
Catch ex As Exception
EL.AddErr("Encountered an error while decrypting the provided text for " & FName & ". Error Details: " & ex.Message, path)
End Try
Return ret
End Function
EK is my key, which I'll not be including. It's just a String though, nothing special.
I've tried several other methods to decrypt based on guidance on the MSDN site, DreamInCode, etc. None worked, but they all had different issues (typically returning a blank string). Seeing as this version of code closely mirrors my encryption code, I'd like to stick with it (or at least as close as I can while still having functional code).
Despite all comments, I still lack understanding of your intentions. Therefore, the sample code below may not provide what you exactly want, but at least should give an idea how to employ cryptographic functions. Particularly, the most notable difference from your approach is that the encryption key and initialization vector are computed once and for all messages, rather than reevaluated on each occasion, because the latter is prone to synchronization errors — such as when you reuse single crypto object to communicate with multiple parties, or when some messages get lost in transmission.
Public Shared Sub Test()
' Note: You should not actually hard-code any sensitive information in your source files, ever!
Dim sKeyPreimage As String = "MySuperPassword"
Dim oMyCrypto As New MyCrypto(sKeyPreimage)
Dim sPlaintext As String = "My super secret data"
Dim sEncrypted As String = oMyCrypto.EncryptText(sPlaintext)
Dim sDecrypted As String = oMyCrypto.DecryptText(sEncrypted)
Console.Out.WriteLine("Plaintext: {0}", sPlaintext) ' "My super secret data"
Console.Out.WriteLine("Encrypted: {0}", sEncrypted) ' "72062997872DC4B4D1BCBF48D5D30DF0D498B20630CAFA28D584CCC3030FC5F1"
Console.Out.WriteLine("Decrypted: {0}", sDecrypted) ' "My super secret data"
End Sub
Public Class MyCrypto
Private Shared TextEncoding As Text.Encoding = Text.Encoding.UTF8
Private CipherEngine As System.Security.Cryptography.SymmetricAlgorithm
' Note: Unlike in the question, same key and IV are reused for all messages.
Private CipherKey() As Byte
Private CipherIV() As Byte
Public Sub New(ByVal sKeyPreimage As String)
Dim abKeyPreimage() As Byte = TextEncoding.GetBytes(sKeyPreimage)
Dim abKeySalt() As Byte = TextEncoding.GetBytes("Ivan Medvedev")
Const KeyDerivationRounds As Integer = 1 << 12
Dim oKeyDerivationEngine As New System.Security.Cryptography.Rfc2898DeriveBytes(abKeyPreimage, abKeySalt, KeyDerivationRounds)
Me.CipherEngine = System.Security.Cryptography.Aes.Create()
Me.CipherEngine.Padding = Security.Cryptography.PaddingMode.PKCS7
Me.CipherKey = oKeyDerivationEngine.GetBytes(Me.CipherEngine.KeySize >> 3)
Me.CipherIV = oKeyDerivationEngine.GetBytes(Me.CipherEngine.BlockSize >> 3)
End Sub
Public Function Encrypt(ByVal abPlaintext() As Byte) As Byte()
Dim abCiphertext() As Byte
Using hStreamSource As New System.IO.MemoryStream(abPlaintext),
hStreamCipher As New System.Security.Cryptography.CryptoStream(
hStreamSource,
Me.CipherEngine.CreateEncryptor(Me.CipherKey, Me.CipherIV),
Security.Cryptography.CryptoStreamMode.Read),
hStreamTarget As New System.IO.MemoryStream
hStreamCipher.CopyTo(hStreamTarget)
abCiphertext = hStreamTarget.ToArray()
End Using
Return abCiphertext
End Function
Public Function Decrypt(ByVal abCiphertext() As Byte) As Byte()
Dim abPlaintext() As Byte
Using hStreamSource As New System.IO.MemoryStream(abCiphertext),
hStreamCipher As New System.Security.Cryptography.CryptoStream(
hStreamSource,
Me.CipherEngine.CreateDecryptor(Me.CipherKey, Me.CipherIV),
Security.Cryptography.CryptoStreamMode.Read),
hStreamTarget As New System.IO.MemoryStream
hStreamCipher.CopyTo(hStreamTarget)
abPlaintext = hStreamTarget.ToArray()
End Using
Return abPlaintext
End Function
Public Function EncryptText(ByVal sPlaintext As String) As String
Dim abPlaintext() As Byte = TextEncoding.GetBytes(sPlaintext)
Dim abCiphertext() As Byte = Me.Encrypt(abPlaintext)
Dim sCiphertext As String = Hex.Format(abCiphertext)
Return sCiphertext
End Function
Public Function DecryptText(ByVal sCiphertext As String) As String
Dim abCiphertext() As Byte = Hex.Parse(sCiphertext)
Dim abPlaintext() As Byte = Me.Decrypt(abCiphertext)
Dim sPlaintext As String = TextEncoding.GetChars(abPlaintext)
Return sPlaintext
End Function
End Class
Public Class Hex
Public Shared Function Format(ByVal abValue() As Byte) As String
Dim asChars(0 To abValue.Length * 2 - 1) As Char
Dim ndxChar As Integer = 0
For ndxByte As Integer = 0 To abValue.Length - 1
Dim bNibbleHi As Byte = abValue(ndxByte) >> 4, bNibbleLo As Byte = CByte(abValue(ndxByte) And &HFUS)
asChars(ndxChar) = Convert.ToChar(If(bNibbleHi <= 9, &H30US + bNibbleHi, &H37US + bNibbleHi)) : ndxChar += 1
asChars(ndxChar) = Convert.ToChar(If(bNibbleLo <= 9, &H30US + bNibbleLo, &H37US + bNibbleLo)) : ndxChar += 1
Next
Return New String(asChars)
End Function
Public Shared Function Parse(ByVal sValue As String) As Byte()
If String.IsNullOrEmpty(sValue) Then Return New Byte() {}
If (sValue.Length Mod 2) > 0 Then Return Nothing
Dim ndxText As Integer = 0
Dim ndxByteMax As Integer = (sValue.Length \ 2) - 1
Dim abValue(0 To ndxByteMax) As Byte
Try
For ndxByte As Integer = 0 To ndxByteMax
abValue(ndxByte) = Convert.ToByte(sValue.Substring(ndxText, 2), 16)
ndxText += 2
Next
Catch ex As Exception
Return Nothing
End Try
Return abValue
End Function
End Class
Again, please, note that this is just an example. I am not endorsing any kind of protection techniques shown here, especially because your task remains unknown. The code above simply illustrates the syntax and semantics — not how to do it right.