Skip to content

密码学基础与加密技术

密码学基础概念

密码学是信息安全的核心技术之一,在数据保护、身份认证、安全通信等场景中发挥着关键作用。本文将系统介绍加密技术的核心概念和实践应用。

加密与签名的本质区别

在实际开发中,加密和签名常常一起使用,但二者的目的完全不同:

mermaid
flowchart LR
    subgraph Encryption["加密与解密"]
        E1["明文数据"] --> E2["加密处理"]
        E2 --> E3["密文传输"]
        E3 --> E4["解密处理"]
        E4 --> E5["还原明文"]
    end
    
    subgraph Signature["签名与验签"]
        S1["原始数据"] --> S2["计算摘要"]
        S2 --> S3["私钥签名"]
        S3 --> S4["传输数据+签名"]
        S4 --> S5["公钥验签"]
    end
    
    style Encryption fill:#e3f2fd,stroke:#1976d2,rx:15
    style Signature fill:#fff3e0,stroke:#ef6c00,rx:15
技术核心目的解决的问题
加密保护数据隐私性即使数据泄露,攻击者也无法读取原文
签名保证数据完整性和来源真实性确保数据未被篡改,且确实来自声称的发送者

加密解密流程

加密是将明文转换为不可读密文的过程,解密则是逆向还原。根据密钥使用方式的不同,分为对称加密和非对称加密。

签名验签流程

签名使用发送方的私钥对数据摘要进行加密,接收方使用发送方的公钥解密验证。若验证通过,说明:

  1. 数据确实来自私钥持有者(来源认证)
  2. 数据传输过程中未被篡改(完整性保证)

对称加密技术

工作原理

对称加密使用同一把密钥进行加密和解密。就像一把钥匙既能锁门也能开门,发送方和接收方必须共享相同的密钥。

mermaid
sequenceDiagram
    participant Sender as 发送方
    participant Receiver as 接收方
    
    Note over Sender,Receiver: 双方预先共享密钥K
    
    Sender->>Sender: 使用密钥K加密明文
    Sender->>Receiver: 发送密文
    Receiver->>Receiver: 使用相同密钥K解密
    Receiver->>Receiver: 获得原始明文

常见对称加密算法

算法密钥长度特点应用场景
AES128/192/256位安全性高,性能优秀文件加密、HTTPS
DES56位已不安全,不推荐使用历史遗留系统
3DES168位DES的增强版金融系统
SM4128位国密标准国内金融、政务

AES加密实现

java
/**
 * AES加密工具类
 * 场景:用户敏感信息存储加密
 */
public class AesEncryptionUtil {
    
    private static final String ALGORITHM = "AES";
    private static final String TRANSFORMATION = "AES/GCM/NoPadding";
    private static final int GCM_IV_LENGTH = 12;
    private static final int GCM_TAG_LENGTH = 128;
    
    /**
     * 加密用户手机号等敏感信息
     */
    public static String encrypt(String plainText, SecretKey key) throws Exception {
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        
        // 生成随机初始化向量
        byte[] iv = new byte[GCM_IV_LENGTH];
        SecureRandom random = new SecureRandom();
        random.nextBytes(iv);
        
        GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
        cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
        
        byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
        
        // 将IV和密文拼接后进行Base64编码
        byte[] combined = new byte[iv.length + encrypted.length];
        System.arraycopy(iv, 0, combined, 0, iv.length);
        System.arraycopy(encrypted, 0, combined, iv.length, encrypted.length);
        
        return Base64.getEncoder().encodeToString(combined);
    }
    
    /**
     * 解密还原原始数据
     */
    public static String decrypt(String cipherText, SecretKey key) throws Exception {
        byte[] combined = Base64.getDecoder().decode(cipherText);
        
        // 提取IV和密文
        byte[] iv = Arrays.copyOfRange(combined, 0, GCM_IV_LENGTH);
        byte[] encrypted = Arrays.copyOfRange(combined, GCM_IV_LENGTH, combined.length);
        
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
        cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);
        
        byte[] decrypted = cipher.doFinal(encrypted);
        return new String(decrypted, StandardCharsets.UTF_8);
    }
    
    /**
     * 生成AES密钥
     */
    public static SecretKey generateKey(int keySize) throws Exception {
        KeyGenerator keyGen = KeyGenerator.getInstance(ALGORITHM);
        keyGen.init(keySize, new SecureRandom());
        return keyGen.generateKey();
    }
}

