หนึ่ง การเข้ารหัสแบบสมมาตร
การเข้ารหัสแบบสมมาตรและแบบอสมมาตรเป็นวิธีเข้ารหัสสองแบบที่แตกต่างกัน ซึ่งมีบทบาทสำคัญต่อความปลอดภัยของข้อมูลและการส่งข้อมูล ต่อไปผมจะแนะนำเทคโนโลยีการเข้ารหัสทั้งสองแบบนี้แยกกัน:
1.1 การเข้ารหัสแบบสมมาตร(Symmetric Encryption)
การเข้ารหัสแบบสมมาตรหมายถึงการใช้กุญแจเดียวกันทั้งในการเข้ารหัสและถอดรหัส นั่นหมายความว่าทั้งผู้ส่งและผู้รับต้องมีกุญแจนี้ จึงจะดำเนินการเข้ารหัสและถอดรหัสได้
อัลกอริทึมการเข้ารหัสแบบสมมาตรที่พบบ่อย:
AES(มาตรฐานการเข้ารหัสขั้นสูง)
DES(มาตรฐานการเข้ารหัสข้อมูล)
3DES(อัลกอริทึมเข้ารหัสข้อมูลสามชั้น)
1.2 ลักษณะของการเข้ารหัสแบบสมมาตร
โดยทั่วไป การเข้ารหัสแบบสมมาตรมีลักษณะดังต่อไปนี้:
ความเร็วค่อนข้างสูง:เนื่องจากการเข้ารหัสและถอดรหัสใช้กุญแจเดียวกัน การเข้ารหัสแบบสมมาตรจึงมักเร็วกว่าแบบอสมมาตร
การจัดการกุญแจเข้ารหัส:ความท้าทายหลักของการเข้ารหัสแบบสมมาตรอยู่ที่การแจกจ่ายและการจัดการกุญแจ หากกุญแจรั่วไหล ความปลอดภัยของการเข้ารหัสก็จะถูกคุกคาม
เหมาะสำหรับข้อมูลจำนวนมาก:เนื่องจากทำงานรวดเร็ว การเข้ารหัสแบบสมมาตรจึงเหมาะกับการเข้ารหัสข้อมูลปริมาณมาก
จากตรงนี้จะเห็นได้ว่า การเข้ารหัสแบบสมมาตรมีข้อได้เปรียบหลักสองประการ:ข้อแรกคือคำนวณได้เร็ว;ข้อที่สองคือเหมาะกับข้อมูลปริมาณมาก。
แต่การเข้ารหัสแบบสมมาตรมีปัญหาร้ายแรงข้อหนึ่ง นั่นคือการจัดการกุญแจ จะส่งกุญแจจากฝั่งเซิร์ฟเวอร์ไปยังไคลเอนต์อย่างปลอดภัยได้อย่างไร นี่เป็นปัญหาหนึ่ง!อีกทั้งเมื่อสื่อสารแบบหนึ่งต่อหลาย การจัดการกุญแจไม่ให้รั่วไหลก็เป็นความท้าทายเช่นกัน นี่คือข้อบกพร่องของการเข้ารหัสแบบสมมาตร。

