Skip to content

Commit

Permalink
this is an update to add cipher support, MAC support, and SecureRando…
Browse files Browse the repository at this point in the history
…m support
  • Loading branch information
dthertell committed Jan 30, 2024
1 parent 7376f31 commit f5b4c1f
Show file tree
Hide file tree
Showing 42 changed files with 4,043 additions and 1,407 deletions.
3 changes: 2 additions & 1 deletion AUTHORS
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
Stephan Fuhrmann <s AT sfuhrm.de>

Daniel Thertell <[email protected]>
Jim Showalter <[email protected]>
12 changes: 3 additions & 9 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
#
# Dockerfile for building a per-platform build
# in github actions.
#
# See .github/workflows/build.yml
#
FROM debian:11
FROM debian:10

ENV JAVA_HOME=/opt/java/openjdk
COPY --from=eclipse-temurin:11 $JAVA_HOME $JAVA_HOME
ENV PATH="${JAVA_HOME}/bin:${PATH}"

RUN apt-get update && apt-get install -y \
make gcc libssl1.1 libssl-dev
make gcc libssl1.1 libssl-dev
COPY . openssl4j
ENV JAVA_HOME=/opt/java/openjdk/
RUN echo "JAVA_HOME is ${JAVA_HOME}"
RUN echo "OS_ARCH is $(cd openssl4j/build-helper && ${JAVA_HOME}/bin/java -Xint OsArch.java)"
RUN cd openssl4j && \
make
make
RUN cd openssl4j/target && ls -al
365 changes: 164 additions & 201 deletions LICENSE

Large diffs are not rendered by default.

120 changes: 92 additions & 28 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,107 @@
# Makefile for generating the native C library
#
####
JAVA_OS_ARCH:=$(shell cd build-helper && ${JAVA_HOME}/bin/java -Xint OsArch.java )
JAVA_OS_ARCH:=$(shell cd build-helper && ${JAVA_HOME}/bin/javac OsArch.java && ${JAVA_HOME}/bin/java -cp . OsArch)
CUR_DIR := $(shell pwd)


$(info JavaArch: ${JAVA_OS_ARCH})
$(info Cur Dir: ${CUR_DIR})

BASE_DIR?=/openssl4j

JNI_JAVA_SOURCES=openssl4j/src/main/java
JNI_C_SOURCES=openssl4j-objects/src/main/c
TARGET=target
INSTALL_TARGET=openssl4j-objects/src/main/resources/objects
JNI_JAVA_FILES=${JNI_JAVA_SOURCES}/de/sfuhrm/openssl4j/OpenSSLMessageDigestNative.java
JNI_HEADER_FILES=${TARGET}/include/de_sfuhrm_openssl4j_OpenSSLMessageDigestNative.h
JNI_C_SOURCES=openssl4j/src/main/c
JNI_C_TEST_SOURCES=openssl4j/src/test/c
TARGET=${BASE_DIR}/openssl4j/c
INSTALL_TARGET=openssl4j/src/main/resources/objects
JNI_JAVA_FILES=${JNI_JAVA_SOURCES}/de/sfuhrm/openssl4j/OpenSSLMessageDigestNative.java ${JNI_JAVA_SOURCES}/de/sfuhrm/openssl4j/OpenSSLCipherNative.java ${JNI_JAVA_SOURCES}/de/sfuhrm/openssl4j/OpenSSLCryptoNative.java ${JNI_JAVA_SOURCES}/de/sfuhrm/openssl4j/OpenSSLSecureRandomNative.java ${JNI_JAVA_SOURCES}/de/sfuhrm/openssl4j/OpenSSLMacNative.java
JNI_HEADER_FILES=${TARGET}/include/de_sfuhrm_openssl4j_OpenSSLMessageDigestNative.h ${TARGET}/include/de_sfuhrm_openssl4j_OpenSSLCipherNative.h ${TARGET}/include/de_sfuhrm_openssl4j_OpenSSLCryptoNative.h ${TARGET}/include/de_sfuhrm_openssl4j_OpenSSLSecureRandomNative.h ${TARGET}/include/de_sfuhrm_openssl4j_OpenSSLMacNative.h
empty:=
space:= $(empty) $(empty)
escapedSpace := \$(space)

