He tenido un momento para mirar el código...
...pero agárrate que el mensaje va para largo, hay mucho que comentar.
El error que tiene tu código está antes incluso d elo que emncionas, concretamente en la función:
Public Shared Function RijndaelDecrypt(ByVal UDecryptU As String, ByVal UKeyU As String) ' Desencryptamos
Dim XoAesProviderX As New System.Security.Cryptography.RijndaelManaged
Dim XbtCipherX() As Byte
Dim XbtSaltX() As Byte = New Byte() {1, 2, 3, 4, 5, 6, 7, 8}
Dim XoKeyGeneratorX As New System.Security.Cryptography.Rfc2898DeriveBytes(UKeyU, XbtSaltX)
XoAesProviderX.Key = XoKeyGeneratorX.GetBytes(XoAesProviderX.Key.Length)
XoAesProviderX.IV = XoKeyGeneratorX.GetBytes(XoAesProviderX.IV.Length)
Dim XmsX As New IO.MemoryStream
Dim XcsX As New System.Security.Cryptography.CryptoStream(XmsX, XoAesProviderX.CreateDecryptor(), _
System.Security.Cryptography.CryptoStreamMode.Write)
Try
XbtCipherX = Convert.FromBase64String(UDecryptU)
XcsX.Write(XbtCipherX, 0, XbtCipherX.Length)
XcsX.Close() ' <<<<<=========== AQUI, EL ERROR
UDecryptU = System.Text.Encoding.UTF8.GetString(XmsX.ToArray)
Catch ' <<<<< ======= AQUÍ DEJAS QUE EL ERROR SE PROPAGUE
End Try
Return UDecryptU ' <<<<< =========== AQUÍ DEVUELVES UN VALOR FALSO SI HUBO UN FALLO.
End Function
Y más concretamente en la parte final, en el bloque Try...catch
El punto exacto es en la línea:
XcsX.Close()Genera un error y salta al bloque 'catch', pero como no 'cazas' nada, entonces sigue la ejecución, ahora devuelves 'UDecryptU' con el valor que tenía a la entrada...
finalmente el código encuentra otro error al llegar a la línea que no señalas (ponía el número de línea, pero claro ese número corresponde a la posición que ocupa en tu editor, luego es información desfasada):
Dim Buffer As Byte() = New Byte(msgLength - 1) {}El caso es que el error ni siqueira procede de dicha entrada (sigue siendo un texto, aunque NO CONTIENE el contenido que toca), el error (que aquí aparece) es que msglenght tiene un valor negativo, producto de usar el tipo Integer en vez de un UInteger.
Es decir esta línea devuelve un valor negativo para mslenght:
Dim msgLength As Integer = BitConverter.ToInt32(GZipBuffer, 0)Esta línea toma los 4 primeros bytes del array y los convierte a un entero con signo de 32 bits, luego es perfectamente normal que arroje un valor negativo... ya que un entero de 4 bytes, apenas el byte más alto sea un valor superior a 127, tormará como un negativo). Valor que luego usas para crear un array de dicho tamaño... como no se pueden crear arrays de cantidad negativa, te salta el error que logras ver (pero que ni es el único, ni empeiza ahí)...
Me temo que quizás quiseras hacer:
Dim msgLength As Integer = GZipBuffer.LengthEs decir tomar el tamaño del array...
En el bloque Try, como mínimo añade código para la captura de error, si no mejor omitirlo...
Un simple ejemplo del añadido preciso.
Try
btData = Convert.FromBase64String(UDecryptU)
cs.Write(btData, 0, btData.Length)
cs.Close()
'UDecryptU = Encoding.UTF8.GetString(ms.ToArray)
'Return UDecryptU
Return Encoding.UTF8.GetString(ms.ToArray)
Catch ex As Exception
MessageBox.Show(ex.Message)
Return ""
End Try
...y como (ahora) en la devolución (si se da un error) la cadena puede estar vacía en la siguiente función precisas un 'if'... La función pués quedaría así...
Public Sub InicialLoader()
Dim ExtractApp As String = "Ca.dll"
Dim FileCoD As String = RijndaelDecrypt(FileCompressC, MasterKey) ' Desencrytamos
If FileCoD.Length > 0 Then
Dim DesFile As String = DecompressData(FileCoD) ' Descomprimimos el String Hex
File.
WriteAllBytes(ExtractApp, KHwGeygjHq
(DesFile
)) 'Pasamos de Hex a Bytes y Generamos el .exe End If
End Sub
Seguimos... el errror en el CriptoStream.close, se produce porque los datos tienen que estar 'alineados', esto es si quieres cifrar 10 bytes, debes añadir algunos bytes de padding necesarios para que tenga el mismo tamaño, que son los mismos que devueltos... el padding suele ser molesto porque a la vuelta (al decodificar), hay que tenerlo en cuenta e ignorar cuando sea el caso (en Base64 también hay padding, pero lo maneja intenamente la librería).
Pero vamos, tu pon el código en 'Catch' (como mínimo lo que te he puesto), para que te señale el error y se pare justo en el primer punto donde se da un error.
También veo que haces algo demasiado complejo para simplemente tomar un ejecutable y convertirlo a un fichero de texto plano, completamente legible aunque incomprensible...
Un comentario más aún, es que el código es ... bueno me callo calificativos, la intención no es que molestar... pero si resulta engorroso, embrollado, no queda adecuadamente separadas las funciones y hay cosas demasiado pesadas que pueden simplificarse...
Por ejemplo la función para convertir a hexadecimal, es muy rebuscada, además adolece a la entrada de duplicar su tamaño, culpa de la línea (en la función previa):
Dim Buffer As Byte() = System.Text.Encoding.
Unicode.GetBytes(Text)
...pués te va a intropducir un byte 0 por cada otro byte existente, luego además malogra la compresión que luego pretende llevarse a cabo.
Otra aberración, es tomar un array de bytes, convertirlo a texto hexadecimal, y luego otra vez convertirlo a un array de bytes... mareas mucho los datos.
He rescrito la function BytesToHex (además la he rebautizado) a algo más explícito, y he añadido una sobrecarga para, que partiendo de un array de bytes, obtener de él el array de bytes hexadecimales... Además acelera el cálculo, porque al crear la instancia se crean sendas tablas, luego basta usar la tabla para la conversión (y se evitan conversiones varias).... Las tablas se crean con el constructor de clase. Si te quedas solo con una, comenta la otra y el código correspondiente en el constructor.
Añado las dos sobrecargas y debajo tu función '
ConvertToHex', para que compares...
Para manejar más cómodamente cada parte, yo he metido el código de sendas partes en sendas clases, manejadas por sendos botones, y todo dentro del código de un formulario (desconozco si tienes algo de interfaz, pero para pruebas y elegir ficheros o rutas suele ser útil, aunque luego al compilar lo descartes y se quede en una dll, por ejemplo).
Imports System.IO
Imports System.Security.Cryptography
Imports System.Text
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim c As New Crypt
c.Inicio()
End Sub
Public Class Crypt
Private Shared StrHex(0 To 255) As String ' Tabla para conversión a string
Private Shared ChrHex(0 To 15) As Byte ' Tabla para conversión a array de bytes.
''' <summary>
''' Se ejecuta automáticamente la 1º vez que se crea una instancia.
''' Crea la tabla de conversión hexadecimal.
''' </summary>
''' <remarks>No aparece en el examinador de objetos.</remarks>
Shared Sub New()
Dim k As Byte, j As Byte, n As Short, h As String
For j = 0 To 15
h = Conversion.Hex(j)
For k = 0 To 15
StrHex(n) = (h & Conversion.Hex(k))
n += 1
Next
Next
' 0,1,2,3,4,5,6,7,8,9
For j = 0 To 9
ChrHex(j) = (48 + j) ' 48-57
Next
' A,B,C,D,E,F
For j = 10 To 15
ChrHex(j) = (55 + j) ' 65-70
Next
End Sub
' Esta sobrecarga evita convertir un array de bytes a String-Hex, para luego volverla a convertir de nuevo en un array de bytes.
''' <summary>
''' Codifica un array de bytes a formato hexadecimal.
''' </summary>
''' <param name="Input">El array de bytes.</param>
''' <param name="ForzarSobrecarga">No importa el valor. Su único propósito es diferenciar para admitir la sobrecarga.</param>
''' <returns>Un array de bytes de valores hexadecimales.</returns>
''' <remarks>El array de salida es el doble del tamaño del array de entrada</remarks>
Public Shared Function ConvertToHex(ByVal Input As Byte(), ByVal ForzarSobrecarga As Boolean) As Byte()
Dim Hex(0 To (Input.Length * 2) - 1) As Byte
Dim n As Integer
For Each b As Byte In Input
Hex(n) = ChrHex(b \ 16)
Hex(n + 1) = ChrHex(b And 15)
n += 2
Next
Return Hex
End Function
''' <summary>
''' Codifica un array de bytes a formato hexadecimal.
''' </summary>
''' <param name="Input">El array de bytes.</param>
''' <param name="ForzarSobrecarga">No importa el valor. Su único propósito es diferenciar para admitir la sobrecarga.</param>
''' <returns>Una cadena de caracteres hexadecimales.</returns>
''' <remarks>Cada byte se codifica con 2 caracteres</remarks>
Public Shared Function ConvertToHex(ByVal Input As Byte(), ByRef ForzarSobrecarga As String) As String
Dim Hex As String = Strings.Space(Input.Length * 2)
Dim n As Integer = 1
For k As Integer = 0 To Input.Length - 1
Mid(Hex, n) = StrHex(Input(k))
n += 2
Next
Return Hex
End Function
' Tu función...
Public Shared Function ConvertToHex(ByVal Input() As Byte) As String 'Funcion para convertir los bytes en hex
Dim Result As New System.Text.StringBuilder(Input.Length * 2)
Dim Part As String
For Each b As Byte In Input
Part = Conversion.Hex(b)
If Part.Length = 1 Then Part = "0" & Part
Result.Append(Part)
Next
Return Result.ToString()
End Function
end Class
End Class
Ahora para aceptar los cambios (y poder usar esas funciones), se debe modificar la función que invoca a Convert y la función Compress
' 'Dim hexBytes As String = Encoding.ASCII.GetString(Encoding.Convert(UTF8Encoding, filebytes,
Public Shared Function ConverexetohexandCompress(ByVal ruta As String, ByVal PasswordtoCrypt As String) As String
Dim filebytes
() As Byte = IO.
File.
ReadAllBytes(ruta
) Dim buffer() As Byte
buffer = ConvertToHex(filebytes, True)
' la otra sobrecarga...
'buffer = Encoding.Unicode.GetBytes(ConvertToHex(filebytes, ""))
' tu función
'buffer = Encoding.Unicode.GetBytes(ConvertToHex(filebytes))
Dim hexCompress As String = CompressedData(buffer) ' Pasamos los bytes a Hex y Comprimimos.
Return Rijndaelcrypt(hexCompress, PasswordtoCrypt) 'Encryptamos (Rijndael)
End Function
' Antes
Public Shared Function CompressedData(ByVal Text As string) As String
Dim Buffer As Byte() = System.Text.Encoding.Unicode.GetBytes(Text)
' ...
End Function
' Ahora
Public Shared Function CompressedData(ByVal Buffer() As Byte) As String
'Dim Buffer As Byte() = System.Text.Encoding.Unicode.GetBytes(Text)
' ...
End Function
Incluso aunque usaras la otra sobrecarga o tu función no uses el
encoding Unicode, ya que duplica la cantidad de caracteres (en todo caso el ASCII) y el efecto de la compresión será menor...
Para terminar como decía, más arriba creo que es demasiado laborioso para simplemente ocultar un ejecutable en texto plano... conversiones a hexadecimal, luego a base64, cifrar con rijndael, comprimir... ni que estuvieras escondiendo el tesoro del tío Gilito.
En definitiva tienes cuando menos un par de errores y mucha morralla de código. ...y eso que no lo he revisado todo al completo...
Yo veo más simple y igualmente complejo de descifrar, usar un cifrado XOR, y finalmente si lo quieres en texto plano, una conversión a base64 y listo.
Me he puesto con ello y en poco más de 2 horas (mediando cena incluído), está funcional y operativo (y al menos en las prueas libre de errores)
La función 'AmpliarClave' toma una clave de pocos caracteres y la convierte en una clave del tamaño que se quiera... bastante aleatoria, que ahora podrá usarse para un simple cifrado con XOR, pero inexpugnable, la solidez será la de la propia clave. Debe asegurarse como mínimo que no sean todas las letras iguales: "FFFFFFFFF", generaría una clave del largo pedido pero con el mismo carácter.
Puede añadirse una fase 4, donde ahora a los bytes pares se les suma 1 (si llega a 255, pasa a valer 0) y a los bytes impares se les resta 1 (si llega a 0, vale 255), algo así rompe incluso dicha posibilidad.
Esta función se puede complicar todo lo que se quiera, su contenido es una muestra.
Ahora la función 'CifradoXor' para cifrar con XOR... ojo, no se toma la clave, si no la clave 'ampliada', que genera un tocho pseudoaleatorio y mas que apto, para cifrar. En el ejemplo se ha puesto para que se amplíe hasta 1Mb. en la práctica
Finalmente solo faltarían las funciones que invocan a esas... en el ejemplo, la general que realiza ambas funciones. Si es para uso propio queda así más cómodo, en cambio si va a ser para que la usen terceros es preferible desmontarlo en dos funciones, 'Codificar' y 'Decodificar'
Imports System.IO
Imports System.Text
Public Class Coder
Public Enum OpCifrado
CIFRADO_OP_ON = 0
CIFRADO_OP_OFF = 1
End Enum
Public Function Generar(ByRef RutaIn As String, ByRef RutaOut As String, ByRef Clave As String, ByVal Op As OpCifrado) As Boolean
Dim fi As IO.FileInfo
Dim key() As Byte
Dim Datos() As Byte
Dim largo As Integer = 1048576 ' 1Mb.
Dim n As Integer
fi = New IO.FileInfo(RutaIn)
If (fi.Exists = True) Then
fi = New IO.FileInfo(RutaOut)
If (fi.Exists = False) Then
key = Encoding.ASCII.GetBytes(Clave)
n = AmpliarClave(key, largo)
'IO.File.WriteAllBytes(Application.StartupPath & "\key-" & Op.ToString & ".bin", key)
' ambas operaciones comparten código antes y después, aquí lo que varía, el ordne inverso de operaciones.
If (Op = OpCifrado.CIFRADO_OP_ON) Then
Datos
= IO.
File.
ReadAllBytes(RutaIn
) Me.CifradoXor(key, Datos, largo)
IO.
File.
WriteAllText(RutaOut, Convert.
ToBase64String(Datos
)) Else
Datos
= Convert.
FromBase64String(IO.
File.
ReadAllText(RutaIn
)) Me.CifradoXor(key, Datos, largo)
IO.
File.
WriteAllBytes(RutaOut, Datos
) End If
Array.Clear(key, 0, key.Length)
Array.Clear(Datos, 0, Datos.Length)
Return True
Else
MessageBox.Show("El fichero de salida ya existe. el programa no sobrescribe ficheros por seguridad...", "operación cancelada")
Return False
End If
Else
MessageBox.Show("El fichero a procesar no existe o no se encuentra en la ruta recibida...", "operación cancelada")
Return False
End If
End Function
Private Function AmpliarClave(ByRef Key() As Byte, ByVal Largo As Integer) As Integer
Dim salida() As Byte
Dim temp(0 To 10) As Byte
Dim k As Integer, j As Integer, n As Integer
Do ' en cada ciclo, el array aumenta al doble de tamaño.
ReDim salida(0 To (Key.Length * 2) - 1)
' Fase1: Ampliación: intercala entre cada dos bytes otro cuyo valor es la media de los dos que el rodean.
' Antes: cocacola Ahora: ciocbaciolfa
n = 0
For k = 0 To Key.Length - 2
salida(n) = Key(k)
salida(n + 1) = (Key(k) + Key(k + 1)) \ 2
n += 2
Next
salida(n) = Key(k) ' el último byte... en sendos arrays.
' Fase2: Intercambios intercambia el orden cada 7 bytes (entre ellos, y solo 1 conserva su posición)
' Antes: ABCDEFG, ahora: FBEAGDC
For k = 0 To salida.Length - 8 Step 7
n = salida(k) : salida(k) = salida(k + 5) : salida(k + 5) = salida(k + 3) : salida(k + 3) = n ' intercambiar 3 entre ellos
n = salida(k + 2) : salida(k + 2) = salida(k + 4) : salida(k + 4) = salida(k + 6) : salida(k + 6) = n ' intercambiar los otros 3 entre ellos
Next
' Fase3: Invertir secciones: invertir el orden entre cierta cantidad de bytes (conviene que sea un valor primo (no gigante)).
For k = 0 To salida.Length - 12 Step 11
Array.Copy(salida, k, temp, 0, 11)
n = 0
For j = k + 10 To k Step -1
salida(j) = temp(n)
n += 1
Next
Next
Key = salida
Loop While (Key.Length < Largo)
Return Key.Length
End Function
Private Sub CifradoXor(ByRef Key() As Byte, ByRef Datos() As Byte, ByVal Largo As Integer)
Dim j As Integer, k As Integer
If (Datos.Length < Largo) Then Largo = Datos.Length
For j = 0 To Datos.Length - 1 Step Largo
For k = 0 To Largo - 1
Datos(j + k) = (Datos(j + k) Xor Key(k))
Next
Next
End Sub
End Class
Quitando las líneas de comentarios, son apenas 100 líneas de código para hacer lo que quieres, ambas partes y lo mejor, funciona bien... aunque quizás haya algún gazapo, pués lo he escrito 'al vuelo' y no lo he probado a fondo, solo con 3 ficheros y ya...
También adjunto debajo el código de llamada, para mostrar su uso. Supuesto un botón...
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Dim b As New Coder
RutaIn = Application.StartupPath & "\CFO32L.dll"
RutaOut = Application.StartupPath & "\coded.txt"
If (b.Generar(RutaIn, RutaOut, "TontoElQueLoLea", Coder.OpCifrado.CIFRADO_OP_ON) = True) Then
RutaIn = RutaOut
RutaOut = Application.StartupPath & "\decoded.data"
b.Generar(RutaIn, RutaOut, "TontoElQueLoLea", Coder.OpCifrado.CIFRADO_OP_OFF)
End If
End Sub
Si al final se calcula el hash al fichero de origen (para el ejemplo yo he puesto un fichero dll que tenía a mano) y el 'decoded.data', si no hubo problemas debe ser el mismo... yo lo he probando y me funciona bien, y solo he perdido poco más o menos que 2 horas. El código es mucho más escueto, sencillo y consigue el mismo resultado. La fuerza de todo estará en tu "TontoElQueLoLea", que es la clave de cifrado... no precisa tanto rodeo.
El fichero de entrada ocupa algo así como 145kb. el 'coded.txt' 194kb. y si lo comprimes (por ejemplo con winrar), sale por 72kb.
OJO: Puestos a comprimir, es preferible que sea la primera operación, pués los ejecutables se prestan mejor a una compresión que un tocho codificado en base64 (que siempre aumenta de tamaño). Así si comprimo la dll original con winrar antes de nada se queda en 33kb.
p.d.: editado para añadir la línea (comentada) siguiente.
IO.File.WriteAllBytes(Application.StartupPath & "\key-" & Op.ToString & ".bin", key)
Guarda a fichero la clave generada , para poder realizar luego un estudio de la aleatoriedad de la 'clave ampliada'... Al ejecutar el programa se crearían pués dos ficheros 'crypt/decrypt', que evidentemente serán idénticos.