希爾密碼(hill密碼)

ckxllf發表於2019-12-24

  簡述

  希爾密碼是利用矩陣進行加密的一種加密演算法,其本質是一種多表代換密碼。

  百科:

  希爾密碼是運用基本矩陣論原理的替換密碼,由Lester S. Hill在1929年發明。

  每個字母當作26進位制數字:A=0, B=1, C=2… 一串字母當成n維向量,跟一個n×n的矩陣相乘,再將得出的結果模26。

  注意用作加密的矩陣(即密匙)在 必須是可逆的,否則就不可能解碼。只有矩陣的行列式和26互質,才是可逆的。

  希爾密碼由於採用矩陣運算加密,因此在相同的明文加密時,可能會出現不同的密文,因此可以很好的抵禦字母頻率攻擊法。

  加解密

  加密:

  1、定義一個矩陣a(須存在逆矩陣)作為加密金鑰:

  [1,2,1]

  [0,2,1]

  [1,0,2]

  2、將需要加密的明文字母轉換為其對應的字母表數字(1-a,2-b……);

  3、將轉換後的明文數字序列按照金鑰矩陣的階數進行分組(如本次為3個字元一組);

  4、每組數字序列和金鑰矩陣進行矩陣的乘法運算(1x3 矩陣乘以 3x3矩陣),結果即為密文數字序列;

  5、可將密文數字序列轉換為其對應字母,即為密文字串。

  解密:

  解密流程與加密相同,唯一不同之處在於:需先求出加密金鑰的逆矩陣

  在做矩陣相成時,用密文分組乘以逆矩陣,結果即為明文

  程式碼實現

  #!/usr/bin/python3.7

  # -*- coding: utf-8 -*-

  # @Time : 2019/12/11 14:53

  # @Author : SystemDefenser

  # @Email : mrwx1116@163.com

  # @Software: PyCharm

  from numpy import linalg

  # 輸入矩陣並判斷是否存在逆矩陣

  def inputMatrix():

  while True:

  # 輸入一行、作為行列式的階數和行列式的第一行

  rank = list(input("").split())

  matrix = [[0] * len(rank) for i in range(len(rank))]

  matrix[0] = rank

  # 輸入行列式剩餘資料

  for i in range(1, len(matrix)):

  matrix[i] = list(input("").split())

  # 判斷每一行輸入是否合法

  if len(matrix[i]) != len(matrix):

  print("輸入有誤,重新輸入。")

  continue

  # 轉換字元型為整型

  for i in range(len(matrix)):

  matrix[i] = list(map(lambda x: int(x), matrix[i]))

  # 判斷是否存在逆矩陣

  if not judgeInverse(matrix):

  print("矩陣不存在逆矩陣,重新輸入。")

  continue

  return matrix

  # 判斷是否存在逆元

  def judgeInverse(matrix):

  try:

  linalg.inv(matrix)

  except:

  return False

  return True

  # 生成金鑰(矩陣的逆矩陣)

  def createMatrixInverse(matrix):

  try:

  matrix_inverse = linalg.inv(matrix)

  except:

  return -1

  return matrix_inverse

  # 生成訊息分組

  def createMassageList(massage, matrix):

  matrixRank = len(matrix)

  massageList = []

  # 擴充訊息序列並建立分組

  while len(massage) % matrixRank != 0:

  massage += " "

  for i in range(1, len(massage) + 1, matrixRank):

  massageList.append(massage[i-1:i + matrixRank - 1])

  return massageList

  # 字母序列轉化為數字

  def letterToDigit(massageList):

  massageDigitList = [] # 替換後的數字列表

  letterList = [] # 字母列表

  for i in range(ord("a"), ord("z") + 1):

  letterList.append(chr(i))

  for i in range(10):

  letterList.append(str(i))

  # 新增空格,解決分組填充問題

  letterList.append(" ")

  # 替換字母為數字

  for massage in massageList:

  listTmp = []

  for i in range(len(massage)):

  listTmp.append(letterList.index(massage[i]))

  massageDigitList.append(listTmp)

  return massageDigitList

  # 數字序列轉化為字母

  def digitToLetter(massageList):

  massageLetterList = [] # 還原後的字母列表

  letterList = []

  for i in range(ord("a"), ord("z") + 1):

  letterList.append(chr(i))

  for i in range(10):

  letterList.append(str(i))

  letterList.append(" ")

  # 替換數字為字母

  for massage in massageList:

  massageLetterList.append(letterList[massage % 37])

  return massageLetterList

  # 加密

  def encrypt(massage, matrix):

  ciphertextList = [] # 加密結果列表

  massageList = createMassageList(massage, matrix)

  massageDigitList = letterToDigit(massageList)

  # 矩陣相乘

  for massageDigit in massageDigitList:

  for i in range(len(massageDigit)):

  sum = 0

  for j in range(len(massageDigit)):

  sum += massageDigit[j] * matrix[j][i % len(matrix)]

  ciphertextList.append(sum % 37)

  return ciphertextList

  # 解密

  def decrypt(massage, matrix):

  plaintextList = [] # 解密結果列表

  matrix_inverse = createMatrixInverse(matrix)

  massageList = createMassageList(massage, matrix)

  # 矩陣相乘

  for msg in massageList:

  for i in range(len(msg)):

  sum = 0

  for j in range(len(msg)):

  sum += msg[j] * matrix_inverse[j][i % len(matrix)]

  plaintextList.append(sum % 37)

  # 浮點型轉換為整型(採用四捨五入——round())

  plaintextList = list(map(lambda x: int(round(x)), plaintextList))

  plaintextList = digitToLetter(plaintextList) # 數字轉換為字母

  plaintext = ""

  for item in plaintextList:

  plaintext += item

  return plaintext

  if __name__ == "__main__":

  while True: 鄭州婦科醫院

  print("—————希爾密碼—————")

  choice = input("1、加密 2、解密\n請選擇:")

  if choice == "1":

  print("輸入矩陣:")

  matrix = inputMatrix()

  massage = input("輸入msg:")

  massageList = createMassageList(massage, matrix)

  ciphertextList = encrypt(massage, matrix)

  print("加密結果:", ciphertextList)

  elif choice == "2":

  massageList = list(map(int, list(input("輸入密文序列:").split(","))))

  print("輸入矩陣:")

  matrix = inputMatrix()

  matrix_inverse = createMatrixInverse(matrix)

  print("逆矩陣:")

  for item in matrix_inverse:

  print(item)

  plaintext = decrypt(massageList, matrix)

  print("解密結果:", plaintext)

  其中,求逆矩陣部分未能手算,呼叫了numpy庫中的linalg函式,慚愧………………

  加密測試

  —————希爾密碼—————

  1、加密 2、解密

  請選擇:1

  輸入矩陣:

  1 0 1 1

  0 1 0 1

  1 1 0 0

  1 1 0 1

  輸入msg:systemdefenser

  加密結果: [18, 24, 18, 24, 11, 19, 4, 20, 36, 35, 5, 27, 2, 15, 4, 20]

  —————希爾密碼—————

  1、加密 2、解密

  請選擇:2

  輸入密文序列:18, 24, 18, 24, 11, 19, 4, 20, 36, 35, 5, 27, 2, 15, 4, 20

  輸入矩陣:

  1 0 1 1

  0 1 0 1

  1 1 0 0

  1 1 0 1

  逆矩陣:

  [ 0. -1. 0. 1.]

  [ 0. 1. 1. -1.]

  [ 1. 1. 1. -2.]

  [ 0. 0. -1. 1.]

  解密結果: systemdefenser

  加密:

  解密:

  部分程式碼詳解

  輸入矩陣部分

  程式碼片段:

  while True:

  # 輸入一行、作為行列式的階數和行列式的第一行

  rank = list(input("").split())

  matrix = [[0] * len(rank) for i in range(len(rank))]

  matrix[0] = rank

  # 輸入行列式剩餘資料

  for i in range(1, len(matrix)):

  matrix[i] = list(input("").split())

  # 判斷每一行輸入是否合法

  if len(matrix[i]) != len(matrix):

  print("輸入有誤,重新輸入。")

  continue

  該片段位於 inputMatrix() 函式中。

  輸入矩陣部分未讓使用者先定義階數,而是透過使用者輸入的矩陣第一行,來決定本次矩陣的階數,並且不斷進行合法判斷。

  解密部分

  程式碼片段:

  # 浮點型轉換為整型(採用四捨五入——round())

  plaintextList = list(map(lambda x: int(round(x)), plaintextList))

  plaintextList = digitToLetter(plaintextList) # 數字轉換為字母

  該片段位於 decrypt(massage, matrix) 函式中。

  由於逆矩陣存在不可約分或整除的小數,因此在此處採用四捨五入round(x) 的方法不嚴謹地解決此問題。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69945560/viewspace-2670140/,如需轉載,請註明出處,否則將追究法律責任。

相關文章