Apache Commons Crypto 常用 CLI 命令与工具详解

以命令行实操演示 Apache Commons Crypto 1.2.0:javac/java -cp 与 jshell 用法、AES 三模式文件加解密工具完整源码与实测输出、OpenSSL 检测及性能对比。

Apache Commons CryptoApache Commons Crypto2026/10/13

Apache Commons Crypto 常用 CLI 命令与工具详解

目标:不依赖 IDE,只用 javac、java -cp 与 jshell 在服务器上完成 Commons Crypto 的编译、运行、文件加解密与性能观察,所有命令、源码与回显均为实测,已在 Ubuntu 22.04 / CentOS 8(JDK 17 + Maven)实测通过。

一、环境信息

本文所有命令与回显来自以下两个环境,二者构成「JCE 路径」与「OpenSSL 路径」的对照:

项目环境1环境2
操作系统Ubuntu 22.04.5 LTSCentOS Linux 8(8.4.2105)
JDKOpenJDK 17.0.20.1OpenJDK 17.0.1
Maven3.6.33.5.4
OpenSSL3.0.21.1.1k FIPS
加密实现加密回退 JceCipher、随机数走 OpenSSL加密与随机数均走 OpenSSL
组件Apache Commons Crypto 1.2.0(commons-crypto-1.2.0.jar,176035 字节)同左

工作目录统一使用 /opt/cc-lab,jar 放在其中:

mkdir -p /opt/cc-lab && cd /opt/cc-lab && cp /opt/commons-crypto-1.2.0/commons-crypto-1.2.0.jar . && ls -l

回显(实测):

total 176
-rw-r--r-- 1 root root 176035 Oct 13 07:40 commons-crypto-1.2.0.jar

二、javac 与 java -cp 基础

命令行使用 Commons Crypto 的核心只有两条规则:编译与运行都要挂 classpath;Linux 分隔符是冒号。先看最常见的错误——编译时漏挂 jar:

javac Probe1.java

回显(实测,节选):

Probe1.java:1: error: package org.apache.commons.crypto does not exist
import org.apache.commons.crypto.Crypto;
                                ^

补上 -cp 即可(-encoding UTF-8 用于 POSIX locale 下带中文注释的源码):

javac -encoding UTF-8 -cp commons-crypto-1.2.0.jar Probe1.java && echo JAVAC_OK

回显(实测):

JAVAC_OK

运行时同样要挂 jar,且类路径分隔符是冒号(Windows 才是分号):

java -cp .:commons-crypto-1.2.0.jar Probe1

回显(实测,Ubuntu 22.04):

component=Apache Commons Crypto-1.2.0
nativeLoaded=true
AES/GCM/NoPadding -> org.apache.commons.crypto.cipher.JceCipher
AES/CTR/NoPadding -> org.apache.commons.crypto.cipher.JceCipher
AES/CBC/PKCS5Padding -> org.apache.commons.crypto.cipher.JceCipher
random -> org.apache.commons.crypto.random.OpenSslCryptoRandom

同一命令在 CentOS 8 上的回显(实测):

component=Apache Commons Crypto-1.2.0
nativeLoaded=true
AES/GCM/NoPadding -> org.apache.commons.crypto.cipher.OpenSslCipher
AES/CTR/NoPadding -> org.apache.commons.crypto.cipher.OpenSslCipher
AES/CBC/PKCS5Padding -> org.apache.commons.crypto.cipher.OpenSslCipher
random -> org.apache.commons.crypto.random.OpenSslCryptoRandom

两个环境一次看清了工厂的自动选择差异,后续工具输出请对照此基准。

💡 技术拓展:本文示例覆盖三种分组加密模式,选型对比:ECB 无 IV、相同明文块得到相同密文块,已不安全,不建议使用;CBC 需随机 IV、串行处理、PKCS5 填充使密文膨胀到块大小的整数倍;CTR 把分组密码变成流密码、无填充、可并行,但同一 IV 绝不能加密两份数据;GCM 在 CTR 基础上附加认证标签(默认 128 bit),可检测篡改,是当前推荐模式。实测三者对同一 86 字节明文的密文长度:GCM 102 字节(含 16 字节 tag)、CTR 86 字节(无膨胀)、CBC 96 字节(填充膨胀)。

三、文件加解密小工具 FileCrypto

