首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 實現 SSH 風格混合加密:RSA 密鑰交換 + DES 對稱加密授權授權體系

     編輯:Java 相關知識

前言

SSH 協議採用「非對稱加密交換對稱密鑰,對稱加密傳輸數據」的混合加密策略,兼顧安全性與效能。本文實戰記錄如何在 Java 中實現此機制,構建一套基於 WebService 的軟體授權/到期控制體系:客戶端請求公鑰 → 生成隨機會話鑰 → 公鑰加密傳遞 → 雙方持有會話鑰 → 後續業務數據對稱加密傳輸。

一、加密架構設計

1.1 核心流程圖解

客戶端                                    服務端
  │                                           │
  ├─ 1. 請求公鑰 ─────────────────────────► │
  │ ◄── 2. 返回公鑰(保留私鑰) ────────────┤
  │                                           │
  ├─ 3. 生成隨機會話鑰 (echoStr)             │
  ├─ 4. 公鑰加密 echoStr ─────────────────► │
  │ ◄── 5. 私鑰解密得 echoStr,存儲會話鑰 ────┤
  │                                           │
  ├─ 6. 業務數據 + echoStr (DES 加密) ─────► │
  │ ◄── 7. echoStr 解密 → 業務邏輯 → 結果    │
  │       echoStr 加密回傳 ─────────────────┤
  │                                           │

1.2 為何混合加密?

加密類型優點缺點適用場景
非對稱 (RSA)無需預共享密鑰,安全分發運算極慢,數據量受限(鑰長度限制)密鑰交換、數位簽名
對稱 (DES/AES)極快、無長度限制、適合大數據需安全分發共享密鑰會話數據加密
混合兼具安全分發與高效傳輸實現複雜度稍高SSH、TLS、本文授權體系

二、核心代碼實作

2.1 服務端:ExchangeService

public class ExchangeService {
    private String privateKey; // 服務端私鑰,生命週期與請求綁定(簡化演示)

    public byte[] request(String param, String resultType) {
        KeyResult keyResult = new KeyResult();
        try {
            // 步驟 1:客戶端請求公鑰
            if (PUBLIC_KEY_TYPE.equals(resultType)) {
                Map<String, Object> keyMap = RSACoder.initKey(); // 生成密鑰對
                privateKey = RSACoder.getPrivateKey(keyMap);
                keyResult.setKey(RSACoder.getPublicKey(keyMap)); // 返回公鑰
                return JsonUtil.objectToByte(keyResult);
            }

            // 步驟 2:接收並解密會話鑰
            if (ECHO_STR_TYPE.equals(resultType)) {
                byte[] paramByte = new BASE64Decoder().decodeBuffer(param);
                // 私鑰解密得到客戶端生成的隨機字串
                this.echoStr = new String(RSACoder.decryptByPrivateKey(paramByte, privateKey));
                return JsonUtil.objectToByte(keyResult); // 確認收到
            }

            // 步驟 3:業務處理(授權驗證)
            // 解密業務參數:BASE64 解碼 → DES 解密(echoStr)
            String decryptedParam = new String(CryptUtil.decrypt(
                    new BASE64Decoder().decodeBuffer(param), echoStr.getBytes()));
            ExchangeInfo info = ExchangeInfo.dao.getInfoByName(decryptedParam);

            String result = "0"; // 預設不可用
            if (PRIVILEGE_TYPE.equals(resultType)) {
                long daysUsed = (System.currentTimeMillis()/1000 - string2DateInt(info.getOpenDay())) / 86400;
                if (info.getUseDays() - daysUsed > 0) result = "1"; // 授權有效
            }

            // 結果用會話鑰加密回傳
            keyResult.setResult(CryptUtil.encrypt(result.getBytes(), echoStr.getBytes()));
            return JsonUtil.objectToByte(keyResult);

        } catch (Exception e) {
            log.error("WebService 處理異常", e);
            return null;
        }
    }
}

2.2 客戶端:ExchangeUtil

public class ExchangeUtil {
    public static boolean canRunForExchange(String resultType) {
        int retry = 0;
        while (retry++ < 10) { // 重試機制應對網路抖動
            try {
                ExchangeServiceProxy proxy = new ExchangeServiceProxy();
                BASE64Encoder encoder = new BASE64Encoder();

                // 1. 獲取公鑰
                KeyResult kr = JsonUtil.byteToObject(
                        proxy.request(null, PUBLIC_KEY_TYPE), KeyResult.class);

                // 2. 生成 10 位隨機會話鑰,公鑰加密發送
                String echoStr = StrUtil.getEchoStrByLength(10);
                byte[] encryptedEcho = RSACoder.encryptByPublicKey(echoStr.getBytes(), kr.getKey());
                proxy.request(encoder.encode(encryptedEcho), ECHO_STR_TYPE);

                // 3. 業務請求:參數 DES 加密
                byte[] encryptedParam = CryptUtil.encrypt(Constants.CLIENT_TYPE.getBytes(), echoStr.getBytes());
                byte[] respBytes = proxy.request(encoder.encode(encryptedParam), resultType);

                // 4. 解密結果
                KeyResult resp = JsonUtil.byteToObject(respBytes, KeyResult.class);
                String decrypted = new String(CryptUtil.decrypt(resp.getResult(), echoStr.getBytes()));
                
                return "1".equals(decrypted);
            } catch (Exception e) {
                log.warn("第 {} 次授權請求失敗: {}", retry, e.getMessage());
                Thread.sleep(1000); // 指數退避可優化
            }
        }
        return false; // 多次失敗視為授權失效
    }
}

