Algoritmo de Resumen del Mensaje 5 (MD5) - Criptografía con Python

Introducción:

Hola amigos de Internet. Les doy la bienvenida a Mi Diario Python. El mejor blog para Aprender Python.

En el día de hoy, veremos el funcionamiento del algoritmo criptográfico MD5. Sí, ya lo se, ya hemos utilizados este algoritmo en alguna ocación. En el articulo Algoritmo Hash - Criptografía con Python y Hashlib vimos un ejemplo de este algoritmo utilizando el modulo Hashlib. 

Les explicare que es el algoritmo MD5 y como funciona. Al final, realizaremos una impleemntación del algoritmo en el lenguaje de programación Python.

¿Qué es MD5?

Imagen relacionada

MD5 es la abreviación en ingles de "Algoritmo de Resumen de Mensaje 5". Es un algoritmo de codificación de 128 bits que genera un hash hexodecimal de 32 caracteres, sin importar la longitud de la entrada, por lo que es una salida de longitud fija.

"Los hash son algoritmos que consiguen crear a partir de una entrada de datos, una salida alfanumérica de longitud normalmente fija que representa un resumen de toda la información que se le ha dado. Más sobre los hash".


Lo que hace interesante a este algoritmo es que es irreversible. Esto significa que es "imposible" encontrar la palabra original a partir de un MD5.

El algoritmo es utilizado como una función de codificación o huella digital de un archivo.
Sabemos que todos los hash MD5 tienen una longitud fija, la cuales son 32 caracteres hexodecimales. Algo que es muy importante de saber, es que al cambiar un pequeño detalle en la cadena de texto, cambiara todo el hash.

Algoritmo de resumen = 817819df56bf09ca39b7c3aa5fbf00b1

Algoritmo d resumen = 43ef0142044b2f8fbe112866262de979

En las ultimas dos lineas, les mostré un ejemplo. Al cambiar una sola letra (borrar la e) cambia todo el hash. 

Uso de MD5 en bases de datos

Amenudo, el algoritmo es empleado para codificar contraseñas en bases de datos. ¿Para que sirve esto? De esta manera la contraseña no se encuentra en texto plano, la persona que logre tener acceso a la base de datos podrá ver el MD5, pero sera incapaz de decodificarlo, incapaz de encontrar la contraseña original. Lo que se hace es que el usuario, al iniciar sesión e introducir la contraseña, esta sera procesado y sometida al algoritmo MD5, luego se comparara los dos MD5 (la entrada codificada y la contraseña codifica en la base de datos), si son exactamente iguales, se le dará acceso, de lo contrario no.

¿Es imposible descifrar un hash MD5?

Bueno, ya he mencionado que es "imposible" descifrar un hash MD5, pero soy uno de esos que le les gusta decir esta palabra. 

Hoy en día, existen muchas bases de datos con palabras y su MD5 equivalente. Y crean cunado les digo que hay muchas personas que invierten tiempo en realizar estas bases de datos. Claro, la probabilidad de que un "decodificador de MD5 Online" pueda descifrar, por ejemplo, en hash MD5 de tu contraseña, es muy baja.

Implementación del algoritmo

Que les parece si escribimos el algoritmo MD5 en Python. Lo que hará, sera cifrar cualquier cadena de texto que le pasemos. Sabes que puedes encontrar el código completo en: https://github.com/LuisAlejandroSalcedo/Algoritmo-de-Resumen-de-Mensaje-5-MD5-/tree/master.

# Algoritmo de Resumen de Mensaje 5 (MD5)

from __future__ import print_function
import math

def rearrange(bitString32):
 if len(bitString32) != 32:
  raise ValueError("Necesita una longitud de 32")
 newString = ""
 for i in [3,2,1,0]:
  newString += bitString32[8*i:8*i+8]
 return newString

