zoukankan      html  css  js  c++  java
  • Python模块——HashLib与base64

    摘要算法(hashlib)

    Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。

    什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)

    你写了一篇文章,内容是一个字符串'how to use python hashlib - by Michael',并附上这篇文章的摘要是'2d73d4f15c0db7f5ecb321b6a65e5d6d'

    如果有人篡改了你的文章,并发表为'how to use python hashlib - by Bob',你可以一下子指出Bob篡改了你的文章,因为根据'how to use python hashlib - by Bob'计算出的摘要不同于原始文章的摘要

    可见,摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。

    摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,计算f(data)很容易,但通过digest反推data却非常困难。而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同。

    MD5

    我们以常见的摘要算法MD5为例,计算出一个字符串的MD5值:

    import hashlib
    
    s = 'tz_spider'
    m = hashlib.md5()
    # 加密数据都是bytes
    m.update(s.encode('utf-8'))
    print('md5 hash %s'%m.hexdigest())
    """
    md5 hash a4499790ea68682695a0a168a8ec1ecc
    """

    如果数据量很大,可以分块多次调用update(),最后计算的结果是一样的:

    import hashlib
    md5 = hashlib.md5()
    md5.update('人生苦短,'.encode('utf-8'))
    md5.update('我学Python'.encode('utf-8'))
    print(md5.hexdigest())
    
    import hashlib
    md5 = hashlib.md5()
    md5.update('人生苦短,我学Python'.encode('utf-8'))
    print(md5.hexdigest())
    
    """
    d51a987403720a379fa5d20ab8b7741c
    d51a987403720a379fa5d20ab8b7741c
    """

    SHA1

    MD5是最常见的摘要算法,速度很快,生成结果是固定的128 bit字节,通常用一个32位的16进制字符串表示。

    另一种常见的摘要算法是SHA1,调用SHA1和调用MD5完全类似:

    import hashlib
    md5 = hashlib.sha1()
    md5.update('人生苦短,'.encode('utf-8'))
    md5.update('我学Python'.encode('utf-8'))
    print(md5.hexdigest())
    
    import hashlib
    md5 = hashlib.sha1()
    md5.update('人生苦短,我学Python'.encode('utf-8'))
    print(md5.hexdigest())
    
    """
    5723b4cd6bc67f1f3682cab2a382e333518ed23a
    5723b4cd6bc67f1f3682cab2a382e333518ed23a
    """

    SHA1的结果是160 bit字节,通常用一个40位的16进制字符串表示。

    摘要算法应用

    摘要算法主要用于用户登录时,对口令进行MD5加密,存储到数据库中,存储MD5的好处是即使运维人员能访问数据库,也无法获知用户的明文口令。

    def get_md5(s):
        md5 = hashlib.md5()
        md5.update(s.encode('utf-8'))
        return md5.hexdigest()
    
    user_md5_dict = {}
    user_dict = {
        'michael': '123456',
        'bob': 'abc'}
    
    for item in user_dict:
        user_md5_dict[item] = get_md5(user_dict.get(item))
    print(user_md5_dict)
    """
    {'michael': 'e10adc3949ba59abbe56e057f20f883e', 'bob': '900150983cd24fb0d6963f7d28e17f72'}
    """

    采用MD5存储口令是否就一定安全呢?也不一定,很多用户喜欢用123456888888password这些简单的口令,于是,黑客可以事先计算出这些常用口令的MD5值,得到一个反推表:

    'e10adc3949ba59abbe56e057f20f883e': '123456'
    '21218cca77804d2ba1922c33e0151105': '888888'

    这样,无需破解,只需要对比数据库的MD5,黑客就获得了使用常用口令的用户账号(撞库)。

    由于常用口令的MD5值很容易被计算出来,所以,要确保存储的用户口令不是那些已经被计算出来的常用口令的MD5,这一方法通过对原始口令加一个复杂字符串来实现,俗称“加盐”

    def calc_md5(password, salt='add salt'):
        md5 = hashlib.md5()
        md5.update((password+salt).encode('utf-8'))
        a = md5.hexdigest()
        return a
    
    user_md5_dict = {}
    user_dict = {
        'michael': '123456',
        'bob': 'abc'}
    
    for item in user_dict:
        user_md5_dict[item] = calc_md5(user_dict.get(item))
    print(user_md5_dict)
    """
    {'michael': '121e5a2806adb57b7f5ddfb49c58cb38', 'bob': '655cb18b65100d50c375826e4a7138d9'}
    """

    经过Salt处理的MD5口令,只要Salt不被黑客知道,即使用户输入简单口令,也很难通过MD5反推明文口令。

     但是如果有两个用户都使用了相同的简单口令比如123456,在数据库中,将存储两条相同的MD5值,这说明这两个用户的口令是一样的。有没有办法让使用相同口令的用户存储不同的MD5呢?

    如果假定用户无法修改登录名,就可以通过把登录名作为Salt的一部分来计算MD5,从而实现相同口令的用户也存储不同的MD5。

    def calc_md5(user, password, salt='add salt'):
        md5 = hashlib.md5()
        md5.update((user + password + salt).encode('utf-8'))
        a = md5.hexdigest()
        return a
    
    user_md5_dict = {}
    user_dict = {
        'michael': '123456',
        'bob': '123456'}
    
    for item in user_dict:
        user_md5_dict[item] = calc_md5(item, user_dict.get(item))
    print(user_md5_dict)
    """
    {'michael': '18833a2efa41021c1659af9eb7ffc0e5', 'bob': 'b2c512421985a2622a64fa84b486dc0b'}
    """

     获取文件的MD5

    import os
    def calc_md5(filename):
        """
        用于获取文件的md5值
        :param filename: 文件名
        :return: MD5码
        """
        if not os.path.isfile(filename):  # 如果校验md5的文件不是文件,返回空
            return
        myhash = hashlib.md5()
        f = open(filename, 'rb')
        while True:
            b = f.read(2048)
            if not b:
                break
            myhash.update(b)
        f.close()
        return myhash.hexdigest()
    
    print(calc_md5('BaiduStockInfo.txt'))
    """
    94da595be98b4c65fc1ccf697a435322
    """

    base64

    base64模块是用来作base64编码解码的。这种编码方式在电子邮件中是很常见的。
    它可以把不能作为文本显示的二进制数据编码为可显示的文本信息。编码后的文本大小会增大1/3。

    Base64的原理很简单,首先,准备一个包含64个字符的数组:

    ['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/']
    

    然后,对二进制数据进行处理,每3个字节一组,一共是3x8=24bit,划为4组,每组正好6个bit

    这样我们得到4个数字作为索引,然后查表,获得相应的4个字符,就是编码后的字符串。

    所以,Base64编码会把3字节的二进制数据编码为4字节的文本数据,长度增加33%,好处是编码后的文本数据可以在邮件正文、网页等直接显示。

    如果要编码的二进制数据不是3的倍数,最后会剩下1个或2个字节怎么办?Base64用x00字节在末尾补足后,再在编码的末尾加上1个或2个=号,表示补了多少字节,解码的时候,会自动去掉。

    import base64
    s = b'1234567'
    s1 = base64.b64encode(s)  # 编码
    print(s1)
    print(base64.b64decode(s1))  # 解码

    输出

    b'MTIzNDU2Nw=='
    b'1234567'

    由于标准的Base64编码后可能出现字符+/,在URL中就不能直接作为参数,所以又有一种"url safe"的base64编码,其实就是把字符+/分别变成-_

    import base64
    s = b'ixb7x1dxfbxefxff'
    s1 = base64.b64encode(s)  # 编码
    print(s1)
    s2 = base64.urlsafe_b64encode(s)
    print(s2)
    print(base64.b64decode(s1))  # 解码
    print(base64.urlsafe_b64decode(s2))  # 解码

     输出

    b'abcd++//'
    b'abcd--__'
    b'ixb7x1dxfbxefxff'
    b'ixb7x1dxfbxefxff'
    

     

    参考https://www.liaoxuefeng.com/

  • 相关阅读:
    mini2440移植uboot 2014.04(四)
    mini2440移植uboot 2014.04(三)
    【这些年】Linux C/C++软件开发用过的工具
    Valgrind的Memcheck快速入门
    《浪潮之巅》读后感
    三层浅析及示例分析
    C语言的代码内存布局详解
    超级立方体小记
    如何和项目经理沟通产品的交付?
    CentOS配置smaba与Windows共享文件
  • 原文地址:https://www.cnblogs.com/xiao-apple36/p/8744213.html
Copyright © 2011-2022 走看看