公鑰私鑰加密和SHA256
公鑰和私鑰
公鑰(Public Key)與私鑰(Private Key)是通過一種演算法得到的一個金鑰對(即一個公鑰和一個私鑰),公鑰是金鑰對中公開的部分,私鑰則是非公開的部分。公鑰通常用於加密會話金鑰、驗證數字簽名,或加密可以用相應的私鑰解密的資料。
公鑰和私鑰是成對出現的,我們會保留有自己的私鑰,同時公開自己的公鑰。一個很典型的例子是GitHub的使用。我們通常不會使用賬號密碼來管理自己的專案,而是通過將自己的公鑰上傳到GitHub的裡,而自己的電腦裡則保留有相對應的私鑰,從而達到免密碼提交程式碼。
當然私鑰和公鑰對是唯一的,而你也可以隨時重新生成自己的公鑰和私鑰密碼對,但當你從新生成金鑰對並覆蓋了就有的金鑰時,你之前的公鑰就作廢了。
簡單來說就是:
公鑰加密,私鑰解密,
私鑰簽名,公鑰驗證。
SHA256
SHA256是一種安全雜湊演算法。
簡單介紹一下就是,對於任何一段資訊,通過SHA256變換之後,都會是一個固定的 256 位由 0 和 1 組成的輸出。同時,對於相同的資訊,輸出一樣,而對於不一樣的資訊,輸出則有很大的區別,即使只有一丁點的輸入不一樣,輸出也會有非常大的不一樣。
同時,雖然資訊輸入是無限的,而 SHA256 的輸出是有限的(256位),但是根據我們目前的條件和計算能力水平來說,幾乎找不到有兩個不同的輸入,可以得到相同的輸出。也即我們可以認為是不可能的。
程式碼
接下來我們,我們使用 Js 的 ursa 庫來生成公鑰和私鑰
generatePrivateAndPubKey.js
const ursa = require("ursa");
const fs = require("fs");
const path = require("path");
const MODULUSBIT = 1024;
class Key {
static generateKeys(pathName) {
const key = ursa.generatePrivateKey(MODULUSBIT, 65537);
const privatePem = key.toPrivatePem("utf8");
const privateKey = ursa.createPrivateKey(privatePem); // generate private key
const privateFileName = path.join(pathName, "private.pem");
const publicPem = key.toPublicPem("utf8");
const publicKey = ursa.createPublicKey(publicPem); // generate public key
const publicFileName = path.join(pathName, "public.pub");
if (!fs.existsSync(pathName)) {
fs.mkdirSync(pathName);
}
fs.writeFileSync(privateFileName, privatePem, "utf8");
fs.writeFileSync(publicFileName, publicPem, "utf8");
return {
privateKey: privateKey,
publicKey: publicKey
};
}
static signMessage(privateKey, message) {}
}
module.exports = exports = Key;
test.js
const Key = require("./generatePrivateAndPubKey.js");
const SHA256 = require("crypto-js/sha256");
const ursa = require("ursa");
const fs = require("fs");
const encoding = "base64";
const algorigthm = "sha256";
const message = "Hello world";
let buffer = new Buffer(message, encoding);
// use Bob's private key to signed message
const BobKeys = Key.generateKeys("Bob");
const signedMessage = BobKeys.privateKey.sign(
algorigthm,
buffer,
encoding,
encoding
);
// use Bob's public to verify the message
const isValid = BobKeys.publicKey.verify(
algorigthm,
buffer,
signedMessage,
encoding
);
// use Bob's public key to encrypt message
const encrypted = BobKeys.publicKey.encrypt(message, encoding, "base64");
// use Bob's private key to dencrypt message
const decrypted = BobKeys.privateKey.decrypt(encrypted, "base64", encoding);
console.log("Original Message:", message, "\n");
console.log("Singed Message:", signedMessage, "\n");
console.log("isValid", isValid, "\n");
console.log("Encrypted message:", encrypted, "\n");
console.log("Decrypted message:", decrypted, "\n");
輸出
Original Message: Hello world
Singed Message: ARSvIgmY7iIznhuk70stOIj8bBzj4A58wlHKKUPerU1WRob+Mk3U7p5hEhX4+lyLi/F6m3O4dfJ4dESQ2aO+deocjQd4F4zv2s2RC7AhST0nIT0F4iJYAct73R1Azum8MQK+7tesKLUYPLTfjQcvaNDhG+yDl9KfMrGO/Jgtoms=
isValid true
Encrypted message: 5ItG99B3H0h4lbpDDLwD8rza1nPWleWlvOok475lFLxYaI5AvdDetGZq+Cw5K/kcOb6qnpYcoNij1OCgiuSAad352vc596jEFExRZlhsTCqXTtqXblfEPOn4PUtrzrS/jn2FEUke3icF4fqtvVKFYeYpTl1rfrANhe3NrY9F5ik=
Decrypted message: HelloworlQ==
接下來,讓我們來測試一下 SHA256
let sha256Message = SHA256("1111").toString();
console.log("1111 After SHA256:", sha256Message, "\n");
sha256Message = SHA256("1112").toString();
console.log("1112 After SHA256:", sha256Message, "\n");
sha256Message = SHA256("111111111111111111").toString();
console.log("111111111111111111 After SHA256:", sha256Message, "\n");
sha256Message = SHA256("111211111111111111").toString();
console.log("111211111111111111 After SHA256:", sha256Message, "\n");
/*
Output:
1111 After SHA256: 0ffe1abd1a08215353c233d6e009613e95eec4253832a761af28ff37ac5a150c
1112 After SHA256: fe91a760983d401d9b679fb092b689488d1f46d92f3af5e9e93363326f3e8aa4
111111111111111111 After SHA256: 26980a9bf7dba794bd14eae90dbda34109d37f66950c16cce7b87dffd4535b40
111211111111111111 After SHA256: 308c3ae83c039dee107a232c4ccb4ee4a65ef3fb1e41357a6b9b240e88b09c0e
*/
Base64 和 Base58
比特幣裡用的編碼方式是 Base58, 但是因為我們使用的測試庫 ursa並不支援 Base58, 因此我們只能使用Base64。Base64 和 Base64 並沒有什麼太大的區別,在這裡並不會影響我們對加解密,數字簽名的理解。
為什麼比特幣要使用Base58呢?這裡引用一下比特幣原始碼裡的註釋。
//
// Why base-58 instead of standard base-64 encoding?
// - Don’t want 0OIl characters that look the same in some fonts and
// could be used to create visually identical looking account numbers.
// - A string with non-alphanumeric characters is not as easily accepted as an account number.
// - E-mail usually won’t line-break if there’s no punctuation to break at.
// - Doubleclicking selects the whole number as one word if it’s all alphanumeric.
//
簡單來說就是去掉 O, I, L 這些不好區分的字元。