本节给出一个完整可用的命令行文件加解密工具:支持 GCM、CTR、CBC 三种模式,密钥与 IV 以 hex 传入,一次 doFinal 完成加解密。

3.1 完整源码

import org.apache.commons.crypto.cipher.CryptoCipher;
import org.apache.commons.crypto.cipher.CryptoCipherFactory;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Properties;

/**
 * 文件加解密小工具(示例程序):
 * FileCrypto <enc|dec> <transformation> <keyHex> <ivHex> <infile> <outfile>
 */
public class FileCrypto {

    private static byte[] hexToBytes(String hex) {
        if (hex.length() % 2 != 0) {
            throw new IllegalArgumentException("hex length must be even: " + hex.length());
        }
        byte[] out = new byte[hex.length() / 2];
        for (int i = 0; i < out.length; i++) {
            out[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
        }
        return out;
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder(bytes.length * 2);
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }

    public static void main(String[] args) throws Exception {
        if (args.length != 6) {
            System.err.println("Usage: FileCrypto <enc|dec> <transformation> <keyHex> <ivHex> <infile> <outfile>");
            System.err.println("  transformation e.g. AES/GCM/NoPadding | AES/CTR/NoPadding | AES/CBC/PKCS5Padding");
            System.exit(2);
        }
        String op = args[0];
        String transformation = args[1];
        byte[] key = hexToBytes(args[2]);
        byte[] iv = hexToBytes(args[3]);
        if (key.length != 16 && key.length != 24 && key.length != 32) {
            throw new IllegalArgumentException("AES key must be 16/24/32 bytes, got " + key.length);
        }
        byte[] input = Files.readAllBytes(Paths.get(args[4]));

        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        String mode = transformation.split("/")[1];
        AlgorithmParameterSpec spec;
        if ("GCM".equals(mode)) {
            spec = new GCMParameterSpec(128, iv);          // GCM: tag 长度 128 bit + nonce
        } else {
            spec = new IvParameterSpec(iv);                // CBC/CTR: 普通 IV
        }

        Properties props = new Properties();
        CryptoCipher cipher = CryptoCipherFactory.getCryptoCipher(transformation, props);
        System.out.println("cipher impl: " + cipher.getClass().getName());

        cipher.init("enc".equals(op) ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, keySpec, spec);

        ByteBuffer inBuf = ByteBuffer.allocateDirect(input.length);
        inBuf.put(input);
        inBuf.flip();
        ByteBuffer outBuf = ByteBuffer.allocateDirect(input.length + 32); // 预留 tag/padding 空间
        cipher.doFinal(inBuf, outBuf);
        outBuf.flip();
        byte[] output = new byte[outBuf.remaining()];
        outBuf.get(output);
        cipher.close();

        Files.write(Paths.get(args[5]), output);
        PrintStream out = System.out;
        out.println(op + " ok: " + args[4] + " (" + input.length + " B) -> " + args[5] + " (" + output.length + " B)");
        if ("enc".equals(op)) {
            out.println("ciphertext(hex, first 32): " + bytesToHex(output).substring(0, Math.min(64, output.length * 2)));
        }
    }
}

源码要点:模式常量取自 javax.crypto.Cipher(Commons Crypto 的 CryptoCipher 接口本身没有 ENCRYPT_MODE 常量);GCM 必须传 GCMParameterSpec,CBC/CTR 传 IvParameterSpec;输出缓冲比明文多预留 32 字节,容纳 tag 与填充。

3.2 编译与三模式加解密

准备明文与固定密钥、IV(示例固定值,生产环境请通过密钥管理系统下发):

cd /opt/cc-lab && printf "Hello Apache Commons Crypto 1.2.0! This line is encrypted with AES on a Linux server.\n" > plain.txt && wc -c plain.txt

回显(实测):

86 plain.txt

GCM 加解密(密钥为 32 字节 AES-256,IV 为 12 字节 nonce):

KEY=603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4
java -cp .:commons-crypto-1.2.0.jar FileCrypto enc AES/GCM/NoPadding $KEY 000102030405060708090a0b plain.txt plain-gcm.enc
java -cp .:commons-crypto-1.2.0.jar FileCrypto dec AES/GCM/NoPadding $KEY 000102030405060708090a0b plain-gcm.enc plain-gcm.dec
cmp plain.txt plain-gcm.dec && echo GCM_ROUNDTRIP_OK

回显(实测,Ubuntu 22.04):

cipher impl: org.apache.commons.crypto.cipher.JceCipher
enc ok: plain.txt (86 B) -> plain-gcm.enc (102 B)
ciphertext(hex, first 32): 4004dd3feb64da0ec7c32accad7df6862813e8918226ebaaa8c393c0588a278d
cipher impl: org.apache.commons.crypto.cipher.JceCipher
dec ok: plain-gcm.enc (102 B) -> plain-gcm.dec (86 B)
GCM_ROUNDTRIP_OK

CTR 与 CBC(IV 为 16 字节):

java -cp .:commons-crypto-1.2.0.jar FileCrypto enc AES/CTR/NoPadding $KEY f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff plain.txt plain-ctr.enc
java -cp .:commons-crypto-1.2.0.jar FileCrypto enc AES/CBC/PKCS5Padding $KEY 000102030405060708090a0b0c0d0e0f plain.txt plain-cbc.enc
ls -l plain-ctr.enc plain-cbc.enc

回显(实测):

cipher impl: org.apache.commons.crypto.cipher.JceCipher
enc ok: plain.txt (86 B) -> plain-ctr.enc (86 B)
ciphertext(hex, first 32): 43ba119d363757433ff9e370e823aa6f370107ee73226b7f2447e91cbe4b49ba
cipher impl: org.apache.commons.crypto.cipher.JceCipher
enc ok: plain.txt (86 B) -> plain-cbc.enc (96 B)
ciphertext(hex, first 32): aec82a22c76025d499761e30bbdabeb0f798ee0c12ee469305527e749933bd41
-rw-r--r-- 1 root root  96 Oct 13 07:56 plain-cbc.enc
-rw-r--r-- 1 root root  86 Oct 13 07:56 plain-ctr.enc

密文长度印证 2 节拓展表:CTR 无膨胀、CBC 膨胀到 96、GCM 含 tag 为 102。

3.3 跨实现互操作与 openssl 交叉验证

把 Ubuntu 上(JceCipher 路径)加密的 GCM 文件拿到 CentOS 8(OpenSslCipher 路径)解密:

java -cp .:commons-crypto-1.2.0.jar FileCrypto dec AES/GCM/NoPadding $KEY 000102030405060708090a0b u22-gcm.enc u22-gcm.dec

回显(实测,CentOS 8):

cipher impl: org.apache.commons.crypto.cipher.OpenSslCipher
dec ok: u22-gcm.enc (102 B) -> u22-gcm.dec (86 B)
Hello Apache Commons Crypto 1.2.0! This line is encrypted with AES on a Linux server.

更重要的是:两种实现产出的密文逐字节相同(同密钥同 IV 下 GCM 均为 4004dd3f 开头、CTR 均为 43ba119d 开头)。再用系统 openssl 命令行对 CTR 全文做交叉验证:

od -An -tx1 plain-ctr.enc | tr -d " \n"; echo
printf "Hello Apache Commons Crypto 1.2.0! This line is encrypted with AES on a Linux server.\n" | openssl enc -aes-256-ctr -K 603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4 -iv f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff | od -An -tx1 | tr -d " \n"; echo

回显(实测,两行完全一致):

43ba119d363757433ff9e370e823aa6f370107ee73226b7f2447e91cbe4b49ba2be00cc869087e7d61e2b8c617e7b9424c3882ba104542d4dac9eb1ad77d56f7ce24df8fb75c68b29b9e606991efb0d23e9d02f04e0c
43ba119d363757433ff9e370e823aa6f370107ee73226b7f2447e91cbe4b49ba2be00cc869087e7d61e2b8c617e7b9424c3882ba104542d4dac9eb1ad77d56f7ce24df8fb75c68b29b9e606991efb0d23e9d02f04e0c

结论:Commons Crypto 的 JCE 路径、OpenSSL 路径与系统 openssl 三者输出完全互通,可放心混用。

四、jshell 交互实操

jshell 适合快速验证 API 行为。把以下内容保存为 demo.jsh:

import org.apache.commons.crypto.cipher.CryptoCipherFactory
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec
import javax.crypto.spec.IvParameterSpec
import java.nio.ByteBuffer
import java.util.HexFormat
import java.util.Properties

var cipher = CryptoCipherFactory.getCryptoCipher("AES/CTR/NoPadding", new Properties())
System.out.println("impl  : " + cipher.getClass().getName())

// 注意:IV 必须传原始字节;这里用 HexFormat.parseHex 解析 hex 字符串
// (常见错误:直接传 "ffffffff..." 字符串,实际得到 16 个 0x66 而不是 16 个 0xFF)
var key = new SecretKeySpec("0123456789abcdef".getBytes("UTF-8"), "AES")
var iv  = new IvParameterSpec(HexFormat.of().parseHex("ffffffffffffffffffffffffffffffff"))
cipher.init(Cipher.ENCRYPT_MODE, key, iv)

var in = ByteBuffer.allocateDirect(16)
in.put("Attack at dawn!!".getBytes("UTF-8"))
in.flip()
var out = ByteBuffer.allocateDirect(32)
cipher.doFinal(in, out)
out.flip()
var ct = new byte[out.remaining()]
out.get(ct)
System.out.println("cipher: " + HexFormat.of().formatHex(ct))
cipher.close()
/exit

运行(脚本内显式 println,因为管道模式下 jshell 默认不回显表达式值):

jshell --class-path commons-crypto-1.2.0.jar demo.jsh

回显(实测,Ubuntu 22.04):

impl  : org.apache.commons.crypto.cipher.JceCipher
cipher: 70960b1d58fafc5007cf33de38071dca

与 openssl 命令行结果一致(同密钥同 IV):

printf "Attack at dawn!!" | openssl enc -aes-128-ctr -K 30313233343536373839616263646566 -iv ffffffffffffffffffffffffffffffff | od -An -tx1 | tr -d " \n"; echo

回显(实测):

70960b1d58fafc5007cf33de38071dca

jshell 三个实测踩坑记录:一是不要加 --execution local,实测会报 NoClassDefFoundError: org/apache/commons/crypto/cipher/CryptoCipherFactory(classpath 未传递给执行引擎);二是 -q 静默模式下表达式值不回显,脚本里要用 System.out.println;三是 IvParameterSpec 构造参数是原始字节,直接传 "ffffffffffffffff" 字符串得到的是 16 个 0x66(字符 f 的 ASCII),实测这条路线的密文与 openssl 不一致,就是 IV 字节含义不同导致的。

五、OpenSSL 与 native 环境检测

排查前先弄清系统 OpenSSL 形态与 Commons Crypto 的 native 加载状态。

5.1 系统 OpenSSL 版本与库文件

openssl version && ldconfig -p | grep libcrypto

回显(实测,Ubuntu 22.04):

OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)
        libcrypto.so.3 (libc6,x86-64) => /lib/x86_64-linux-gnu/libcrypto.so.3
        libcrypto.so (libc6,x86-64) => /lib/x86_64-linux-gnu/libcrypto.so

