Wyliczanie kryptograficznych sum kontrolnych (HMAC) plików i łańcuchów znakowych

Kody HMAC (Hashing Message Authentication Codes) mogą być traktowane jako kryptograficzne sumy kontrolne. W tego rodzaju zastosowaniu pełnią rolę elementu skutecznego mechanizmu kontroli integralności danych, w tym w szczególności plików i rekordów baz danych. Mogą być także wykorzystane do nieodwracalnego kodowania haseł użytkowników czy też innych danych, uwierzytelniając ich źródło pochodzenia, a więc tym samym potwierdzając autentyczność tych danych.

digital_fingerpint1Czym jest HMAC? Jest efektem działania funkcji kryptograficznej opisanej w dokumencie RFC 2104HMAC: Keyed-Hashing for Message Authentication” . W dużym uproszczeniu rzecz ujmując, HMAC jest swego rodzaju „odciskiem palca” czyli skrótem wyliczonym na podstawie danych wejściowych oraz klucza uwierzytelniającego ich źródło pochodzenia. Wspomniana funkcja przyjmuje na wejściu dowolną ilość danych oraz dowolnej wielkości tajny klucz uwierzytelniający, a następnie w drodze jednokierunkowej transformacji przekształca je nieodwracalnie do stałej długości sumy kontrolnej zwanej potocznie haszem, czyli wspomnianego, unikalnego kryptograficznego „odcisku palca”. Wielkość sumy kontrolnej zależy od użytej funkcji skrótu i wynosi dla MD5 – 16 bajtów, SHA1 – 20 bajtów, RIPEMD – 20 bajtów, SHA256 – 32 bajty.

Przewaga HMAC nad „zwyczajną” funkcją skrótu jest ewidentna – HMAC wprowadza tajny klucz jako składnik hasza. Bez jego znajomości nie jest możliwe zafałszowanie „odcisku palca”. Z tego też powodu funkcje HMAC znajdują powszechne zastosowanie wszędzie tam, gdzie kontrola integralności danych połączona z wiarygodną weryfikacją ich źródła pochodzenia pełnią kluczową rolę. Przykładem niech będzie choćby kryptografia w SSL/TLS czy w VPN-ach IPSec.

Potencjalnych zastosowań HMAC jest bardzo wiele. Na przykład proces uwierzytelnień dostępu użytkowników do systemów, odbywający się na zasadzie porównania HMAC-a utworzonego z hasła użytkownika i HMAC-a przechowywanego w bazie poświadczeń tożsamości, z której korzysta aplikacja. Operator aplikacji nie musi w rezultacie takiego schematu procesu uwierzytelnień przechowywać haseł użytkowników w swoich bazach. Do uwierzytelnienia starczy sam HMAC hasła.

Inne pożyteczne zastosowanie HMAC, to kontrola integralności ważnych plików. Mechanizm jest bardzo prosty – wyliczamy sumy kontrolne wskazanych plików, a potem okresowo ponawiamy proces i dokonujemy porównania aktualnie wyliczonych haszy z haszami zapisanymi wcześniej w bazie. Nieidentyczność haszy jest tożsama ze stwierdzeniem zmian w treści pliku. Dalsza weryfikacja powinna wykazać, czy zmiany są uprawnione, czy też nie.  Na podobnej zasadzie działają komercyjne narzędzia typu Tripwire.

W tym artykule chciałbym zaprezentować skryptowe rozwiązania do wyliczania kodów HMAC zarówno łańcuchów znakowych, jak  i plików. Prezentowane trzy skrypty zostały tym razem napisane w języku Python. Testując je, należy jedynie pamiętać o tym, że Python ma swoje rygorystyczne wymagania odnośnie struktury kodu źródłowego. Nie ma w nim bowiem nawiasów czy struktur begin-end ograniczających bloki kodu. W Pythonie funkcje ograniczników bloków kodu pełnią jedynie wcięcia, do których zatem należy podchodzić w zdyscyplinowany sposób. Mianowicie, jeśli przyjmiemy, że wcięcia robimy przy pomocy szeregu po sobie następujących spacji (dobre praktyki programistyczne opracowane dla języka Python zalecają 4 spacje jako jedno wcięcie), to tego się konsekwentnie trzymajmy. Podobna dyscyplina obowiązuje przy wcięciach robionych przy pomocy tabulatora. Mieszanie dwóch rodzajów wcięć w jednym pliku z kodem źródłowym, interpreter Pythona zgłosi jako błąd. Poza tym Python jest COOL🙂.

