Skip to content

加密基础:对称加密、非对称加密、哈希与签名 ​

安全相关知识不能只背“对称加密快,非对称加密安全”。这句话太浅,真正做项目时会遇到这些问题:

  1. 接口传输要不要加密。
  2. 数据库存手机号、身份证号、病案号要不要加密。
  3. JWT 为什么叫签名,不叫加密。
  4. HTTPS 为什么既用非对称加密,又用对称加密。
  5. 密码为什么不能“解密”,而是用哈希校验。
  6. 第三方接口为什么要“加密 + 签名 + 时间戳 + nonce”。

本章按零基础也能看懂的方式,把对称加密、非对称加密、哈希摘要、数字签名、HTTPS 混合加密、商业场景和 Java Demo 串起来。

先分清四个概念 ​

概念解决什么问题是否能还原原文常见算法典型场景
对称加密保护数据内容不被看见能,用同一把密钥解密AES、SM4字段加密、文件加密、HTTPS 数据传输
非对称加密解决密钥分发、身份与签名能,用另一把密钥解密RSA、ECC、SM2HTTPS 握手、加密 AES 密钥、验签
哈希摘要证明内容有没有变不能SHA-256、SHA-3、SM3文件完整性、密码存储、数据指纹
数字签名证明是谁发的、内容没被改不能,签名不是加密RSA-PSS、ECDSA、SM2开放接口签名、JWT 签名、软件包验签

最重要的边界:

text
加密解决“别人看不见内容”。
哈希解决“内容变没变”。
签名解决“是不是你发的,并且内容有没有被改”。
编码解决“怎么表示数据”,不是安全手段。

Base64 只是编码,不是加密。任何人拿到 Base64 字符串都能还原。

mermaid
flowchart TD
    A["安全目标"] --> B{"要保护内容不被看见?"}
    B -- "是" --> C["加密<br/>AES / RSA-OAEP"]
    B -- "否" --> D{"要判断内容有没有被改?"}
    D -- "是" --> E["哈希摘要<br/>SHA-256"]
    D -- "否" --> F{"要证明发送方身份?"}
    F -- "是" --> G["数字签名<br/>私钥签名 / 公钥验签"]
    F -- "否" --> H["可能只是编码或普通传输"]

对称加密是什么 ​

对称加密是指加密和解密使用同一把密钥。

text
密文 = 加密算法(明文, 密钥)
明文 = 解密算法(密文, 同一把密钥)

可以把它理解成:两个人共用同一把钥匙。A 用这把钥匙锁箱子,B 也用同一把钥匙打开箱子。

mermaid
flowchart TD
    A["明文:患者身份证号"] --> B["AES 加密"]
    C["同一把密钥 key"] --> B
    B --> D["密文:不可直接看懂"]
    D --> E["AES 解密"]
    C --> E
    E --> F["恢复明文"]

对称加密的优点 ​

  1. 速度快。
  2. 适合大数据量。
  3. 算法成熟。
  4. 适合文件、字段、网络数据流加密。

例如 HTTPS 真正传输大量业务数据时,主要靠对称加密。

对称加密的核心问题:密钥怎么给对方 ​

如果 A 和 B 还没有安全通道,A 直接把密钥发给 B,攻击者可能截获密钥。

mermaid
flowchart TD
    A["客户端生成 AES 密钥"] --> B["网络传输密钥"]
    B --> C["服务端收到密钥"]
    D["攻击者监听网络"] -. "截获密钥" .-> B
    D --> E["后续密文都可能被解开"]

所以对称加密本身不解决“安全分发密钥”的问题。这就是非对称加密登场的原因。

为什么推荐 AES-GCM ​

AES 是常见对称加密算法。使用时不要只写 AES,因为不同工作模式安全性差异很大。

推荐学习和项目里优先理解:

text
AES/GCM/NoPadding

GCM 的价值是:它不仅能加密内容,还能校验密文有没有被篡改。

模式是否推荐原因
AES/ECB不推荐相同明文块会得到相同密文块,会泄露模式
AES/CBC可用但要谨慎需要随机 IV,还需要额外完整性校验
AES/GCM推荐同时提供机密性和完整性校验