UNAME_S := $(shell uname -s)

libs:=
test_libs := ${TARGET}/libopenssl4j-${JAVA_OS_ARCH}.so

INCLUDES= -I${TARGET}/include/ -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -I/usr/local/include/openssl
TEST_INCLUDES = -I${TARGET}/include/ -I${JNI_C_SOURCES}/ -I${JAVA_HOME}/include -I/usr/local/include/openssl

$(info Target: ${TARGET})

ifeq ("${JAVA_OS_ARCH}", "Mac_OS_X-aarch64")
$(info Building for Apple arm)
INCLUDES+= -I${JAVA_HOME}/include/darwin -I${BASE_DIR}/OpenSSL/include
TEST_INCLUDES+= -I${JAVA_HOME}/include/darwin -I${BASE_DIR}/OpenSSL/include
libs+= -L${BASE_DIR}/OpenSSL/lib/ ${BASE_DIR}/OpenSSL/lib/libssl.dylib ${BASE_DIR}/OpenSSL/lib/libcrypto.dylib
else ifeq ("${JAVA_OS_ARCH}", "Mac_OS_X-x86_64")
$(info Building for Apple x86)
INCLUDES+= -I${JAVA_HOME}/include/darwin -I${BASE_DIR}/OpenSSL/include
TEST_INCLUDES+= -I${JAVA_HOME}/include/darwin -I${BASE_DIR}/OpenSSL/include
libs+= -L${BASE_DIR}/OpenSSL/lib/ ${BASE_DIR}/OpenSSL/lib/libssl.dylib ${BASE_DIR}/OpenSSL/lib/libcrypto.dylib
else
$(info Building for RedHat x86)
INCLUDES+= -I/usr/local/include/openssl
TEST_INCLUDES+= -I${JAVA_HOME}/include/linux
libs+= -L/lib64/ /lib64/libssl.so.3 /lib64/libcrypto.so.3
endif

.PHONY: all
.PHONY: clean
.PHONY: install

install: ${TARGET}/libopenssl4j-${JAVA_OS_ARCH}.so
mkdir -p ${INSTALL_TARGET}
cp $< ${INSTALL_TARGET}

sudo mkdir -p ${INSTALL_TARGET}
sudo cp $< ${INSTALL_TARGET}
cp ./openssl4j/src/main/java/de/sfuhrm/openssl4j/OpenSSLCryptoNative.java temp.java
ls -lah ./openssl4j/src/main/java/de/sfuhrm/openssl4j/
sed 's@private static String openssl4JBasePath = "\/openssl4j";@private static String openssl4JBasePath = "\${BASE_DIR}";@g' ./temp.java > ./openssl4j/src/main/java/de/sfuhrm/openssl4j/OpenSSLCryptoNative.java
clean:
rm -fr ${TARGET} ${INSTALL_TARGET}

buildTest:
mkdir output
ifeq ($(UNAME_S),Darwin)
gcc -Wall -Werror -fPIC -o output/openssl4jTest -lc -g -O0 -Wl,-v \
-Wl,-rpath,/usr/local/lib64,-rpath,@loader_path ${test_libs} ${TEST_INCLUDES} \
${JNI_C_TEST_SOURCES}/main.c
else
sudo gcc -Wall -Werror -fPIC -o output/openssl4jTest -lc -g -O0 -Wl,-v \
-Wl,-z,defs -Wl,-rpath,/lib64/ossl-modules,-rpath,'$$ORIGIN',-rpath,/lib64 -Wl,-z,origin ${TEST_INCLUDES} \
${JNI_C_TEST_SOURCES}/main.c \
${test_libs}
endif
cp ${TARGET}/libopenssl4j-${JAVA_OS_ARCH}.so output/libopenssl4j-${JAVA_OS_ARCH}.so
$(info Test files copied to ./output/)


