哈希 in python

# 哈希的介绍

 

## 哈希

 

本节内容从介绍哈希的基本概念 。主要包含数论基础,介绍哈希表的介绍及实现,双哈希,混沌机,以及摘要算法。

 

#### 知识点

 

+ 哈希的概念

+ 数论基础

+ 哈希表的介绍及实现

+ 双哈希

+ 混沌机

+ 摘要算法

 

## 哈希的概念

 

#### 什么是哈希

 

哈希(Hash),一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入(又叫做预映射 pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。

 

#### 概念介绍

 

若结构中存在和关键字 K 相等的记录,则必定在 f(K)的存储位置上。由此,不需比较便可直接取得所查记录。称这个对应关系 f 为**散列函数(Hash function)**,按这个事先建立的表为**散列表**.

 

对不同的关键字可能得到同一散列地址,即 key1≠key2,而 f(key1)=f(key2),这种现象称**碰撞**。具有相同函数值的关键字对该散列函数来说称做**同义词**。综上所述,根据散列函数 H(key)和处理冲突的方法将一组关键字映象到一个有限的连续的地址集(区间)上,并以关键字在地址集中的“象” 作为记录在表中的存储位置,这种表便称为**散列表**,这一映象过程称为散列造表或散列,所得的存储位置称**散列地址**。

 

若对于关键字集合中的任一个关键字,经散列函数映象到地址集合中任何一个地址的概率是相等的,则称此类散列函数为**均匀散列函数(Uniform Hash function)**,这就是使关键字经过散列函数得到一个“随机的地址”,从而减少冲突。

 

注:定义来自百度百科。

 

## 数论基础

 

素数,也称质数,只包含两个因数,且一个因数为 1,一个因数为它本身。

 

素数有以下性质:

 

+ 素数 p 的因子有且只有两个:1 和 p

 

+ 素数一定是奇数

 

+ 任意一个大于 1 的正整数 N,一定可以质因式分解为它的有限个质因子之积

 

+ 素数的个数是无限的

 

+ 所有大于 10 的素数中,其个位数只能是 1,3,7,9 其中之一

 

+ 一个充分大的偶数一定可以写成:一个素数加上一个最多由 2 个质因子所组成的合成数

 

#### 判断素数

 

寻找素数算法,有很多种,包括短除法,筛选法两种。

 

**短除法**

 

设计思想:最直接的方法就是从定义出发,对于任意整数 p,用[2,p-1]去整除 p,如果发现 p 可以被整除,p 就不是素数

 

下面给出短除法的参考代码。

 

```python

def find_prime(num):

if num > 1:# 查看因子

for i in range(2,num):

if (num % i) == 0:

print(num,"不是质数")

print(i,"乘于",num//i,"是",num)

break

else:

print(num,"是质数")

else:

print(num,"不是质数")# 如果输入的数字小于或等于 1,不是

num = int(input("请输入一个数字: "))

find_prime(num)

```

 

显然,这个方法的效率简直低的让人难以接受,优化空间非常大

 

**筛选法**

 

设计思想:

 

1. 将 n 个数字全部放进数组,并都置为肯定状态

 

2. 将数组下标是偶数的数字全部置为否定状态

 

3. 依次遍历数组长度的平方根个数字

 

4. 如果当前数字处于被肯定的状态,则将其倍数的数字状态置为否定

 

```python

def find_prime_2(num):

primes_bool=[False,False]+[True]*(num-1)

for i in range(3,len(primes_bool)):

if i&1==0:

prims_bool[i]=False

for i in range(3,int(math.sqrt(num))+1):

if prims_bool[i] is True:

for j in range(i+j,num+1,i):

prims_bool[j]=False

prims=[]

[prims.append(i) for i,v in enumerate(prims_bool) if v is True]

return prims

```

 

## 哈希表

 

哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做**散列函数**,存放记录的数组叫做**散列表**。

 

给定表 M,存在函数 f(key),对任意给定的关键字值 key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表 M 为**哈希(Hash)表**,函数 f(key)为**哈希(Hash) 函数**。

 

在平均情况下,在哈希表中查找一个元素的期望时间是 O(1) ,因此效率极高。Python 中的字典就是采用了哈希表的结构。

 

注:定义来自百度百科。

 

#### 哈希表特点

 

哈希表具有以下特点:

 

1. 若关键字为 k,则其值存放在 f(k)的存储位置上。由此,不需比较便可直接取得所查记录。称这个对应关系 f 为散列函数,按这个思想建立的表为散列表。

 

2. 对不同的关键字可能得到同一散列地址,即 k1≠k2,而 f(k1)=f(k2),这种现象称为冲突。具有相同函数值的关键字对该散列函数来说称做同义词。综上所述,根据散列函数 f(k)和处理冲突的方法将一组关键字映射到一个有限的连续的地址集(区间)上,并以关键字在地址集中的“像”作为记录在表中的存储位置,这种表便称为散列表,这一映射过程称为散列造表或散列,所得的存储位置称散列地址。

 

3. 若对于关键字集合中的任一个关键字,经散列函数映象到地址集合中任何一个地址的概率是相等的,则称此类散列函数为均匀散列函数(Uniform Hash function),这就是使关键字经过散列函数得到一个“随机的地址”,从而减少冲突。

 

#### 哈希表的实现

 

总的来说,哈希表就是一个具备映射关系的表,你可以通过映射关系由键找到值。有没有现成的例子?当然有,不过你直接用就没意思了。

 

反正就是要实现 f(k),即实现 key-value 的映射关系。我们试着自己实现一下:

 

```python

class hashtable(object):

def __init__(self):

self.items=[]

def put(self,k,v):

self.items.append((k,v)) #添加对应关系

def get(self,k):

for key,value in self.items:

if(k==key):

return value #返回正确匹配出的结果

```

 

这样实现的 hashtable,查找的时间复杂度为 O(n)。 “这太简单了,看上去与 key 没什么关系啊,这不是顺序查找么,逗我呢?” 这只是一个热身,好吧,下面我们根据定义,来搞一个有映射函数的:

 

```python

class hashtable(object):

def __init__(self):

self.items=[None]*100

def hash(self,a):

return a*1+1 #线性映射关系

def put(self,k,v):

self.items[self.hash(k)]=v #根据哈希结果,添加映射关系

def get(self,k):

hashcode=self.hash(k)

return self.items[hashcode] #根据哈希结果,返回正确匹配出结果

```

 

“这 hash 函数有点简单啊” 是的,它是简单,但简单不妨碍它成为一个哈希函数,事实上,它叫直接定址法,是一个线性函数: hash(k)= a*k+b

 

直接定址法的优点很明显,就是它不会产生重复的 hash 值。但由于它与键值本身有关系,所以当键值分布很散的时候,会浪费大量的存储空间。所以一般是不会用到直接定址法的。

 

#### 处理冲突

 

处理冲突假如某个 hash 函数产生了一堆哈希值,而这些哈希值产生了冲突怎么办(实际生产环境中经常发生)?在各种哈希表的实现里,处理冲突是必需的一步。 比如你定义了一个 hash 函数: hash(k)=k mod 10 假设 key 序列为:[15,1,24,32,55,64,42,93,82,76],一趟下来,冲突的元素有四个,下面有几个办法解决冲突。

 

+ 开放寻址法:Hi=(H(key) + di) MOD m,i=1,2,...,k(k<=m-1),其中 H(key)为散列函数,m 为散列表长,di 为增量序列,可有下列三种取法:

 

![开放寻址法](C:/Users/15193/Desktop/screen/hash_开放寻址.jpg)

 

1. di=1,2,3,...,m-1,称线性探测再散列;

 

2. di=1^2,-1^2,2^2,-2^2,⑶^2,...,±(k)^2,(k<=m/2)称平方探测再散列;

 

3. di=伪随机数序列,称伪随机探测再散列。

 

+ 再散列法:Hi=RHi(key),i=1,2,...,k RHi 均是不同的散列函数,即在同义词产生地址冲突时计算另一个散列函数地址,直到冲突不再发生,这种方法不易产生“聚集”,但增加了计算时间。

 

+ 链地址法(拉链法)

 

+ 建立一个公共溢出区

 

说了这么一堆,举个例子,用开放地址法(线性探测):

 

```python

class hashtable(object):

def __init__(self):

self.hash_table=[[None,None] for i in range(10)]

def hash(self,k,i):

h_value=(k+i)%10

if self.hash_table[h_value][0]==k:

return h_value

if self.hash_table[h_value][0]!=None:

i=i+1

h_value=self.hash(k,i)

return h_value

def put(self,k,v):

hash_v=self.hash(k,0)

self.hash_table[hash_v][0]=k

self.hash_table[hash_v][1]=v

def get(self,k):

hash_v=self.hash(k,0)

return self.hash_table[hash_v][1]

table=hashtable()

for i in range(9):

table.put(i,i)

print(table.get(3))

print(table.hash_table)

```

 

“能不能不要定死长度?10 个完全不够用啊”

 

这是刚才的问题,所以有了另外一个概念,叫做载荷因子(load factor)。载荷因子的定义为: α= 已有的元素个数/表的长度由于表长是定值, α与“填入表中的元素个数”成正比,所以, α越大,表明填入表中的元素越多,产生冲突的可能性就越大;反之,α越小,表明填入表中的元素越少,产生冲突的可能性就越小。

 

实际上,散列表的平均查找长度是载荷因子 α的函数,只是不同处理冲突的方法有不同的函数。所以当到达一定程度,表的长度是要变的,即 resize,载荷因子被设计为 0.75;超过 0.8,cpu 的 cache missing 会急剧上升。具体扩容多少,一般选择扩到已插入元素数量的两倍.

 

接着上面,再升级一下我们的 hashtable:

 

```python

class hashtable(object):

def __init__(self):

self.capacity=10

self.hash_table=[[None,None]for i in range(self.capacity)]

self.num=0

self.load_factor=0.75

def hash(self,k,i):

h_value=(k+i)%self.capacity

if self.hash_table[h_value][0]==k:

return h_value

if self.hash_table[h_value][0]!=None:

i=i+1

h_value=self.hash(k,i)

return h_value

def resize(self):

self.capacity=self.num*2 #扩容到原有元素数量的两倍

temp=self.hash_table[:]

self.hash_table=[[None,None]for i in range(self.capacity)]

for i in temp:

if(i[0]!=None): #把原来已有的元素存入

hash_v=self.hash(i[0],0)

self.hash_table[hash_v][0]=i[0]

self.hash_table[hash_v][1]=i[1]

def put(self,k,v):

hash_v=self.hash(k,0)

self.hash_table[hash_v][0]=k

self.hash_table[hash_v][1]=v

self.num=self.num+1 #暂不考虑key重复的情况,具体自己可以优化

if(self.num/len(self.hash_table)>self.load_factor):# 如果比例大于载荷因子

self.resize()

def get(self,k):

hash_v=self.hash(k,0)

return self.hash_table[hash_v][1]

table=hashtable()

for i in range(15):

table.put(i,i)

print(table.get(3))

print(table.hash_table)

```

 

哈希表最终呈现为:

[[None, None],

[1, 1],

[2, 2],

[3, 3],

[4, 4],

[5, 5],

[6, 6],

[7, 7],

[8, 8],

[9, 9],

[10, 10],

[11, 11],

[12, 12],

[None, None],

[None, None],

[None, None]]

 

添加了一个 resize 函数就不需要担心哈希表的容量问题啦。

 

关于哈希表,原理的东西都基本差不多了。可以看到,它本质要解决的是查找时间的问题。如果顺序查找的话,时间复杂度为 O(n);而哈希表,时间复杂度则为 O(1)!直接甩了一个次元,这也就是为什么在大量数据存储查找的时候,哈希表得到大量应用的原因。

 

## 双重哈希

 

双重哈希,也叫一致性哈希,属于开放地址哈希中的一种解决冲突方案,也就是说如果一次哈希不能解决问题的时候,要再次哈希,与再哈希方法不同的是,第二次使用的哈希函数与第一次是不同的:(hash1(key) + i * hash2(key)) % TABLE_SIZE

 

一般来讲,

 

+ hash1(key) = key % TABLE_SIZE

 

+ hash2(key) = PRIME – (key % PRIME)

 

其中 PRIME 一般选一个比 TABLE_SIZE 小的一个质数就可以了,例如如果 TABLE_SIZE=16,那么 PRIME=13。

 

注意:第二个哈希函数结果不能为 0,而且第二个哈希函数要覆盖表的每一个单元。

 

至于 i 就从 1 开始尝试就是了,如果有冲突,则再尝试 i++。

 

## 混沌机 chaos machine

 

在数学中,混沌机是一类基于混沌理论(主要是确定性混沌)构造的算法,用于产生伪随机预言。 它代表了创建具有模块化设计和可定制参数的通用方案的想法,可以应用于需要随机性和敏感性的任何地方。

 

混沌机的理论模型,它结合了哈希函数和伪随机函数的优点,形成了灵活的单向推挽界面。它提出了创建具有模块化设计和可定制参数的通用工具(设计模式)的想法,可以应用于需要随机性和敏感性的地方(随机预言),以及适当的结构决定了应用的情况,参数的选择提供了首选的属性和安全级别。机器可用于实现许多加密原语,包括加密哈希,消息验证码和伪随机数生成器。

 

**算法概述:**

 

chaos machine 模型包含推挽复位功能和缓冲空间的动态系统。 以下组件是成型机接口:

 

+ push 功能(输入)。 主要是输入功能,它吸收位串(通常为 32 位或 16 位值,相对较小)并用于系统评估(控制理论,将在后面讨论)。

 

+ pull 功能(输出)。 它包含混沌映射和伪随机函数的构造,可以自由替换。 拉函数的输出是 xed 长度的比特串(例如,32 或 16 比特)。

+ reset 功能(复位)。 此函数清除缓冲区。

 

此后,操作机器处于初始状态。

 

这里给出 Maciej A. Czyzewski 博士的示例算法,如果大家对混沌机感兴趣的,可以自行学习。

 

```python

# example of simple chaos machine

# Chaos Machine (K, t, m)

K = [0.33, 0.44, 0.55, 0.44, 0.33]; t = 3; m = 5

# Buffer Space (with Parameters Space)

buffer_space, params_space = [], []

# Machine Time

machine_time = 0

def push(seed):

global buffer_space, params_space, machine_time, \

K, m, t

# Choosing Dynamical Systems (All)

for key, value in enumerate(buffer_space):

# Evolution Parameter

e = float(seed / value)

# Control Theory: Orbit Change

value = (buffer_space[(key + 1) % m] + e) % 1

# Control Theory: Trajectory Change

r = (params_space[key] + e) % 1 + 3

# Modification (Transition Function) - Jumps

buffer_space[key] = \

round(float(r * value * (1 - value)), 10)

params_space[key] = \

r # Saving to Parameters Space

# Logistic Map

assert max(buffer_space) < 1

assert max(params_space) < 4

# Machine Time

machine_time += 1

def pull():

global buffer_space, params_space, machine_time, \

K, m, t

# PRNG (Xorshift by George Marsaglia)

def xorshift(X, Y):

X ^= Y >> 13

Y ^= X << 17

X ^= Y >> 5

return X

# Choosing Dynamical Systems (Increment)

key = machine_time % m

# Evolution (Time Length)

for i in range(0, t):

# Variables (Position + Parameters)

r = params_space[key]

value = buffer_space[key]

# Modification (Transition Function) - Flow

buffer_space[key] = \

round(float(r * value * (1 - value)), 10)

params_space[key] = \

(machine_time * 0.01 + r * 1.01) % 1 + 3

# Choosing Chaotic Data

X = int(buffer_space[(key + 2) % m] * (10 ** 10))

Y = int(buffer_space[(key - 2) % m] * (10 ** 10))

# Machine Time

machine_time += 1

return xorshift(X, Y) % 0xFFFFFFFF

def reset():

global buffer_space, params_space, machine_time, \

K, m, t

buffer_space = K; params_space = [0] * m

machine_time = 0

#######################################

# Initialization

reset()

# Pushing Data (Input)

import random

message = random.sample(range(0xFFFFFFFF), 100)

for chunk in message:

push(chunk)

# for controlling

inp = ""

# Pulling Data (Output)

while inp in ("e", "E"):

print("%s" % format(pull(), '#04x'))

print(buffer_space); print(params_space)

inp = input("(e)exit? ").strip()

```

 

创建该算法的主要目的是提出混沌机的实现模型。

 

+ **伪随机数生成器:**基本设计是维护一个假定为攻击者不知道的随机位的熵池。通过推送功能将新的随机性添加到缓冲区,从而演变机器的状态。

 

+ **哈希函数:**该算法可以有效地用作消息认证码,其中速度不是那么重要。可计算的周期和优异的质量创建了作为哈希函数的窄范围应用。

 

## 摘要算法

 

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 的修改,都会导致计算出的摘要完全不同。

 

注:廖雪峰老师博客中[摘要算法介绍](https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014319556588648dd1fb0047a34d0c945ee33e8f4c90cc000)对摘要算法的相关概念讲得很通彻。

 

#### MD5 算法介绍

 

Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。MD5 算法 属 Hash 算法一类。MD5 算法对输入任意长度的消息进行运行,产生一个 128 位的消息摘要。

 

以下所描述的消息长度、填充数据都以位(Bit)为单位,字节序为小端字节。

 

**算法原理:**

 

1. **数据填充:**对消息进行数据填充,使消息的长度对 512 取模得 448,设消息长度为 X,即满足 X mod 512=448。根据此公式得出需要填充的数据长度。填充方法:在消息后面进行填充,填充第一位为 1,其余为 0。

 

2. **添加消息长度:**在第一步结果之后再填充上原消息的长度,可用来进行的存储长度为 64 位。如果消息长度大于 264,则只使用其低 64 位的值,即(消息长度 对 264 取模)。在此步骤进行完毕后,最终消息长度就是 512 的整数倍。

 

3. **数据处理:**准备需要用到的数据:4 个常数: A = 0x67452301, B = 0x0EFCDAB89, C = 0x98BADCFE, D = 0x10325476 ; 4 个函数:F(X,Y,Z)=(X & Y) | ((~X) & Z); G(X,Y,Z)=(X & Z) | (Y & (~Z)); H(X,Y,Z)=X ^ Y ^ Z; I(X,Y,Z)=Y ^ (X | (~Z)); 把消息分以 512 位为一分组进行处理,每一个分组进行 4 轮变换,以上面所说 4 个常数为起始变量进行计算,重新输出 4 个变量,以这 4 个变量再进行下一分组的运算,如果已经是最后一个分组,则这 4 个变量为最后的结果,即 MD5 值。

 

![MD5](C:/Users/15193/Desktop/screen/md5.png)

 

这里写一个利用 md5 进行用户登陆网站进行注册之后密码加密的基本事例,加深理解

 

```python

import hashlib

#hashlib简单使用

def md5(arg):#这是加密函数,将传进来的函数加密

md5_pwd = hashlib.md5(bytes('abd',encoding='utf-8'))

md5_pwd.update(bytes(arg,encoding='utf-8'))

return md5_pwd.hexdigest()#返回加密的数据

def log(user,pwd):#登陆时候时候的函数,由于md5不能反解,因此登陆的时候用正解

with open('db','r',encoding='utf-8') as f:

for line in f:

u,p=line.strip().split('|')

if u ==user and p == md5(pwd):#登陆的时候验证用户名以及加密的密码跟之前保存的是否一样

return True

def register(user,pwd):#注册的时候把用户名和加密的密码写进文件,保存起来

with open('db','a',encoding='utf-8') as f:

temp = user+'|'+md5(pwd)

f.write(temp)

i=input('1表示登陆,2表示注册:')

if i=='2':

user = input('用户名:')

pwd =input('密码:')

register(user,pwd)

elif i=='1':

user = user = input('用户名:')

pwd =input('密码:')

r=log(user,pwd)#验证用户名和密码

if r ==True:

print('登陆成功')

else:

print('登陆失败')

else:

print('账号不存在')

```

 

#### SHA 算法介绍

 

SHA 的全称是 Secure Hash Algorithm(安全散列算法)

 

+ SHA 家族有五个算法,分别是 SHA-1、SHA-224、SHA-256、SHA-384、SHA-512,由美国国家安全局(NSA)所设计,并由美国国家标准与技术研究院(NIST)发布,是美国的政府标准;

 

+ SHA-224、SHA-256、SHA-384、SHA-512 有时并称为 SHA-2;

 

+ SHA-1、SHA-224、SHA-256 适用于长度不超过 2^64 二进制位的消息;

 

+ SHA 算法主要用于:数字签名、数字时间戳、数字证书等。

 

**SHA-1 算法原理:**

 

1. 将 512 位的明文分组划分为 16 个子明文分组,每个子明文分组为 32 位。

 

2. 申请 5 个 32 位的链接变量,记为 A、B、C、D、E。

 

3. 16 份子明文分组扩展为 80 份。

 

4. 80 份子明文分组进行 4 轮运算。

 

5. 链接变量与初始链接变量进行求和运算。

 

6. 链接变量作为下一个明文分组的输入重复进行以上操作。

 

7. 最后,5 个链接变量里面的数据就是 SHA1 摘要。

 

注意:这里介绍的两种数据加密算法,已为不安全的数据加密算法。美国已采用 SHA-256 算法,替代传统的 SHA-1 的算法。

 

早在 2005 年,山东大学的 王小云教授就发现了对 SHA-1 的有效攻击方法,能在最多 2^69 次运算中发生一次碰撞。 这个时候,对于充足资金、充足计算资源的攻击者 的攻击者,已经不是难题了,比如曾经大名鼎鼎的 Flamme 病毒,就是用碰撞方法破解了微软的更新证书,之后把 Flamme 病毒签名成了微软的更新。这次公开是意味着对于运算能力并非无限的攻击者,也是经济的了。按照 Google 的漏洞公布惯例,最多九十天,Google 就会公开全部的细节,那个时候就意味着 SHA-1 碰撞的方式将为公众所知。

 

#### 哈希在信息安全中的应用

 

Hash 算法在信息安全方面的应用主要体如今下面的 3 个方面:

 

1. 文件校验我们比较熟悉的校验算法有奇偶校验和 CRC 校验,这 2 种校验并没有抗数据篡改的能力,它们一定程度上能检測并纠正传输数据中的信道误码,但却不能防止对数据的恶意破坏。MD5 Hash 算法的”数字指纹”特性,使它成为眼下应用最广泛的一种文件完整性校验和(Checksum)算法,不少 Unix 系统有提供计算 md5 checksum 的命令。

 

2. 数字签名 Hash 算法也是现代 password 体系中的一个重要组成部分。因为非对称算法的运算速度较慢,所以在数字签名协议中,单向散列函数扮演了一个重要的角色。 对 Hash 值,又称“数字摘要”进行数字签名,在统计上能够觉得与对文件本身进行数字签名是等效的。并且这种协议还有其它的长处。基于数字签名的不可抵赖性,现如今广泛运用在区块链领域中。

 

3. 鉴权协议例如以下的鉴权协议又被称作挑战–认证模式:在传输信道是可被侦听,但不可被篡改的情况下,这是一种简单而安全的方法。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寒冰团长

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值