密碼學是現代資訊安全的基石,而 Java 平台透過 java.security 與 javax.crypto 套件提供了完整的加密、雜湊、數位簽名與憑證管理功能。本文將透過實際程式碼範例,帶領讀者快速掌握 Java 中常見的密碼學操作,包含訊息摘要、對稱加密、非對稱加密、數位簽名與數位憑證等核心概念。文中範例基於 JDK 1.5 環境,但核心 API 至今仍廣泛適用於現代 Java 版本。
訊息摘要演算法將任意長度的輸入資料轉換為固定長度的雜湊值,具有單向不可逆、極難碰撞的特性,常用於密碼儲存、檔案完整性驗證等場景。Java 透過 java.security.MessageDigest 類別提供標準化介面,支援 MD5、SHA-1、SHA-256 等演算法。
import java.security.MessageDigest;
public class MessageDigestExample {
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("用法: java MessageDigestExample <文字>");
System.exit(1);
}
byte[] plainText = args[0].getBytes("UTF-8");
// 取得 SHA-1 摘要演算法實例
MessageDigest md = MessageDigest.getInstance("SHA-1");
System.out.println("\n提供者資訊: " + md.getProvider().getInfo());
// 計算摘要
md.update(plainText);
byte[] digest = md.digest();
// 以十六進位格式輸出(實務上建議用 Hex 編碼,避免中文亂碼)
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b));
}
System.out.println("\n摘要值 (Hex): " + sb.toString());
}
}
補充說明:若需攜帶金鑰的完整性驗證,可使用 javax.crypto.Mac 實作 HMAC(如 HmacSHA256),此處不再贅述。
對稱加密使用相同金鑰進行加解密,速度快、適合大量資料加密。常見演算法包含 AES(建議優先使用)、DESede(3DES)、DES(已淘汰)。Java 透過 KeyGenerator 產生金鑰,再配合 Cipher 完成加解密流程。
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import java.security.Key;
public class SymmetricEncryptionExample {
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("用法: java SymmetricEncryptionExample <文字>");
System.exit(1);
}
byte[] plainText = args[0].getBytes("UTF-8");
// 1. 產生 AES 金鑰
System.out.println("\n開始產生 AES 金鑰...");
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128); // 金鑰長度 128/192/256 bits
Key secretKey = keyGen.generateKey();
System.out.println("金鑰產生完成");
// 2. 建立 Cipher 實例(演算法/模式/填充)
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
System.out.println("\n提供者資訊: " + cipher.getProvider().getInfo());
// 3. 加密
System.out.println("\n開始加密...");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] cipherText = cipher.doFinal(plainText);
System.out.println("加密完成,密文長度: " + cipherText.length + " bytes");
// 4. 解密
System.out.println("\n開始解密...");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decryptedText = cipher.doFinal(cipherText);
System.out.println("解密完成: " + new String(decryptedText, "UTF-8"));
}
}
最佳實務提醒:
非對稱加密使用金鑰對:公鑰加密、私鑰解密(機密性);私鑰簽名、公鑰驗證(不可否認性)。RSA 是最經典的演算法,JDK 1.5 起原生支援。雖然速度較對稱加密慢 100~1000 倍,但適合用於金鑰交換、數位簽名等場景。
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import javax.crypto.Cipher;
public class AsymmetricEncryptionExample {
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("用法: java AsymmetricEncryptionExample <文字>");
System.exit(1);
}
byte[] plainText = args[0].getBytes("UTF-8");
// 1. 產生 RSA 金鑰對(建議 2048 bits 以上)
System.out.println("\n開始產生 RSA 金鑰對...");
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair keyPair = keyGen.generateKeyPair();
System.out.println("金鑰對產生完成");
// 2. 公鑰加密
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
System.out.println("\n提供者資訊: " + cipher.getProvider().getInfo());
System.out.println("\n開始公鑰加密...");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] cipherText = cipher.doFinal(plainText);
System.out.println("加密完成");
// 3. 私鑰解密
System.out.println("\n開始私鑰解密...");
cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
byte[] decryptedText = cipher.doFinal(cipherText);
System.out.println("解密完成: " + new String(decryptedText, "UTF-8"));
}
}
數位簽名結合雜湊函數與非對稱加密:發送方用私鑰對訊息摘要簽名,接收方用對應公鑰驗證。Java 透過 java.security.Signature 類別支援多種簽名演算法(如 SHA256withRSA、SHA1withDSA、SHA256withECDSA)。
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
public class DigitalSignatureExample {
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("用法: java DigitalSignatureExample <文字>");
System.exit(1);
}
byte[] plainText = args[0].getBytes("UTF-8");
// 1. 產生金鑰對
System.out.println("\n開始產生 RSA 金鑰對...");
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair keyPair = keyGen.generateKeyPair();
System.out.println("金鑰對產生完成");
// 2. 簽名(私鑰)
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(keyPair.getPrivate());
signature.update(plainText);
byte[] digitalSignature = signature.sign();
System.out.println("\n簽名完成,長度: " + digitalSignature.length + " bytes");
System.out.println("提供者資訊: " + signature.getProvider().getInfo());
// 3. 驗證(公鑰)
System.out.println("\n開始驗證簽名...");
signature.initVerify(keyPair.getPublic());
signature.update(plainText);
boolean verified = signature.verify(digitalSignature);
System.out.println(verified ? "✓ 簽名驗證通過" : "✗ 簽名驗證失敗");
}
}
為防止中間人攻擊(攻擊者冒充公鑰),需要可信任的第三方機構(CA)對公鑰進行簽名,形成數位憑證(X.509 標準)。Java 提供 keytool 工具管理金鑰庫(Keystore),支援 JKS、PKCS12 等格式。
# 產生自簽憑證與金鑰對
keytool -genkeypair -alias myKey -keyalg RSA -keysize 2048 -keystore mykeystore.p12 -storetype PKCS12 -validity 365
# 列出金鑰庫內容
keytool -list -keystore mykeystore.p12 -storetype PKCS12 -v
# 匯出憑證
keytool -exportcert -alias myKey -keystore mykeystore.p12 -storetype PKCS12 -file mycert.cer
# 匯入受信任憑證
keytool -importcert -alias trustedCA -file ca.cer -keystore truststore.p12 -storetype PKCS12
對 JAR 檔進行數位簽名可確保程式碼來源真實性與完整性。接收方可根據簽名者身分決定是否授予權限(如透過 Policy 檔案配置)。
# 對 JAR 簽名
jarsigner -keystore mykeystore.p12 -storetype PKCS12 MyApp.jar myKey
# 驗證簽名
jarsigner -verify -verbose -certs MyApp.jar
在 Applet 或 Java Web Start 部署中,瀏覽器會提示使用者檢視憑證並決定是否信任執行。
SSL/TLS 為網路通訊提供加密、身分驗證與完整性保護。Java 透過 javax.net.ssl 套件支援 SSL/TLS 客戶端與伺服端開發。
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLServerSocket;
import java.io.*;
public class SimpleSSLServer {
public static void main(String[] args) throws Exception {
// 需設定系統屬性:javax.net.ssl.keyStore、javax.net.ssl.keyStorePassword
SSLServerSocketFactory factory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
SSLServerSocket serverSocket = (SSLServerSocket) factory.createServerSocket(8443);
System.out.println("SSL 伺服器啟動於 port 8443...");
while (true) {
try (var socket = serverSocket.accept();
var in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
var out = new PrintWriter(socket.getOutputStream(), true)) {
String request = in.readLine();
System.out.println("收到請求: " + request);
out.println("HTTP/1.1 200 OK");
out.println("Content-Type: text/plain");
out.println();
out.println("Hello, Secure World!");
}
}
}
}
啟動參數:
java -Djavax.net.ssl.keyStore=keystore.p12 \
-Djavax.net.ssl.keyStorePassword=changeit \
-Djavax.net.ssl.keyStoreType=PKCS12 \
SimpleSSLServer
Java 提供了完整且標準化的密碼學 API,涵蓋雜湊、對稱/非對稱加密、數位簽名、憑證管理與 SSL/TLS 通訊。實務開發時應注意:
建議進一步閱讀 Bruce Schneier 著作《Applied Cryptography》與 NIST SP 800 系列文件,深化密碼學工程實務能力。