查看 Pretty Good Privacy 的详细信息,我对使用会话密钥和通过 RSA 使用收件人的公钥加密消息的原因感到困惑。我看不出这比直接对实际消息进行 RSA 加密更安全。
很可能是我在安全和加密方面太天真了,但是如果我能获得你的 RSA 私钥,那我是否可以访问消息正文或其他可以解锁消息的密钥又有什么关系呢?身体?
查看 Pretty Good Privacy 的详细信息,我对使用会话密钥和通过 RSA 使用收件人的公钥加密消息的原因感到困惑。我看不出这比直接对实际消息进行 RSA 加密更安全。
很可能是我在安全和加密方面太天真了,但是如果我能获得你的 RSA 私钥,那我是否可以访问消息正文或其他可以解锁消息的密钥又有什么关系呢?身体?
RSA 并不是真正为加密大段明文而构建的。每个 RSA“轮”可以加密 117 字节的数据,要加密更多,你必须使用一些链接模式。目前,这意味着额外的开销、缓慢(请记住,RSA 非常慢)和安全不确定性(RSA-chaningMode 尚未作为其他类型的加密方案进行审查)。<-不好
通过使用混合加密(对称+非对称对称密钥),您可以获得非对称的好处,即不必担心交换密钥;并且对称,速度非常快,并且经过了严格的审查。<- 好
有关更多信息,请查看:如何使用非对称加密(例如 RSA)来加密任意长度的明文?
RSA 是一种慢速算法,尤其是对于解密而言。您必须计算m = c^d mod N
其中 d 大致是模数的大小。对于 RSA-1024/2048/4096(其中 1024/2048/4096 是模数 N 的字节数),即使通过重复平方进行快速模幂运算,它也需要大约 1.5 lg N (~1500/~3000/~6000) N位整数的乘法。N 位整数的乘法不是原始操作,但对于 n 位整数,其缩放比例为 O(N^1.6)(因此每次乘法大约比 64 位乘法原语慢 84/256/776 倍)。
注意:RSA 加密比解密要快得多,因为使用适当的填充,选择像 e=65537 这样的小公钥指数没有任何弱点,您的求幂步骤只需要约 17 次乘法;事实上 e=3 只需要两次乘法就可以在适当的填充下完美地工作。
此外,b 位 RSA 的一种应用意味着您必须生成两个大致具有该长度的素数)只能解密最多大约 b-128 位(使用 128 位的随机填充)或大约 112/240/ 496 个字节,因为您只能对 0 到 N 之间的数字进行唯一解码。这个使用 8 位 ASCII 编码的答案需要 7449 个字节。
因此,通常发生的情况是您只需加密对称密钥,因为对称密钥会更快,并且使用加密模式(例如,CBC、CTR)可以加密任意大小的消息。
证明上述分析合理的时间信息
我随机创建了一个 4096 位 RSA 密钥对 (N,e) 和 (N,d)(使用import rsa; rsa.newkeys(nbits=4096)
),然后对“教科书”(弱 - 没有填充随机字节)RSA 加密/解密与 AES 加密进行比较。
计时 RSA 的设置如下加密/解密:
>>> encryption = "pow(m,e,N)"
>>> decryption = "pow(c,d,N)"
>>> setup = """N = 650022002033202164791638561174816123258916492020045683486079596172818033518252144860009135975860423604411399274822133841546708494765449009472683563317182198759309684914356008659922538583279515419473227210977474088638850782595732910857797571156318425669817244314450827318145758475770172116229990603884173470104902799641093008914867436680133631971559712828465243806241512864086546090728020169682901285441149037545107765998640217114788723715575098893307499744794060936075307573516246976128444578474543743151813381476261995022381469996299316134038828450334308951123635607639233510908350391842701316709834010335732144351192249236953330927673972067894385163240378028762678952835544528074929660347162229394149982403274141710996800529609644247153732931740566379474447028470447165473543764460712691302632667942726305345440613110797535733482536487121364569032950159436896042439632454394173272272373593467791776848612054118558722637286863035388661691125609333937871344046274188817824229897604542696165894703693345718292207375417809743565484488696015562272970275127353631067240331072960179800283770558952793368549384848991363395614092594834723746484679602433778811932755737361396956451757817273043140309312219744371338360280947729616063853738641
e = 65537
d = 163256819101675505935126275492278764497028632049833711493978518287449606050176698725845711249563797130302144316394896364372168726426893063398086141449880510117616574052677112204439095245140620165777031598832556014144612720776443287192263118867708337069520909431555619232749121633751575949969416441703671137188560661642925231956585104715733090960096935672315454064890600755952584778878850298197667808388563912873529057301030226798746088352508752731797937742028323588321094384242144517250929974849184277771012531228150089843422784017258750683831715157735366668225506845014904307329056055723208632277201486994123654288753880392020556964543443597828229493325467616467308118864809565200248599428162198788308364968380312577988297310500286974136209331121821893719017982045957945241683426015912952303440579610553088057331105677592306795472995839704525934407019706992740653789223747419222232691256397983243926154436341276701812083801394467756814989897357722664273229362826815661611670740504736110949984419908621950792127278167263015929591331285519955311164883226113070661776218890597216617564529563317568347817244502395174702566462626236588608296425638109493962233171124725237010153412040065506840529586822535766472003407847003802265141256193
m= int("This is a secret message provided as a test for RSA-4096 and AES encryption. To be fair it is 496 bytes long. Keyboard mashing follows: asdbklajsdgl;sdjgl;kasjdgiowepgj opijg aslekgjase;lgk jasel; elask;gj lask;lgjl;ske gjasle;k asel;k gjl;asekgj asljsel;k jseal; gkjasel; kjsael;kjg aal;se asegkl;j asel;kjasegl;k jasegl;k jasel;gk jasegl;k jasel gkjsael;gkjasel;gkj sea;lekgjseal;kjasegl;asekgjasel;kgjsael;jkasel;gjkgasel;asfasfl;kjasf;lkjsadfkljdl;kfaskl;jfasldk;fkfkjl;akasdfasdfasdfkjkjga".encode('hex'),16)
c = pow(m, e, N)
"""
>>> import timeit
>>> timeit.repeat(encryption, setup=setup, repeat=3, number=1)
[0.0044488906860351562, 0.0045480728149414062, 0.0044538974761962891]
>>> timeit.timeit(decryption, setup=setup, repeat=3, number=1)
[1.9231810569763184, 1.9048171043395996, 1.9280149936676025]
4096 位 RSA 加密每次运行需要约 4.5 毫秒,解密需要 1.9 秒(比加密慢约 500 倍,因为 d 远大于 e)。用 RSA 加密这条消息需要 16 个块,解密大约需要 30 秒。重复其他 RSA 大小,我发现 1024 位 RSA 需要大约 0.5 毫秒的加密时间和 40 毫秒的解密时间。2048 位 RSA 加密大约需要 1.6 毫秒,解密大约需要 270 毫秒。
现在让我们看看使用 pycrypto 库的 AES-128。
setup = """
from Crypto.Cipher import AES
from Crypto import Random
from hashlib import sha256
def aes_encrypt(plaintext, key):
block_size = AES.block_size
iv = Random.new().read(block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
pad_len = block_size - (len(plaintext) % block_size)
padding = ''.join([chr(pad_len)]*pad_len)
encrypted_msg = iv + cipher.encrypt(plaintext + padding)
return encrypted_msg
def aes_decrypt(encrypted_msg, key):
block_size = AES.block_size
iv = encrypted_msg[:block_size]
cipher = AES.new(key, AES.MODE_CBC, iv)
padded_msg = cipher.decrypt(encrypted_msg[block_size:])
pad_len = ord(padded_msg[-1])
msg = padded_msg[:len(padded_msg)-pad_len]
return msg
key = sha256("secret passphrase").digest()[0:16]
message = "This is a secret message provided as a test for RSA-4096 and AES encryption. To be fair it is 496 bytes long. Keyboard mashing follows: asdbklajsdgl;sdjgl;kasjdgiowepgj opijg aslekgjase;lgk jasel; elask;gj lask;lgjl;ske gjasle;k asel;k gjl;asekgj asljsel;k jseal; gkjasel; kjsael;kjg aal;se asegkl;j asel;kjasegl;k jasegl;k jasel;gk jasegl;k jasel gkjsael;gkjasel;gkj sea;lekgjseal;kjasegl;asekgjasel;kgjsael;jkasel;gjkgasel;asfasfl;kjasf;lkjsadfkljdl;kfaskl;jfasldk;fkfkjl;akasdfasdfasdfkjkjga"
cipher = aes_encrypt(message,key)
"""
>>> timeit.repeat("aes_encrypt(message,key)", setup=setup, repeat=3, number=1)
[6.198883056640625e-05, 6.198883056640625e-05, 6.008148193359375e-05]
>>> timeit.repeat("aes_decrypt(cipher,key)", setup=setup, repeat=3, number=1)
[1.0967254638671875e-05, 9.059906005859375e-06, 9.059906005859375e-06]
底线,AES 加密比 RSA 快大约8/25/500倍,AES 解密比 RSA 快大约4000/25000/200000倍(分别为 1024/2048/4096 位加密)。因此,只要您需要多个块,混合加密总是有意义的。
来自我 3 年前写的一段话:“我们生活中的每一天都使用两种基本类型的加密。每个人都在使用它们。甚至是我的祖母。
类型 1 是对称的,又名单密钥,又名秘密密钥,又名超快...注意所有 S 词是如何组合在一起的 :)
类型 2 是非对称的,又名公钥/私钥(或简称为公钥),又名两密钥,又名非常慢的加密。这是人们在使用 PGP 时通常认为他们正在使用的东西。(他们是,但他们也使用对称加密来完成大部分工作)” http://www.infosecisland.com/blogview/4497-Public-Key-Private-Key-Secret-Key-Everyday-Encryption- .html
基本上,正如 jimbob 博士在上面展示的那样,公钥太慢而无法用于批量加密。将它与 Symmetric 混合可以解决这个问题,我们不单独使用对称的原因是存在密钥交换问题。我们如何确保我们的预期接收者获得密钥并且他们是唯一获得密钥的人?
这很有用的另一个原因是因为它增加了灵活性。例如,仅加密会话密钥允许您以相对较低的开销使用多个密钥对。您只需为每个密钥对添加一个加密的会话密钥。