对称加密的优缺点

优点:

  • 加解密速度快,适合大数据量加密
  • 算法公开,实现简单

缺点:

  • 密钥分发困难,双方需要安全地共享密钥
  • 密钥数量膨胀,N个用户两两通信需要N*(N-1)/2个密钥

非对称加密技术

工作原理

非对称加密使用一对密钥:公钥和私钥。公钥可以公开分发,私钥必须保密。用公钥加密的数据只能用私钥解密,反之亦然。

mermaid
flowchart TB
    subgraph KeyPair["密钥对"]
        PubKey(["公钥<br/>可公开"])
        PriKey(["私钥<br/>保密"])
    end
    
    subgraph Encrypt["公钥加密场景"]
        E1["发送方用接收方公钥加密"]
        E2["只有接收方私钥能解密"]
    end
    
    subgraph Sign["私钥签名场景"]
        S1["发送方用自己私钥签名"]
        S2["任何人可用公钥验证"]
    end
    
    PubKey --> E1
    PriKey --> E2
    PriKey --> S1
    PubKey --> S2
    
    style PubKey fill:#4caf50,stroke:#388e3c,color:#fff,rx:20
    style PriKey fill:#f44336,stroke:#d32f2f,color:#fff,rx:20

常见非对称加密算法

算法密钥长度特点应用场景
RSA2048/3072/4096位应用最广泛数字证书、HTTPS
ECC256位密钥短,安全性高移动设备、物联网
SM2256位国密标准,基于ECC国内电子认证

RSA加密实现

java
/**
 * RSA非对称加密工具类
 * 场景:跨系统安全数据传输
 */
public class RsaEncryptionUtil {
    
    private static final String ALGORITHM = "RSA";
    private static final String TRANSFORMATION = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
    private static final int KEY_SIZE = 2048;
    
    /**
     * 生成密钥对
     */
    public static KeyPair generateKeyPair() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(ALGORITHM);
        keyPairGen.initialize(KEY_SIZE, new SecureRandom());
        return keyPairGen.generateKeyPair();
    }
    
    /**
     * 公钥加密 - 用于加密发送给对方的数据
     */
    public static String encryptWithPublicKey(String plainText, PublicKey publicKey) 
            throws Exception {
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        
        byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encrypted);
    }
    
    /**
     * 私钥解密 - 用于解密收到的加密数据
     */
    public static String decryptWithPrivateKey(String cipherText, PrivateKey privateKey) 
            throws Exception {
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        
        byte[] encrypted = Base64.getDecoder().decode(cipherText);
        byte[] decrypted = cipher.doFinal(encrypted);
        return new String(decrypted, StandardCharsets.UTF_8);
    }
}

数字签名技术

签名流程

mermaid
sequenceDiagram
    participant Sender as 发送方
    participant Receiver as 接收方
    
    Note over Sender: 持有私钥
    Note over Receiver: 持有发送方公钥
    
    Sender->>Sender: 1. 对原始数据计算哈希摘要
    Sender->>Sender: 2. 用私钥加密摘要(签名)
    Sender->>Receiver: 3. 发送原始数据 + 签名
    Receiver->>Receiver: 4. 用公钥解密签名得到摘要A
    Receiver->>Receiver: 5. 对收到的数据计算摘要B
    Receiver->>Receiver: 6. 比较摘要A和B是否一致
    
    alt 摘要一致
        Receiver->>Receiver: 验签成功,数据可信
    else 摘要不一致
        Receiver->>Receiver: 验签失败,数据被篡改
    end

签名验签实现

java
/**
 * 数字签名工具类
 * 场景:支付回调接口验证请求来源
 */
public class DigitalSignatureUtil {
    
    private static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
    
    /**
     * 生成签名
     * 用于发送请求时,对请求参数进行签名
     */
    public static String sign(String data, PrivateKey privateKey) throws Exception {
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateKey);
        signature.update(data.getBytes(StandardCharsets.UTF_8));
        
