bigywater.tech
Java · 411

KeyPairGenerator:生成公钥和私钥

生成密钥对只有两行代码,但默认值、边界和耗时都有细节。这篇用多个 JDK 版本和 OpenSSL 量出:默认参数怎么随版本变化,RSA 与 EC 能接受什么参数,生成耗时的波动,以及固定种子能不能重现密钥。

2048 → 3072
RSA 不 initialize 时的默认模数位数:JDK 17 与 JDK 21 / 27
28.5–294.1 ms
RSA 2048 生成 30 次的最快与最慢(中位数 65.2 ms);EC P-256 为 0.12–0.25 ms
相同 vs 不同
用 setSeed(12345) 的 SHA1PRNG 与 DRBG 各生成两把 RSA 1024 密钥,模数是否相同

默认参数

实测 · 本机执行输出
  [JDK 17]
      RSA         不 initialize 直接 generateKeyPair():2048 位模数;公钥编码 294 字节、私钥编码 1217 字节;提供者 SunRsaSign
      DSA         不 initialize 直接 generateKeyPair():2048 位素数 p;公钥编码 838 字节、私钥编码 608 字节;提供者 SUN
      EC          不 initialize 直接 generateKeyPair():曲线阶 256 位;公钥编码 91 字节、私钥编码 67 字节;提供者 SunEC
      DH          不 initialize 直接 generateKeyPair():2048 位素数 p;公钥编码 556 字节、私钥编码 327 字节;提供者 SunJCE
      Ed25519     不 initialize 直接 generateKeyPair():EdDSA;公钥编码 44 字节、私钥编码 48 字节;提供者 SunEC
      X25519      不 initialize 直接 generateKeyPair():XDH;公钥编码 44 字节、私钥编码 48 字节;提供者 SunEC
      RSASSA-PSS  不 initialize 直接 generateKeyPair():2048 位模数;公钥编码 292 字节、私钥编码 1217 字节;提供者 SunRsaSign
  [JDK 21]
      RSA         不 initialize 直接 generateKeyPair():3072 位模数;公钥编码 422 字节、私钥编码 1794 字节;提供者 SunRsaSign
      DSA         不 initialize 直接 generateKeyPair():2048 位素数 p;公钥编码 838 字节、私钥编码 608 字节;提供者 SUN
      EC          不 initialize 直接 generateKeyPair():曲线阶 384 位;公钥编码 120 字节、私钥编码 80 字节;提供者 SunEC
      DH          不 initialize 直接 generateKeyPair():3072 位素数 p;公钥编码 813 字节、私钥编码 459 字节;提供者 SunJCE
      Ed25519     不 initialize 直接 generateKeyPair():EdDSA;公钥编码 44 字节、私钥编码 48 字节;提供者 SunEC
      X25519      不 initialize 直接 generateKeyPair():XDH;公钥编码 44 字节、私钥编码 48 字节;提供者 SunEC
      RSASSA-PSS  不 initialize 直接 generateKeyPair():3072 位模数;公钥编码 420 字节、私钥编码 1791 字节;提供者 SunRsaSign
  [JDK 27]
      RSA         不 initialize 直接 generateKeyPair():3072 位模数;公钥编码 422 字节、私钥编码 1793 字节;提供者 SunRsaSign
      DSA         不 initialize 直接 generateKeyPair():2048 位素数 p;公钥编码 838 字节、私钥编码 609 字节;提供者 SUN
      EC          不 initialize 直接 generateKeyPair():曲线阶 384 位;公钥编码 120 字节、私钥编码 80 字节;提供者 SunEC
      DH          不 initialize 直接 generateKeyPair():3072 位素数 p;公钥编码 812 字节、私钥编码 459 字节;提供者 SunJCE
      Ed25519     不 initialize 直接 generateKeyPair():EdDSA;公钥编码 44 字节、私钥编码 48 字节;提供者 SunEC
      X25519      不 initialize 直接 generateKeyPair():XDH;公钥编码 44 字节、私钥编码 48 字节;提供者 SunEC
      RSASSA-PSS  不 initialize 直接 generateKeyPair():3072 位模数;公钥编码 420 字节、私钥编码 1792 字节;提供者 SunRsaSign

RSA 的边界

实测 · 本机执行输出
  RSA initialize(511):InvalidParameterException:java.security.InvalidAlgorithmParameterException: Invalid key sizes
  RSA initialize(512):成功,模数 512 位
  RSA initialize(1023):成功,模数 1023 位
  RSA initialize(1024):成功,模数 1024 位
  RSA initialize(2048):成功,模数 2048 位
  RSA initialize(4097):成功,模数 4097 位
  RSA initialize(16384):成功(未实际生成,只检查参数)
  RSA initialize(16385):InvalidParameterException:java.security.InvalidAlgorithmParameterException: Invalid key sizes
  默认公钥指数 e = 65537
  RSAKeyGenParameterSpec(2048, e = 3):成功,公钥指数 3
  RSAKeyGenParameterSpec(2048, e = 17):成功,公钥指数 17
  RSAKeyGenParameterSpec(2048, e = 65537):成功,公钥指数 65537
  RSAKeyGenParameterSpec(2048, e = 65539):成功,公钥指数 65539
  RSAKeyGenParameterSpec(2048, e = 2):InvalidAlgorithmParameterException:Public exponent must be an odd number
  RSAKeyGenParameterSpec(2048, e = 4):InvalidAlgorithmParameterException:Public exponent must be an odd number
  RSAKeyGenParameterSpec(2048, e = 1):InvalidAlgorithmParameterException:Public exponent must be 3 or larger
  私钥是 RSAPrivateCrtKey:p 1024 位,q 1024 位,p×q = n true,d 2045 位,保存了 CRT 参数 dP、dQ、qInv
  生成 200 把 initialize(2048) 的 RSA 密钥:模数恰好 2048 位的有 200 把

EC 的曲线

实测 · 本机执行输出
  EC initialize(192):ProviderException:Curve not supported: secp192r1 [NIST P-192,X9.62 prime192v1] (1.2.840.10045.3.1.1)
  EC initialize(224):ProviderException:Curve not supported: secp224r1 [NIST P-224] (1.3.132.0.33)
  EC initialize(255):InvalidParameterException:No EC parameters available for key size 255 bits
  EC initialize(256):成功,曲线 secp256r1 [NIST P-256,阶 256 位
  EC initialize(384):成功,曲线 secp384r1 [NIST P-384] (1.3.132.0.34),阶 384 位
  EC initialize(521):成功,曲线 secp521r1 [NIST P-521] (1.3.132.0.35),阶 521 位
  EC initialize(522):InvalidParameterException:No EC parameters available for key size 522 bits
  ECGenParameterSpec("secp256r1"):成功,阶 256 位
  ECGenParameterSpec("secp384r1"):成功,阶 384 位
  ECGenParameterSpec("secp521r1"):成功,阶 521 位
  ECGenParameterSpec("secp256k1"):InvalidAlgorithmParameterException:Curve not supported: secp256k1 (1.3.132.0.10)
  ECGenParameterSpec("secp224r1"):InvalidAlgorithmParameterException:Curve not supported: secp224r1 [NIST P-224] (1.3.132.0.33)
  ECGenParameterSpec("brainpoolP256r1"):InvalidAlgorithmParameterException:Curve not supported: brainpoolP256r1 (1.3.36.3.3.2.8.1.1.7)
  ECGenParameterSpec("prime256v1"):InvalidAlgorithmParameterException:Unknown curve name: prime256v1
  ECGenParameterSpec("NIST P-256"):成功,阶 256 位
  ECGenParameterSpec("X9.62 prime256v1"):成功,阶 256 位
  ECGenParameterSpec("1.2.840.10045.3.1.7"):成功,阶 256 位
  所有提供者里注册的 KeyPairGenerator 算法:[DSA, DiffieHellman, EC, Ed25519, Ed448, EdDSA, RSA, RSASSA-PSS, X25519, X448, XDH]

生成耗时

实测 · 本机执行输出
  RSA      2048  30 次:最快 28.51 ms,中位数 65.21 ms,最慢 294.14 ms(最慢 / 中位数 = 4.5)
  RSA      3072  30 次:最快 99.32 ms,中位数 275.19 ms,最慢 786.68 ms(最慢 / 中位数 = 2.9)
  RSA      4096  12 次:最快 724.95 ms,中位数 1226.56 ms,最慢 2275.17 ms(最慢 / 中位数 = 1.9)
  EC       256   200 次:最快 0.12 ms,中位数 0.14 ms,最慢 0.25 ms(最慢 / 中位数 = 1.8)
  EC       384   200 次:最快 0.66 ms,中位数 0.73 ms,最慢 1.58 ms(最慢 / 中位数 = 2.2)
  EC       521   200 次:最快 1.17 ms,中位数 1.48 ms,最慢 2.19 ms(最慢 / 中位数 = 1.5)
  Ed25519        200 次:最快 0.22 ms,中位数 0.27 ms,最慢 0.80 ms(最慢 / 中位数 = 2.9)
  X25519         200 次:最快 0.11 ms,中位数 0.18 ms,最慢 0.29 ms(最慢 / 中位数 = 1.6)
  DH       2048  6 次:最快 0.31 ms,中位数 0.34 ms,最慢 0.38 ms(最慢 / 中位数 = 1.1)
  DSA      2048  6 次:最快 0.33 ms,中位数 0.34 ms,最慢 0.36 ms(最慢 / 中位数 = 1.1)

固定种子

实测 · 本机执行输出
  SHA1PRNG:用 setSeed(12345) 的 SecureRandom 各生成一把 RSA 1024 密钥,两次的模数完全相同(密钥可重现):[88e453ac7d8e84fdc67b82c9, 88e453ac7d8e84fdc67b82c9]
  DRBG:用 setSeed(12345) 的 SecureRandom 各生成一把 RSA 1024 密钥,两次的模数不同:[801b409b5ada23633e8839c4, c017bb169cff4ac17c038a51]
  NativePRNG:用 setSeed(12345) 的 SecureRandom 各生成一把 RSA 1024 密钥,两次的模数不同:[9368ab1444a2ffc035dd3588, 8ac7ef1e04d9fb29d4ea3cb4]
  SHA1PRNG + setSeed(1):EC P-256 私钥相同 = true
  默认 new SecureRandom() 的算法 NativePRNG,提供者 SUN;KeyPairGenerator 不传 SecureRandom 时使用 JDK 内部的默认实例(与它相同类型)
  SHA1PRNG:先 setSeed(42) 再取字节 d50ac288b90ede2e;先取字节再 setSeed(42)(补充种子而不是替换)得到 986aadeb165af734(两者不同:setSeed 在第一次取字节之后只是追加熵)

编码与 OpenSSL

实测 · 本机执行输出
  RSA:私钥 getFormat() = PKCS#8,公钥 getFormat() = X.509,私钥 1217 字节
  EC:私钥 getFormat() = PKCS#8,公钥 getFormat() = X.509,私钥 67 字节
  Ed25519:私钥 getFormat() = PKCS#8,公钥 getFormat() = X.509,私钥 48 字节
  openssl pkey 读取 RSA.priv.der:Private-Key: (2048 bit, 2 primes);读取 RSA.pub.der:Public-Key: (2048 bit)
  openssl pkey 读取 EC.priv.der:Private-Key: (256 bit field, 128 bit security level);读取 EC.pub.der:Public-Key: (256 bit field, 128 bit security level)
  openssl pkey 读取 Ed25519.priv.der:ED25519 Private-Key:;读取 Ed25519.pub.der:ED25519 Public-Key:

分步演示

KeyPairGenerator
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 17.0.20(默认值)、21.0.12(其余)、27(默认值);OpenSSL 4.0.3;耗时是自写的 nanoTime 循环,次数见各行
    • 没有验证:Windows 与 Linux 上 NativePRNG 的差异、PKCS#11 与 HSM 提供者、BouncyCastle 的 KeyPairGenerator、RSA 超过 8192 位的实际生成、FIPS 模式、JDK 22–26 的默认值变化的具体版本
    京ICP备2026067502号-1京公网安备11010502063487号