feature: Implement a new SM3MessageDigest with JIN and OpenSSL 3.5.0#12
bash configure \
--with-boot-jdk=/path/to/jdk17 \
--with-openssl-dir=/path/to/openssl \
--disable-warnings-as-errors
报错:
configure: error: unrecognized options: --with-openssl-dir
configure exiting with result code 1
| original file line number | diff file line number | diff file change |
|---|
抱歉误导了您,您应该使用以下命令来配置
./configure
--with-debug-level=release
--with-native-debug-symbols=internal
--with-jvm-variants=server
--enable-unlimited-crypto
--disable-warnings-as-errors
--with-toolchain-type=gcc
--with-boot-jdk=/usr/lib/jvm/java-17-openjdk-amd64
我新提交了commit:
1.使Lib.gmk支持Linux,去除多余的配置
2.修改了SM3ComparisonBenchmark
3.清除底层EVP_MD_CTX,防止内存泄漏
至于opensslsm3.h,如果您成功构建了JDK运行了JMH测试,我将移除它
抱歉误导了您,您应该使用以下命令来配置
./configure
--with-debug-level=release
--with-native-debug-symbols=internal
--with-jvm-variants=server
--enable-unlimited-crypto
--disable-warnings-as-errors
--with-toolchain-type=gcc
--with-boot-jdk=/usr/lib/jvm/java-17-openjdk-amd64我新提交了commit:
1.使Lib.gmk支持Linux,去除多余的配置
2.修改了SM3ComparisonBenchmark
3.清除底层EVP_MD_CTX,防止内存泄漏至于opensslsm3.h,如果您成功构建了JDK运行了JMH测试,我将移除它
上面的命令没有引入OpenSSL,它是会使用系统默认安装的OpenSSL吗?
但我希望能够指定使用一个openssl的安装目录。
上面的命令没有引入OpenSSL,它是会使用系统默认安装的OpenSSL吗?
但我希望能够指定使用一个openssl的安装目录。
@johnjiang 是的,使用的是系统的默认的openssl,但是我更新为了openssl 3.5.0了
关于Lib.gmk文件,我所做的issue里也要用到同样的OpenSSL3.5.0,如果您的版本可以成功支持Linux(x86_64和aarch64)的话,我想或许可以复用。
@StephenSu 我没有看您的issue,您最好根据您自己的情况进行更改
@johnjiang 是的,使用的是系统的默认的openssl,但是我更新为了openssl 3.5.0了
从你修改的Lib.gmk来看,可以使用环境变量OPENSSL_HOME来设置。
不过,一般来看,还是希望支持参数--with-openssl。
从你修改的Lib.gmk来看,可以使用环境变量OPENSSL_HOME来设置。
不过,一般来看,还是希望支持参数--with-openssl。
@johnjiang 如果要支持的话,是不是在make目录下新创建相关文件进行配置呢?然后更改Lib.gmk呢?
@johnjiang 如果要支持的话,是不是在make目录下新创建相关文件进行配置呢?然后更改Lib.gmk呢?
我觉得我们可以一步步来进行改进,直到我们都乐于看见的结果。您可以先尝试用原命令来进行构建,然后进行JMH测试,如果符合相关指标,我们再进行下一步优化
@johnjiang 如果要支持的话,是不是在make目录下新创建相关文件进行配置呢?然后更改Lib.gmk呢?
这应该是需要的。
或者,目前先简单一些,先使用一个特别的环境变量,比如KONA_OPENSSL_HOME。
因为系统(如我自己的系统)中可能已经设置了OPENSSL_HOME,使用KONA_OPENSSL_HOME可以与之区分开。
| original file line number | diff file line number | diff file change |
|---|
您觉得先构造纯Java版本的实现的JDK(即./configure不带相应的--with-openssl-dir参数,也不要有相应的openssl环境),然后进行相应的JMH测试。再进行构造openssl版本的测试,然后再进行相同的JMH测试。这样就可以比较出两种结果,这种思路您觉得怎么样,要是符合我现在着手去办
支持OpenSSL的JDK版本不会包含OpenSSL的libcrypto.so,而是在运行时动态加载系统已安装的OpenSSL库。
从Lib.gmk可以看出,OPENSSL_LIBS := -L/usr/lib -lssl -lcrypto,这表明JDK构建的libopensslsm3.so是动态链接到系统的OpenSSL库,而不是静态包含。
在SM3MessageDigest.java中,会加载我们构建的JNI库System.loadLibrary("opensslsm3");然后验证OpenSSL是否可用available = nativeSupportsSM3();
那么在构建JDK时并不会指定OpenSSL的本地安装目录?
也就是说,你既不会使用make选项--with-openssl-dir,也不会使用环境变量KONA_OPENSSL_HOME?
但我不希望使用系统中的OpenSSL,因为其版本不可控。
而且也不灵活。比如,我可能会使用不同的OpenSSL 3.0+版本进行构建与测试。
在最新的commit中,提供了--with-openssl-dir支持和重构了JMH测试类
现在您可以通过这样来配置工具类来指定特定版本的openssl
./configure autogen
--with-debug-level=release
--with-native-debug-symbols=internal
--with-jvm-variants=server
--enable-unlimited-crypto
--disable-warnings-as-errors
--with-toolchain-type=gcc
--with-boot-jdk=/usr/lib/jvm/java-17-openjdk-amd64
--with-openssl-dir=/usr
我刚才提交了如下的commit,
johnshajiang@3682ce31
它支持了在构建JDK时使用--with-openssl指定一个本地的OpenSSL目录。
并且提供了OpenSSLUtil用于帮助加载libcrypto.so。在运行时,需要使用系统属性jdk.openssl.cryptoLibPath去指定该文件的位置。
这样,你的PR只需要关注JNI部分,而不需要关注OpenSSL的加载了。
请先同步代码,然后再更新你的PR。
或许JMH测试不用创建新的,直接在test/micro/org/openjdk/bench/java/security/MessageDigests.java的基础上,添加SM3即可进行测试,如果您接受的话我可以删去原来的JMH测试
或许JMH测试不用创建新的,直接在test/micro/org/openjdk/bench/java/security/MessageDigests.java的基础上,添加SM3即可进行测试,如果您接受的话我可以删去原来的JMH测试
还是需要一个新的JMH测试,需要把MessageDigest md = MesssgeDigest("SM3")也纳入测试中。
@Benchmark
public byte[] digest() throws Exception {
MessageDigest md = MessageDigest.getInstance("SM3");
return md.digest(MESSAGE);
}
在执行JMH测试时,需要观察一下GC状态。
我刚才提交了如下的commit,
johnshajiang@3682ce31它支持了在构建JDK时使用
--with-openssl指定一个本地的OpenSSL目录。
并且提供了OpenSSLUtil用于帮助加载libcrypto.so。在运行时,需要使用系统属性jdk.openssl.cryptoLibPath去指定该文件的位置。
这样,你的PR只需要关注JNI部分,而不需要关注OpenSSL的加载了。请先同步代码,然后再更新你的PR。
之前忘记合并我的commit,刚才合并进去了。
9d01dbc6
还是需要一个新的JMH测试,需要把
MessageDigest md = MesssgeDigest("SM3")也纳入测试中。@Benchmark public byte[] digest() throws Exception { MessageDigest md = MessageDigest.getInstance("SM3"); return md.digest(MESSAGE); }在执行JMH测试时,需要观察一下GC状态。
@johnjiang 我待会会把完整的JMH测试执行情况更新在上面的pr描述中
| original file line number | diff file line number | diff file change |
|---|
由于前期开发的时候您还没有给出OpenSSLUtil,我采用了编译时动态链接的方法(Lib.gmk),这样JVM就可以自动加载对应的库依赖关系了。
# 从Lib.gmk可以看到完整的OpenSSL检测和链接过程
ifneq ($(KONA_OPENSSL_HOME),)
OPENSSL_CFLAGS := -I$(KONA_OPENSSL_HOME)/include
OPENSSL_LIBS := -L$(KONA_OPENSSL_HOME)/lib -lssl -lcrypto
else
# 使用pkg-config检测系统OpenSSL
OPENSSL_CFLAGS := $(shell pkg-config --cflags openssl)
OPENSSL_LIBS := $(shell pkg-config --libs openssl)
endif
# 构建libopensslsm3.so,链接OpenSSL
$(eval $(call SetupJdkLibrary, BUILD_LIBOPENSSLSM3, \
NAME := opensslsm3, \
CFLAGS := $(CFLAGS_JDKLIB) $(OPENSSL_CFLAGS), \
LIBS := $(OPENSSL_LIBS) $(LIBDL), \ # 这里链接了-lssl -lcrypto
))
我待会会同步成OpenSSLUtil的方式
由于JDK中没有OpenSSL的库文件,那么运行时,如何确保能够加载到OpenSSL的库?
是加载系统中标准路径下的OpenSSL库吗?
如果系统中没有安装OpenSSL,或者版本不合适呢?
是加载系统中标准路径下的OpenSSL库吗?
如果系统中没有安装OpenSSL,或者版本不合适呢?
对的,所以有局限性。我会改成用OpenSSLUtil的方式,这样就可以指定相应的路径和版本了。
我目前的想法比较简单,就是不要依赖系统中的OpenSSL。
从现实来看,很多Linux系统并没有安装3.0+版本。
OpenSSLUtil中提供了一个系统属性jdk.openssl.cryptoLibPath,用于在运行时指定libcrypto的路径。
| original file line number | diff file line number | diff file change |
|---|
| original file line number | diff file line number | diff file change |
|---|
意思是原来的sun.security.provider.SM3Engine应该修改为接口吗
现在有一个问题
// SM2Engine.java 和 SM2KeyAgreement.java
import sun.security.provider.SM3Engine;
private SM3Engine sm3 = new SM3Engine(); // 接口不能实例化
按这样的修改后,新的SM3EngineImpl和NativeSM3EngineImpl按道理也是无法工作的,需要修改SM2Engine和SM2KeyAgreement等,使它们直接实例化OpenSSL实现或者纯Java实现,而且我们新修改的SM3EngineImpl和NativeSM3EngineImpl也需要改为public外部包的类才可访问
我觉得可以把相关的类,比如SM2Engine和SM2KeyAgreement的实例化改为OpenSSL实现或者纯Java实现比较好
希望得到您的意见@johnjiang(jiangsha)
现在有一个问题
// SM2Engine.java 和 SM2KeyAgreement.java import sun.security.provider.SM3Engine; private SM3Engine sm3 = new SM3Engine(); // 接口不能实例化按这样的修改后,新的
SM3EngineImpl和NativeSM3EngineImpl按道理也是无法工作的,需要修改SM2Engine和SM2KeyAgreement等,使它们直接实例化OpenSSL实现或者纯Java实现,而且我们新修改的SM3EngineImpl和NativeSM3EngineImpl也需要改为public外部包的类才可访问
@cnb.acvgNwzIQGA
确实有这个问题。
我觉得可以把相关的类,比如
SM2Engine和SM2KeyAgreement的实例化改为OpenSSL实现或者纯Java实现比较好
让两个类不要直接使用Engine实现,而是使用MessageDigest实例,这样它们应该就被解耦了。
现在有一个问题
// SM2Engine.java 和 SM2KeyAgreement.java import sun.security.provider.SM3Engine; private SM3Engine sm3 = new SM3Engine(); // 接口不能实例化按这样的修改后,新的
SM3EngineImpl和NativeSM3EngineImpl按道理也是无法工作的,需要修改SM2Engine和SM2KeyAgreement等,使它们直接实例化OpenSSL实现或者纯Java实现,而且我们新修改的SM3EngineImpl和NativeSM3EngineImpl也需要改为public外部包的类才可访问我觉得可以把相关的类,比如
SM2Engine和SM2KeyAgreement的实例化改为OpenSSL实现或者纯Java实现比较好希望得到您的意见@johnjiang(jiangsha)
@cnb.acvgNwzIQGA(maple) 但是这个是其他的issue的任务,我们应该用其他issue的同学做好的?
但是这个是其他的issue的任务,我们应该用其他issue的同学做好的
我觉得这个更改是有必要的,改动SM3之后,应该要修复它涉及的问题。如果这个pr率先合并,其他issue的同学应该需要同步代码,解决代码冲突
我觉得这个更改是有必要的,改动SM3之后,应该要修复它涉及的问题。如果这个pr率先合并,其他issue的同学应该需要同步代码,解决代码冲突
@cnb.acvgNwzIQGA(maple) 所以还得先确定pr合并顺序再说🙏
@cnb.acvgNwzIQGA(maple) @cnb.awOE5sooQFA(momo)
在现在工作中,文件冲突是不可避免的。
在OpenJDK社区中,经常会出现因为某个commit进入仓库之后,有些PR就显示存在文件冲突,导致该PR无法自动合入。
解决文件冲突本来就是开发的一部分。
@cnb.acvgNwzIQGA(maple) @cnb.awOE5sooQFA(momo)
在现在工作中,文件冲突是不可避免的。
在OpenJDK社区中,经常会出现因为某个commit进入仓库之后,有些PR就显示存在文件冲突,导致该PR无法自动合入。
解决文件冲突本来就是开发的一部分。
@johnjiang 好嘞,受教了
bash configure \
--with-boot-jdk=/data/home/johnsjiang/jdk/konajdk17 \
--with-jmh=build/jmh/jars \
--with-openssl=/path/to/openssl-3.5.0 \
--disable-warnings-as-errors
使用上面的配置构建JDK,make images时会遇到下面的错误,
Building target 'images' in configuration 'linux-x86_64-server-release'
Building OpenSSL SM3 with configured OpenSSL: /path/to/openssl-3.5.0
/usr/bin/ld: cannot find -lssl
/usr/bin/ld: cannot find -lcrypto
collect2: error: ld returned 1 exit status
请直接使用--with-openssl指定的OpenSSL,不要试图使用系统中的OpenSSL库。
--with-openssl=/usr \
# ... 其他参数
您或许需要添加autogen。
像
bash configure autogen \
--with-boot-jdk=/data/home/johnsjiang/jdk/konajdk17 \
--with-jmh=build/jmh/jars \
--with-openssl=/path/to/openssl-3.5.0 \
--disable-warnings-as-errors
现在您可以使用
bash configure autogen \
--with-boot-jdk=/data/home/johnsjiang/jdk/konajdk17 \
--with-jmh=build/jmh/jars \
--with-openssl=/path/to/openssl-3.5.0 \
--disable-warnings-as-errors
来配置,然后再使用make images构建新JDK了
在执行SM3ComparisonBenchmark时,有如下错误:
Failed to load OpenSSL libcrypto: java.lang.UnsatisfiedLinkError: no opensslcrypto in system library path: ...
能否让它用上test/jdk/openssl/lib/linux-x86_64/libopensslcrypto.so?
请见类sun.security.util.OpenSSLUtil,可以通过系统属性jdk.openssl.cryptoLibPath指定OpenSSL libcrypto的路径。
在执行
SM3ComparisonBenchmark时,有如下错误:Failed to load OpenSSL libcrypto: java.lang.UnsatisfiedLinkError: no opensslcrypto in system library path: ...能否让它用上
test/jdk/openssl/lib/linux-x86_64/libopensslcrypto.so?
请见类sun.security.util.OpenSSLUtil,可以通过系统属性jdk.openssl.cryptoLibPath指定OpenSSL libcrypto的路径。
@johnjiang(jiangsha) 您目前使用
make build-microbenchmark
make test TEST="micro:org.openjdk.bench.java.security.SM3ComparisonBenchmark" \
MICRO="OPTIONS=-jvmArgs=-Djdk.openssl.cryptoLibPath=/usr/lib/x86_64-linux-gnu/libcrypto.so.3 -prof gc"
这里的/usr/lib/x86_64-linux-gnu/libcrypto.so.3改为test/jdk/openssl/lib/linux-x86_64/libopensslcrypto.so不能使用吗
注意这里的jdk.openssl.cryptoLibPath要是绝对路径。
make test TEST="micro:org.openjdk.bench.java.security.SM3ComparisonBenchmark" \
MICRO="OPTIONS=-jvmArgs=-Djdk.openssl.cryptoLibPath=$(pwd)/test/jdk/openssl/lib/linux-x86_64/libopensslcrypto.so -prof gc"
我的意思是,能否自动地使用到那个库文件libopensslcrypto.so?
在jtreg测试中,可以通过系统属性test.src获得测试文件所在的绝对路径,test.root可以获得测试根目录。
通过这些路径,可以间接地获取其它文件的路径。
不过,我不确定这些系统属性对于micro benchmark测试同样有效。
也许你可以试试。
您是想实现这样的效果吗?
make test TEST="micro:org.openjdk.bench.java.security.SM3ComparisonBenchmark" \
MICRO="OPTIONS=-jvmArgs=-prof gc"
我用以下命令是可以成功运行的
make test TEST="micro:org.openjdk.bench.java.security.SM3ComparisonBenchmark" \
MICRO="OPTIONS=-jvmArgs=-Djdk.openssl.cryptoLibPath=$(pwd)/test/jdk/openssl/lib/linux-x86_64/libopensslcrypto.so -prof gc"
您或许可以先尝试运行JMH测试,看看和我之前的测试的结果是否符合。
能否自动地使用到那个库文件libopensslcrypto.so?
在jtreg测试中,可以通过系统属性test.src获得测试文件所在的绝对路径,test.root可以获得测试根目录。
通过这些路径,可以间接地获取其它文件的路径
我觉得可能超出了目前的issue的范围了
我觉得可能超出了目前的issue的范围了
我可能需要解释一下这句话的意思。在其他开源社区中,比如seata社区,每个pr需要专注于它所解决issue,如果验证了解决了该issue即可。如果要解决其他由该pr引出的问题,则需要另外提出一个pr来进行解决。
想得到您的意见❤️
| original file line number | diff file line number | diff file change |
|---|
由于这些异常处理,使我认为还是使用SM3Engine更好一些。
可以让SM3MessageDigest提供了一个方法,让它创建具体的SM3Engine的实现,SM3EngineImpl或NativeSM3Engine。
SM2KeyAgreement也同理处理。
您的意思是像这样吗:在SM2KeyAgreement和SM2Engine中,通过SM3MessageDigest的新建的公有方法,直接获取SM3EngineImpl(我比较倾向于原来的纯Java实现)
| original file line number | diff file line number | diff file change |
|---|
可以定义一个函数用来抛异常。比如,像下面这样,
#define NULL_POINTER_EXCEPTION "java/lang/NullPointerException"
void throw_ex(JNIEnv *env, const char *exceptionName, const char *message) {
jclass exceptionClazz = (*env)->FindClass(env, exceptionName);
if (exceptionClazz != NULL) {
(*env)->ThrowNew(env, exceptionClazz, message);
}
}
需要增加一个测试程序,用于确保测试到Native SM3。
也就是说,这个测试需要使用到test/jdk/openssl/lib/linux-x86_64/libopensslcrypto.so。
需要增加一个测试程序,用于确保测试到Native SM3。
也就是说,这个测试需要使用到test/jdk/openssl/lib/linux-x86_64/libopensslcrypto.so。
@johnjiang(jiangsha) 可以再详细解释一下是什么意思吗?目前新增的SM3ComparisonBenchmark不是可以使用test/jdk/openssl/lib/linux-x86_64/libopensslcrypto.so吗?而且也可以检测OpenSSL的可用性
我是说,加上jtreg测试,不是JMH的测试。
在目录test/jdk/sm/SM3/中加一个测试程序。要测试到Native SM3的实现。
为了能够测试到Native SM3,测试程序需要设置系统属性jdk.openssl.cryptoLibPath。
下面的方法可以帮助这个测试程序获得文件test/jdk/openssl/lib/linux-x86_64/libopensslcrypto.so的路径。
public static String opensslCryptoPath() {
String osName = System.getProperty("os.name").toLowerCase(Locale.ROOT);
String os = "unsupported";
String ext = "unsupported";
if (osName.contains("linux")) {
os = "linux";
ext = ".so";
} else if (osName.contains("mac")) {
os = "macos";
ext = ".dylib";
}
String archName = System.getProperty("os.arch").toLowerCase(Locale.ROOT);
String arch = "unsupported";
if (archName.contains("x86_64") || archName.contains("amd64")) {
arch = "x86_64";
} else if (archName.contains("aarch64") || archName.contains("arm64")) {
arch = "aarch64";
}
String platformName = os + "-" + arch;
String libName = "libopensslcrypto" + ext;
Path testRoot = Paths.get(System.getProperty("test.root"));
Path libPath = testRoot.resolve("openssl").resolve("lib")
.resolve(platformName).resolve(libName);
return Files.exists(libPath) ? libPath.toString() : null;
}
由于这些异常处理,使我认为还是使用SM3Engine更好一些。
可以让SM3MessageDigest提供了一个方法,让它创建具体的SM3Engine的实现,SM3EngineImpl或NativeSM3Engine。
SM2KeyAgreement也同理处理
如果按照这样的想法去做,也不能够完全去除捕获异常。原因在于SM3EngineImpl或NativeSM3Engine都是默认级别的访问。
假如在SM3MessageDigest.java 中添加静态方法
public static MessageDigest createPureJavaInstance() {
SM3Engine engine = new SM3EngineImpl();
return new SM3MessageDigest(engine);
}
在那两个类中则只能去除NoSuchAlgorithmException这个异常捕获,而不能去除DigestException的异常捕获。
用的还是digest(c3, 0, c3.length)这个方法,而不是纯Java实现的doFinal(),因为无法访问
期待您的建议@johnjiang(jiangsha)
public static SM3Engine newSM3Engine() {
return isNativeSM3Supported() ? new NativeSM3Engine() : new SM3EngineImpl();
}
此时,SM3Engine接口需要是public。
Project summary report
Project Goals
Use JNI and OpenSSL 3.5.0 to implement the new SM3MessageDigest, and improve the SM3 hash calculation performance while maintaining API compatibility
fixes #7
Work Completed
Renovate based on the original SM3MessageDigest.java. If OpenSSL can be used, OpenSSL is preferred. Otherwise, it falls back to the original pure Java implementation to achieve the effect of automatically switching between OpenSSL and pure Java implementation
Renovation details: Successfully use JNI to call OpenSSL 3.5.0, correctly implement the SM3 hash of the GB/T 32905-2016 standard, maintain the compatibility of the original MessageDigest API, and the OpenSSL implementation significantly improves performance
7.17目前的优化完成工作
重构后,目前主要是减少了很多冗余代码,基于应用场景主要为每次都会创建新的MessageDigest实例,并不会复用实例。
所以基于用时创建,用完销毁的思想 -> 每次生成哈希时都要新创建ctx,哈希生成之后就立即释放它.
同时这种处理方式还有其他好处,每次调用都是完整的create→use→destroy,代表着是零native资源泄漏的。同时避免了繁杂的底层Context清除方法。
也减少了JNI的调用开销(多次->一次),进一步降低了gc压力,移除了复杂的状态管理和资源追踪。
在内存效率方面,使用ByteArrayOutputStream高效累积数据
有更清晰的异常信息和边界检查。
7.19的优化工作
7.22的优化工作
7.25的优化工作
以上更改均已通过测试
Code verification results
Performance improvement
根据附录的优化结果分析:
1.gc.time大大降低。(重点结果)
2.在吞吐量方面,优化后的吞吐量在某些场景要么略微下降(比如singleHash大数据块(1048576B)场景下),在其他场景则有显著上升(比如smallChunks)。
OpenSSL 实现总体上具有更高的吞吐量,并且在大多数情况下,GC分配速率、GC次数和GC时间都优于或接近纯Java 实现。具体来说:
吞吐量:OpenSSL 实现普遍表现出更高的吞吐量,尤其在处理大数据块时优势更为明显。
GC性能:虽然OpenSSL 实现的GC分配速率有时较高,但由于其较高的吞吐量和较短的GC时间,整体性能仍然优于纯Java 实现。
内存管理:OpenSSL 实现通常需要更多的内存分配,但通过减少GC停顿时间,使得其整体效率更高
Instructions
1.How to use
# 构建微基准测试 JAR make build-microbenchmark这是7月17号的JMH测试附件
56e0b0a0-184b-45da-8230-2592da6db986.xlsx
openssl实现的gc.time可能有点异常,可能的原因有JMH测试每个方法都创建相关实例,没有复用,增加了gc压力。
7.17最新优化后的附件对比
极大优化了gc表现!
1ff43a0c-d4f7-4056-9591-d6d453bad827.xlsx
这里展示部分数据

纯Java实现数据:
OpenSSL实现数据:

7.19最新优化表现

纯Java实现数据
OpenSSL实现数据

实现附件如下
sheet2是OpenSSL实现
sheet3是纯Java实现的
f1c2db21-3337-4876-99aa-1866658aebb7.xlsx