        byte[] signatureBytes = signature.sign();
        return Base64.getEncoder().encodeToString(signatureBytes);
    }
    
    /**
     * 验证签名
     * 用于接收请求时,验证请求是否来自可信来源
     */
    public static boolean verify(String data, String signatureStr, PublicKey publicKey) 
            throws Exception {
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicKey);
        signature.update(data.getBytes(StandardCharsets.UTF_8));
        
        byte[] signatureBytes = Base64.getDecoder().decode(signatureStr);
        return signature.verify(signatureBytes);
    }
}

/**
 * 支付回调验签示例
 */
@RestController
@RequestMapping("/payment/callback")
public class PaymentCallbackController {
    
    @Autowired
    private PublicKey paymentPlatformPublicKey;
    
    @PostMapping
    public ResponseEntity<String> handleCallback(@RequestBody PaymentCallback callback) {
        // 按约定规则拼接待验签字符串
        String signContent = buildSignContent(callback);
        
        try {
            boolean verified = DigitalSignatureUtil.verify(
                signContent, 
                callback.getSign(), 
                paymentPlatformPublicKey
            );
            
            if (!verified) {
                return ResponseEntity.status(HttpStatus.FORBIDDEN)
                    .body("签名验证失败");
            }
            
            // 处理业务逻辑
            processPaymentResult(callback);
            return ResponseEntity.ok("success");
            
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body("验签异常");
        }
    }
}

散列算法

什么是哈希算法

哈希算法将任意长度的数据映射为固定长度的摘要值,具有以下特性:

  • 单向性:无法从摘要值反推原始数据
  • 确定性:相同输入必定产生相同输出
  • 雪崩效应:输入微小变化导致输出巨大差异
  • 抗碰撞性:难以找到两个不同输入产生相同输出

常见哈希算法对比

mermaid
flowchart LR
    subgraph HashAlgorithms["哈希算法对比"]
        direction TB
        MD5["MD5<br/>128位<br/>已不安全"]
        SHA1["SHA-1<br/>160位<br/>逐渐淘汰"]
        SHA256["SHA-256<br/>256位<br/>推荐使用"]
        SM3["SM3<br/>256位<br/>国密标准"]
    end
    
    style MD5 fill:#ffcdd2,stroke:#c62828,rx:10
    style SHA1 fill:#fff3e0,stroke:#ef6c00,rx:10
    style SHA256 fill:#c8e6c9,stroke:#388e3c,rx:10
    style SM3 fill:#bbdefb,stroke:#1976d2,rx:10

密码存储最佳实践

直接存储密码哈希值存在彩虹表攻击风险,应使用加盐哈希或专用密码哈希算法:

java
/**
 * 密码加密工具类
 * 使用BCrypt算法,自动加盐
 */
public class PasswordEncryptionUtil {
    
    /**
     * 加密密码
     * 场景:用户注册时加密存储密码
     */
    public static String hashPassword(String rawPassword) {
        // cost因子12,平衡安全性和性能
        return BCrypt.hashpw(rawPassword, BCrypt.gensalt(12));
    }
    
    /**
     * 验证密码
     * 场景:用户登录时验证密码
     */
    public static boolean verifyPassword(String rawPassword, String hashedPassword) {
        return BCrypt.checkpw(rawPassword, hashedPassword);
    }
}

// 使用示例
@Service
public class UserAuthService {
    
    @Autowired
    private UserRepository userRepository;
    
    public void register(String username, String password) {
        User user = new User();
        user.setUsername(username);
        // 存储哈希后的密码
        user.setPassword(PasswordEncryptionUtil.hashPassword(password));
        userRepository.save(user);
    }
    
    public boolean login(String username, String password) {
        User user = userRepository.findByUsername(username);
        if (user == null) {
            return false;
        }
        // 验证密码
        return PasswordEncryptionUtil.verifyPassword(password, user.getPassword());
    }
}

国密算法体系

国密算法(SM系列)是中国国家密码管理局制定的商用密码算法标准,广泛应用于金融、政务、电信等领域。

国密算法概览