${TARGET}/include/%.h: ${JNI_JAVA_FILES}
mkdir -p ${TARGET}/include
${JAVA_HOME}/bin/javac -J-Xint -classpath ${JNI_JAVA_SOURCES} -h ${TARGET}/include -d ${TARGET} -s ${TARGET} ${JNI_JAVA_FILES}

${TARGET}/%.o: ${JNI_C_SOURCES}/%.c ${JNI_HEADER_FILES}
gcc -Wall -Werror -fPIC -c -o $@ \
-I${TARGET}/include/ \
-I${JAVA_HOME}/include \
-I${JAVA_HOME}/include/linux \
$<

${TARGET}/libopenssl4j-${JAVA_OS_ARCH}.so: ${TARGET}/openssl4j_common.o ${TARGET}/openssl4j_messagedigest.o
# link libssl statically, libc dynamically
# this avoids the need for specific libssl versions
# in the system
ld --verbose --pic-executable -fPIC -shared -o $@ \
${TARGET}/openssl4j_common.o \
${TARGET}/openssl4j_messagedigest.o \
--whole-archive -Bstatic -lssl \
--no-whole-archive -Bdynamic -lcrypto -lc
sudo mkdir -p ${TARGET}/include
mvn install -DskipTests
sudo ${JAVA_HOME}/bin/javac -J-Xint -classpath ${JNI_JAVA_SOURCES} -h ${TARGET}/include -d ${TARGET} -s ${TARGET} ${JNI_JAVA_FILES}

${TARGET}/libopenssl4j-${JAVA_OS_ARCH}.so: ${JNI_HEADER_FILES}
$(info Includes: ${INCLUDES})
ifeq ($(UNAME_S),Darwin)
sudo gcc -Wall -Werror -fPIC -o "$@" -lc -Wl,-v \
-Wl,-rpath,/usr/local/lib64,-rpath,@loader_path ${libs} -shared ${INCLUDES} \
${JNI_C_SOURCES}/openssl4j_common.c \
${JNI_C_SOURCES}/openssl4j_messagedigest.c \
${JNI_C_SOURCES}/openssl4j_cipher.c \
${JNI_C_SOURCES}/openssl4j_crypto.c \
${JNI_C_SOURCES}/openssl4j_secureRandom.c \
${JNI_C_SOURCES}/openssl4j_mac.c
$(info Output File: "$@")
else
sudo gcc -Wall -Werror -fPIC -o "$@" -lc -Wl,-v \
-Wl,-z,defs -Wl,-rpath,/lib64/ossl-modules,-rpath,'$$ORIGIN',-rpath,/lib64 -Wl,-z,origin -shared ${INCLUDES} \
${JNI_C_SOURCES}/openssl4j_common.c \
${JNI_C_SOURCES}/openssl4j_messagedigest.c \
${JNI_C_SOURCES}/openssl4j_cipher.c \
${JNI_C_SOURCES}/openssl4j_crypto.c \
${JNI_C_SOURCES}/openssl4j_secureRandom.c \
${JNI_C_SOURCES}/openssl4j_mac.c ${libs}
$(info Output File: "$@")
endif
130 changes: 37 additions & 93 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,101 +1,51 @@
OpenSSL4J JNI Java Library
===================

