bigywater.tech
Java · 361

AlgorithmParameters:保存算法参数而非密钥

加密需要的不只有密钥:IV、tag 长度、盐、迭代次数、曲线,都属于「算法参数」。这篇逐个看 AlgorithmParameters 保存什么、编码成什么样、错的时候报什么。

19 字节
AES/GCM 参数的 DER 编码长度(12 字节 IV + tag 长度 16)
复用 IV 没被拦截
新建两个 Cipher、同一个密钥与同一个 IV 加密同一段明文:都成功且密文相同(同一个 Cipher 对象才会被拦截)
79 字节
PBEWithHmacSHA256AndAES_256 参数的编码长度(盐 + 迭代次数 + IV)

有哪些 AlgorithmParameters

实测 · 本机执行输出
  [services]
    JDK 21.0.12.1;AlgorithmParameters 服务 34 个:[AES, Blowfish, ChaCha20-Poly1305, DES, DESede, DSA, DiffieHellman, EC, GCM, OAEP, PBES2, PBEWithHmacSHA1AndAES_128, PBEWithHmacSHA1AndAES_256, PBEWithHmacSHA224AndAES_128, PBEWithHmacSHA224AndAES_256, PBEWithHmacSHA256AndAES_128, PBEWithHmacSHA256AndAES_256, PBEWithHmacSHA384AndAES_128, PBEWithHmacSHA384AndAES_256, PBEWithHmacSHA512/224AndAES_128, PBEWithHmacSHA512/224AndA

AES/GCM:取出、编码与错误

实测 · 本机执行输出
  [gcm]
    c.getParameters():算法 GCM;提供者 SunJCE;toString = “
      iv:
  [0000: 44 37 FB 39 16 8E DD A9   03 25 82 7F              D7.9.....%..
  ]
  tLen(bits):
  128
  ”
    getEncoded() 长度 19:3011040c4437fb39168edda90325827f020110(30 11 04 0c <12 字节 IV> 02 01 10:SEQUENCE{OCTET STRING IV,INTEGER 16 = tag 字节数})
    getParameterSpec(GCMParameterSpec):IV 4437fb39168edda90325827f(12 字节),tLen = 128 位
    getEncoded("ASN.1") 与 getEncoded() 相同? true;getEncoded("JSON") → 19
    另一方只拿到编码后的参数 + 密钥解密:hello gcm
    解密时不给参数(没有 IV):InvalidKeyException: No GCMParameterSpec specified
    解密时 tag 长度写成 96 位(加密时是 128):AEADBadTagException: Tag mismatch
    解密时 IV 错了一位:AEADBadTagException: Tag mismatch
    同一个 Cipher 对象第二次 init 加密、IV 重复(同一密钥):InvalidAlgorithmParameterException: Cannot reuse iv for GCM encryption
    同一个 Cipher 对象 doFinal 之后再 doFinal(未重新 init):IllegalStateException: Must use either different key or  iv for GCM encryption
    新建的 Cipher + 同一个固定 IV 加密两次(同一个密钥):两个 Cipher 都成功;密文相同? true(IV 复用没有被拦截,明文相同时密文相同)
    不指定 IV 时每次 init 生成的 IV:75b7707d2e9fcd88351b748b 与 e8e1eb476933d9783ee04a9d

CBC / ECB / ChaCha20 与状态错误

实测 · 本机执行输出
  [cbc]
    CBC:ap.getAlgorithm = AES;toString = “
      iv:
  [0000: DA D6 A8 8E D0 6A CD 43   57 2E C2 C8 0B 8D C7 95  .....j.CW.......
  ]
  ”;getEncoded 长度 18:0410dad6a88ed06acd43572ec2c80b8dc795
    getParameterSpec(IvParameterSpec).IV = dad6a88ed06acd43572ec2c80b8dc795
    getParameterSpec(AlgorithmParameterSpec.class) 的实际类型:IvParameterSpec
    getParameterSpec(GCMParameterSpec.class)(类型不符):InvalidParameterSpecException: Inappropriate parameter specification
    CBC 解密时不给 IV:InvalidKeyException: Parameters missing
    CBC 解密时 IV 全 0(错误的 IV):????J?!4??c?is a longer message(消息 34 字节,共 3 块;只有第一块被错误的 IV 破坏)
    CBC 加密时指定了 8 字节 IV:InvalidAlgorithmParameterException: Wrong IV length: must be 16 bytes long
    ECB 的 getParameters() = null(没有参数)
    ECB 加密时传 IvParameterSpec:InvalidAlgorithmParameterException: ECB mode cannot use IV
    ChaCha20-Poly1305:getParameters 算法 ChaCha20-Poly1305;编码 040cb85277df7fa62899ba83a888(OCTET STRING nonce,12 字节)
    AlgorithmParameters.getInstance("AES") 未 init 就 getEncoded:IOException: not initialized;getParameterSpec:InvalidParameterSpecException: not initialized
    init 一次后再 init:InvalidParameterSpecException: already initialized
    init(byte[]) 的数据格式不对:EOFException: not enough content
    AES 参数的 init(GCMParameterSpec):InvalidParameterSpecException: Inappropriate parameter specification

EC 与 DH 的参数

实测 · 本机执行输出
  [ec]
        getInstance(EC).init(ECGenParameterSpec("secp256r1"         )) → 名称 secp256r1 [NIST P-256,X9.62 prime256v1] (1.2.840.10045.3.1.7);阶 256 位;余因子 1;曲线域 256 位;编码 06082a8648ce3d030107
        getInstance(EC).init(ECGenParameterSpec("NIST P-256"        )) → 名称 secp256r1 [NIST P-256,X9.62 prime256v1] (1.2.840.10045.3.1.7);阶 256 位;余因子 1;曲线域 256 位;编码 06082a8648ce3d030107
        getInstance(EC).init(ECGenParameterSpec("prime256v1"        )) → InvalidParameterSpecException: Not a supported curve: java.security.spec.ECGenParameterSpec@799f7e29
        getInstance(EC).init(ECGenParameterSpec("1.2.840.10045.3.1.7")) → 名称 secp256r1 [NIST P-256,X9.62 prime256v1] (1.2.840.10045.3.1.7);阶 256 位;余因子 1;曲线域 256 位;编码 06082a8648ce3d030107
        getInstance(EC).init(ECGenParameterSpec("secp384r1"         )) → 名称 secp384r1 [NIST P-384] (1.3.132.0.34);阶 384 位;余因子 1;曲线域 384 位;编码 06052b81040022
        getInstance(EC).init(ECGenParameterSpec("secp521r1"         )) → 名称 secp521r1 [NIST P-521] (1.3.132.0.35);阶 521 位;余因子 1;曲线域 521 位;编码 06052b81040023
        getInstance(EC).init(ECGenParameterSpec("secp256k1"         )) → 名称 secp256k1 (1.3.132.0.10);阶 256 位;余因子 1;曲线域 256 位;编码 06052b8104000a
        getInstance(EC).init(ECGenParameterSpec("x25519"            )) → InvalidParameterSpecException: Not a supported curve: java.security.spec.ECGenParameterSpec@4b9af9a9
        getInstance(EC).init(ECGenParameterSpec("nope"              )) → InvalidParameterSpecException: Not a supported curve: java.security.spec.ECGenParameterSpec@5387f9e0
    EC 公钥的 getParams():ECPublicKeyImpl;参数 toString = secp256r1 [NIST P-256,X9.62 prime256v1] (1.2.840.10045.3.1.7)
    从密钥的 ECParameterSpec 构造 AlgorithmParameters,toString = secp256r1 [NIST P-256,X9.62 prime256v1] (1.2.840.10045.3.1.7);编码 06082a8648ce3d030107
    用 secp384r1 的 OID(06 05 2b 81 04 00 22)的编码 init:secp384r1 [NIST P-384] (1.3.132.0.34);getParameterSpec(ECGenParameterSpec) = 1.3.132.0.34
    DiffieHellman 的 AlgorithmParameters.init(DHGenParameterSpec(512, 160)):InvalidParameterSpecException: Inappropriate parameter specification
    AlgorithmParameterGenerator(DH, 1024) 生成的参数:p 1024 位,g 的位数 1023,l = 0;编码长度 267

PBE 参数

实测 · 本机执行输出
  [pbe]
    PBE 的 getParameters():算法 PBEWithHmacSHA256AndAES_256;toString 摘要 “PBEWithHmacSHA256AndAES_256”
    getEncoded 长度 79:304d302c06092a864886f70d01050c301f0408010203040506070802022710020120300c06082a864886f70d02090500301d060960864801650304012a0410ef79c2a629bbddcc260738cc9bfd81ae
    getParameterSpec(PBEParameterSpec):salt 0102030405060708,迭代 10000,参数 IvParameterSpec IV ef79c2a629bbddcc260738cc9bfd81ae
    只用编码后的参数 + 口令解密:secret text
    口令错误:BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
    PBEParameterSpec 迭代次数 0 / 负数 / 1:0 / -1 / 迭代 1 可用
    PBEParameterSpec 的 salt 为空数组:InvalidAlgorithmParameterException: Salt must be at least 8 bytes long;salt 为 7 字节:InvalidAlgorithmParameterException: Salt must be at least 8 bytes long
    加密时不给 PBEParameterSpec:成功;参数 salt 长度 20,迭代 4096

存进文件再读回

实测 · 本机执行输出
  [persist]
    保存参数文件 19 字节;密文长度 23(明文 7 字节 + 16 字节 tag)
    从文件读回参数后解密(AAD 一致):payload
    AAD 不一致:AEADBadTagException: Tag mismatch
    密文被改动一位:AEADBadTagException: Tag mismatch
    用 AlgorithmParameters("AES") 去 init GCM 编码的参数:IOException: DER input not an octet string
    把 GCM 的参数交给 CBC 的 Cipher:InvalidAlgorithmParameterException: Wrong parameter type: IV expected

分步演示

AlgorithmParameters
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12,SunJCE / SunEC 提供者;IV 与盐的数值是各次运行随机生成的
    • 没有验证:OAEP / RSASSA-PSS 参数的编码、DSA 参数、EC 参数用显式曲线(非命名曲线)构造、PBES2 的非 AES 变体、JDK 17 / 27 上的差异(包括 prime256v1 这个别名是否被接受)、其它提供者(BouncyCastle)的 AlgorithmParameters、IV 唯一性在高并发下的风险
    京ICP备2026067502号-1京公网安备11010502063487号