mermaid
flowchart TB
    subgraph SM_Family["国密算法体系"]
        direction LR
        subgraph SM2_Box["SM2"]
            SM2["非对称加密"]
            SM2_Use["数字签名<br/>密钥交换"]
        end
        
        subgraph SM3_Box["SM3"]
            SM3["哈希算法"]
            SM3_Use["数据完整性<br/>密码存储"]
        end
        
        subgraph SM4_Box["SM4"]
            SM4["对称加密"]
            SM4_Use["数据加密<br/>文件保护"]
        end
        
        subgraph SM9_Box["SM9"]
            SM9["标识密码"]
            SM9_Use["身份认证<br/>物联网"]
        end
    end
    
    style SM2_Box fill:#e8f5e9,stroke:#4caf50,rx:15
    style SM3_Box fill:#fff3e0,stroke:#ff9800,rx:15
    style SM4_Box fill:#e3f2fd,stroke:#2196f3,rx:15
    style SM9_Box fill:#fce4ec,stroke:#e91e63,rx:15

国密与国际算法对比

国密算法对应国际算法密钥长度安全性评价
SM2RSA/ECC256位SM2效率更高,密钥更短
SM3SHA-256256位安全性相当
SM4AES-128128位安全性相当

国密算法使用

使用Hutool工具库可以方便地使用国密算法:

xml
<dependency>
    <groupId>cn.hutool</groupId>

    <artifactId>hutool-all</artifactId>

    <version>5.8.25</version>

</dependency>

<dependency>
    <groupId>org.bouncycastle</groupId>

    <artifactId>bcpkix-jdk18on</artifactId>

    <version>1.78.1</version>

</dependency>
java
/**
 * 国密算法使用示例
 * 场景:银行系统数据加密与签名
 */
public class SmCryptoExample {
    
    /**
     * SM4对称加密 - 加密交易流水
     */
    public void sm4Example() {
        String transactionData = "用户A向用户B转账1000元";
        
        // 自动生成密钥
        SymmetricCrypto sm4 = SmUtil.sm4();
        
        // 加密
        String encrypted = sm4.encryptHex(transactionData);
        System.out.println("加密后: " + encrypted);
        
        // 解密
        String decrypted = sm4.decryptStr(encrypted, CharsetUtil.CHARSET_UTF_8);
        System.out.println("解密后: " + decrypted);
    }
    
    /**
     * SM2非对称加密 - 敏感信息传输
     */
    public void sm2Example() {
        String sensitiveInfo = "用户身份证号: 110101199001011234";
        
        SM2 sm2 = SmUtil.sm2();
        
        // 公钥加密
        String encrypted = sm2.encryptBcd(sensitiveInfo, KeyType.PublicKey);
        System.out.println("加密后: " + encrypted);
        
        // 私钥解密
        String decrypted = StrUtil.utf8Str(
            sm2.decryptFromBcd(encrypted, KeyType.PrivateKey)
        );
        System.out.println("解密后: " + decrypted);
    }
    
    /**
     * SM3哈希 - 文件完整性校验
     */
    public void sm3Example() {
        String fileContent = "重要合同文件内容...";
        
        // 计算哈希摘要
        String hash = SmUtil.sm3(fileContent);
        System.out.println("SM3摘要: " + hash);
        // 输出: 136ce3c86e4ed909b76082055a61586af20b4dab674732ebd4b599eef080c9be
    }
}

加密技术综合应用

HTTPS通信中的加密流程

HTTPS结合了对称加密和非对称加密的优势:

mermaid
sequenceDiagram
    participant Client as 客户端
    participant Server as 服务器
    
    Note over Client,Server: 握手阶段 - 非对称加密
    Client->>Server: 1. 请求建立连接
    Server->>Client: 2. 返回证书(含公钥)
    Client->>Client: 3. 验证证书
    Client->>Client: 4. 生成随机对称密钥
    Client->>Server: 5. 用公钥加密对称密钥
    Server->>Server: 6. 用私钥解密获得对称密钥
    
    Note over Client,Server: 数据传输阶段 - 对称加密
    Client->>Server: 7. 用对称密钥加密数据
    Server->>Client: 8. 用对称密钥加密响应

安全通信最佳实践

  1. 传输层:始终使用HTTPS,配置TLS 1.2+
  2. 数据层:敏感数据二次加密存储
  3. 密钥管理:使用专业的密钥管理系统(KMS)
  4. 算法选择:政务、金融系统优先使用国密算法
  5. 定期更新:定期更换密钥,淘汰不安全算法

更新: 2025-12-04 17:36:47
原文: https://www.yuque.com/u22210564/zoxfmt/doc-20-04

Java 后端面试知识库