ECB 的问题可以这样理解:如果一张图片中有大量重复块,ECB 加密后仍可能看出轮廓。业务数据虽然不是图片,但相同明文生成相同密文,会泄露重复模式。

AES-GCM Java Demo ​

下面 Demo 演示:

  1. 生成 AES 密钥。
  2. 使用随机 IV 加密。
  3. 把 IV 和密文一起保存。
  4. 解密时取出 IV。
java
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;

public class AesGcmDemo {
    private static final int IV_LENGTH = 12;
    private static final int TAG_LENGTH_BIT = 128;
    private static final SecureRandom RANDOM = new SecureRandom();

    public static void main(String[] args) throws Exception {
        SecretKey key = generateKey();

        String plainText = "患者身份证号: 110101199001011234";
        String encrypted = encrypt(plainText, key.getEncoded());
        String decrypted = decrypt(encrypted, key.getEncoded());

        System.out.println(encrypted);
        System.out.println(decrypted);
    }

    public static SecretKey generateKey() throws Exception {
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        keyGenerator.init(256);
        return keyGenerator.generateKey();
    }

    public static String encrypt(String plainText, byte[] keyBytes) throws Exception {
        byte[] iv = new byte[IV_LENGTH];
        RANDOM.nextBytes(iv);

        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
        GCMParameterSpec gcmSpec = new GCMParameterSpec(TAG_LENGTH_BIT, iv);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);

        byte[] cipherText = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));

        ByteBuffer buffer = ByteBuffer.allocate(iv.length + cipherText.length);
        buffer.put(iv);
        buffer.put(cipherText);

        return Base64.getEncoder().encodeToString(buffer.array());
    }

    public static String decrypt(String encryptedText, byte[] keyBytes) throws Exception {
        byte[] allBytes = Base64.getDecoder().decode(encryptedText);
        ByteBuffer buffer = ByteBuffer.wrap(allBytes);

        byte[] iv = new byte[IV_LENGTH];
        buffer.get(iv);

        byte[] cipherText = new byte[buffer.remaining()];
        buffer.get(cipherText);

        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
        GCMParameterSpec gcmSpec = new GCMParameterSpec(TAG_LENGTH_BIT, iv);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec);

        byte[] plainBytes = cipher.doFinal(cipherText);
        return new String(plainBytes, StandardCharsets.UTF_8);
    }
}

关键点:

点为什么
使用 SecureRandom加密随机数不能用普通 Random
GCM 推荐 12 字节 IV常见实现性能和安全性更合适
同一密钥下 IV 不能重复GCM 重复 IV 会造成严重安全风险
保存 IV 不等于泄露密钥IV 可以和密文一起保存,密钥必须保密
密钥不要硬编码要放 KMS、配置加密系统、环境变量或密钥管理平台

对称加密适合什么场景 ​

场景是否适合说明
数据库敏感字段加密适合手机号、身份证号、病案号等可用 AES 加密
文件内容加密适合文件较大,对称加密性能好
HTTPS 数据传输适合TLS 握手后用对称密钥传输数据
密码存储不适合密码应该用慢哈希,不能可逆加密
接口身份认证不单独适合还需要签名、时间戳、nonce、防重放

对称加密用错会怎样 ​

错误做法后果正确做法
使用 ECB 模式相同明文暴露相同密文模式使用 GCM
IV 固定或重复可能泄露明文关系甚至破坏安全性每次加密生成随机 IV
密钥写在代码里代码泄露等于密钥泄露用密钥管理系统
多环境共用密钥测试环境泄露影响生产按环境隔离密钥
以为加密后无需权限拿到密钥仍可解密加密、权限、审计一起做
密文不能轮换密钥密钥泄露后难恢复设计 keyVersion

非对称加密是什么 ​

非对称加密使用一对密钥:

  1. 公钥:可以公开。
  2. 私钥:必须自己保存,不能泄露。

常见用法有两类:

用法谁操作用什么密钥目的
公钥加密,私钥解密发送方加密,接收方解密公钥加密,私钥解密只有私钥持有者能看到内容
私钥签名,公钥验签发送方签名,接收方验签私钥签名,公钥验签证明消息确实来自私钥持有者
mermaid
flowchart TD
    A["服务端生成密钥对"] --> B["公钥发给客户端"]
    A --> C["私钥服务端自己保存"]
    D["客户端用公钥加密数据"] --> E["密文"]
    E --> F["服务端用私钥解密"]

非对称加密解决了“可以把公钥公开给别人”的问题。别人拿到公钥只能加密或验签,不能推出私钥。

非对称加密的优点 ​

  1. 公钥可以公开,降低密钥分发难度。
  2. 可以做数字签名,证明身份。
  3. 适合密钥交换、接口验签、证书体系。

非对称加密的缺点 ​

  1. 速度比对称加密慢。
  2. 不适合直接加密大文件、大 JSON。
  3. 加密数据长度有限。
  4. 私钥管理要求很高。

所以真实系统不会用 RSA 直接加密整个大报文,而是采用“混合加密”。

RSA-OAEP Java Demo ​

下面 Demo 演示公钥加密、私钥解密。注意使用 OAEP 填充,不要用老旧的 RSA/ECB/PKCS1Padding 作为新系统首选。

java
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;

public class RsaEncryptDemo {
    public static void main(String[] args) throws Exception {
        KeyPair keyPair = generateKeyPair();

        String plainText = "AES会话密钥或小段敏感数据";
        String encrypted = encrypt(plainText, keyPair.getPublic());
        String decrypted = decrypt(encrypted, keyPair.getPrivate());

        System.out.println(encrypted);
        System.out.println(decrypted);
    }

    public static KeyPair generateKeyPair() throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
        generator.initialize(2048);
        return generator.generateKeyPair();
    }

    public static String encrypt(String plainText, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] cipherText = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(cipherText);
    }

    public static String decrypt(String encryptedText, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] plainBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText));
        return new String(plainBytes, StandardCharsets.UTF_8);
    }
}

这里的 ECB 是 Java 对 RSA transformation 的历史命名,不等同于 AES 的 ECB 分组模式。实际关注点是 RSA 的填充方式,推荐使用 OAEP。

数字签名是什么 ​

签名不是加密。签名不隐藏内容,它解决的是:

  1. 消息是不是私钥持有者发的。
  2. 消息传输中有没有被篡改。
  3. 发送方事后难以否认。

签名流程:

mermaid
flowchart TD
    A["原始报文"] --> B["计算摘要 SHA-256"]
    B --> C["用私钥对摘要签名"]
    C --> D["签名值"]
    A --> E["报文 + 签名一起发送"]
    D --> E
    E --> F["接收方用公钥验签"]
    F --> G{"验签是否通过"}
    G -- "通过" --> H["确认来源可信且内容未改"]
    G -- "失败" --> I["拒绝请求"]

RSA 签名验签 Java Demo ​

java
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.util.Base64;

public class RsaSignatureDemo {
    public static void main(String[] args) throws Exception {
        KeyPair keyPair = generateKeyPair();

        String body = "{\"batchNo\":\"B202607010001\",\"hospitalCode\":\"H001\"}";
        String sign = sign(body, keyPair.getPrivate());
        boolean ok = verify(body, sign, keyPair.getPublic());

        System.out.println(sign);
        System.out.println(ok);
    }

    public static KeyPair generateKeyPair() throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
        generator.initialize(2048);
        return generator.generateKeyPair();
    }

    public static String sign(String body, PrivateKey privateKey) throws Exception {
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(body.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(signature.sign());
    }

    public static boolean verify(String body, String sign, PublicKey publicKey) throws Exception {
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initVerify(publicKey);
        signature.update(body.getBytes(StandardCharsets.UTF_8));
        return signature.verify(Base64.getDecoder().decode(sign));
    }
}

如果报文被改了,验签就会失败。如果公钥不是对应私钥生成的,验签也会失败。

