python实现RSA数字签名(纯算法实现)
python实现RSA数字签名(纯算法实现)
一:什么是数字签名
数字签名是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。它是一种类似写在纸上的普通的物理签名,但是在使用了公钥加密领域的技术来实现的,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。数字签名是非对称密钥加密技术与数字摘要技术的应用。
二:数字签名的原理
发送方将先生成一对公私钥,将公钥放到网络上,发送方利用私钥将文件或者消息进行签名。接收方得到发送方的文件或者消息、公钥以及生成的签名;首先利用公钥将得到签名生成消息摘要,在对比文件或者消息的消息摘要,如果匹配,则签名认证成功。
三:数字签名的作用
四:实现数字签名的算法
分为三种:RSA、DSA、ECDSA
五:具备前提知识RSA算法
RSA加密过程:
步骤 | 说明 | 描述 |
---|---|---|
1 | 选择一对不相等且足够大的质数 | p,q |
2 | 计算p,q的乘积 | n=p*q |
3 | 计算n的欧拉函数 | φ(n)=(p-1)*(q-1) |
4 | 选一个与φ(n)互质的整数e | 1<e<φ(n) |
5 | 计算出e对于φ(n)的模反元素d | de mod φ(n)=1 |
6 | 公钥 | KU=(e,n) |
7 | 私钥 | KR=(d,n) |
相关概念:
质数:质数又称素数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;否则称为合数(规定1既不是质数也不是合数)。
欧拉函数:在数论,对正整数n,欧拉函数是小于n的正整数中与n互质的数的数目
例如:
- 如果n可以分解成2个质数的整数之积,那么n的欧拉函数等于这两个因子的欧拉函数之积。
- φ(n)= φ(pxq)=(p-1)*(q-1)
互质:公约数只有1的两个整数,叫做互质整数
六:消息摘要的前提知识
消息摘要算法的特点:
消息摘要是把任意长度的输入揉和而产生长度固定的伪随机输出的算法。消息摘要的主要特点有:
①无论输入的消息有多长,计算出来的消息摘要的长度总是固定的。例如应用MD5算法摘要的消息有128个比特位,用SHA-1算法摘要的消息最终有160比特位的输出,SHA-1的变体可以产生192比特位和256比特位的消息摘要。一般认为,摘要的最终输出越长,该摘要算法就越安全。
②消息摘要看起来是“随机的”。这些比特看上去是胡乱的杂凑在一起的。可以用大量的输入来检验其输出是否相同,一般,不同的输入会有不同的输出,而且输出的摘要消息可以通过随机性检验。但是,一个摘要并不是真正随机的,因为用相同的算法对相同的消息求两次摘要,其结果必然相同;而若是真正随机的,则无论如何都是无法重现的。因此消息摘要是“伪随机的”。
③一般地,只要输入的消息不同,对其进行摘要以后产生的摘要消息也必不相同;但相同的输入必会产生相同的输出。这正是好的消息摘要算法所具有的性质:输入改变了,输出也就改变了;两条相似的消息的摘要确不相近,甚至会大相径庭。
④消息摘要函数是无陷门的单向函数,即只能进行正向的信息摘要,而无法从摘要中恢复出任何的消息,甚至根本就找不到任何与原信息相关的信息。当然,可以采用强力攻击的方法,即尝试每一个可能的信息,计算其摘要,看看是否与已有的摘要相同,如果这样做,最终肯定会恢复出摘要的消息。但实际上,要得到的信息可能是无穷个消息之一,所以这种强力攻击几乎是无效的。
⑤好的摘要算法,没有人能从中找到“碰撞”,虽然“碰撞”是肯定存在的。即对于给定的一个摘要,不可能找到一条信息使其摘要正好是给定的。或者说,无法找到两条消息,使它们的摘要相同。
七:python实现消息摘要
该处我采用的是hash256: SHA256算法使用的哈希值长度是256位
python代码:
def Sha256sum(message: bytes) -> bytes:# 定义常量# 前8个素数2..19的平方根的小数部分的前32位h0 = 0x6a09e667h1 = 0xbb67ae85h2 = 0x3c6ef372h3 = 0xa54ff53ah4 = 0x510e527fh5 = 0x9b05688ch6 = 0x1f83d9abh7 = 0x5be0cd19# 定义常数K 64# 前64个素数2..311的立方根的小数部分的前32位K = (0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2)# R为循环右移,# 右移之后可能会超过32位,所以要和0xffffffff做与运算,确保结果为32位。R = lambda x, n: ((x >> n) | (x << (32 - n))) & 0xffffffff# 大端 0x12,0x34,0x56,0x78 -> 0x12345678W = lambda i1, i2, i3, i4: (i1 << 24) | (i2 << 16) | (i3 << 8) | i4# 对每一个输入先添加一个'0x80',即'10000000', 即128ascii_list = list(map(lambda x: x, message))msg_length = len(ascii_list) * 8ascii_list.append(128)# 补充0while (len(ascii_list) * 8 + 64) % 512 != 0:ascii_list.append(0)# 最后64为存放消息长度,以大端数存放。# 例如,消息为'a',则长度为'0x0000000000000008'for i in range(8):ascii_list.append(msg_length >> (8 * (7 - i)) & 0xff)# print(ascii_list)# print(len(ascii_list)//64)for i in range(len(ascii_list) // 64): # 64*8=512bits# print(ascii_list[i*64:(i+1)*64])# 每个512bits的块进行循环w = []# 将512bits扩展到64*32bits=2048bits存入32位无符号数数组for j in range(16):s = i * 64 + j * 4w.append(W(ascii_list[s], ascii_list[s + 1], ascii_list[s + 2], ascii_list[s + 3]))for j in range(16, 64):s0 = (R(w[j - 15], 7)) ^ (R(w[j - 15], 18)) ^ (w[j - 15] >> 3)s1 = (R(w[j - 2], 17)) ^ (R(w[j - 2], 19)) ^ (w[j - 2] >> 10)w.append((w[j - 16] + s0 + w[j - 7] + s1) & 0xffffffff)# print(hex(s0)+':'+hex(s1)+':' + hex(R(w[j - 2], 17)))# 初始化a, b, c, d, e, f, g, h = h0, h1, h2, h3, h4, h5, h6, h7# for j in w:# print(hex(j)[2:])for j in range(64):s0 = R(a, 2) ^ R(a, 13) ^ R(a, 22)maj = (a & b) ^ (a & c) ^ (b & c)t2 = s0 + majs1 = R(e, 6) ^ R(e, 11) ^ R(e, 25)ch = (e & f) ^ ((~e) & g)t1 = h + s1 + ch + K[j] + w[j]h = g & 0xffffffffg = f & 0xfffffffff = e & 0xffffffffe = (d + t1) & 0xffffffffd = c & 0xffffffffc = b & 0xffffffffb = a & 0xffffffffa = (t1 + t2) & 0xffffffffh0 = (h0 + a) & 0xffffffffh1 = (h1 + b) & 0xffffffffh2 = (h2 + c) & 0xffffffffh3 = (h3 + d) & 0xffffffffh4 = (h4 + e) & 0xffffffffh5 = (h5 + f) & 0xffffffffh6 = (h6 + g) & 0xffffffffh7 = (h7 + h) & 0xffffffffdigest = (h0 << 224) | (h1 << 192) | (h2 << 160) | (h3 << 128)digest |= (h4 << 96) | (h5 << 64) | (h6 << 32) | h7# print(hex(digest)[2:]) # .rjust(32, '0'))return hex(digest)[2:] # .rjust(32, '0')
if __name__=="__main__":aa='你好,中国'.encode('utf-8')print(Sha256sum(aa))print(len(Sha256sum(aa)))
消息摘要运行效果:
八:RSA公私钥生成
生成公钥与私钥:此步骤按照第五步
明文 M 加密 Me mod n= C
密文 C 解密 Cd mod n=M
#D、E和N的密钥生成
def generatePublicAndSecretKeys(size = 5):p, q = randPrime(size), randPrime(size) #生成一对不相等且足够大的质数N = p * q #计算p、q的乘积f = (p - 1) * (q - 1) #计算n的欧拉函数e = randGcd1(f) #选出一个与f互质的整数ed = liyuan(e, f)#计算出e对于f的模反元素d de mod f =1keys = {'d' : d, 'e' : e, 'n' : N} #得出公钥与私钥return keys
1、选择一对不相等且足够大的质数 p,q
#确定素数
def isPrime(num):if (num < 2):return Falseelse:i = 2flag=Truewhile i < num:# 如果num能被i整除,说明num不是质数if num % i == 0:# 只要num不是质数,将flag的值修改为 Falseflag = Falsei += 1return flag
#大质数生成
def randPrime(n):rangeStart = 10 ** (n-1) #10**4rangeEnd = (10 ** n) - 1 #10**5-1while True:num = random.randint(rangeStart, rangeEnd) #返回rangestart到rangeend任意一个数if isPrime(num): #判断是否是质数,如果是则生成return num
2、寻找与f互质的整数e,利用欧几里算法,如果值等于1·,那么这两个数互质
#寻找与f互质整数e
def randGcd1(b):rangeStart = 2rangeEnd = b - 1while True:num = random.randint(rangeStart, rangeEnd)if oujilide(num, b) == 1: #利用欧几里算法,如果值等于1,那么这个两个数互质return num
#欧几里得算法
def oujilide(a,b):if a > b:x = ay = belse:x = by = awhile True:if y == 0:return xelse:r = x % yx = yy = r
3、使用扩展欧几里得算法,计算出e对于φ(n)的模反元素d。
#扩展欧几里得算法,求逆元
def liyuan(a, n):x1, x2, x3 = 1, 0, ny1, y2, y3 = 0, 1, awhile y3 != 1 and y3 != 0 and y3 > 0:Q = math.floor(x3 / y3)t1, t2, t3 = x1 - Q * y1, x2 - Q * y2, x3 - Q * y3x1, x2, x3 = y1, y2, y3y1, y2, y3 = t1, t2, t3if y3 == 0:return 0if y3 == 1:if y2 >0:return y2else:return n+y2
4、SHA256算法得到消息摘要,并哈希值转为整型
将消息摘要进行数字签名与验证
#SHA256算法得到消息摘要
def hashing(M, size = 5):aa=zy.Sha256sum(M) #得到哈希值cc=int(aa, 16) % 10 ** (size * 2 - 2)#将哈希值转化为整型return cc
#将消息摘要进行签名
def signMessage(M, d, N):s = power(M, d, N) #使用私钥签名 hashM**d mod N 得到签名内容return s
#将得到
def verifySign(s, e,n):w = power(s, e, n)return w
九:实现数字签名的整体实现代码
摘要:zaiyao.py
def Sha256sum(message: bytes) -> bytes:# 定义常量# 前8个素数2..19的平方根的小数部分的前32位h0 = 0x6a09e667h1 = 0xbb67ae85h2 = 0x3c6ef372h3 = 0xa54ff53ah4 = 0x510e527fh5 = 0x9b05688ch6 = 0x1f83d9abh7 = 0x5be0cd19# 定义常数K 64# 前64个素数2..311的立方根的小数部分的前32位K = (0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2)# R为循环右移,# 右移之后可能会超过32位,所以要和0xffffffff做与运算,确保结果为32位。R = lambda x, n: ((x >> n) | (x << (32 - n))) & 0xffffffff# 大端 0x12,0x34,0x56,0x78 -> 0x12345678W = lambda i1, i2, i3, i4: (i1 << 24) | (i2 << 16) | (i3 << 8) | i4# 对每一个输入先添加一个'0x80',即'10000000', 即128ascii_list = list(map(lambda x: x, message))msg_length = len(ascii_list) * 8ascii_list.append(128)# 补充0while (len(ascii_list) * 8 + 64) % 512 != 0:ascii_list.append(0)# 最后64为存放消息长度,以大端数存放。# 例如,消息为'a',则长度为'0x0000000000000008'for i in range(8):ascii_list.append(msg_length >> (8 * (7 - i)) & 0xff)# print(ascii_list)# print(len(ascii_list)//64)for i in range(len(ascii_list) // 64): # 64*8=512bits# print(ascii_list[i*64:(i+1)*64])# 每个512bits的块进行循环w = []# 将512bits扩展到64*32bits=2048bits存入32位无符号数数组for j in range(16):s = i * 64 + j * 4w.append(W(ascii_list[s], ascii_list[s + 1], ascii_list[s + 2], ascii_list[s + 3]))for j in range(16, 64):s0 = (R(w[j - 15], 7)) ^ (R(w[j - 15], 18)) ^ (w[j - 15] >> 3)s1 = (R(w[j - 2], 17)) ^ (R(w[j - 2], 19)) ^ (w[j - 2] >> 10)w.append((w[j - 16] + s0 + w[j - 7] + s1) & 0xffffffff)# print(hex(s0)+':'+hex(s1)+':' + hex(R(w[j - 2], 17)))# 初始化a, b, c, d, e, f, g, h = h0, h1, h2, h3, h4, h5, h6, h7# for j in w:# print(hex(j)[2:])for j in range(64):s0 = R(a, 2) ^ R(a, 13) ^ R(a, 22)maj = (a & b) ^ (a & c) ^ (b & c)t2 = s0 + majs1 = R(e, 6) ^ R(e, 11) ^ R(e, 25)ch = (e & f) ^ ((~e) & g)t1 = h + s1 + ch + K[j] + w[j]h = g & 0xffffffffg = f & 0xfffffffff = e & 0xffffffffe = (d + t1) & 0xffffffffd = c & 0xffffffffc = b & 0xffffffffb = a & 0xffffffffa = (t1 + t2) & 0xffffffffh0 = (h0 + a) & 0xffffffffh1 = (h1 + b) & 0xffffffffh2 = (h2 + c) & 0xffffffffh3 = (h3 + d) & 0xffffffffh4 = (h4 + e) & 0xffffffffh5 = (h5 + f) & 0xffffffffh6 = (h6 + g) & 0xffffffffh7 = (h7 + h) & 0xffffffffdigest = (h0 << 224) | (h1 << 192) | (h2 << 160) | (h3 << 128)digest |= (h4 << 96) | (h5 << 64) | (h6 << 32) | h7# print(hex(digest)[2:]) # .rjust(32, '0'))return hex(digest)[2:] # .rjust(32, '0')
if __name__=="__main__":aa='你好,中国'.encode('utf-8')print(Sha256sum(aa))print(len(Sha256sum(aa)))
RSA公私钥生成:shuziqianming.py
import random
import math
import zhaiyao as zy
#确定素数
def isPrime(num):if (num < 2):return Falseelse:i = 2flag=Truewhile i < num:# 如果num能被i整除,说明num不是质数if num % i == 0:# 只要num不是质数,将flag的值修改为 Falseflag = Falsei += 1return flag
#大质数生成
def randPrime(n):rangeStart = 10 ** (n-1) #10**4rangeEnd = (10 ** n) - 1 #10**5-1while True:num = random.randint(rangeStart, rangeEnd) #返回rangestart到rangeend任意一个数if isPrime(num): #判断是否是质数,如果是则生成return num
#扩展欧几里得算法,求逆元
def liyuan(a, n):x1, x2, x3 = 1, 0, ny1, y2, y3 = 0, 1, awhile y3 != 1 and y3 != 0 and y3 > 0:Q = math.floor(x3 / y3)t1, t2, t3 = x1 - Q * y1, x2 - Q * y2, x3 - Q * y3x1, x2, x3 = y1, y2, y3y1, y2, y3 = t1, t2, t3if y3 == 0:return 0if y3 == 1:if y2 >0:return y2else:return n+y2#寻找与f互质整数e
def randGcd1(b):rangeStart = 2rangeEnd = b - 1while True:num = random.randint(rangeStart, rangeEnd)if oujilide(num, b) == 1: #利用欧几里算法,如果值等于1,那么这个两个数互质return num
#欧几里得算法
def oujilide(a,b):if a > b:x = ay = belse:x = by = awhile True:if y == 0:return xelse:r = x % yx = yy = r
#从数字幂快速搜索模块
def power(x, n, mod): #x**n mod modif n == 0:return 1elif n % 2 == 0:p = power(x, n / 2, mod)return (p * p) % modelse:return (x * power(x, n - 1, mod)) % mod
#D、E和N的密钥生成
def generatePublicAndSecretKeys(size = 5):p, q = randPrime(size), randPrime(size) #生成一对不相等且足够大的质数N = p * q #计算p、q的乘积f = (p - 1) * (q - 1) #计算n的欧拉函数e = randGcd1(f) #选出一个与f互质的整数ed = liyuan(e, f)#计算出e对于f的模反元素d de mod f =1keys = {'d' : d, 'e' : e, 'n' : N} #得出公钥与私钥return keys
#SHA256算法得到消息摘要
def hashing(M, size = 5):aa=zy.Sha256sum(M) #得到哈希值cc=int(aa, 16) % 10 ** (size * 2 - 2)#将哈希值转化为整型return cc
#将消息摘要进行签名
def signMessage(M, d, N):s = power(M, d, N) #使用私钥签名 hashM**d mod N 得到签名内容return s
#将得到
def verifySign(s, e,n):w = power(s, e, n)return w
十:运行截图
1、对汉字的数字签名运行结果
2、对英文hello world 进行数字签名的运行结果
3、对后缀为.png图片生成数字签名的运行结果
4、对后缀为.doc的文档进行数字签名的运行结果
十一:整体代码
此处没有写出主函数,如要参考,请参考github
如要参加完整代码、ppt讲解、文档:请参考(纯算法,没有调用库)python实现RSA数字签名代码、ppt、文档.rar
最后:如有任何问题,可以私信博主
看到博主编写文章不容易,不点赞,关注,收藏在走吗???
python实现RSA数字签名(纯算法实现)相关推荐
- Python实现RSA数字签名一
利用Python实现RSA数字签名的产生和验证过程. 任务1:准备一个私钥文件,一个公钥文件,一个数据文件: 任务2:定义一个函数,能够使用指定的私钥对数据文件进行签 名,并将签名结果输出到文件返 ...
- 【算法】位运算符基础之某CTF赛题使用Python与易语言纯算法还原
什么是位运算? 程序中的所有数在计算机内存中都是以二进制的形式储存的.位运算就是直接对整数在内存中的二进制位进行操作.比如,and运算本来是一个逻辑运算符,但整数与整数之间也可以进行and运算.举个例 ...
- python实现rsa加密解密代码_使用python实现rsa算法代码
RSA算法是一种非对称加密算法,是现在广泛使用的公钥加密算法,主要应用是加密信息和数字签名. 维基百科给出的RSA算法简介如下: 假设Alice想要通过一个不可靠的媒体接收Bob的一条私人讯息.她可以 ...
- python基于rsa的数字签名实现_OpenSSL和Python实现RSA Key数字签名和验证
OpenSSL和Python实现RSA Key数字签名和验证,基于非对称算法的RSA Key主要有两个用途,数字签名和验证(私钥签名,公钥验证),以及非对称加解密(公钥加密,私钥解密).本文提供一个基 ...
- python实现rsa加密源代码_python实现RSA加密(解密)算法
RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密标准. 今天只有短的RSA钥匙才可能被强力方式解破.到2008年为止,世界上还没有任何可靠 ...
- java和c 的rsa加密算法_RSA算法签名技术Java与C++统一(加密解密结果一样)
RSA算法签名技术Java与C++统一 (加密解密结果一样) 源代码下载地址:http://www.doczj.com/doc/64f44a94a0116c175f0e484d.html/produc ...
- RSA算法和RSA数字签名算法的实现
RSA算法和RSA数字签名算法的实现 http://blog.chinaunix.net/uid-21880738-id-1813146.html 顾婷婷 李涛 (四川大学计算机系(西区) 成都 61 ...
- Python的RSA签名
Python的rsa签名方法 需要安装pycrypto pip install pycrypto python代码 #!-*- coding:utf-8 -*-from Crypto.PublicKe ...
- 用实例给新手讲解易懂的RSA加密解密算法
用实例给新手讲解易懂的RSA加密算法 RSA加密算法是最常用的非对称加密算法,CFCA在证书服务中离不了它.我查过论坛上很少这方面的介绍,恰好看到一本书中作者用实例对它进行了简化而生动的描述,使得高深 ...
最新文章
- 进程外Session和进程内Session存储
- year range in CRM Fiori Sales Pipeline
- 还驾驭不了4核? 别人已模拟出百万核心上的并行
- Python连接telnet客户端连接服务端程序
- 360回归A股,周鸿祎来给BAT和小米添堵了
- EMC测量的常用计量单位分贝(dB)及其换算
- C#/ASP.NET完善的DBHelper,配套Model生成器
- 天瑞地安科技集团:APP软件如何优化
- Join()--用法
- I2C 总线详解-转
- C# Spire操作Word文档生成PDF或JPG格式
- ERROR: Cannot uninstall ‘certifi‘. It is a distutils installed project and thus we cannot accurately
- 网络设置错误造成cluster不能启动(oifcfg setif cluster_interconnect )
- codeforces 1526 C
- 【WPF、UWP】搜索蓝牙设备
- 电压源电流源电路符号及2B法
- html/css简单描述
- 计算机维护专业自我鉴定,2016届计算机系统维护专业大学生毕业自我鉴定优秀范文...
- 人工智能领域期刊和顶会(中国计算机学会推荐国际学术期刊-2019)
- Oracle如何查询大于1的结果,ORACLE的一些查询
热门文章
- win10电脑没有声音(电脑放音乐没声音怎么回事)
- 推荐系统:电商推荐系统架构
- vm虚拟机安装以及镜像和网路配置
- Win32_NetworkAdapter 网卡 参数说明
- 【plang 1.4.6】Plang高级编程语言(发布)
- ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.
- JS临时死区(TDZ)
- 数据库 之 round函数
- 网站seo诊断,网站seo诊断方法
- PDF批量反色打印解决方案