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