对称加密和非对称加密对比 ​

对比项对称加密非对称加密
密钥数量一把共享密钥一对公私钥
性能快慢
加密大数据适合不适合
密钥分发困难公钥可公开,分发更容易
典型算法AES、SM4RSA、ECC、SM2
典型用途数据内容加密密钥交换、签名、证书
最大风险共享密钥泄露私钥泄露

不要说“非对称加密一定比对称加密安全”。它们解决的问题不同:

text
对称加密适合大量数据保密。
非对称加密适合密钥分发和身份可信。

为什么 HTTPS 两种都用 ​

HTTPS/TLS 使用的是混合思路:

  1. 用非对称加密和证书体系解决“连接的服务端是不是真的”以及“如何安全协商密钥”。
  2. 握手完成后,用对称加密传输业务数据。
mermaid
flowchart TD
    A["客户端访问 https://api.example.com"] --> B["服务端返回证书和公钥信息"]
    B --> C["客户端校验证书链和域名"]
    C --> D{"证书是否可信"}
    D -- "否" --> E["浏览器或客户端报错"]
    D -- "是" --> F["协商会话密钥"]
    F --> G["后续 HTTP 数据用对称密钥加密传输"]

为什么不全用非对称加密传数据?因为太慢,而且不适合大数据量。

为什么不只用对称加密?因为客户端一开始不知道安全密钥怎么传给服务端。

HTTPS 就是把两者组合起来:非对称负责建立信任和协商密钥,对称负责高效传输数据。

混合加密:商业接口常见方案 ​

第三方接口常见报文安全方案:

  1. 客户端生成随机 AES 密钥。
  2. 用 AES 加密业务报文。
  3. 用服务端 RSA 公钥加密 AES 密钥。
  4. 用客户端私钥对报文摘要签名。
  5. 服务端用私钥解出 AES 密钥。
  6. 服务端解密报文。
  7. 服务端用客户端公钥验签。
  8. 校验时间戳和 nonce,防止重放。
mermaid
flowchart TD
    A["客户端生成 AES key"] --> B["AES 加密业务报文"]
    A --> C["服务端 RSA 公钥加密 AES key"]
    B --> D["计算报文摘要"]
    D --> E["客户端私钥签名"]
    B --> F["发送 encryptedData"]
    C --> F
    E --> F
    F --> G["服务端 RSA 私钥解出 AES key"]
    G --> H["AES 解密业务报文"]
    H --> I["服务端用客户端公钥验签"]
    I --> J["校验 timestamp + nonce"]
    J --> K["处理业务"]

这种方案解决三个问题:

安全目标靠什么实现
内容保密AES 加密业务报文
AES 密钥安全传输RSA 公钥加密 AES key
防篡改和身份确认私钥签名、公钥验签
防重放timestamp + nonce + Redis 去重

混合加密请求结构示例 ​

json
{
  "appId": "hospital-a",
  "timestamp": 1782892800000,
  "nonce": "8f1c6d2a0c7b4a4d",
  "encryptedKey": "RSA加密后的AES密钥",
  "encryptedData": "AES-GCM加密后的业务报文",
  "sign": "客户端私钥签名"
}

服务端处理顺序建议:

mermaid
flowchart TD
    A["收到请求"] --> B["校验 appId 是否存在"]
    B --> C["校验 timestamp 是否在允许窗口"]
    C --> D["校验 nonce 是否已使用"]
    D --> E["用服务端私钥解 encryptedKey"]
    E --> F["用 AES key 解 encryptedData"]
    F --> G["按约定字段重建待签名字符串"]
    G --> H["用客户端公钥验签"]
    H --> I{"验签是否通过"}
    I -- "否" --> J["拒绝请求并告警"]
    I -- "是" --> K["进入业务处理"]
    K --> L["记录 nonce 防重放"]

注意:签名字段要有稳定排序规则。JSON 原始字符串如果格式化、字段顺序、空格不同,签名可能不一致。项目里常用“按字段名排序后拼接”或“对规范化 JSON 签名”。

哈希摘要不是加密 ​