回显(实测,CentOS 8):

OpenSSL 1.1.1k  FIPS 25 Mar 2021
        libcrypto.so.1.1 (libc6,x86-64) => /lib64/libcrypto.so.1.1
        libcrypto.so (libc6,x86-64) => /lib64/libcrypto.so

关键检查点:是否存在未带版本号的 libcrypto.so。缺失时 Commons Crypto 随机数 native 初始化会抛 UnsatisfiedLinkError: Cannot load libcrypto.so 并直接崩溃(工厂不回退 Error)。

5.2 native 加载过程追踪

java -Dcommons.crypto.debug=true -cp .:commons-crypto-1.2.0.jar Probe1 2>&1 | grep -E "Extract|System.load|libraryLoaded"

回显(实测):

Extracting '/org/apache/commons/crypto/native/Linux/x86_64/libcommons-crypto.so' to '/tmp/commons-crypto-ad3c9409-4d1e-44b9-8b9e-ae8f728d484d-libcommons-crypto.so'...
NativeCodeLoader System.load('/tmp/commons-crypto-ad3c9409-4d1e-44b9-8b9e-ae8f728d484d-libcommons-crypto.so')
NativeCodeLoader libraryLoaded = true, libraryLoadingError = null

可见 jar 内 22,552 字节的胶水库被解压到临时目录后加载,文件在 JVM 退出时自动删除。