def reformatHex(i):
 hexrep = format(i,'08x')
 thing = ""
 for i in [3,2,1,0]:
  thing += hexrep[2*i:2*i+2]
 return thing

def pad(bitString):
 startLength = len(bitString)
 bitString += '1'
 while len(bitString) % 512 != 448:
  bitString += '0'
 lastPart = format(startLength,'064b')
 bitString += rearrange(lastPart[32:]) + rearrange(lastPart[:32]) 
 return bitString

def getBlock(bitString):
 currPos = 0
 while currPos < len(bitString):
  currPart = bitString[currPos:currPos+512]
  mySplits = []
  for i in range(16):
   mySplits.append(int(rearrange(currPart[32*i:32*i+32]),2))
  yield mySplits
  currPos += 512
  
def not32(i):
 i_str = format(i,'032b')
 new_str = ''
 for c in i_str:
  new_str += '1' if c=='0' else '0'
 return int(new_str,2)

def sum32(a,b):
 return (a + b) % 2**32

def leftrot32(i,s):
 return (i << s) ^ (i >> (32-s))

def md5me(testString):
 bs =''
 for i in testString:
  bs += format(ord(i),'08b')
 bs = pad(bs)

 tvals = [int(2**32 * abs(math.sin(i+1))) for i in range(64)]

 a0 = 0x67452301
 b0 = 0xefcdab89
 c0 = 0x98badcfe
 d0 = 0x10325476

 s = [7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22, 
  5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20, 
  4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23, 
  6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21 ]

 for m in getBlock(bs):
  A = a0 
  B = b0
  C = c0
  D = d0
  for i in range(64):
   if i <= 15:
    #f = (B & C) | (not32(B) & D)
    f = D ^ (B & (C ^ D))
    g = i
   elif i<= 31:
    #f = (D & B) | (not32(D) & C)
    f = C ^ (D & (B ^ C))
    g = (5*i+1) % 16
   elif i <= 47:
    f = B ^ C ^ D
    g = (3*i+5) % 16
   else:
    f = C ^ (B | not32(D))
    g = (7*i) % 16
   dtemp = D
   D = C
   C = B
   B = sum32(B,leftrot32((A + f + tvals[i] + m[g]) % 2**32, s[i]))
   A = dtemp
  a0 = sum32(a0, A)
  b0 = sum32(b0, B)
  c0 = sum32(c0, C)
  d0 = sum32(d0, D)

 digest = reformatHex(a0) + reformatHex(b0) + reformatHex(c0) + reformatHex(d0)
 return digest

def test():
    mensaje = "mypassword"
    hash_md5 = md5me(mensaje)
    print('"%s" = %s' % (mensaje, hash_md5))

if __name__ == "__main__":
 test()

"mypassword" = 34819d7beeabb9260a5c854bc85b3e44

Este seria mi resultado para "mypassword". El algoritmo esta terminado y funciona exitosamente.

¿Que te parece? Interesante ¿verdad?.

¿Alguna duda? Entonces no dudes en dejar tu comentario.

Mi nombre es Luis, y fue un placer compartir mis conocimientos con todos ustedes :D.
  1. edgardo001 dice:

    Gracias por el codigo, muy bien explicado.

    Te comento que MD5 fue reemplazado por SHA1, SHA1 fue reemplazado por la rama SHA2 aproximadamente el 2014 y actualmente se esta estudiando el reemplazo de SHA2 por SHA3, el algoritmo usado para SHA3 esta siendo estudiado por el NIST, en este estudio se verifica el tiempo de calculo necesario para romper cada clave.

    Te dejo un link:
    https://blog.segu-info.com.ar/2017/06/hashing-sha-1-sha-2-sha-3-keccak-cual.html

  2. Luis Salcedo dice:

    Hola, muchas gracias por su aporte. Saludos.

  3. Carlos dice:

    Y en el programa te saltaste las rondas??? Son 16 operaciones por ronda. Hay un error tremendo acá.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir
White Monkey