哈希是单向的,不能解密。

text
digest = SHA-256(原文)

同样输入得到同样摘要;输入改一点,摘要会完全变化。

java
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.HexFormat;

public class Sha256Demo {
    public static void main(String[] args) throws Exception {
        System.out.println(sha256("hello"));
        System.out.println(sha256("hello!"));
    }

    public static String sha256(String text) throws Exception {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] bytes = digest.digest(text.getBytes(StandardCharsets.UTF_8));
        return HexFormat.of().formatHex(bytes);
    }
}

适合场景:

  1. 文件完整性校验。
  2. 请求参数摘要。
  3. 数据指纹。
  4. 密码哈希的基础组成。

不适合场景:

  1. 需要还原明文的数据。
  2. 直接 SHA-256(password) 存密码。

密码存储需要慢哈希和盐,例如 BCrypt、Argon2、PBKDF2。Spring Security 中常用 BCryptPasswordEncoder。密码不能用 AES 存,因为 AES 是可逆的,密钥泄露后所有密码都能被还原。

HMAC:共享密钥签名 ​

HMAC 可以理解成“带密钥的哈希”。双方共享同一个 secret,用它计算签名。

java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class HmacDemo {
    public static void main(String[] args) throws Exception {
        String secret = "shared-secret";
        String body = "batchNo=B202607010001&timestamp=1782892800000";
        System.out.println(hmacSha256(body, secret));
    }

    public static String hmacSha256(String data, String secret) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
        byte[] sign = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(sign);
    }
}

HMAC 常用于内部接口签名。它比 RSA 签名简单、速度快,但双方都要保存同一个 secret。任何一方泄露 secret,攻击者就能伪造签名。

JWT 是加密还是签名 ​

普通 JWT 通常是签名,不是加密。

JWT 内容由三段组成:

text
header.payload.signature

header 和 payload 通常只是 Base64Url 编码,不是加密。任何人拿到 Token 都能解码看到 payload。因此 JWT 里不要放身份证号、密码、手机号、病历内容等敏感信息。

JWT 签名的作用是:服务端收到 Token 后,可以验证它有没有被篡改。

mermaid
flowchart TD
    A["JWT header + payload"] --> B["服务端密钥或私钥签名"]
    B --> C["生成 signature"]
    C --> D["客户端携带 JWT"]
    D --> E["服务端重新验签"]
    E --> F{"签名是否一致"}
    F -- "一致" --> G["Token 未被篡改"]
    F -- "不一致" --> H["拒绝请求"]

如果想让 JWT 内容也不可见,需要使用 JWE 或者不要把敏感数据放进 Token。

医疗数据采集平台怎么用 ​

医疗数据涉及患者标识、就诊记录、检查检验结果、数据资产权限,安全设计不能只靠“用了 HTTPS”。

场景推荐做法原因
医院接口到平台传输HTTPS + 请求签名 + timestamp + nonce防窃听、防篡改、防重放
极敏感字段落库AES-GCM 字段加密 + keyVersion数据库泄露时降低明文暴露风险
用户密码BCrypt / Argon2 等慢哈希密码不应可逆
JWT 登录签名 + 短有效期 + 黑名单防篡改,支持撤销
文件上传文件摘要 + 病毒扫描 + 权限控制防篡改和越权下载
配置密钥配置加密或 KMS防止密钥散落在配置文件
开放 API非对称签名或 HMAC证明调用方身份

接口签名字段示例 ​

text
待签名字符串:
appId=hospital-a
&encryptedData=...
&nonce=8f1c6d2a0c7b4a4d
&timestamp=1782892800000

服务端必须校验:

  1. appId 是否有效。
  2. timestamp 是否过期,例如只允许 5 分钟内。
  3. nonce 是否已使用,使用 Redis 记录短期 nonce。
  4. 签名是否通过。
  5. 解密后的业务数据是否符合 schema。
  6. 当前 appId 是否有权限提交这个医院的数据。

只做加密不做签名,攻击者可能无法看懂内容,但仍可能替换密文或重放旧请求。只做签名不做加密,能防篡改但不能防止内容被看见。所以要按场景组合。