5.3 加密路径可用性检测

EnvCheck(见 2 节回显)能区分两种实现。若想强制只用 OpenSSL 实现并拿到失败原因,把 commons.crypto.cipher.classes 设为仅 OpenSslCipher:

openssl-only FAILED: CryptoCipher {org.apache.commons.crypto.cipher.OpenSslCipher} is not available or transformation AES/CTR/NoPadding is not supported.
  caused by: java.lang.IllegalStateException: java.lang.UnsatisfiedLinkError: EVP_CIPHER_CTX_block_size

Ubuntu 22.04 的 OpenSSL 3.0 缺少该 JNI 符号,因此加密固定回退 JCE;CentOS 8 的 OpenSSL 1.1.1 则完整可用。

💡 技术拓展:三种加密实现路径的吞吐量级差异显著——JCE 走 JVM 内置代码,JDK 17 在 AES-NI 硬件上有内建优化;OpenSSL JNI 直接调用 libcrypto 的汇编优化实现,长数据场景通常最快;JNA 无需编译但每次调用有映射开销,吞吐最低,仅适合低频调用。下节的实测数据量化了前两者的差距。

六、JCE 与 OpenSSL 性能对比

用同一个程序分别强制两条实现路径,测 AES-CTR 与 AES-GCM 的加密吞吐。完整源码(Perf.java):