[![Single-Platform Build](https://github.com/sfuhrm/openssl4j/actions/workflows/build-singleplatform.yml/badge.svg)](https://github.com/sfuhrm/openssl4j/actions/workflows/build-singleplatform.yml)
[![Java Build](https://github.com/sfuhrm/openssl4j/actions/workflows/build-java.yml/badge.svg)](https://github.com/sfuhrm/openssl4j/actions/workflows/build-java.yml)
[![Crossplatform Build](https://github.com/sfuhrm/openssl4j/actions/workflows/build-crossplatform.yml/badge.svg)](https://github.com/sfuhrm/openssl4j/actions/workflows/build-crossplatform.yml)
![Travis CI Status](https://travis-ci.org/sfuhrm/openssl4j.svg?branch=master)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/de.sfuhrm/openssl4j/badge.svg)](https://maven-badges.herokuapp.com/maven-central/de.sfuhrm/openssl4j)
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

OpenSSL4J is a Java bridge to the native OpenSSL library.
On the Java side it's offering the
conventional [MessageDigest](https://docs.oracle.com/javase/8/docs/api/java/security/MessageDigest.html) class. In the background the calls
will be translated to the native OpenSSL library with all its
[optimizations](https://www.openssl.org/docs/faq-4-build.txt):

> On x86, the assembly code uses the CPUID instruction (see the
> OPENSSL_ia32cap.pod manpage) to determine if various instructions (AES,
> SSE, MMX, etc) are available and will use them if so. For other processors,
> similar tests are performed if at all possible.
## Features

* Performance: The main feature of OpenSSL4J is performance: The MD5-implementation of OpenSSL4J is
typically 67% to 102% faster than the pure Java version from SUN.
* Functionality: There are some algorithms available in OpenSSL4J that are not available in the normal SUN crypto provider.

## Performance

The following picture shows a performance comparison of

* BouncyCastle crypto provider (version 1.70)
* Adoptium JDK SUN crypto provider (JDK 17.0.6)
* OpenSSL4j (version 0.3.0)
[![License: LGPL v3](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0)

Each bar shows different throughputs in megabytes per second.
The per-bar throughputs contain multiple different test scenarios
regarding blocks sizes and data structures used for
data passing (byte, array, direct ByteBuffer, heap ByteBuffer).
The median of the tests is presented by a dark-blue horizontal line
within the bar. The 25% and 75% quantile make up the
area of the bars.
OpenSSL4J is a Java bridge to the native OpenSSL library. On the Java side you're
using the conventional MessageDigest class, but this library calls in the
background the native OpenSSL library with all its
optimizations for performance reasons.

![bc-sun-ossl-performance.png](./images/bc-sun-ossl-performance.png)

The benchmark was conducted on a i7-3840QM CPU.

## Building OpenSSL4J for your platform
## Building OpenSSL4J

For building the application you need

* JDK 8+,
* JDK 8,
* Apache Maven,
* GNU Make,
* GNU GCC,
* OpenSSL development headers

To build the C library for your current platform, wrap it into a maven artifact (openssl4j-objects), build the java parts (openssl4j), execute:
To build the C library and install it to the right place in `openssl4j/src/main/resources/objects`, execute:

```bash
$ build.sh
...
[INFO] Reactor Summary for OpenSSL4J Parent 0.2.1-SNAPSHOT:
[INFO]
[INFO] OpenSSL4J Parent ................................... SUCCESS [ 0.953 s]
[INFO] OpenSSL4J JNI ...................................... SUCCESS [ 5.859 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.912 s
[INFO] Finished at: 2023-05-28T20:38:43+02:00
[INFO] ------------------------------------------------------------------------
```
$ make

## Building OpenSSL4J for cross-platform
To build the Java package, execute:

The current cross-platform build is driven by github actions, using QEMU
to build different platform shared object library.
The github actions are visible to everyone.
For the cross-platform build to work with your fork, there
are some project secrets needed to be set in your
Github fork settings:
$ mvn clean package

* DOCKERHUB_USERNAME: Dockerhub username for getting the parent of the build image.
* DOCKERHUB_TOKEN: Dockerhub secret token.
* GH_USER: Github username for storing artifacts.
* GH_PASSWORD: Github password for storing artifacts.
* SONATYPE_USER: (optional) sonatype username for pushing snapshots.
* SONATYPE_PASSWORD: (optional) sonatype password for pushing snapshots.
## Features

(Date of last update: 2023-05-28)
* Performance: The main feature of OpenSSL4J is performance: The MD5-implementation of OpenSSL4J is
typically 67% to 102% faster than the pure Java version from SUN.
* Functionality: There are some algorithms available in OpenSSL4J that are not available in the
normal SUN crypto provider.
* FIPS: When compiled against OpenSSL FIPS
(https://csrc.nist.gov/CSRC/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp4271.pdf,
https://csrc.nist.gov/CSRC/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp4282.pdf),
this library delivers FIPS-140-2 compliance. This is preferable to using bc-fips in combination with bouncy-castle,
because this library doesn't know or care at all about Java, specific Java versions, or classpath ordering. You can
use any version of Java you want, with whatever classpath you desire. And, unlike with bc-fips, you don't have to wait
years for the next Java version to be supported.

## Restrictions

* MessageDigest restriction: The current milestone only contains MessageDigest algorithms.
* Restricted platforms: The code uses dynamic linking to an object library on the machine.
Native object code within the JAR file is used for binding the Java code to the native code.
There is a restricted amount of platforms supported by the Github Actions
builder (see below).
* This library is not a full wrapper over Open SSL. All message digests are supported, but only those ciphers,
algorithms, HMAC, and SecureRandom needed for FIPS are currently supported. However, there is no technical reason full
Open SSL support can't be added, and we welcome contributions.

## Usage

Expand All @@ -111,9 +61,9 @@ import de.sfuhrm.openssl4j.OpenSSL4JProvider;

...

MessageDigest messageDigest = MessageDigest.getInstance("MD5", new OpenSSL4JProvider());
messageDigest.update("hello world!".getBytes(Charset.forName("ASCII")));
byte[] digest = messageDigest.digest();
MessageDigest messageDigest = MessageDigest.getInstance("MD5", OpenSSL4JProvider.getInstance());
messageDigest.update("hello world!".getBytes(Charset.forName("ASCII")));
byte[]digest=messageDigest.digest():
```

---------------------------------------
Expand Down Expand Up @@ -171,17 +121,16 @@ The recommended way of including the library into your project is using maven:
---------------------------------------

```xml

<dependency>
<groupId>de.sfuhrm</groupId>
<artifactId>openssl4j</artifactId>
<version>0.5.0</version>
<groupId>de.sfuhrm</groupId>
<artifactId>openssl4j</artifactId>
<version>0.2.0</version>
</dependency>
```

---------------------------------------

## Native platforms supported

There are the following native implementations available inside the JAR file:

* Linux-aarch64
Expand All @@ -196,20 +145,15 @@ Please note that the current version is experimental.

## Versions

The version numbers used by `openssl4j` itself comply to the
The version numbers comply to the
[semantic versioning](https://semver.org/) schema.
Especially major version changes come with breaking API
changes.

The temporary internal `openssl4j-objects` artifact is using
date-derived versions, but it is invisible to maven users.

## Author

Written 2020-2023 by Stephan Fuhrmann. You can reach me via email to s (at) sfuhrm.de
Written 2020-2022 by Stephan Fuhrmann. You can reach me via email to s (at) sfuhrm.de

## License

The project *is* licensed under [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) after excluding OpenSSL4j release v0.3.0.

The project *was* licensed under [LGPL 3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html) until including OpenSSL4j release v0.3.0.
The project is licensed under [LGPL 3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html).
15 changes: 8 additions & 7 deletions openssl4j/pom.xml
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>de.sfuhrm</groupId>
<artifactId>openssl4j-parent</artifactId>
<version>0.5.1-SNAPSHOT</version>
<version>0.3.1-SNAPSHOT</version>
</parent>
<artifactId>openssl4j</artifactId>
<name>OpenSSL4J JNI</name>
<description>Java binding to OpenSSL library functions</description>
<properties>
<openssl4j-objects.version>2023-10-21-08-46-22</openssl4j-objects.version>
<openssl4j-objects.version>2023-05-28-18-05-08</openssl4j-objects.version>
</properties>
<build>
<resources>
Expand Down Expand Up @@ -57,10 +58,10 @@
<dependencies>
<!-- for speedtest only -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.77</version>
<scope>test</scope>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Loading

0 comments on commit f5b4c1f

Please sign in to comment.