Pierwszy skrypt służy do wyliczania kryptograficznych sum kontrolnych łańcuchów znakowych w trybie konsolowym, czyli uruchamia się go z poziomu linii poleceń powłoki systemu operacyjnego. Uruchomienie go z opcją –h lub –help wyprowadza oczekiwaną składnię poprawnego wywołania.

Drugi skrypt także liczy kody HMAC z podanego łańcucha znakowego i tajnego klucza, ale został wyposażony w graficzny interfejs użytkownika napisany z wykorzystaniem biblioteki Tkinter.

Trzeci skrypt wylicza sumy kontrolne plików w katalogu aktualnym i zasila nimi bazę danych SQLite.

Zachęcam, jak zwykle do eksperymentowania z kodami. Na skutek tego rodzaju eksperymentów i dalszego rozwoju kodów mogą powstać całkiem użyteczne narzędzia.

Składnia konsolowej wersji skryptu do wyliczania kodów HMAC łańcuchów znakowych przedstawia się następująco:

Usage: python hmac_calc.py -s input_string -k key
where -s input_string (--string input_string) points out to raw string to be processed into HMAC
      -k secret_key (--key secret_key) is the secret key component of HMAC
       hmac_calc.py -h (or --help) displays this banner

Interfejs graficzny drugiego skryptu wygląda tak:
hmac_generatorKod w wersji konsolowej:

# ------------------------------------------------------------------------------
# HMAC calculation for given raw input string and secret key
# Author: Janusz Nawrat - 2015
# 
# Usage: python hmac.calc.py -s raw_input_string -k secret_key
# ------------------------------------------------------------------------------
import sys, getopt, os, signal
import hashlib, hmac

def calculate_hmac(base_string, key):
    """
    HMAC hash calculation and returning the results in dictionary collection
    """
    hmacs = dict()
    # --- MD5 ---
    hashed = hmac.new(key, base_string, hashlib.md5)
    hmac_md5 = hashed.digest().encode("base64").rstrip('\n')
    hmacs['MD5'] = hmac_md5
    # --- SHA-1 ---
    hashed = hmac.new(key, base_string, hashlib.sha1)
    hmac_sha1 = hashed.digest().encode("base64").rstrip('\n')
    hmacs['SHA-1'] = hmac_sha1
    # --- SHA-224 ---
    hashed = hmac.new(key, base_string, hashlib.sha224)
    hmac_sha224 = hashed.digest().encode("base64").rstrip('\n')
    hmacs['SHA-224'] = hmac_sha224
    # --- SHA-256 ---
    hashed = hmac.new(key, base_string, hashlib.sha256)
    hmac_sha256 = hashed.digest().encode("base64").rstrip('\n')
    hmacs['SHA-256'] = hmac_sha256
    # --- SHA-384 ---
    hashed = hmac.new(key, base_string, hashlib.sha384)
    hmac_sha384 = hashed.digest().encode("base64").rstrip('\n')
    hmacs['SHA-384'] = hmac_sha384
    # --- SHA-512 ---
    hashed = hmac.new(key, base_string, hashlib.sha512)
    hmac_sha512 = hashed.digest().encode("base64").rstrip('\n')
    hmacs['SHA-512'] = hmac_sha512
    return hmacs
# End of function calculate_hmac()

def usage():
    print """
\nUsage: {0} -s input_string -k key
where -s input_string (--string input_string) points out to raw string to be processed into HMAC
      -k secret_key (--key secret_key) is the secret key component of HMAC
       {1} -h (or --help) displays this banner
    """.format(os.path.basename(sys.argv[0]), os.path.basename(sys.argv[0]))
# End of function usage()

def handler(signal, frame):
    print "Interrupted by user... Signal: %s: %s\n" % (signal, frame)
    sys.exit(2)
# End of function handler()

signal.signal(signal.SIGINT, handler)

raw_input_string = ""; secret_key = ""

if len(sys.argv)  {1}".format(alg, signed[alg])

Kod w wersji z graficznym interfejsem użytkownika:

# ------------------------------------------------------------------------------
# HMAC calculator with GUI
# Author: Janusz Nawrat - 2015
# ------------------------------------------------------------------------------
import sys, os, signal
import hashlib, hmac
import tkMessageBox

from Tkinter import *