1.3 ตัวอย่างโค้ด
ต่อไปพี่ซงจะสาธิตให้ทุกคนดูว่าเขียนโค้ด Java เพื่อเข้ารหัสและถอดรหัสแบบสมมาตรอย่างไร。
การใช้การเข้ารหัสแบบสมมาตรใน Java โดยทั่วไปจะใช้คลาสและอินเทอร์เฟซที่ Java Cryptography Architecture(JCA)จัดเตรียมไว้
ด้านล่างคือตัวอย่างง่าย ๆ ของการเข้ารหัสและถอดรหัสแบบสมมาตรด้วยอัลกอริทึม AES(มาตรฐานการเข้ารหัสขั้นสูง):
public class SymmetricEncryptionExample { // 生成密钥
public static SecretKey generateKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128); // 可以是128, 192或256位
return keyGenerator.generateKey();
}
// 加密方法
public static String encrypt(String data, SecretKey key) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = cipher.doFinal(data.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}
// 解密方法
public static String decrypt(String encryptedData, SecretKey key) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
return new String(decryptedBytes);
}
public static void main(String[] args) {
try {
// 生成密钥
SecretKey key = generateKey();
// 原始数据
String originalData = "Hello, JavaBoy!";
// 加密
String encryptedData = encrypt(originalData, key);
System.out.println("加密数据: " + encryptedData);
// 解密
String decryptedData = decrypt(encryptedData, key);
System.out.println("解密数据: " + decryptedData);
} catch (Exception e) {
e.printStackTrace();
}
}
}สร้างกุญแจ:ใช้
KeyGeneratorสร้างกุญแจ AES หนึ่งชุด。การเข้ารหัส:ใช้
Cipherคลาสเพื่อเข้ารหัส โดยแปลงข้อมูลเป็นไบต์ก่อนเข้ารหัส แล้วใช้การเข้ารหัส Base64 แปลงเป็นสตริง เพื่อให้จัดเก็บหรือส่งต่อได้สะดวก。ถอดรหัส:ถอดรหัสสตริงที่เข้ารหัสไว้กลับเป็นไบต์ จากนั้นใช้กุญแจเดียวกันถอดรหัสลับ。
สิ่งที่ทุกคนต้องสังเกตในโค้ดข้างต้นคือ:
ควรเลือกความยาวของกุญแจ(เช่น 128 บิต)ตามความต้องการด้านความปลอดภัย
จัดเก็บกุญแจให้ปลอดภัย อย่าฮาร์ดโค้ดกุญแจไว้ในโค้ด。
สำหรับสภาพแวดล้อมการใช้งานจริง ควรพิจารณาใช้กลยุทธ์จัดการกุญแจที่ปลอดภัยยิ่งขึ้น。
เพื่อความปลอดภัย โดยทั่วไปเราจะใช้วิธีข้างต้นสร้างกุญแจ กุญแจที่สร้างด้วยวิธีนี้มีลักษณะอย่างหนึ่งคือจะเปลี่ยนทุกครั้งที่ระบบเริ่มทำงานใหม่ หากคุณต้องการควบคุมการสร้างกุญแจเอง ก็สามารถสร้างกุญแจด้วยวิธีต่อไปนี้:
public static SecretKey generateKeyFromPassword(String password, int keySize) throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password.toCharArray(), "salt".getBytes(), 65536, keySize);
return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
}เท่านี้ก็สามารถควบคุมกุญแจเข้ารหัสผ่านพารามิเตอร์ที่ส่งเข้ามาเองได้。
1.4 การทำงานร่วมกันของฟรอนต์เอนด์และแบ็กเอนด์
เมื่อใช้อัลกอริทึม AES เข้ารหัสและถอดรหัสข้ามภาษา(เช่น JavaScript และ Java) สิ่งสำคัญคือต้องแน่ใจว่าทั้งสองฝั่งใช้กุญแจเดียวกัน โหมดอัลกอริทึมเดียวกัน(เช่น CBC, ECB เป็นต้น) รูปแบบการเติมข้อมูลเดียวกัน(เช่น PKCS5Padding, PKCS7Padding เป็นต้น) และเวกเตอร์เริ่มต้นเดียวกัน(IV หากใช้โหมดที่ต้องมี IV เช่น CBC)。
ก่อนหน้านี้มีเพื่อนคนหนึ่งบอกว่า หลังเข้ารหัสที่ฝั่งฟรอนต์เอนด์แล้ว ฝั่งแบ็กเอนด์กลับถอดรหัสไม่ได้อยู่เสมอ พี่ซงจึงขอยกตัวอย่างการใช้ฟรอนต์เอนด์คู่กับแบ็กเอนด์ไว้ที่นี่ด้วย。
เข้ารหัสที่ส่วนหน้า ถอดรหัสที่ส่วนหลัง
โค้ดส่วนหน้า:
<!DOCTYPE html><html lang="en"><head>
<meta charset="UTF-8">
<title>AES加密示例</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
</head>
<body>
<script>
function encryptAES(text, secretKey) {
const key = CryptoJS.enc.Utf8.parse(secretKey);
const iv = CryptoJS.lib.WordArray.random(128 / 8);
// 对于CBC模式,需要IV
const encrypted = CryptoJS.AES.encrypt(text, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
// 返回加密后的文本和IV(Base64格式),实际使用中可能需要安全地传输这些值
return {
ciphertext: encrypted.toString(),
iv: iv.toString(CryptoJS.enc.Base64)
};
}
const secretKey = 'helloworldhelloworldhelloworld11'; /
/ 确保密钥是32个字符长(256位)
const text = 'Hello, javaboy!';
const result = encryptAES(text, secretKey);
console.log(result);
</script>
</body>
</html>โค้ดฝั่งแบ็กเอนด์:
import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import java.util.Base64;public class AESDecrypt { public static String decryptAES(String encryptedData, String secretKey, String iv) throws Exception { IvParameterSpec ivParameterSpec = new IvParameterSpec(Base64.getDecoder().decode(iv)); SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); byte[] decodedValue = Base64.getDecoder().decode(encryptedData); byte[] decryptedValue = cipher.doFinal(decodedValue); return new String(decryptedValue, "UTF-8");
} public static void main(String[] args) { try { //前端加密后的文本
String encryptedText = "PYANpAjMsRnBIEhovtEXQw=="; String secretKey = "helloworldhelloworldhelloworld11"; //前端 IV,要和加密后的文本一起传到后端
String iv = "y/jUHcgSOpOiyNlsfjNUBg=="; String decryptedText = decryptAES(encryptedText, secretKey, iv);
System.out.println("解密文本: " + decryptedText);
} catch (Exception e) {
e.printStackTrace();
}
}
}โปรดสังเกตว่าเมื่อเข้ารหัสที่ฝั่งฟรอนต์เอนด์จะสร้างพารามิเตอร์ iv ขึ้นมา ซึ่งต้องส่งไปยังแบ็กเอนด์พร้อมกับผลการเข้ารหัสจากฟรอนต์เอนด์
เข้ารหัสที่แบ็กเอนด์ ถอดรหัสที่ฟรอนต์เอนด์
เมื่อเข้ารหัส AES ใน Java แล้วถอดรหัสใน JavaScript จำเป็นต้องตรวจสอบเช่นกันว่าทั้งสองฝั่งใช้กุญแจเดียวกัน โหมดอัลกอริทึมเดียวกัน(เช่น CBC、ECB เป็นต้น)โหมดการเติมข้อมูลเดียวกัน(เช่น PKCS5Padding、PKCS7Padding เป็นต้น)และเวกเตอร์เริ่มต้น(IV)เดียวกัน(หากมีการใช้งาน)。
โค้ดฝั่งแบ็กเอนด์:
public class AESEncrypt { public static String encryptAES(String plainText, String secretKey, String iv) throws Exception { KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(256); // 对于AES-256
SecretKey secret = new SecretKeySpec(secretKey.getBytes("UTF-8"), "AES"); IvParameterSpec ivParameterSpec = new IvParameterSpec(Base64.getDecoder().decode(iv)); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret, ivParameterSpec); byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8")); return Base64.getEncoder().encodeToString(encrypted);
} public static void main(String[] args) { try { String plainText = "Hello, 江南一点雨!"; String secretKey = "helloworldhelloworldhelloworld11"; // 确保密钥是32个字符长(256位)
String iv = Base64.getEncoder().encodeToString(new byte[16]); // 示例IV,实际应用中应更安全地生成
String encryptedText = encryptAES(plainText, secretKey, iv);
System.out.println("Encrypted text: " + encryptedText);
System.out.println("IV (Base64): " + iv); // 确保将IV发送给解密方
} catch (Exception e) {
e.printStackTrace();
}
}
}โค้ดส่วนหน้า:
<!DOCTYPE html><html lang="en"><head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
<script>
function decryptAES(ciphertext, secretKey, iv) { const key = CryptoJS.enc.Utf8.parse(secretKey); const ivParsed = CryptoJS.enc.Base64.parse(iv); const decrypted = CryptoJS.AES.decrypt(
{ ciphertext: CryptoJS.enc.Base64.parse(ciphertext)
},
key,
{ iv: ivParsed, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7
}
); return decrypted.toString(CryptoJS.enc.Utf8);
} const secretKey = 'helloworldhelloworldhelloworld11'; const iv = 'AAAAAAAAAAAAAAAAAAAAAA=='; // 从Java代码获取
const ciphertext = '1lPNVF1injas78KUWeKp5FusEr6f0pGgcrLAg9ELFr8='; // 从Java代码获取
const decryptedText = decryptAES(ciphertext, secretKey, iv); console.log(decryptedText); </script></head><body></body></html>โค้ดเข้ารหัสและถอดรหัสสำหรับการสื่อสารระหว่างส่วนหน้ากับส่วนหลังข้างต้น พี่ซ่งทดสอบด้วยตนเองแล้วว่าไม่มีปัญหา ใครต้องการใช้งานด้านนี้อย่าลืมบันทึกบทความนี้ไว้เพื่อใช้อ้างอิง
สอง การเข้ารหัสแบบอสมมาตร
2.1 การเข้ารหัสแบบอสมมาตรคืออะไร
การเข้ารหัสแบบอสมมาตร หรือเรียกอีกอย่างว่าการเข้ารหัสด้วยกุญแจสาธารณะ เป็นวิธีเข้ารหัสที่ใช้กุญแจสองดอกที่แตกต่างกัน(กุญแจสาธารณะและกุญแจส่วนตัว)。
โดยทั่วไป การเข้ารหัสแบบอสมมาตรมีลักษณะเด่นหลายประการดังต่อไปนี้:
กุญแจสาธารณะและกุญแจส่วนตัว
การสร้างกุญแจสาธารณะและกุญแจส่วนตัว:การเข้ารหัสแบบอสมมาตรใช้กุญแจหนึ่งคู่ กุญแจสาธารณะเปิดเผยให้ทุกคนเข้าถึงได้ ส่วนกุญแจส่วนตัวเป็นความลับ มีเพียงผู้ถือกุญแจเท่านั้นที่เข้าถึงได้ กุญแจทั้งสองสร้างขึ้นด้วยอัลกอริทึมทางคณิตศาสตร์ มีความสัมพันธ์กัน แต่ไม่สามารถอนุมานกุญแจอีกดอกหนึ่งจากกุญแจเพียงดอกเดียวได้
มีความปลอดภัยสูง
ถอดรหัสได้ยาก:เนื่องจากกุญแจสาธารณะและกุญแจส่วนตัวมีความสัมพันธ์ทางคณิตศาสตร์ที่ซับซ้อน การเข้ารหัสแบบอสมมาตรจึงมีความปลอดภัยสูง ผู้โจมตีอนุมานกุญแจส่วนตัวจากกุญแจสาธารณะได้ยาก จึงช่วยให้ข้อมูลที่เข้ารหัสมีความปลอดภัย。
การต้านทานการโจมตีด้วยคอมพิวเตอร์ควอนตัม:โครงการบล็อกเชนบางโครงการเริ่มใช้อัลกอริทึมเข้ารหัสที่ต้านทานการโจมตีด้วยคอมพิวเตอร์ควอนตัม เช่น อัลกอริทึมลายเซ็นดิจิทัลเส้นโค้งวงรี(ECDSA)รุ่นที่ปลอดภัยจากควอนตัม เพื่อรับมือกับภัยคุกคามจากคอมพิวเตอร์ควอนตัมในอนาคต
แจกจ่ายกุญแจสาธารณะได้สะดวก
การเปิดเผยกุญแจสาธารณะ:กุญแจสาธารณะสามารถเปิดเผยให้ใครก็ได้ จึงแจกจ่ายได้สะดวกมาก ทุกคนสามารถใช้กุญแจสาธารณะเข้ารหัสข้อมูลได้ แต่มีเพียงผู้ถือกุญแจส่วนตัวเท่านั้นที่ถอดรหัสได้。
การยืนยันตัวตนและลายเซ็นดิจิทัล
การยืนยันตัวตน:กุญแจสาธารณะใช้เป็นตัวระบุผู้ใช้ได้ ผู้อื่นสามารถตรวจสอบตัวตนของผู้ใช้โดยไม่จำเป็นต้องรู้กุญแจส่วนตัวของผู้ใช้นั้น สิ่งนี้ช่วยสร้างตัวตนที่น่าเชื่อถือในเครือข่ายแบบกระจายศูนย์ เช่น บล็อกเชน
ลายเซ็นดิจิทัล:ผู้ถือกุญแจส่วนตัวสามารถใช้กุญแจส่วนตัวลงลายเซ็นในข้อความ ส่วนผู้อื่นสามารถใช้กุญแจสาธารณะตรวจสอบความแท้จริงของลายเซ็นได้ ซึ่งช่วยรับรองความครบถ้วนสมบูรณ์ของข้อความและความน่าเชื่อถือของแหล่งที่มา
การสื่อสารที่ปลอดภัย
การสื่อสารแบบเข้ารหัส:สามารถใช้กุญแจสาธารณะเข้ารหัสข้อความได้ และมีเพียงผู้ที่ถือกุญแจส่วนตัวที่ตรงกันเท่านั้นที่ถอดรหัสได้ วิธีนี้ช่วยรักษาความปลอดภัยของข้อมูลระหว่างการสื่อสาร ป้องกันไม่ให้ผู้ที่ไม่ได้รับอนุญาตเข้าถึงข้อมูล
การควบคุมสินทรัพย์ดิจิทัล
ที่อยู่บล็อกเชนและกุญแจส่วนตัว:โดยทั่วไปที่อยู่บล็อกเชนจะสร้างขึ้นจากกุญแจสาธารณะ ผู้ใช้ควบคุมสินทรัพย์ดิจิทัลที่ผูกกับที่อยู่นั้นผ่านกุญแจส่วนตัว ความปลอดภัยของกุญแจส่วนตัวมีความสำคัญอย่างยิ่งต่อความปลอดภัยของสินทรัพย์ดิจิทัล
ความเร็วในการเข้ารหัสและถอดรหัส
ค่อนข้างช้า:การเข้ารหัสและถอดรหัสแบบอสมมาตรช้ากว่าแบบสมมาตรมาก เพราะต้องคำนวณทางคณิตศาสตร์ที่ซับซ้อนกว่า อย่างไรก็ตาม เรื่องนี้ไม่ได้กระทบต่อการนำไปใช้ในด้านการสื่อสารที่ปลอดภัย การยืนยันตัวตน และด้านอื่น ๆ
2.2 การใช้งานการเข้ารหัสแบบอสมมาตร
การเข้ารหัสแบบอสมมาตรมีกรณีการใช้งานหลักที่พบได้ทั่วไปสองแบบ。
การเข้ารหัส:นี่คือการใช้งานที่เราคุ้นเคยที่สุด คือใช้กุญแจสาธารณะเข้ารหัส และใช้กุญแจส่วนตัวถอดรหัส。
ลายเซ็น:เนื่องจากเครือข่ายไม่น่าไว้วางใจ ข้อมูลอาจถูกแก้ไขระหว่างส่งได้ ในกรณีนี้สามารถใช้คีย์สาธารณะและคีย์ส่วนตัวสลับบทบาทกัน โดยใช้คีย์ส่วนตัวลงลายเซ็นข้อมูล และใช้คีย์สาธารณะตรวจสอบลายเซ็น เพื่อให้แน่ใจว่าข้อมูลปลอดภัยและครบถ้วน
สำหรับการใช้งานข้อที่สอง บางคนจะอธิบายว่าใช้กุญแจส่วนตัวเข้ารหัส แล้วใช้กุญแจสาธารณะถอดรหัส อย่างไรก็แล้วแต่ ทุกคนเข้าใจว่ากำลังพูดถึงเรื่องเดียวกัน。
แม้อัลกอริทึมการเข้ารหัสแบบอสมมาตรจะใช้เข้ารหัสข้อมูลและสร้างลายเซ็นดิจิทัลได้ในทางทฤษฎี แต่ในทางปฏิบัติ ความซับซ้อนในการคำนวณที่สูงและประสิทธิภาพที่ต่ำกลายเป็นอุปสรรคสำคัญ
เมื่อเทียบกับอัลกอริทึมการเข้ารหัสแบบสมมาตร การเข้ารหัสแบบอสมมาตรมีความเร็วในการคำนวณช้ากว่าหลายลำดับขนาด ซึ่งส่งผลอย่างมากต่อความสามารถในการจัดการข้อมูลปริมาณมาก นอกจากนี้ เนื่องจากกระบวนการเข้ารหัสและถอดรหัสของอัลกอริทึมแบบอสมมาตรสัมพันธ์อย่างใกล้ชิดกับความยาวกุญแจ และไม่รองรับโหมดการเข้ารหัสแบบบล็อก จึงทำให้จัดการได้เพียงข้อมูลปริมาณเล็กน้อยที่มีความยาวไม่เกินความยาวกุญแจ และไม่สามารถเข้ารหัสข้อมูลปริมาณมากได้。
เพื่อเอาชนะข้อจำกัดด้านประสิทธิภาพของการเข้ารหัสแบบอสมมาตร ระบบเข้ารหัสสมัยใหม่มักใช้กลยุทธ์การเข้ารหัสแบบผสม ซึ่งรวมข้อดีของการเข้ารหัสแบบสมมาตรและแบบอสมมาตรเข้าด้วยกัน ในกลยุทธ์นี้ การเข้ารหัสแบบอสมมาตรใช้เป็นหลักในการส่งกุญแจสำหรับการเข้ารหัสแบบสมมาตรให้อีกฝ่ายอย่างปลอดภัย(หรือ “การตกลงกุญแจ”) เมื่อทั้งสองฝ่ายมีกุญแจสมมาตรร่วมกันอย่างปลอดภัยแล้ว ก็สามารถใช้อัลกอริทึมการเข้ารหัสแบบสมมาตรที่มีประสิทธิภาพสูงเพื่อเข้ารหัสและถอดรหัสข้อมูลจำนวนมากได้ วิธีผสมผสานนี้ทั้งเพิ่มประสิทธิภาพการเข้ารหัสและเสริมความปลอดภัยในการสื่อสาร จึงถูกใช้อย่างแพร่หลายในโพรโทคอลการสื่อสารที่ปลอดภัยต่าง ๆ เช่น SSL/TLS
ในด้านลายเซ็นดิจิทัล มีการปรับวิธีใช้งานบางอย่างเพื่อเพิ่มประสิทธิภาพของการเข้ารหัสแบบอสมมาตรเช่นกัน วิธีการคือ เริ่มจากคำนวณไดเจสต์ของข้อมูลต้นฉบับ(อาจใช้ MD5、SHA เป็นต้น)เพื่อให้ได้ค่าไดเจสต์ที่มีความยาวคงที่ จากนั้นใช้อัลกอริทึมการเข้ารหัสแบบอสมมาตรเข้ารหัสค่าไดเจสต์นี้เพื่อสร้างลายเซ็นดิจิทัล เนื่องจากอัลกอริทึมไดเจสต์สามารถย่อข้อมูลที่มีความยาวเท่าใดก็ได้ให้เป็นไดเจสต์ความยาวคงที่อย่างมีประสิทธิภาพ ไม่ว่าข้อมูลต้นฉบับจะมีขนาดใหญ่เพียงใด ข้อมูลไดเจสต์ก็มีความยาวเท่ากัน กระบวนการลงลายเซ็นจึงยังคงมีประสิทธิภาพ เมื่อตรวจสอบลายเซ็น เพียงคำนวณไดเจสต์ของข้อมูลต้นฉบับอีกครั้ง แล้วเปรียบเทียบกับลายเซ็นที่ถอดรหัสแล้ว ก็สามารถระบุได้อย่างรวดเร็วว่าข้อมูลถูกแก้ไขหรือไม่
2.3 ตัวอย่างการเข้ารหัส
ตัวอย่างโค้ด Java ที่ใช้ RSA เข้ารหัสและถอดรหัส:
public class RsaDemo { public static void main(String[] args) { try { KeyPair keyPair = generateRSAKeyPair(); PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); String originalData = "Hello, 江南一点雨!"; String encryptedData = encrypt(publicKey, originalData); String decryptedData = decrypt(privateKey, encryptedData);
System.out.println("加密后的数据: " + encryptedData);
System.out.println("解密后的数据: " + decryptedData);
} catch (Exception e) {
e.printStackTrace();
}
} public static String decrypt(PrivateKey privateKey, String encryptedData) throws Exception { Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData)); return new String(decryptedBytes);
} public static String encrypt(PublicKey publicKey, String data) throws Exception { Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] bytes = cipher.doFinal(data.getBytes()); return Base64.getEncoder().encodeToString(bytes);
} public static KeyPair generateRSAKeyPair() throws NoSuchAlgorithmException { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048); // 可以指定密钥长度,如2048位
return keyGen.generateKeyPair();
}
}2.4 การใช้ส่วนหน้าและส่วนหลังร่วมกัน
เข้ารหัสที่ส่วนหน้า ถอดรหัสที่ส่วนหลัง
โค้ดฝั่งแบ็กเอนด์เหมือนกับตัวอย่างข้างต้น ต่างกันตรงที่หลังจากได้รับกุญแจสาธารณะแล้ว เราสามารถพิมพ์กุญแจสาธารณะออกมาได้ โดยกุญแจนี้จะต้องส่งต่อให้ฟรอนต์เอนด์ในภายหลัง:
public static void main(String[] args) { try { KeyPair keyPair = generateRSAKeyPair(); PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate();
System.out.println("公钥: " + Base64.getEncoder().encodeToString(publicKey.getEncoded())); String originalData = "Hello, 江南一点雨!"; String encryptedData = encrypt(publicKey, originalData); String decryptedData = decrypt(privateKey, encryptedData);
System.out.println("加密后的数据: " + encryptedData);
System.out.println("解密后的数据: " + decryptedData);
} catch (Exception e) {
e.printStackTrace();
}
}โค้ดส่วนหน้ามีดังนี้:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>RSA Encryption Example</title> <!-- 引入jsencrypt库 --> <script src="https://cdn.jsdelivr.net/npm/[email protected]/bin/jsencrypt.min.js"></script></head><body><script> // 重写前端加密方法 function encryptData(publicKey, data) { // 创建一个新的JSEncrypt对象 var encryptor = new JSEncrypt(); // 设置公钥 encryptor.setPublicKey(publicKey); // 加密数据 var encrypted = encryptor.encrypt(data); return encrypted; } // 示例公钥(实际使用时应该替换为服务器提供的公钥) var publicKey = `MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnP5FOXIghidLDE2FgFwzi4Y+qTdYjnc0eMoiL5i4gdOeTE+7Uu9YvDB51GcBWKD9lvmZr5rX8z0OBpwe526qsTekNpXoIVk9DB34US0HOrAXEEwpUTVuSR656cJGAwmWBkVaalQynEz4Dlzrm53zExVYueruYUuzFyuZDaQcFnl3rWUH/XDkCIe23z0R1TfT3Q2OYNKft0u56r0S/ko99utXuYJK9yowe7QGT6q4cSJwsITQTomCARAq9q+bSNuGEYa4FlYCKIKWIhKbMhz0FYIMB2fJN10GyZbbvKASqeMkuCoD2Efgd8/6uMwOaMcx9LgEkcFaQ3qgDutsPXNUswIDAQAB`; // 要加密的数据 var data = 'Hello, javaboy!'; // 调用加密方法 var encryptedData = encryptData(publicKey, data); // 输出加密后的数据 console.log('Encrypted Data:', encryptedData);</script></body></html>
เข้ารหัสที่แบ็กเอนด์ ถอดรหัสที่ฟรอนต์เอนด์
การใช้ขั้นตอนวิธี RSA เข้ารหัสข้อมูลใน Java แล้วถอดรหัสข้อมูลเหล่านั้นใน JavaScript หมายความว่าฝั่งเซิร์ฟเวอร์ใช้กุญแจสาธารณะของส่วนหน้าเข้ารหัส และส่วนหน้าใช้กุญแจส่วนตัวของตัวเองถอดรหัส ในสถานการณ์เช่นนี้ กุญแจส่วนตัวของส่วนหน้าถูกขโมยได้ง่าย จึงไม่แนะนำวิธีใช้งานแบบนี้ ผมจึงไม่ยกตัวอย่าง
เอาละ ได้คุยกับเพื่อน ๆ เรื่องการเข้ารหัสแบบสมมาตรและแบบอสมมาตรอีกครั้งแล้ว โค้ดตัวอย่างข้างต้นพี่ซงทดสอบผ่านหมดแล้ว เพื่อน ๆ สามารถนำไปใช้อ้างอิงได้。