import org.apache.commons.crypto.cipher.CryptoCipher;
import org.apache.commons.crypto.cipher.CryptoCipherFactory;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Properties;

/**
 * JCE 与 OpenSSL(JNI) 两条实现路径的吞吐对比(功能验证参考,非生产基准)。
 * 用法: Perf <transformation> <dataMB> [jce|openssl|default]
 */
public class Perf {

    private static final SecureRandom RND = new SecureRandom();

    public static void main(String[] args) throws Exception {
        String transformation = args.length > 0 ? args[0] : "AES/CTR/NoPadding";
        int dataMB = args.length > 1 ? Integer.parseInt(args[1]) : 128;
        String impl = args.length > 2 ? args[2] : "default";

        Properties props = new Properties();
        if ("jce".equals(impl)) {
            props.setProperty(CryptoCipherFactory.CLASSES_KEY,
                    CryptoCipherFactory.CipherProvider.JCE.getClassName());
        } else if ("openssl".equals(impl)) {
            props.setProperty(CryptoCipherFactory.CLASSES_KEY,
                    CryptoCipherFactory.CipherProvider.OPENSSL.getClassName());
        }

        int size = dataMB * 1024 * 1024;
        byte[] data = new byte[size];
        RND.nextBytes(data);

        byte[] keyBytes = new byte[16];
        RND.nextBytes(keyBytes);
        SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");

        CryptoCipher cipher = CryptoCipherFactory.getCryptoCipher(transformation, props);
        System.out.println("transformation=" + transformation + ", impl=" + cipher.getClass().getName()
                + ", data=" + dataMB + " MB");

        int chunk = 64 * 1024;
        ByteBuffer in = ByteBuffer.allocateDirect(chunk);
        ByteBuffer out = ByteBuffer.allocateDirect(chunk + 16);

        // 预热 16 MB(GCM 每次加密必须换新 nonce,否则 JDK 17 SunJCE 抛 InvalidAlgorithmParameterException)
        cipher.init(Cipher.ENCRYPT_MODE, key, newSpec(transformation));
        for (int off = 0; off < 16 * 1024 * 1024; off += chunk) {
            encryptChunk(cipher, data, off, chunk, in, out);
        }

        long t0 = System.nanoTime();
        cipher.init(Cipher.ENCRYPT_MODE, key, newSpec(transformation));
        for (int off = 0; off < size; off += chunk) {
            encryptChunk(cipher, data, off, chunk, in, out);
        }
        long t1 = System.nanoTime();
        cipher.close();

        double seconds = (t1 - t0) / 1_000_000_000.0;
        System.out.printf("encrypt %d MB in %.3f s -> %.1f MB/s%n", dataMB, seconds, dataMB / seconds);
    }

    /** GCM 用 12 字节 nonce 的 GCMParameterSpec,CBC/CTR 用 16 字节 IvParameterSpec;每次调用生成新随机值 */
    private static AlgorithmParameterSpec newSpec(String transformation) {
        boolean gcm = transformation.contains("GCM");
        byte[] iv = new byte[gcm ? 12 : 16];
        RND.nextBytes(iv);
        return gcm ? new GCMParameterSpec(128, iv) : new IvParameterSpec(iv);
    }