def calculate_hmac():
    """
    HMAC hash calculation and returning the results in dictionary collection
    """
    global raw_input_string, secret_key, md5_hmac, sha1_hmac, sha224_hmac, sha256_hmac, sha384_hmac, sha512_hmac
    key = secret_key.get()
    base_string = raw_input_string.get()
    # --- MD5 ---
    hashed = hmac.new(key, base_string, hashlib.md5)
    hmac_md5 = hashed.digest().encode("base64").rstrip('\n')
    md5_hmac.set(hmac_md5)
    # --- SHA-1 ---
    hashed = hmac.new(key, base_string, hashlib.sha1)
    hmac_sha1 = hashed.digest().encode("base64").rstrip('\n')
    sha1_hmac.set(hmac_sha1)
    # --- SHA-224 ---
    hashed = hmac.new(key, base_string, hashlib.sha224)
    hmac_sha224 = hashed.digest().encode("base64").rstrip('\n')
    sha224_hmac.set(hmac_sha224)
    # --- SHA-256 ---
    hashed = hmac.new(key, base_string, hashlib.sha256)
    hmac_sha256 = hashed.digest().encode("base64").rstrip('\n')
    sha256_hmac.set(hmac_sha256)
    # --- SHA-384 ---
    hashed = hmac.new(key, base_string, hashlib.sha384)
    hmac_sha384 = hashed.digest().encode("base64").rstrip('\n')
    sha384_hmac.set(hmac_sha384)
    # --- SHA-512 ---
    hashed = hmac.new(key, base_string, hashlib.sha512)
    hmac_sha512 = hashed.digest().encode("base64").rstrip('\n')
    sha512_hmac.set(hmac_sha512)
# End of function calculate_hmac()

master = Tk()
master.wm_title("HMAC calculator - Janusz Nawrat (2015)")

raw_input_string = StringVar()
raw_input_string.set(r"raw example input string")
secret_key = StringVar(); secret_key.set("$3cR@Tkee")
# -----
md5_hmac = StringVar()
sha1_hmac = StringVar()
sha224_hmac = StringVar()
sha256_hmac = StringVar()
sha384_hmac = StringVar()
sha512_hmac = StringVar()

raw_input_label = Label(master, text="RAW input string ").grid(row=0, column=0, sticky=E, pady=5, padx=5)
raw_input_entry = Entry(master, bd=2, textvariable=raw_input_string, width=60)
raw_input_entry.grid(row=0, column=1, sticky=W+E, pady=5, padx=5)
key_label = Label(master, text="Secret key ").grid(row=1, column=0, sticky=E, pady=2, padx=5)
key_entry = Entry(master, bd=2, textvariable=secret_key, width=60)
key_entry.grid(row=1, column=1, sticky=W+E+N+S, pady=5, padx=5)

Grid.columnconfigure(master, 0, weight=1)
Grid.columnconfigure(master, 1, weight=2)

results_frame = LabelFrame(master, text=" Results ")
results_frame.grid(row=2, column=0, columnspan=2, pady=2, padx=5, sticky=W+E+N+S)
# -----
md5_hmac_label = Label(results_frame, text="MD5 HMAC ").grid(row=2, column=0, sticky=E, pady=3, padx=5)
md5_hmac_entry = Entry(results_frame, bd=2, textvariable=md5_hmac, width=60, fg="red")
md5_hmac_entry.grid(row=2, column=1, sticky=W+E+N+S, pady=3, padx=5)
# -----
sha1_hmac_label = Label(results_frame, text="SHA-1 HMAC ").grid(row=3, column=0, sticky=E, pady=3, padx=5)
sha1_hmac_entry = Entry(results_frame, bd=2, textvariable=sha1_hmac, width=60, fg="red")
sha1_hmac_entry.grid(row=3, column=1, sticky=W+E+N+S, pady=3, padx=5)
# -----
sha224_hmac_label = Label(results_frame, text="SHA-224 HMAC ").grid(row=4, column=0, sticky=E, pady=3, padx=5)
sha224_hmac_entry = Entry(results_frame, bd=2, textvariable=sha224_hmac, width=60, fg="red")
sha224_hmac_entry.grid(row=4, column=1, sticky=W+E+N+S, pady=3, padx=5)
# -----
sha256_hmac_label = Label(results_frame, text="SHA-256 HMAC ").grid(row=5, column=0, sticky=E, pady=3, padx=5)
sha256_hmac_entry = Entry(results_frame, bd=2, textvariable=sha256_hmac, width=60, fg="red")
sha256_hmac_entry.grid(row=5, column=1, sticky=W+E+N+S, pady=3, padx=5)
# -----
sha384_hmac_label = Label(results_frame, text="SHA-384 HMAC ").grid(row=6, column=0, sticky=E, pady=3, padx=5)
sha384_hmac_entry = Entry(results_frame, bd=2, textvariable=sha384_hmac, width=60, fg="red")
sha384_hmac_entry.grid(row=6, column=1, sticky=W+E+N+S, pady=3, padx=5)
# -----
sha512_hmac_label = Label(results_frame, text="SHA-512 HMAC ").grid(row=7, column=0, sticky=E, pady=3, padx=5)
sha512_hmac_entry = Entry(results_frame, bd=2, textvariable=sha512_hmac, width=60, fg="red")
sha512_hmac_entry.grid(row=7, column=1, sticky=W+E+N+S, pady=3, padx=5)

