iT邦幫忙

2021 iThome 鐵人賽

DAY 22
0
自我挑戰組

一個月的演算法挑戰系列 第 22

Day22:安全性和演算法-公開金鑰密碼系統(Public-key Cryptosystem)

前言

前一天有提到共用金鑰密碼系統(Shared-key CryptoSystem),又稱為「對稱密鑰演算法」(Symmetric-key algorithm),稍微複習一下,Shared-key CryptoSystem是公鑰與私鑰為同一把「key」的系統,安全性相較於今天所提到的Public-key Cryptosystem來得低。


公開金鑰密碼系統(Public-key Cryptosystem)

公開金鑰密碼系統(Public-key Cryptosystem)又稱為非對稱式密碼學(Asymmetric cryptography)。與前一天提到的共同金鑰密碼系統不同,他擁有兩把「key」,分別用於加密(公鑰,Publickey)與解密(私鑰,Privatekey),安全性比共同金鑰密碼來得高,看看下圖:

參考資料:基礎密碼學(對稱式與非對稱式加密技術

從上圖可以看到,公鑰與私鑰非同一把,有心人士拿到了公鑰,也無法解讀訊息,大大加深了安全性。

公鑰(Publickey)與私鑰(Privatekey)為一組,Publickey加密後,必須有對應的Privatekey才能進行解密。

優點:安全性高,僅拿到Publickey是無法解密。
缺點:加密和解密花費時間長、速度慢,只適合對少量資料進行加密。不適用於連續傳輸零碎數據的情況,若要解決連續傳輸零碎數據,解決方法是使用「混成密碼系統」

可信度-數位憑證

關於公開金鑰可信度問題的成因是,A無法判斷接收到的公開金鑰製作者是否為為B,需要利用「數位憑證」。關於「數位憑證」,之後會有進一步探討。


公開金鑰密碼系統使用的演算法為「RSA加密」和「橢圓曲線密碼學」(elliptic-curve cryptography),接下來我們來看看使用Python和JavaScript如何實現:

Python在Public-key Cryptosystem的實現

要找出實現公開金鑰密碼系統演算法必要條件:

  1. 能用某個數加密(計算)數據
  2. 能用其他的數來計算還原原始數據
  3. 能防止從一個金鑰推算出另一個金鑰的運算

產生RSA 金鑰:

from Crypto.PublicKey import RSA

# 產生 2048 位元 RSA 金鑰
key = RSA.generate(2048)

# RSA 私鑰
privateKey = key.export_key()
with open("private.pem", "wb") as f:
    f.write(privateKey)

# RSA 公鑰
publicKey = key.publickey().export_key()
with open("public.pem", "wb") as f:
    f.write(publicKey)

讀取 RSA 金鑰:

from Crypto.PublicKey import RSA

# 讀取
encodedKey = open("private.pem", "rb").read()

# 解密
key = RSA.import_key(encodedKey)

# 輸出 RSA 私鑰
print(key.export_key().decode('utf-8'))
print(key.publickey().export_key().decode('utf-8'))

RSA 金鑰解密資料:

from Crypto.PublicKey import RSA
from Crypto.Cipher import AES, PKCS1_OAEP

# 讀取 RSA 私鑰
privateKey = RSA.import_key(open("private.pem").read())

# 從檔案讀取加密資料
with open("encrypted_data.bin", "rb") as f:
    encSessionKey = f.read(privateKey.size_in_bytes())
    nonce = f.read(16)
    tag = f.read(16)
    ciphertext = f.read(-1)

# 以 RSA 金鑰解密 Session 金鑰
cipherRSA = PKCS1_OAEP.new(privateKey)
sessionKey = cipherRSA.decrypt(encSessionKey)

# 以 AES Session 金鑰解密資料
cipherAES = AES.new(sessionKey, AES.MODE_EAX, nonce)
data = cipherAES.decrypt_and_verify(ciphertext, tag)

# 輸出解密後的資料
print(data.decode("utf-8"))

JavaScript在Public-key Cryptosystem的實現

安裝node.js後,在安裝npm,利用npm安裝下列庫:

npm install tweetnacl tweetnacl-util

//import the libraries
const nacl = require('tweetnacl');
nacl.util = require('tweetnacl-util');

//Generate the keys
const david = nacl.box.keyPair();
const viktoria = nacl.box.keyPair();

//encrypting the message
function davidEncrypting(){
    const one_time_code = nacl.randomBytes(24);

    //Get the message from david
    const plain_text = "Hello there Viktoria";

    //Get the cipher text
    const cipher_text = nacl.box(
        nacl.util.decodeUTF8(plain_text),
        one_time_code,
        viktoria.publicKey,
        david.secretKey
    );

    //message to be sent to Viktoria
    const message_in_transit = {cipher_text,one_time_code};

    return message_in_transit;
    
};

//decrypting the message
function viktoriaDecrypting(message){
    //Get the decoded message
    let decoded_message = nacl.box.open(message.cipher_text, message.one_time_code, david.publicKey, viktoria.secretKey);

    //Get the human readable message
    let plain_text = nacl.util.encodeUTF8(decoded_message)

    //return the plaintext
    return plain_text;
};

參考資料:Implementing Public Key Cryptography in JavaScript


上一篇
Day21:安全性和演算法-共用金鑰密碼系統(shared-key crypto system)
下一篇
Day23:安全性和演算法-混成密碼系統(Hybrid Cryptosystem)
系列文
一個月的演算法挑戰30

尚未有邦友留言

立即登入留言