    private static void encryptChunk(CryptoCipher cipher, byte[] data, int off, int len,
                                     ByteBuffer in, ByteBuffer out) throws Exception {
        in.clear();
        in.put(data, off, len);
        in.flip();
        out.clear();
        cipher.update(in, out);
        long residue = in.remaining();
        if (residue != 0) {
            throw new IllegalStateException("input not fully consumed: " + residue);
        }
        out.clear();
    }
}
javac -encoding UTF-8 -cp commons-crypto-1.2.0.jar Perf.java
java -Xmx512m -cp .:commons-crypto-1.2.0.jar Perf AES/CTR/NoPadding 128 openssl

回显(实测,CentOS 8):

transformation=AES/CTR/NoPadding, impl=org.apache.commons.crypto.cipher.OpenSslCipher, data=128 MB
encrypt 128 MB in 0.034 s -> 3774.9 MB/s

其余三组实测:

transformation=AES/CTR/NoPadding, impl=org.apache.commons.crypto.cipher.JceCipher, data=128 MB
encrypt 128 MB in 0.090 s -> 1416.0 MB/s

transformation=AES/GCM/NoPadding, impl=org.apache.commons.crypto.cipher.OpenSslCipher, data=128 MB
encrypt 128 MB in 0.046 s -> 2809.3 MB/s

transformation=AES/GCM/NoPadding, impl=org.apache.commons.crypto.cipher.JceCipher, data=128 MB
encrypt 128 MB in 0.700 s -> 182.9 MB/s

Ubuntu 22.04 上(加密只有 JCE 可用)的对照实测:

transformation=AES/CTR/NoPadding, impl=org.apache.commons.crypto.cipher.JceCipher, data=128 MB
encrypt 128 MB in 0.080 s -> 1599.7 MB/s

transformation=AES/GCM/NoPadding, impl=org.apache.commons.crypto.cipher.JceCipher, data=128 MB
encrypt 128 MB in 0.729 s -> 175.5 MB/s

汇总(128MB 数据、64KB 分块、单次预热 16MB):

环境模式实现吞吐
CentOS 8AES-CTROpenSslCipher3774.9 MB/s
CentOS 8AES-CTRJceCipher1416.0 MB/s
CentOS 8AES-GCMOpenSslCipher2809.3 MB/s
CentOS 8AES-GCMJceCipher182.9 MB/s
Ubuntu 22.04AES-CTRJceCipher1599.7 MB/s
Ubuntu 22.04AES-GCMJceCipher175.5 MB/s

逐项说明:CTR 下 OpenSSL 路径约为 JCE 的 2.7 倍;GCM 差距最大(约 15 倍),因为这台 CentOS 8 的旧版 SunJCE 未启用 GCM 向量化优化。以上数据为功能验证参考,非生产基准——绝对值受 CPU、内核、JDK 小版本与内存频率影响,生产选型请以目标硬件上的复测为准。

补充一个失败场景实测:在 Ubuntu 22.04 上强制 openssl 路径会直接抛异常终止:

Exception in thread "main" java.security.GeneralSecurityException: CryptoCipher {org.apache.commons.crypto.cipher.OpenSslCipher} is not available or transformation AES/CTR/NoPadding is not supported.
        at org.apache.commons.crypto.cipher.CryptoCipherFactory.getCryptoCipher(CryptoCipherFactory.java:199)

另有一个实测 API 约束:Commons Crypto 的 OpenSSL GCM 实现要求 GCMParameterSpec,传 IvParameterSpec 会抛 InvalidAlgorithmParameterException: Illegal parameters;而 JDK 17 SunJCE 的 GCM 在同一 Cipher 实例上重复 init 复用旧 nonce 会抛 InvalidAlgorithmParameterException: Cannot reuse iv for GCM encryption,所以 Perf 每次加密前都生成新 nonce。

七、参考链接

  1. Apache Commons Crypto 官方主页:https://commons.apache.org/proper/commons-crypto/
  2. 官方用户指南:https://commons.apache.org/proper/commons-crypto/userguide.html
  3. Javadoc 在线文档:https://commons.apache.org/proper/commons-crypto/javadocs/api-release/index.html
  4. OpenSSL 官方文档:https://www.openssl.org/docs/
  5. 项目源码仓库:https://github.com/apache/commons-crypto