Grid.columnconfigure(results_frame, 0, weight=1)
Grid.columnconfigure(results_frame, 1, weight=2)

button_frame = Frame(master)
button_frame.grid(row=8, column=0, columnspan=2, padx=5)
ok_button = Button(button_frame, text="OK", width=15, command=calculate_hmac)
ok_button.grid(row=8, column=0, sticky=E+W, pady=5, padx=2)
exit_button = Button(button_frame, text="Exit", width=15, command=exit)
exit_button.grid(row=8, column=2, sticky=E+W, pady=5, padx=2)

Grid.columnconfigure(button_frame, 0, weight=1)
Grid.columnconfigure(button_frame, 1, weight=1)

mainloop()

Kod do wyliczania kryptograficznych sum kontrolnych plików:

# -*- coding: utf-8 -*-
import hmac, hashlib
import sys, glob, datetime
import sqlite3 as lite

def file_hmac(filename, secret="$3cRetKee"):
    crypto_checksum = hmac.new(secret, '', hashlib.sha256)

    f = open(filename, 'rb')
    try:
        content = f.read()
        if not content:
            return None
        crypto_checksum.update(content)
    except:
        print "Something went wrong..."
        sys.exit(1)
    finally:
        f.close()
    
    digest = crypto_checksum.hexdigest()
    return digest
# End of function file_hmac

counter = 1
today = datetime.date.today()

try:
    database = lite.connect('digest.db')
    cur = database.cursor()
    with database: 
        answer = raw_input("Do you want to clean the current data in table Hashes? (y/n): ")
        if answer.upper() == "Y":
            cur.execute("DROP TABLE IF EXISTS Hashes")
        print " Loading data into database ".center(70, "-")    
        cur.execute("CREATE TABLE IF NOT EXISTS Hashes(id INT, file TEXT, date TEXT, hmac TEXT)")
    
    for file in glob.glob('./*'):
        hmac_checksum = file_hmac(file)        
        print "File ----> %s\nHMAC: %s" % (file, hmac_checksum)
        with database:
            cur.execute("INSERT INTO Hashes VALUES(?, ?, ?, ?)", (counter, file, today, hmac_checksum))
        counter += 1
except:
    print "Error in opening file %s or loading data into database" % file        

answer = raw_input("Do you want to display data stored in table Hashes? (y/n) ")
if answer.upper() == "Y":    
    print " Table contents ".center(70, "-")    
    with database:
        cur.execute("SELECT * FROM Hashes")
        rows = cur.fetchall()
        for row in rows:
            print "ID: %s\tFILE: %s\tDATE: %s\nHMAC: %s" % (row[0], row[1], row[2], row[3])
else:
    print "Thank you. Bye!"
# end of if answer.upper() == "Y"

Informacje Janusz Nawrat
Just ordinary man who likes thinking...

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s

TOMASZ WEŁNA

artysta grafik | wykładowca

PRACOWNIA OKO

Szkoła Rysunku Malarstwa i Grafiki DR TOMASZA WEŁNY | KRAKÓW | Plac Matejki 10 | tel 691 81 75 74

Piękno neurobiologii

Blog Jerzego Vetulaniego

Teoria muzyki, zasady muzyki, podstawy muzyki

Teoria muzyki, zasady muzyki, podstawy muzyki - czyli to co każdy amator muzyki wiedzieć powinien :)

Personal Development & Inspirations

Przemyślenia i refleksje, którymi warto się podzielić (blog by Janusz Nawrat)

Business IT Cooperation Platform

Biznes i IT - dwa światy, które muszą współdziałać

%d bloggers like this: