Access: Encriptar contraseñas con SHA-256 utilizando biblioteca de clases .NET con C#
Después de un tiempo centrado en las pruebas de rendimiento de Access, hago un inciso (sobre todo porque me lo pide el cuerpo) y voy a retomar en este artículo la interoperabilidad COM.
Es posible que si habéis seguido los anteriores artículos sobre interoperabilidad COM, tengáis la sensación de que hemos abordado en profundidad la manera de hacer compatibles las bibliotecas, pero no hemos visto su funcionalidad en «fuego real». Por ello, voy a volver sobre un artículo del pasado en el que explicaba como Encriptar contraseñas con SHA-2 de 256 bits utilizando VBA y voy a hacerlo después utilizando un biblioteca de clases .NET.
En el mencionado artículo, utilizaba un módulo de VBA para encriptar las contraseñas. Vamos a recordar el código de dicho módulo:
Attribute VB_Name = "SHA256" Option Compare Database '******************************************************************************************************************************************** '******************************************************************************************************************************************** '******************************************************************************************************************************************** 'Algoritmo para encriptar contraseñas con SHA2 - 256 bits Function SHA(ByVal sMessage) Dim i, result(32), temp(8) As Double, fraccubeprimes, hashValues Dim done512, index512, words(64) As Double, index32, mask(4) Dim s0, s1, t1, t2, maj, ch, strLen mask(0) = 4294967296# mask(1) = 16777216 mask(2) = 65536 mask(3) = 256 hashValues = Array( _ 1779033703, 3144134277#, 1013904242, 2773480762#, _ 1359893119, 2600822924#, 528734635, 1541459225) fraccubeprimes = Array( _ 1116352408, 1899447441, 3049323471#, 3921009573#, 961987163, 1508970993, 2453635748#, 2870763221#, _ 3624381080#, 310598401, 607225278, 1426881987, 1925078388, 2162078206#, 2614888103#, 3248222580#, _ 3835390401#, 4022224774#, 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, _ 2554220882#, 2821834349#, 2952996808#, 3210313671#, 3336571891#, 3584528711#, 113926993, 338241895, _ 666307205, 773529912, 1294757372, 1396182291, 1695183700, 1986661051, 2177026350#, 2456956037#, _ 2730485921#, 2820302411#, 3259730800#, 3345764771#, 3516065817#, 3600352804#, 4094571909#, 275423344, _ 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, 1537002063, 1747873779, _ 1955562222, 2024104815, 2227730452#, 2361852424#, 2428436474#, 2756734187#, 3204031479#, 3329325298#) sMessage = Nz(sMessage, "") strLen = Len(sMessage) * 8 sMessage = sMessage & Chr(128) done512 = False index512 = 0 If (Len(sMessage) Mod 64) < 60 Then sMessage = sMessage & String(60 - (Len(sMessage) Mod 64), Chr(0)) ElseIf (Len(sMessage) Mod 64) > 60 Then sMessage = sMessage & String(124 - (Len(sMessage) Mod 64), Chr(0)) End If sMessage = sMessage & Chr(Int((strLen / mask(0) - Int(strLen / mask(0))) * 256)) sMessage = sMessage & Chr(Int((strLen / mask(1) - Int(strLen / mask(1))) * 256)) sMessage = sMessage & Chr(Int((strLen / mask(2) - Int(strLen / mask(2))) * 256)) sMessage = sMessage & Chr(Int((strLen / mask(3) - Int(strLen / mask(3))) * 256)) Do Until done512 For i = 0 To 15 words(i) = Asc(Mid(sMessage, index512 * 64 + i * 4 + 1, 1)) * mask(1) + Asc(Mid(sMessage, index512 * 64 + i * 4 + 2, 1)) * mask(2) + Asc(Mid(sMessage, index512 * 64 + i * 4 + 3, 1)) * mask(3) + Asc(Mid(sMessage, index512 * 64 + i * 4 + 4, 1)) Next For i = 16 To 63 s0 = largeXor(largeXor(rightRotate(words(i - 15), 7, 32), rightRotate(words(i - 15), 18, 32), 32), Int(words(i - 15) / 8), 32) s1 = largeXor(largeXor(rightRotate(words(i - 2), 17, 32), rightRotate(words(i - 2), 19, 32), 32), Int(words(i - 2) / 1024), 32) words(i) = Mod32Bit(words(i - 16) + s0 + words(i - 7) + s1) Next For i = 0 To 7 temp(i) = hashValues(i) Next For i = 0 To 63 s0 = largeXor(largeXor(rightRotate(temp(0), 2, 32), rightRotate(temp(0), 13, 32), 32), rightRotate(temp(0), 22, 32), 32) maj = largeXor(largeXor(largeAnd(temp(0), temp(1), 32), largeAnd(temp(0), temp(2), 32), 32), largeAnd(temp(1), temp(2), 32), 32) t2 = Mod32Bit(s0 + maj) s1 = largeXor(largeXor(rightRotate(temp(4), 6, 32), rightRotate(temp(4), 11, 32), 32), rightRotate(temp(4), 25, 32), 32) ch = largeXor(largeAnd(temp(4), temp(5), 32), largeAnd(largeNot(temp(4), 32), temp(6), 32), 32) t1 = Mod32Bit(temp(7) + s1 + ch + fraccubeprimes(i) + words(i)) temp(7) = temp(6) temp(6) = temp(5) temp(5) = temp(4) temp(4) = Mod32Bit(temp(3) + t1) temp(3) = temp(2) temp(2) = temp(1) temp(1) = temp(0) temp(0) = Mod32Bit(t1 + t2) Next For i = 0 To 7 hashValues(i) = Mod32Bit(hashValues(i) + temp(i)) Next If (index512 + 1) * 64 >= Len(sMessage) Then done512 = True index512 = index512 + 1 Loop For i = 0 To 31 result(i) = Int((hashValues(i \ 4) / mask(i Mod 4) - Int(hashValues(i \ 4) / mask(i Mod 4))) * 256) Next SHA = result End Function Function Mod32Bit(value) Mod32Bit = Int((value / 4294967296# - Int(value / 4294967296#)) * 4294967296#) End Function Function rightRotate(value, amount, totalBits) 'To leftRotate, make amount = totalBits - amount Dim i rightRotate = 0 For i = 0 To (totalBits - 1) If i >= amount Then rightRotate = rightRotate + (Int((value / (2 ^ (i + 1)) - Int(value / (2 ^ (i + 1)))) * 2)) * 2 ^ (i - amount) Else rightRotate = rightRotate + (Int((value / (2 ^ (i + 1)) - Int(value / (2 ^ (i + 1)))) * 2)) * 2 ^ (totalBits - amount + i) End If Next End Function Function largeXor(value, xorValue, totalBits) Dim i, a, b largeXor = 0 For i = 0 To (totalBits - 1) a = (Int((value / (2 ^ (i + 1)) - Int(value / (2 ^ (i + 1)))) * 2)) b = (Int((xorValue / (2 ^ (i + 1)) - Int(xorValue / (2 ^ (i + 1)))) * 2)) If a <> b Then largeXor = largeXor + 2 ^ i End If Next End Function Function largeNot(value, totalBits) Dim i, a largeNot = 0 For i = 0 To (totalBits - 1) a = Int((value / (2 ^ (i + 1)) - Int(value / (2 ^ (i + 1)))) * 2) If a = 0 Then largeNot = largeNot + 2 ^ i End If Next End Function Function largeAnd(value, andValue, totalBits) Dim i, a, b largeAnd = 0 For i = 0 To (totalBits - 1) a = Int((value / (2 ^ (i + 1)) - Int(value / (2 ^ (i + 1)))) * 2) b = (Int((andValue / (2 ^ (i + 1)) - Int(andValue / (2 ^ (i + 1)))) * 2)) If a = 1 And b = 1 Then largeAnd = largeAnd + 2 ^ i End If Next End Function '******************************************************************************************************************************************** '******************************************************************************************************************************************** '******************************************************************************************************************************************** '********************************************************************************************************************************************
Como veis, es lo suficientemente complejo como para que un error nos haga perder mucho tiempo, además de que (aunque funciona correctamente) el encriptado se hace utilizando el algoritmo completo y no utilizando librerías de encriptado ya creadas. Este es la gran diferencia de VBA con la plataforma .NET, hay librerías muy potentes ya creadas y preparadas para que las utilicemos.
Ahora vamos a crear una librería sencillita con Visual Studio que haga lo mismo y que se pueda utilizar en Access. Apara ello, podemos utilizar uno de los métodos de nuestros anteriores artículos sobre interoperabilidad COM, lo dejo a vuestra elección. Yo simplemente voy a crear la librería y después la voy a añadir en las referencias de Access. La función que nos permitirá encriptar las contraseñas es la siguiente:
public string SHA256Encripta(string input) { SHA256CryptoServiceProvider provider = new SHA256CryptoServiceProvider(); byte[] inputBytes = Encoding.UTF8.GetBytes(input); byte[] hashedBytes = provider.ComputeHash(inputBytes); StringBuilder output = new StringBuilder(); for (int i = 0; i < hashedBytes.Length; i++) output.Append(hashedBytes[i].ToString("x2").ToLower()); return output.ToString(); }
Y tendremos que añadir la siguiente referencia a nuestro proyecto de Visual Studio:
System.Security.Cryptography;
Así que la librería quedará parecida a lo siguiente (dependiendo del método que utilicemos para la compatibilidad COM):
Ahora solo tendremos que añadir nuestra nueva librería a las referencias de nuestro proyecto Access. Repasad los artículos sobre interoperabilidad COM si tenéis alguna duda sobre esto.
Una vez añadida voy a mostrar una función sencillita a la que le pasaremos una cadena de texto con la contraseña y nos devolverá el hash de esa misma cadena creado con el algoritmo SHA-2 de 256 bits. Para los que no hayáis leído mis artículos sobre cómo encriptar contraseñas, una pequeña explicación sobre funciones Hash:
Se puede calcular una función Hash (también llamada Digest) de cualquier cadena de texto mediante distintos algoritmos. Una función hash es una especie de «resumen» de la cadena que se le pasa al algoritmo y siempre será la misma para una cadena dada. Lo interesante de estas funciones es que es un proceso irreversible, es decir, a partir de una función hash no se puede llegar a la cadena original (siempre que no consigan romper el algoritmo). Esto las hace muy interesantes para almacenar contraseñas ya que aunque consigan capturarla, no les sirve para acceder al sistema.
Si es irreversible, ¿cómo hacemos para comprobarla? Muy sencillo, simplemente guardamos en nuestra base de datos el hash de la contraseña y en el proceso de login hacemos que el usuario meta su contraseña. Calculamos el hash de la cadena que ha introducido el usuario y la comparamos con la que tenemos guardada. Con esto logramos no tener nunca las contraseñas almacenadas y además evitamos que viajen por la red (protegiéndonos de posibles sniffers).
De momento el algoritmo SHA-2 no ha sido atacado con éxito como otros conocidos algoritmos muy utilizados en el pasado (como MD5 y SHA1 ). Además, vamos a utilizar 256 bits para el encriptado, es decir, el hash tendrá 32 bytes.
Después de esta pequeña explicación, vamos a ver la diferencia de código entre la versión anterior y esta. Vamos con la función:
Function probarNET(ByVal contraseña As String) Dim Encriptador As New Seguridad.Encriptar encriptarNET = Encriptador.SHA256Encripta(contraseña) End Function
Y ya está, con esto tenemos una función que nos calcula el hash con el algoritmo SHA-2 de 256 bits. Vamos a comprobarlo utilizando el mismo ejemplo que en el artículo anterior. Utilizábamos SHA-2 de 256 bits con el resultado truncado a 64 bytes. La palabra pasada era «hola»:
-El hash era b221d9dbb083a7f33428d7c2a3c3198ae925614d70210e28716ccaa7cd4ddb79
Comprobamos ahora con nuestra nueva función pasando la cadena «hola»:
Ahora agregamos una inspección en la variable en donde guardamos el resultado y…
Vaya por dios! el resultado no es el mismo, el algoritmo no funciona correctamente. CUIDADO, le he pasado el valor «Hola» y no el valor «hola» del artículo anterior. Por supuesto, el hash es distinto. Vamos ahora a pasar el valor correcto:
Y probamos de nuevo:
Ahora si, el resultado es el correcto. Como veis, el código es mucho más sencillo y la posibilidad de cometer errores mucho menor.
Ahora si que podemos ver la verdadera funcionalidad de crear bibliotecas .NET para utilizarlas en Access. Con una buena cantidad de bibliotecas podremos llevar nuestros proyectos Access a niveles que hace unos años eran impensables y además lo haremos fácilmente y sin necesidad de desarrollar largos algoritmos.
Hay infinidad de bibliotecas .NET ya creadas que podremos utilizar simplemente añadiendo interoperabilidad a la biblioteca. La mayoría funciona sin problemas, aunque tenemos que tener en cuenta que hay cosas que varían un poco (como por ejemplo constructores con parámetros que no se pueden utilizar para interoperabilidad COM), pero eso ya lo iremos viendo.
Arkaitz Arteaga
Latest posts by Arkaitz Arteaga (see all)
- Access: Encriptar contraseñas con SHA-256 utilizando biblioteca de clases .NET con C# - 4 mayo, 2014
- Rendimiento de Access contra backend Access en servidor de archivos remoto. Cuarta parte. - 27 abril, 2014
- Rendimiento de Access contra backend Access en servidor de archivos remoto. Aclaración. - 21 abril, 2014
- Utilizar biblioteca de clases .NET en Access. Tercera aproximación a la Interoperabilidad COM - 14 abril, 2014
- Vincular tablas en Access con Visual Basic - 11 abril, 2014
- .NET | Aplicaciones | C# | FrontEnd | Interoperabilidad COM | Programación | Seguridad | Visual Basic | Visual Studio
2 Respuestas a Access: Encriptar contraseñas con SHA-256 utilizando biblioteca de clases .NET con C#
Deja una respuesta
Lo siento, debes estar conectado para publicar un comentario.
Soy aficionado a la programación, utilizo Access, VBA y NET .
En mi tiempo libre desarrollo solo por diversión y conocimiento.
Solo queria felicitarte por los aportes y el esfuerzo que desempeñas compartiendo tus conocimientos a disposición de los demas.
Muchas Gracias, todos los temas que expones son muy interesantes y utiles.
gracias buen aporte de mucha ayuda