2.3 共享加密工具:CryptUtil (DES/CBC/PKCS5Padding)

public class CryptUtil {
    private static final String ALGO = "DES/CBC/PKCS5Padding";

    // 密鑰截取/補齊為 8 字節
    private static byte[] fixKey(byte[] key) {
        byte[] k = new byte[8];
        System.arraycopy(key, 0, k, 0, Math.min(key.length, 8));
        return k;
    }

    public static byte[] encrypt(byte[] data, byte[] key) throws Exception {
        key = fixKey(key);
        Cipher cipher = Cipher.getInstance(ALGO);
        DESKeySpec spec = new DESKeySpec(key);
        SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");
        SecretKey secretKey = factory.generateSecret(spec);
        IvParameterSpec iv = new IvParameterSpec(key); // IV = Key (簡化,生產應隨機)
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
        return cipher.doFinal(data);
    }

    public static byte[] decrypt(byte[] data, byte[] key) throws Exception {
        key = fixKey(key);
        Cipher cipher = Cipher.getInstance(ALGO);
        DESKeySpec spec = new DESKeySpec(key);
        SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");
        SecretKey secretKey = factory.generateSecret(spec);
        IvParameterSpec iv = new IvParameterSpec(key);
        cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
        return cipher.doFinal(data);
    }
}

2.4 RSA 工具類摘要

public class RSACoder {
    public static final String KEY_ALGORITHM = "RSA";
    public static final String PUBLIC_KEY = "RSAPublicKey";
    public static final String PRIVATE_KEY = "RSAPrivateKey";

    // 初始化密鑰對 (預設 1024 位,建議 2048+)
    public static Map<String, Object> initKey() throws Exception {
        KeyPairGenerator gen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        gen.initialize(1024); // 生產環境建議 2048 或 4096
        KeyPair pair = gen.generateKeyPair();
        Map<String, Object> map = new HashMap<>();
        map.put(PUBLIC_KEY, pair.getPublic());
        map.put(PRIVATE_KEY, pair.getPrivate());
        return map;
    }

    // 公鑰加密 / 私鑰解密 (BASE64 編碼傳輸)
    public static byte[] encryptByPublicKey(byte[] data, String publicKey) { ... }
    public static byte[] decryptByPrivateKey(byte[] data, String privateKey) { ... }
    
    // 獲取編碼後的鑰字串
    public static String getPublicKey(Map<String, Object> keyMap) { ... }
    public static String getPrivateKey(Map<String, Object> keyMap) { ... }
}

三、安全性與工程化增強建議

  1. 演算法升級:DES 已不安全(56 位有效鑰),**強制改用 AES-256-GCM**(提供機密性+完整性+防重放)。RSA 升級至 2048/4096 位,填充模式用 OAEP。
  2. 會話鑰管理:隨機鑰長度 ≥ 16 字節,使用 SecureRandom;每次會話生新鑰,避免重放攻擊。
  3. IV 隨機化:CBC 模式 IV 必須隨機且唯一,隨密文傳輸(非固定等於 Key)。
  4. 完整性校驗:引入 HMAC-SHA256 或改用 AEAD 模式(GCM/CCM),防止密文竄改。
  5. 私鑰保護:服務端私鑰應存於 HSM 或加密密鑰庫,不應明文保留在記憶體/代碼中。
  6. 重試策略:指數退避 + 抖動,避免驚群效應;區分網路錯誤與業務拒絕。
  7. 授權邏輯防繞過:關鍵判斷下沉至服務端,客戶端僅作觸發器;考慮簽名授權檔(Licence File)離線驗證。
  8. 傳輸層安全:全鏈路強制 HTTPS(雙向認證更佳),防止中間人攻擊降級加密。

四、總結

本文完整演示了 Java 實現 SSH 風格混合加密授權體系:RSA 安全交換 DES 會話鑰,後續業務對稱加密輸。代碼結構清晰分層,涵蓋密鑰生成、交換、加解密、業務邏輯、重試容錯。生產環境務必升級至 AES-GCM + RSA-OAEP,並完善密鑰全生命週期管理,才能構建真正可信的軟體保護機制。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved