Lua和RSA加密

4
我正在使用Lua编写RSA加密脚本,借助于BigNumbers(http://oss.digirati.com.br/luabignum/bn/index.htm),现在基本上已经有一个可用的代码。然而,我遇到了麻烦,因为在一小部分情况下,加密后的原始消息无法解密为原始消息,我无法找出原因。请注意,这将处理非常大的数字(例如1.08e107)。下面是我编写的完整代码,但这里是它应该执行的步骤。
print(rsa_getkey())

p: 83
q: 23
n: 1909
e: 19
d: 1899
phi: 1804

上述代码设置了关键值,其中公钥由[n,e]表示,私钥由[n,d]表示。以下是相应的代码:

function rsa_getkey()
  rsa_e = 0
  local primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 57, 71, 73, 79, 83, 89, 97}

  math.randomseed = os.time()

  rsa_p = primes[math.random(5,#primes)]
  rsa_q = rsa_p

  while rsa_q == rsa_p do
      math.randomseed = os.time()
      rsa_q = primes[math.random(5,#primes)]
  end

  rsa_n = rsa_p*rsa_q
  rsa_phi = (rsa_p-1)*(rsa_q-1)

  while rsa_e == 0 do
      local prime = primes[math.random(1,10)]
      if rsa_phi%prime > 0 then
          rsa_e = prime
      end
  end

  for i = 2, rsa_phi/2 do
      if ((i*rsa_phi)+1)%rsa_e == 0 then
          rsa_d = ((i*rsa_phi)+1)/rsa_e
          break
      end
  end
  return "p: ",rsa_p,"\nq: ",rsa_q,"\nn: ",rsa_n,"\ne: ",rsa_e,"\nd: ",rsa_d,"\nphi: ",rsa_phi,"\n"
end

在确定了密钥之后,您可以加密消息。为了将纯文本(“Hello world”)转换为数字系统,我创建了一个函数,它并不完全,但以最基本的形式工作:

print(rsa_plaintext("Hello_world"))

1740474750625850534739

以下函数是确定该消息的方法:
function rsa_plaintext(x)
  local alphanum = {A=10, B=11, C=12, D=13, E=14, F=15, G=16, H=17, I=18, J=19, K=20, L=21, M=22, N=23, O=24, P=25, Q=26, R=27, S=28, T=29, U=30, V=31, W=32, X=33, Y=34, Z=35, a=36, b=37, c=38, d=39, e=40, f=41, g=42, h=43, i=44, j=45, k=46, l=47, m=48, n=49, o=50, p=51, q=52, r=53, s=54, t=55, u=56, v=57, w=58, x=59, y=60, z=61, _=62}
      rsa_cipher = ""
  for i = 1, #x do
      local s = x:sub(i,i)
      rsa_cipher = rsa_cipher .. alphanum[s]
  end
  return rsa_cipher
end

最后,为了使这更易于管理,我必须将其分解成段。为了节省时间和代码,我已将实际加密与将明文转换为数字格式的加密相结合,尽管出于调试目的添加了解密功能。该代码还考虑在消息中添加0以确保每个分组中有4位数。这就是我的问题所在;消息和解密应该是相同的。
print(rsa_group("Hello world"))

Msg: 1740
Encrypted: 1560
Decrypted: 1740

Msg: 4747
Encrypted: 795
Decrypted: 929

Msg: 5062
Encrypted: 1659
Decrypted: 1244

Msg: 5850
Encrypted: 441
Decrypted: 123

Msg: 5347
Encrypted: 429
Decrypted: 1529

Msg: 3900
Encrypted: 1244
Decrypted: 82

这可以通过以下两个函数实现:

function rsa_group(str)
  local cipher = {}
  local str = rsa_plaintext(str:gsub(" ","_"))
  local len = #str
  local fillin = ""
  if len%4 ~= 0 then
      fillin = string.rep(0,(4-len%4))
  end
  str = str..fillin
  for i = 1, #str, 4 do
      local s,e = i, i+3
      local part = str:sub(s,e)
      print(rsa_encrypt(part))
  end
end

function rsa_encrypt(msg)
  bnrsa_e = BigNum.new(rsa_e)
  bnrsa_n = BigNum.new(rsa_n)
  bnmsg = BigNum.new(msg)
  result = 0
  quo = BigNum.new()
  rsa_c = BigNum.new()
  result = BigNum.pow(bnmsg, bnrsa_e)
  BigNum.div(result, bnrsa_n, quo, rsa_c)

  bnrsa_c = BigNum.new(rsa_c)
  bnrsa_d = BigNum.new(rsa_d)
  result = 0
  quo = BigNum.new()
  rsa_C = BigNum.new()
  result = BigNum.pow(bnrsa_c, bnrsa_d)
  BigNum.div(result, bnrsa_n, quo, rsa_C)

  return "Msg:",msg,"\nEncrypted:",rsa_c,"\nDecrypted:",rsa_C,"\n"
end

现在,我知道这是一个很长的问题,并且问题本身有许多组成部分。我只是不知道如何找出我的问题所在。我是否遗漏了什么?一双新的眼睛可能是我的解决方案。


1
为了确保全局变量不会引起微妙的错误,把 RSA 参数打包成一个表格然后传递该表格如何? - greatwolf
3
RSA加密要求被加密的信息M与RSA模数互质。使用普通的加密参数,该事件的概率非常小,不值得检查,但是当你使用这么小的数字时,这种情况会经常发生。 - President James K. Polk
1
这段代码是为了娱乐还是为了安全?像教科书上的RSA算法只是用于教育,实际上并不安全。 - CodesInChaos
4
@GregS:并没有要求M与模数互质,但是指数必须与phi(n)互质。 - Iridium
1
代码主要是为了好玩,而不是为了安全。@lhf,我肯定会查看它,尽管我对使用makefile构建有一种厌恶的关系,哈哈。我认为greatwolf说得很对。我没有想到M必须小于n。今天学到了东西! - Josh
显示剩余4条评论
1个回答

2
经过仔细检查,看起来消息 M 必须小于你的两个质数的乘积 less than the product n。在您上面的测试用例中,除第一个外,所有消息都无法正确解密,因为它们大于 n = 1909
例如,考虑当 M 刚好超过 n = 1909 的情况:
Msg: 1910
Encrypted: 1
Decrypted: 1

Msg: 1911
Encrypted: 1222
Decrypted: 2

Msg: 1912
Encrypted: 1179
Decrypted: 3

在现实世界的例子中,n 当然会大得多,因此这个问题出现的可能性要小得多。

处理长消息,只需将其分成小块即可。 - lhf
2
@lhf 不要为RSA发明一个ECB模式。那样会很慢且不安全。创建一个随机的AES密钥,使用AES加密实际消息和RSA加密AES密钥。 - CodesInChaos
@CodesInChaos,真正的RSA具有随机填充,因此即使在块之间没有交互,它也不完全与ECB相同。 - finnw
@finnw 我知道。这就是为什么我说“类ECB”而不是“ECB”。它仍然是一个可疑的模式。 - CodesInChaos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接