Apache Commons Crypto 配置参数详解

系统梳理 Apache Commons Crypto 1.2.0 全部配置参数的作用域、默认值与实测行为,含配置入口对照实验、参数速查表与三类排错场景。

Apache Commons CryptoApache Commons Crypto2026/10/13

Apache Commons Crypto 配置参数详解

目标:逐项讲清 Apache Commons Crypto 1.2.0 全部配置参数的名称、默认值、作用域与真实行为,每个参数都配可运行示例与实测输出,全部已在 Ubuntu 22.04 / CentOS 8(JDK 17 + Maven)实测通过。

一、环境信息

本文所有参数行为都在以下两个环境实测,覆盖两种典型服务器形态:

项目环境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
组件Apache Commons Crypto 1.2.0(单 jar,176035 字节)同左

两个环境的差异恰好构成对照实验:环境2 上加密与随机数都走 OpenSSL JNI 路径,环境1 上加密因 OpenSSL 3.0 缺少 JNI 符号而回退 JCE(随机数正常)。同一套参数在两边的表现差异会在文中明确标注。

验证基础环境是否可用(EnvCheck 打印实现类选择):

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

回显(实测,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

二、配置入口与作用域

这是理解本组件配置最重要的一章:1.2.0 的参数有三个入口,但每个入口只对特定参数生效,混用会静默失效。源码层面(Utils.java、NativeCodeLoader.java 实测确认):

配置入口生效的参数底层读取者
CryptoCipherFactory/CryptoRandomFactory 方法传入的 Properties 对象commons.crypto.cipher.classes、commons.crypto.cipher.jce.provider、commons.crypto.secure.random.classes、commons.crypto.secure.random.java.algorithm工厂类直接 props.getProperty(...)
JVM -D 系统属性commons.crypto.lib.path、commons.crypto.lib.name、commons.crypto.lib.tempdir、commons.crypto.debugNativeCodeLoader 经 Utils.getDefaultProperties()
classpath 根下的 commons.crypto.properties 文件同上(native 一组),且可被 -D 覆盖同上

逐项说明:实现选择类参数只认方法传入的 Properties 对象;native 加载类参数读「系统属性为底、properties 文件覆盖、-D 最终生效」的合并链。下面用两组对照实验证明。

2.1 实测:-D 对实现选择参数无效

java -Dcommons.crypto.secure.random.classes=org.apache.commons.crypto.random.JavaCryptoRandom -cp .:commons-crypto-1.2.0.jar GenKey | head -1

回显(实测):

random impl: org.apache.commons.crypto.random.OpenSslCryptoRandom

-D 设置被无视,仍然是 OpenSSL 实现。同一个参数改由 Properties 对象传入:

import org.apache.commons.crypto.random.CryptoRandomFactory;
import java.util.Properties;

public class T {
    public static void main(String[] a) throws Exception {
        Properties p = new Properties();
        p.setProperty(CryptoRandomFactory.CLASSES_KEY, "org.apache.commons.crypto.random.JavaCryptoRandom");
        System.out.println(CryptoRandomFactory.getCryptoRandom(p).getClass().getName());
    }
}
java -cp .:commons-crypto-1.2.0.jar T

回显(实测):

org.apache.commons.crypto.random.JavaCryptoRandom

结论:想切换加密器/随机数实现,必须把参数放进传给工厂方法的 Properties 对象。

2.2 实测:properties 文件对 native 参数生效

在 classpath 根放置 /opt/cc-conf2/commons.crypto.properties:

commons.crypto.lib.tempdir=/opt/cc-conf2

运行任意触发 native 加载的代码(开启 debug 观察):

cd /opt/cc-conf2 && java -Dcommons.crypto.debug=true -cp .:commons-crypto-1.2.0.jar GenKey 2>&1 | grep -E "Extracted|System.load"

回显(实测):

Extracted '/org/apache/commons/crypto/native/Linux/x86_64/libcommons-crypto.so' to '/opt/cc-conf2/commons-crypto-8c0965b7-af6b-4d8a-bdd9-9ffdb0fc7283-libcommons-crypto.so': 22,552 bytes [X-]
NativeCodeLoader System.load('/opt/cc-conf2/commons-crypto-8c0965b7-af6b-4d8a-bdd9-9ffdb0fc7283-libcommons-crypto.so')

native 库被解压到了 properties 文件指定的目录而非默认的 java.io.tmpdir,证明文件入口生效。

💡 技术拓展:Commons Crypto 的加密实现路径对比——JCE(JVM 内置软件实现,JDK 17 下带 AES-NI 内建优化)、OpenSSL JNI(jar 内嵌各平台 JNI 胶水库,调用系统 OpenSSL,长数据吞吐最高)、JNA(经 net.java.dev.jna 动态映射调用 OpenSSL,免编译但开销大,1.2.0 中 jna 5.13.0 是传递依赖,JNA 路径代码仍在但工厂默认实现列表只有 JNI 版 OpenSslCipher 与 JceCipher)。本文所有实验均基于工厂默认列表(JNI 优先、JCE 兜底)。

三、加密器参数

加密器由 CryptoCipherFactory.getCryptoCipher(transformation, properties) 创建,参数共两个。

3.1 commons.crypto.cipher.classes:实现类优先级列表

默认值(源码 CryptoCipherFactory.CLASSES_DEFAULT 实测):

org.apache.commons.crypto.cipher.OpenSslCipher,org.apache.commons.crypto.cipher.JceCipher

取值为逗号分隔的全限定类名,按顺序尝试:前一个初始化失败就换下一个,全部失败才抛 GeneralSecurityException。强制只用 JCE 的写法:

Properties p = new Properties();
p.setProperty(CryptoCipherFactory.CLASSES_KEY, CryptoCipherFactory.CipherProvider.JCE.getClassName());
CryptoCipher c = CryptoCipherFactory.getCryptoCipher("AES/CTR/NoPadding", p);
java -cp .:commons-crypto-1.2.0.jar Probe2

回显(实测,Ubuntu 22.04):

jce-forced -> org.apache.commons.crypto.cipher.JceCipher

在 OpenSSL 1.1 的 CentOS 8 上,默认列表会选中 OpenSslCipher(实测回显):

AES/CTR/NoPadding -> org.apache.commons.crypto.cipher.OpenSslCipher

3.2 commons.crypto.cipher.jce.provider:JCE 提供者名

仅对 JceCipher 生效,缺省走 JVM 默认提供者查找。显式指定内置的 SunJCE:

Properties p = new Properties();
p.setProperty(CryptoCipherFactory.CLASSES_KEY, "org.apache.commons.crypto.cipher.JceCipher");
p.setProperty(CryptoCipherFactory.JCE_PROVIDER_KEY, "SunJCE");

回显(实测):

provider=SunJCE -> org.apache.commons.crypto.cipher.JceCipher

指定一个不存在的提供者(如未引入 BouncyCastle 时写 BC),整个工厂调用失败:

provider=BC FAILED: CryptoCipher {org.apache.commons.crypto.cipher.JceCipher} is not available or transformation AES/CTR/NoPadding is not supported.
  caused by: java.security.NoSuchProviderException: No such provider: BC

注意这个错误信息有迷惑性:看起来是「实现类不可用」,真实原因是 provider 名不存在,必须看 caused by。

四、随机数参数

随机数由 CryptoRandomFactory.getCryptoRandom(properties) 创建,参数共三个。

4.1 commons.crypto.secure.random.classes:实现类优先级列表

默认值(源码 CryptoRandomFactory.CLASSES_DEFAULT 实测):

org.apache.commons.crypto.random.OpenSslCryptoRandom,org.apache.commons.crypto.random.JavaCryptoRandom

两台实测机器上都选中了 OpenSSL 实现:

random -> org.apache.commons.crypto.random.OpenSslCryptoRandom

强制 JCE 实现的写法与回显(实测):

java-random-forced -> org.apache.commons.crypto.random.JavaCryptoRandom

4.2 commons.crypto.secure.random.java.algorithm:Java 随机算法名

默认值 SHA1PRNG(源码 JAVA_ALGORITHM_DEFAULT),仅对 JavaCryptoRandom 生效。实测一个重要行为:填错算法名不会报错——源码捕获 NoSuchAlgorithmException 后静默回退 new SecureRandom():

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

回显(实测,设置算法名为 FOO123 时):

(无任何输出,实例化成功,实际使用 new SecureRandom() 的默认算法)

排查这类配置错误只能靠代码评审或显式打印,不能指望异常。

4.3 commons.crypto.secure.random.device.file.path:系统随机设备路径

默认值 /dev/urandom(源码 DEVICE_FILE_PATH_DEFAULT)。该参数供内部类 OsCryptoRandom 读取设备文件生成随机数;注意 OsCryptoRandom 在 1.2.0 中是包私有类,无法通过工厂实现列表选择,生产场景保持默认即可。

五、native 库加载参数

这一组参数控制 jar 内嵌的 JNI 胶水库(libcommons-crypto.so,Linux x86_64 下实测 22,552 字节)如何被找到与加载,全部读取系统属性与 properties 文件入口。

5.1 commons.crypto.lib.path:优先搜索目录

设置了该目录且其中存在 libcommons-crypto.so 时直接加载,跳过解压(实测,先把 jar 内的库解到 /opt/cc-native):

java -Dcommons.crypto.lib.path=/opt/cc-native -Dcommons.crypto.debug=true -cp .:commons-crypto-1.2.0.jar Probe1 2>&1 | grep -E "nativeLib|System.load"

回显(实测):

NativeCodeLoader nativeLib /opt/cc-native/libcommons-crypto.so exists = true
NativeCodeLoader System.load('/opt/cc-native/libcommons-crypto.so')

目录存在但没有目标文件时,回退到 jar 内解压(实测):

NativeCodeLoader nativeLib /opt/cc-empty/libcommons-crypto.so exists = false
NativeCodeLoader nativeLibraryPath = /org/apache/commons/crypto/native/Linux/x86_64

典型用途:把胶水库放到只读挂载或固定版本目录,避免每次 JVM 启动向 /tmp 写文件。

5.2 commons.crypto.lib.tempdir:解压目标目录

默认取 java.io.tmpdir(即 /tmp)。自定义后(实测):

Extracted '/org/apache/commons/crypto/native/Linux/x86_64/libcommons-crypto.so' to '/opt/cc-tmp/commons-crypto-b4874f29-3d5e-4c11-a936-938b7c5bd63b-libcommons-crypto.so': 22,552 bytes [X-]
NativeCodeLoader System.load('/opt/cc-tmp/commons-crypto-b4874f29-3d5e-4c11-a936-938b7c5bd63b-libcommons-crypto.so')

解压文件名带随机 UUID 前缀(支持同一 JVM 内多类加载器各自加载),JVM 正常退出时自动删除;/tmp 挂成 noexec 的服务器必须把该参数指到可执行挂载点。

5.3 commons.crypto.lib.name:库文件名

默认 System.mapLibraryName("commons-crypto"),Linux 下即 libcommons-crypto.so。用于配合 lib.path 指定非默认文件名;随意改名会直接找不到文件而回退 jar 解压。

5.4 commons.crypto.debug:加载过程日志

设为 true 后输出完整加载链路(实测首次运行的关键行):

NativeCodeLoader nativeLibraryPath commons.crypto.lib.path = null
NativeCodeLoader nativeLibraryName commons.crypto.lib.name = libcommons-crypto.so
NativeCodeLoader nativeLibraryPath = /org/apache/commons/crypto/native/Linux/x86_64
NativeCodeLoader resource /org/apache/commons/crypto/native/Linux/x86_64/libcommons-crypto.so exists = true
Extracting '/org/apache/commons/crypto/native/Linux/x86_64/libcommons-crypto.so' to '/tmp/commons-crypto-ad3c9409-4d1e-44b9-8b9e-ae8f728d484d-libcommons-crypto.so'...
Extracted '/org/apache/commons/crypto/native/Linux/x86_64/libcommons-crypto.so' to '/tmp/commons-crypto-ad3c9409-4d1e-44b9-8b9e-ae8f728d484d-libcommons-crypto.so': 22,552 bytes [X-]
NativeCodeLoader System.load('/tmp/commons-crypto-ad3c9409-4d1e-44b9-8b9e-ae8f728d484d-libcommons-crypto.so')
NativeCodeLoader libraryLoaded = true, libraryLoadingError = null

排查「到底有没有走 native」时先开它。注意该参数经 Boolean.getBoolean 读取,只认 -D 系统属性形式。

六、流缓冲区参数

流式加解密(CryptoInputStream/CryptoOutputStream)的内部缓冲区由 commons.crypto.stream.buffer.size 控制。

6.1 默认值与合法取值

默认 8192(源码 STREAM_BUFFER_SIZE_DEFAULT)。设置为 2048 后流可正常创建并读取(实测):

buffer=2048 -> stream created, read=102

取值经 Integer.parseInt 解析,传非法字符串会在构造流时抛数字格式异常(实测):

buffer-bad -> java.lang.NumberFormatException: For input string: "abc"

配置要点:该值过小会增加加解密调用次数,过大抬高单次内存占用,一般保持默认;确需调整建议 4096 的整数倍并配合 -Xmx 上限(示例程序建议 256 至 512MB)评估。

七、参数速查表

全部 10 个参数汇总(默认值与作用域均为 1.2.0 源码与实测结论):

参数名作用默认值作用域
commons.crypto.cipher.classes加密器实现优先级OpenSslCipher,JceCipher 全限定名逗号列表工厂方法 Properties
commons.crypto.cipher.jce.providerJCE 提供者名未设置(走默认查找)工厂方法 Properties
commons.crypto.secure.random.classes随机数实现优先级OpenSslCryptoRandom,JavaCryptoRandom工厂方法 Properties
commons.crypto.secure.random.java.algorithmJava 随机算法SHA1PRNG工厂方法 Properties
commons.crypto.secure.random.device.file.path系统随机设备/dev/urandom内部 OsCryptoRandom
commons.crypto.lib.pathnative 库优先搜索目录未设置(直接用 jar 内库)-D / properties 文件
commons.crypto.lib.namenative 库文件名libcommons-crypto.so-D / properties 文件
commons.crypto.lib.tempdir解压目标目录java.io.tmpdir-D / properties 文件
commons.crypto.debugnative 加载调试日志false-D
commons.crypto.stream.buffer.size流式加解密缓冲区8192工厂方法 Properties

另有一个特殊「配置」:classpath 根的 commons.crypto.properties 文件本身,其文件名也是可配置的——Utils.SYSTEM_PROPERTIES_FILE 常量值为 commons.crypto.properties,该文件对 native 一组参数生效且优先级低于 -D 系统属性(源码注释与 2.2 节实测一致)。

八、典型回退与排错场景

三个实测过的排错场景,覆盖绝大多数线上问题。

8.1 OpenSSL 3.0 下加密静默回退 JCE

现象:随机数是 OpenSslCryptoRandom,加密却是 JceCipher,无任何告警。定位方法:强制列表只剩 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

根因:1.2.0 的 JNI 代码引用了 OpenSSL 3.0 中已移除的 EVP_CIPHER_CTX_block_size 符号。Ubuntu 22.04(OpenSSL 3.0.2)属于此类,功能正确性不受影响(实测同密钥同 IV 下 JCE 与 OpenSSL 路径密文逐字节一致),吞吐受损;CentOS 8(OpenSSL 1.1.1k)无此问题。

8.2 libcrypto.so 缺失导致 JVM 崩溃(不回退)

现象:

Caused by: java.lang.UnsatisfiedLinkError: Cannot load libcrypto.so (libcrypto.so: cannot open shared object file: No such file or directory)!
        at org.apache.commons.crypto.random.OpenSslCryptoRandomNative.initSR(Native Method)

根因:工厂回退循环只捕获 Exception,而 OpenSslCryptoRandom 静态初始化失败抛出的是 ExceptionInInitializerError(Error),直接冒泡终止 JVM。解法:装系统开发包生成 libcrypto.so 符号链接(Ubuntu 装 libssl-dev,CentOS 8 装 openssl-devel),不要指望自动回退。

8.3 强制 OpenSSL 失败时的完整异常形态

cipher.classes 只填 OpenSslCipher 而环境不可用时,异常链完整形态(实测首部):

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)
Caused by: java.lang.IllegalArgumentException: java.lang.reflect.InvocationTargetException
        at org.apache.commons.crypto.utils.ReflectionUtils.newInstance(ReflectionUtils.java:137)

排查口诀:先看是否唯一实现(唯一实现才会抛出),再看 caused by 的 UnsatisfiedLinkError 具体缺哪个符号。

九、参考链接

  1. Apache Commons Crypto 官方主页:https://commons.apache.org/proper/commons-crypto/
  2. 官方用户指南(含配置说明):https://commons.apache.org/proper/commons-crypto/userguide.html
  3. 1.2.0 源码 jar(Config 常量定义以此为准):https://repo1.maven.org/maven2/org/apache/commons/commons-crypto/1.2.0/commons-crypto-1.2.0-sources.jar
  4. Maven 中央仓库 1.2.0 目录:https://repo1.maven.org/maven2/org/apache/commons/commons-crypto/1.2.0/
  5. 项目源码仓库:https://github.com/apache/commons-crypto