防重放 Demo ​

java
public class ReplayProtector {
    private final StringRedisTemplate redisTemplate;

    public ReplayProtector(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public void check(String appId, long timestamp, String nonce) {
        long now = System.currentTimeMillis();
        if (Math.abs(now - timestamp) > 5 * 60 * 1000) {
            throw new SecurityException("请求已过期");
        }

        String key = "api:nonce:" + appId + ":" + nonce;
        Boolean success = redisTemplate.opsForValue()
                .setIfAbsent(key, "1", Duration.ofMinutes(5));
        if (!Boolean.TRUE.equals(success)) {
            throw new SecurityException("重复请求");
        }
    }
}

这个 Demo 的重点不是 Redis API,而是防重放思想:同一个 appId + nonce 在有效时间窗口内只能使用一次。

密钥管理原则 ​

原则解释
不硬编码不把密钥写在 Java 代码、前端代码、Git 仓库
分环境隔离dev、test、prod 不共用密钥
最小权限服务只能拿自己需要的密钥
定期轮换支持 keyVersion,旧密钥逐步淘汰
审计访问谁在什么时候读取了密钥要可追踪
泄露预案密钥泄露后能禁用、换新、重加密

字段加密落库建议保存 keyVersion:

sql
create table t_patient_secure_info (
    id bigint primary key,
    patient_id bigint not null,
    id_card_cipher varchar(512) not null,
    phone_cipher varchar(512),
    key_version varchar(32) not null,
    created_at datetime not null,
    updated_at datetime not null
);

这样密钥轮换时,可以知道这条数据用哪一版密钥加密。

常见误区 ​

误区为什么错正确理解
Base64 是加密Base64 可以直接解码它只是编码
MD5 加盐就适合存密码MD5 太快,容易被撞库用 BCrypt/Argon2/PBKDF2
RSA 比 AES 更安全,所以都用 RSARSA 慢且加密长度有限RSA 用于密钥交换/签名,AES 加密数据
HTTPS 之后不用签名HTTPS 保护传输链路,不一定解决业务级防重放和调用方身份开放 API 仍常用签名
JWT 内容别人看不到JWT payload 通常只是 Base64Url不放敏感信息
加密后就不用权限控制有密钥仍能解密加密、鉴权、审计都要有
前端保存私钥前端环境无法保密私钥必须在可信服务端或安全硬件中

面试回答模板 ​

text
对称加密和非对称加密解决的问题不同。对称加密使用同一把密钥加解密,性能高,适合加密大量数据,比如字段、文件和 HTTPS 传输数据,常见算法是 AES。它的问题是密钥分发困难。非对称加密使用公钥和私钥,公钥可以公开,私钥必须保密,适合密钥交换和数字签名,比如 RSA、ECC。非对称加密性能较低,所以生产中常用混合加密:用非对称加密协商或保护对称密钥,再用对称加密传输业务数据。哈希不是加密,不能解密;签名也不是加密,它用于证明消息来源和防篡改。

项目话术:

text
在医疗数据采集平台中,医院接口到平台的链路会走 HTTPS;对开放接口还会加 appId、timestamp、nonce 和签名,防止篡改和重放。业务报文如果涉及敏感字段,可以用 AES-GCM 做字段或报文加密,AES 密钥通过密钥管理系统管理,必要时用 RSA 保护会话密钥。用户密码不会用可逆加密,而是用 BCrypt 这类慢哈希存储。

本章小结 ​

对称加密、非对称加密、哈希和签名不是互相替代关系,而是各司其职:

  1. 数据内容不想被看见,用加密。
  2. 大量数据加密,用对称加密。
  3. 密钥分发、签名验签,用非对称加密。
  4. 内容完整性,用哈希摘要。
  5. 证明身份和防篡改,用数字签名。
  6. 防重放,要加时间戳、nonce 和服务端去重。

真正安全的系统不是“用了某个算法”,而是算法选择、密钥管理、权限控制、审计、轮换和补偿都设计完整。