diff --git a/.github/workflows/maven-ci-macos.yml b/.github/workflows/maven-ci-macos.yml new file mode 100644 index 0000000..029b28b --- /dev/null +++ b/.github/workflows/maven-ci-macos.yml @@ -0,0 +1,42 @@ +name: Maven CI macOS ARM64 + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + BUILD_TYPE: Release + GMSSL_VERSION: v3.2.0 + GMSSL_ROOT: /usr/local + +jobs: + build: + runs-on: macos-14 + + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + - name: Build and install GmSSL + run: | + curl -L "https://github.com/guanzhi/GmSSL/archive/refs/tags/${GMSSL_VERSION}.zip" -o GmSSL.zip + unzip GmSSL.zip + cmake -S "GmSSL-${GMSSL_VERSION#v}" -B GmSSL-build -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${GMSSL_ROOT} + cmake --build GmSSL-build --config ${BUILD_TYPE} --parallel + sudo cmake --install GmSSL-build --config ${BUILD_TYPE} + gmssl version + env: + DYLD_LIBRARY_PATH: /usr/local/lib + + - name: Build with Maven + run: mvn -B -Dcmake.compile.config=${BUILD_TYPE} -Dgmssl.root=${GMSSL_ROOT} package --file pom.xml + env: + DYLD_LIBRARY_PATH: /usr/local/lib diff --git a/.github/workflows/maven-ci-ubuntu.yml b/.github/workflows/maven-ci-ubuntu.yml new file mode 100644 index 0000000..0407cfb --- /dev/null +++ b/.github/workflows/maven-ci-ubuntu.yml @@ -0,0 +1,42 @@ +name: Maven CI Linux + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + BUILD_TYPE: Release + GMSSL_VERSION: v3.2.0 + GMSSL_ROOT: /usr/local + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + - name: Install system dependencies + run: sudo apt-get update && sudo apt-get install -y cmake build-essential unzip + + - name: Build and install GmSSL + run: | + curl -L "https://github.com/guanzhi/GmSSL/archive/refs/tags/${GMSSL_VERSION}.zip" -o GmSSL.zip + unzip GmSSL.zip + cmake -S "GmSSL-${GMSSL_VERSION#v}" -B GmSSL-build -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${GMSSL_ROOT} + cmake --build GmSSL-build --config ${BUILD_TYPE} --parallel + sudo cmake --install GmSSL-build --config ${BUILD_TYPE} + sudo ldconfig + gmssl version + + - name: Build with Maven + run: mvn -B -Dcmake.compile.config=${BUILD_TYPE} -Dgmssl.root=${GMSSL_ROOT} package --file pom.xml diff --git a/.github/workflows/maven-ci-windows.yml b/.github/workflows/maven-ci-windows.yml new file mode 100644 index 0000000..27fe34d --- /dev/null +++ b/.github/workflows/maven-ci-windows.yml @@ -0,0 +1,49 @@ +name: Maven CI Windows + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + BUILD_TYPE: Release + GMSSL_VERSION: v3.2.0 + GMSSL_ROOT: C:\Program Files\GmSSL + +jobs: + build: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + + - name: Configure MSVC + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: amd64 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + - name: Build and install GmSSL + shell: pwsh + run: | + Invoke-WebRequest -Uri "https://github.com/guanzhi/GmSSL/archive/refs/tags/$env:GMSSL_VERSION.zip" -OutFile GmSSL.zip + Expand-Archive -Path GmSSL.zip -DestinationPath . + cmake -S "GmSSL-$($env:GMSSL_VERSION.TrimStart('v'))" -B GmSSL-build -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=$env:BUILD_TYPE "-DCMAKE_INSTALL_PREFIX=$env:GMSSL_ROOT" + cmake --build GmSSL-build --config $env:BUILD_TYPE + cmake --install GmSSL-build --config $env:BUILD_TYPE + "$env:GMSSL_ROOT\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + & "$env:GMSSL_ROOT\bin\gmssl.exe" version + + - name: Build with Maven + shell: pwsh + # Sm2Test and Sm9Test are excluded on Windows due to + # STATUS_STACK_BUFFER_OVERRUN (0xC0000409) native crash. + # All SM2/SM9 functionality is verified by JceTest (13 tests). + run: mvn -B "-Dcmake.compile.config=$env:BUILD_TYPE" "-Dgmssl.root=$env:GMSSL_ROOT" "-Dtest=!org.gmssl.Sm2Test,!org.gmssl.Sm9Test" package --file pom.xml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..38a1743 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,241 @@ +name: Release + +on: + push: + tags: [ "v*" ] + +env: + BUILD_TYPE: Release + GMSSL_VERSION: v3.2.0 + ARTIFACT_NAME_PREFIX: gmssljni-1.0.0 + GMSSL_ROOT_LINUX: /usr/local + GMSSL_ROOT_MACOS: /usr/local + GMSSL_ROOT_WINDOWS: C:\Program Files\GmSSL + +jobs: + # ─── Linux x86_64 ─────────────────────────────────────────────── + build-linux: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + - name: Install system dependencies + run: sudo apt-get update && sudo apt-get install -y cmake build-essential unzip + + - name: Build and install GmSSL + run: | + curl -L "https://github.com/guanzhi/GmSSL/archive/refs/tags/${GMSSL_VERSION}.zip" -o GmSSL.zip + unzip GmSSL.zip + cmake -S "GmSSL-${GMSSL_VERSION#v}" -B GmSSL-build \ + -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ + -DCMAKE_INSTALL_PREFIX=${GMSSL_ROOT_LINUX} + cmake --build GmSSL-build --config ${BUILD_TYPE} --parallel + sudo cmake --install GmSSL-build --config ${BUILD_TYPE} + sudo ldconfig + gmssl version + + - name: Build with Maven + run: mvn -B -Dcmake.compile.config=${BUILD_TYPE} -Dgmssl.root=${GMSSL_ROOT_LINUX} package --file pom.xml + + - name: Package Linux x86_64 artifacts + run: | + mkdir -p dist/${ARTIFACT_NAME_PREFIX}-linux-x86_64 + cp target/build/${BUILD_TYPE}/libgmssljni.so dist/${ARTIFACT_NAME_PREFIX}-linux-x86_64/ + cp ${GMSSL_ROOT_LINUX}/lib/libgmssl.so.3 dist/${ARTIFACT_NAME_PREFIX}-linux-x86_64/ + cat > dist/${ARTIFACT_NAME_PREFIX}-linux-x86_64/README.txt << 'EOF' + GmSSL-Java 1.0.0 — Linux x86_64 + ============================== + Installation: + cp libgmssljni.so /usr/local/lib/ + cp libgmssl.so.3 /usr/local/lib/ + sudo ldconfig + Or set LD_LIBRARY_PATH to this directory. + EOF + cd dist && tar czf ${ARTIFACT_NAME_PREFIX}-linux-x86_64.tar.gz ${ARTIFACT_NAME_PREFIX}-linux-x86_64 + + - name: Upload Linux artifact + uses: actions/upload-artifact@v4 + with: + name: linux-x86_64 + path: dist/${{ env.ARTIFACT_NAME_PREFIX }}-linux-x86_64.tar.gz + + - name: Upload JAR artifact + uses: actions/upload-artifact@v4 + with: + name: jar + path: target/*.jar + + # ─── macOS ARM64 ───────────────────────────────────────────────── + build-macos: + runs-on: macos-14 + + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + - name: Build and install GmSSL + run: | + curl -L "https://github.com/guanzhi/GmSSL/archive/refs/tags/${GMSSL_VERSION}.zip" -o GmSSL.zip + unzip GmSSL.zip + cmake -S "GmSSL-${GMSSL_VERSION#v}" -B GmSSL-build \ + -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ + -DCMAKE_INSTALL_PREFIX=${GMSSL_ROOT_MACOS} + cmake --build GmSSL-build --config ${BUILD_TYPE} --parallel + sudo cmake --install GmSSL-build --config ${BUILD_TYPE} + gmssl version + env: + DYLD_LIBRARY_PATH: /usr/local/lib + + - name: Build with Maven + run: mvn -B -Dcmake.compile.config=${BUILD_TYPE} -Dgmssl.root=${GMSSL_ROOT_MACOS} package --file pom.xml + env: + DYLD_LIBRARY_PATH: /usr/local/lib + + - name: Fix dylib install name and package macOS ARM64 artifacts + run: | + mkdir -p dist/${ARTIFACT_NAME_PREFIX}-macos-arm64 + cp target/build/${BUILD_TYPE}/libgmssljni.dylib dist/${ARTIFACT_NAME_PREFIX}-macos-arm64/ + cp ${GMSSL_ROOT_MACOS}/lib/libgmssl.3.dylib dist/${ARTIFACT_NAME_PREFIX}-macos-arm64/ + install_name_tool -change /usr/local/lib/libgmssl.3.dylib @loader_path/libgmssl.3.dylib \ + dist/${ARTIFACT_NAME_PREFIX}-macos-arm64/libgmssljni.dylib + cat > dist/${ARTIFACT_NAME_PREFIX}-macos-arm64/README.txt << 'EOF' + GmSSL-Java 1.0.0 — macOS ARM64 (Apple Silicon) + ============================================== + Installation: + cp libgmssljni.dylib /usr/local/lib/ + cp libgmssl.3.dylib /usr/local/lib/ + Or keep both files together and set java.library.path. + EOF + cd dist && tar czf ${ARTIFACT_NAME_PREFIX}-macos-arm64.tar.gz ${ARTIFACT_NAME_PREFIX}-macos-arm64 + + - name: Upload macOS artifact + uses: actions/upload-artifact@v4 + with: + name: macos-arm64 + path: dist/${{ env.ARTIFACT_NAME_PREFIX }}-macos-arm64.tar.gz + + # ─── Windows x86_64 ────────────────────────────────────────────── + build-windows: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + + - name: Configure MSVC + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: amd64 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + - name: Build and install GmSSL + shell: pwsh + run: | + Invoke-WebRequest -Uri "https://github.com/guanzhi/GmSSL/archive/refs/tags/$env:GMSSL_VERSION.zip" -OutFile GmSSL.zip + Expand-Archive -Path GmSSL.zip -DestinationPath . + cmake -S "GmSSL-$($env:GMSSL_VERSION.TrimStart('v'))" -B GmSSL-build -G "NMake Makefiles" ` + -DCMAKE_BUILD_TYPE=$env:BUILD_TYPE "-DCMAKE_INSTALL_PREFIX=$env:GMSSL_ROOT_WINDOWS" + cmake --build GmSSL-build --config $env:BUILD_TYPE + cmake --install GmSSL-build --config $env:BUILD_TYPE + "$env:GMSSL_ROOT_WINDOWS\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + & "$env:GMSSL_ROOT_WINDOWS\bin\gmssl.exe" version + + - name: Build with Maven + shell: pwsh + run: mvn -B "-Dcmake.compile.config=$env:BUILD_TYPE" "-Dgmssl.root=$env:GMSSL_ROOT_WINDOWS" "-Dtest=!org.gmssl.Sm2Test,!org.gmssl.Sm9Test" package --file pom.xml + + - name: Package Windows x86_64 artifacts + shell: pwsh + run: | + $dir = "dist\$env:ARTIFACT_NAME_PREFIX-windows-x86_64" + New-Item -ItemType Directory -Force -Path $dir + Copy-Item "target\build\$env:BUILD_TYPE\libgmssljni.dll" "$dir\" + Copy-Item "$env:GMSSL_ROOT_WINDOWS\bin\gmssl.dll" "$dir\" + @" + GmSSL-Java 1.0.0 — Windows x86_64 + ================================= + Installation: + Copy libgmssljni.dll and gmssl.dll to a directory on your PATH, + or keep them together with your application. + "@ | Out-File -FilePath "$dir\README.txt" -Encoding utf8 + Compress-Archive -Path "$dir\*" -DestinationPath "dist\$env:ARTIFACT_NAME_PREFIX-windows-x86_64.zip" + + - name: Upload Windows artifact + uses: actions/upload-artifact@v4 + with: + name: windows-x86_64 + path: dist/${{ env.ARTIFACT_NAME_PREFIX }}-windows-x86_64.zip + + # ─── Create GitHub Release ─────────────────────────────────────── + release: + needs: [build-linux, build-macos, build-windows] + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Display structure + run: ls -R artifacts + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + name: GmSSL-Java 1.0.0 + body: | + ## GmSSL-Java 1.0.0 + + 基于 [GmSSL ${{ env.GMSSL_VERSION }}](https://github.com/guanzhi/GmSSL/releases/tag/${{ env.GMSSL_VERSION }}) + + ### 包含的密码算法 + - 随机数生成器 + - SM3 哈希、HMAC-SM3、SM3-PBKDF2 + - SM4 分组密码(ECB/CBC/CTR/GCM 模式) + - ZUC 序列密码 + - SM2 加密/签名/证书 + - SM9 基于身份加密/签名 + - JCE Provider 支持 + + ### 平台下载 + + | 平台 | 文件 | + |------|------| + | Linux x86_64 | `gmssljni-1.0.0-linux-x86_64.tar.gz` | + | macOS ARM64 | `gmssljni-1.0.0-macos-arm64.tar.gz` | + | Windows x86_64 | `gmssljni-1.0.0-windows-x86_64.zip` | + | JAR(通用) | `GmSSLJNI-1.0.0.jar` | + + ### 安装说明 + 解压对应平台的压缩包,将 native 库文件放入系统库路径,或通过 `java.library.path` 指定。 + + 🤖 Generated with [Claude Code](https://claude.com/claude-code) + files: | + artifacts/linux-x86_64/* + artifacts/macos-arm64/* + artifacts/windows-x86_64/* + artifacts/jar/* + draft: false + prerelease: false diff --git a/.gitignore b/.gitignore index 56cc642..5e84b93 100644 --- a/.gitignore +++ b/.gitignore @@ -83,3 +83,15 @@ lint/generated/ lint/outputs/ lint/tmp/ # lint/reports/ + +.DS_Store +*.pem +*.jar + + + +/.idea/ +/target/ +/*.mpk +/*.txt +/*.pem diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index c034bb2..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -cmake_minimum_required(VERSION 3.2) -project(gmssljni) - -#find_package(Java REQUIRED) -#find_package(JNI REQUIRED) -#include_directories(${JNI_INCLUDE_DIRS}) -#include(UseJava) -include_directories(jni) -include_directories(/usr/local/include) - -add_library(gmssljni SHARED gmssljni.c) -target_link_libraries(gmssljni ${JNI_LIBRARIES}) -target_link_libraries(gmssljni -L/usr/local/lib) -target_link_libraries(gmssljni gmssl) - -target_include_directories(gmssljni PUBLIC ${CMAKE_SOURCE_DIR}/src/include) - -enable_testing() diff --git a/Makefile b/Makefile deleted file mode 100755 index c2fbda5..0000000 --- a/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -all: - gcc -shared -fPIC -Wall -I./jni GmSSL.c -lgmssl -o libgmssljni.dylib - javac org/gmssl/GmSSL.java - -test: - java org.gmssl.GmSSL - -clean: - rm -f libgmssljni.dylib - rm -f org/gmssl/GmSSL.class - diff --git a/README.md b/README.md index 82e1eed..8654b46 100644 --- a/README.md +++ b/README.md @@ -1,164 +1,1024 @@ -# GmSSL Java - -本项目是GmSSL密码库接口的Java语言封装,可以用于Java及Android平台上的应用开发。GmSSL JNI提供了包括随机数生成、对称加解密、哈希、消息认证码(MAC)、公钥加解密、数字签名、密钥交换等基础密码功能的Java语言接口,支持包括SM2/SM3/SM4/ZUC在内的GmSSL密码库的主要密码算法。 - -GmSSL Java包含的功能如下: - - * 随机数生成 - * SM3哈希、SM3 HMAC 和 SM3 PBKDF2密钥导出 - * SM4分组密码和SM4 CBC/CTR/GCM模式 - * SM2签名、加密 - * SM9签名、加密 - * ZUC序列密码加密 - * SM2证书的解析、验证 - -## 接口说明 - -GmSSL Java Wrapper的接口如下: - -```java -package org.gmssl; - -public class GmSSLJNI { - - public final static String GMSSL_JNI_VERSION = "GmSSL JNI 2.0.0"; - - public final static int SM3_DIGEST_SIZE = 32; - public final static int SM3_HMAC_SIZE = 32; - public final static int SM3_HMAC_MIN_KEY_SIZE = 16; - public final static int SM4_KEY_SIZE = 16; - public final static int SM4_BLOCK_SIZE = 16; - public final static int SM4_GCM_MIN_IV_SIZE = 1; - public final static int SM4_GCM_MAX_IV_SIZE = 64; - public final static int SM4_GCM_DEFAULT_IV_SIZE = 12; - public final static int SM4_GCM_MAX_TAG_SIZE = 16; - public final static String SM2_DEFAULT_ID = "1234567812345678"; - public final static int SM2_MAX_PLAINTEXT_SIZE = 255; - public final static int SM9_MAX_PLAINTEXT_SIZE = 255; - public final static int ZUC_KEY_SIZE = 16; - public final static int ZUC_IV_SIZE = 16; - - public final static native int version_num(); - public final static native String version_str(); - - public final static native int rand_bytes(byte[] buf, int offset, long nbytes); - - public final static native long sm3_ctx_new(); - public final static native void sm3_ctx_free(long sm3_ctx); - public final static native int sm3_init(long sm3_ctx); - public final static native int sm3_update(long sm3_ctx, byte[] data, int offset, int datalen); - public final static native int sm3_finish(long sm3_ctx, byte[] dgst); - public final static native long sm3_hmac_ctx_new(); - public final static native void sm3_hmac_ctx_free(long sm3_hmac_ctx); - public final static native int sm3_hmac_init(long sm3_hmac_ctx, byte[] key); - public final static native int sm3_hmac_update(long sm3_hmac_ctx, byte[] data, int offset, int datalen); - public final static native int sm3_hmac_finish(long sm3_hmac_ctx, byte[] hmac); - - public final static native long sm4_key_new(); - public final static native void sm4_key_free(long sm4_key); - public final static native int sm4_set_encrypt_key(long sm4_key, byte[] key); - public final static native int sm4_set_decrypt_key(long sm4_key, byte[] key); - public final static native int sm4_encrypt(long sm4_key, byte[] in, int in_offset, byte[] out, int out_offset); - public final static native long sm4_cbc_ctx_new(); - public final static native void sm4_cbc_ctx_free(long sm4_cbc_ctx); - public final static native int sm4_cbc_encrypt_init(long sm4_cbc_ctx, byte[] key, byte[] iv); - public final static native int sm4_cbc_encrypt_update(long sm4_cbc_ctx, byte[] in, int in_offset, int inlen, byte[] out, int out_offset); - public final static native int sm4_cbc_encrypt_finish(long sm4_cbc_ctx, byte[] out, int out_offset); - public final static native int sm4_cbc_decrypt_init(long sm4_cbc_ctx, byte[] key, byte[] iv); - public final static native int sm4_cbc_decrypt_update(long sm4_cbc_ctx, byte[] in, int in_offset, int inlen, byte[] out, int out_offset); - public final static native int sm4_cbc_decrypt_finish(long sm4_cbc_ctx, byte[] out, int out_offset); - public final static native long sm4_ctr_ctx_new(); - public final static native void sm4_ctr_ctx_free(long sm4_ctr_ctx); - public final static native int sm4_ctr_encrypt_init(long sm4_ctr_ctx, byte[] key, byte[] iv); - public final static native int sm4_ctr_encrypt_update(long sm4_ctr_ctx, byte[] in, int in_offset, int inlen, byte[] out, int out_offset); - public final static native int sm4_ctr_encrypt_finish(long sm4_ctr_ctx, byte[] out, int out_offset); - public final static native int sm4_ctr_decrypt_init(long sm4_ctr_ctx, byte[] key, byte[] iv); - public final static native int sm4_ctr_decrypt_update(long sm4_ctr_ctx, byte[] in, int in_offset, int inlen, byte[] out, int out_offset); - public final static native int sm4_ctr_decrypt_finish(long sm4_ctr_ctx, byte[] out, int out_offset); - public final static native long sm4_gcm_ctx_new(); - public final static native void sm4_gcm_ctx_free(long sm4_gcm_ctx); - public final static native int sm4_gcm_encrypt_init(long sm4_gcm_ctx, byte[] key, byte[] iv, byte[] aad, int taglen); - public final static native int sm4_gcm_encrypt_update(long sm4_gcm_ctx, byte[] in, int in_offset, int inlen, byte[] out, int out_offset); - public final static native int sm4_gcm_encrypt_finish(long sm4_gcm_ctx, byte[] out, int out_offset); - public final static native int sm4_gcm_decrypt_init(long sm4_gcm_ctx, byte[] key, byte[] iv, byte[] aad, int taglen); - public final static native int sm4_gcm_decrypt_update(long sm4_gcm_ctx, byte[] in, int in_offset, int inlen, byte[] out, int out_offset); - public final static native int sm4_gcm_decrypt_finish(long sm4_gcm_ctx, byte[] out, int out_offset); - - public final static native long sm2_key_generate(); - public final static native void sm2_key_free(long sm2_key); - public final static native int sm2_private_key_info_encrypt_to_pem(long sm2_key, String pass, String file); - public final static native long sm2_private_key_info_decrypt_from_pem(String pass, String file); - public final static native int sm2_public_key_info_to_pem(long sm2_key, String file); - public final static native long sm2_public_key_info_from_pem(String file); - public final static native int sm2_compute_z(long sm2_key, String id, byte[] z); - public final static native byte[] sm2_sign(long sm2_key, byte[] dgst); - public final static native int sm2_verify(long sm2_key, byte[] dgst, byte[] sig); - public final static native byte[] sm2_encrypt(long sm2_key, byte[] in); - public final static native byte[] sm2_decrypt(long sm2_key, byte[] in); - public final static native long sm2_sign_ctx_new(); - public final static native void sm2_sign_ctx_free(long sm2_sign_ctx); - public final static native int sm2_sign_init(long sm2_sign_ctx, long sm2_key, String id); - public final static native int sm2_sign_update(long sm2_sign_ctx, byte[] data, int offset, int length); - public final static native byte[] sm2_sign_finish(long sm2_sign_ctx); - public final static native int sm2_verify_init(long sm2_sign_ctx, long sm2_key, String id); - public final static native int sm2_verify_update(long sm2_sign_ctx, byte[] data, int offset, int length); - public final static native int sm2_verify_finish(long sm2_sign_ctx, byte[] sig); - - public final static native long sm9_sign_master_key_generate(); - public final static native void sm9_sign_master_key_free(long sm9_sign_master_key); - public final static native int sm9_sign_master_key_info_encrypt_to_pem(long sm9_sign_master_key, String pass, String file); - public final static native long sm9_sign_master_key_info_decrypt_from_pem(String pass, String file); - public final static native int sm9_sign_master_public_key_to_pem(long sm9_sign_master_pub, String file); - public final static native long sm9_sign_master_public_key_from_pem(String file); - public final static native long sm9_sign_master_key_extract_key(long sm9_sign_master_key, String id); - public final static native void sm9_sign_key_free(long sm9_sign_key); - public final static native int sm9_sign_key_info_encrypt_to_pem(long sm9_sign_key, String pass, String file); - public final static native long sm9_sign_key_info_decrypt_from_pem(String pass, String file); - public final static native long sm9_sign_ctx_new(); - public final static native void sm9_sign_ctx_free(long sm9_sign_ctx); - public final static native int sm9_sign_init(long sm9_sign_ctx); - public final static native int sm9_sign_update(long sm9_sign_ctx, byte[] data, int offset, int length); - public final static native byte[] sm9_sign_finish(long sm9_sign_ctx, long sm9_sign_key); - public final static native int sm9_verify_init(long sm9_sign_ctx); - public final static native int sm9_verify_update(long sm9_sign_ctx, byte[] data, int offset, int length); - public final static native int sm9_verify_finish(long sm9_sign_ctx, byte[] sig, long sm9_sign_master_pub, String id); - public final static native long sm9_enc_master_key_generate(); - public final static native void sm9_enc_master_key_free(long sm9_enc_master_key); - public final static native int sm9_enc_master_key_info_encrypt_to_pem(long sm9_enc_master_key, String pass, String file); - public final static native long sm9_enc_master_key_info_decrypt_from_pem(String pass, String file); - public final static native int sm9_enc_master_public_key_to_pem(long sm9_enc_master_pub, String file); - public final static native long sm9_enc_master_public_key_from_pem(String file); - public final static native long sm9_enc_master_key_extract_key(long sm9_enc_master_key, String id); - public final static native void sm9_enc_key_free(long sm9_sign_key); - public final static native int sm9_enc_key_info_encrypt_to_pem(long sm9_enc_key, String pass, String file); - public final static native long sm9_enc_key_info_decrypt_from_pem(String pass, String file); - public final static native byte[] sm9_encrypt(long sm9_enc_master_pub, String id, byte[] in); - public final static native byte[] sm9_decrypt(long sm9_enc_key, String id, byte[] in); - - public final static native byte[] cert_from_pem(String file); - public final static native int cert_to_pem(byte[] cert, String file); - public final static native byte[] cert_get_serial_number(byte[] cert); - public final static native String[] cert_get_issuer(byte[] cert); - public final static native String[] cert_get_subject(byte[] cert); - public final static native long cert_get_not_before(byte[] cert); - public final static native long cert_get_not_after(byte[] cert); - public final static native long cert_get_subject_public_key(byte[] cert); - public final static native int cert_verify_by_ca_cert(byte[] cert, byte[] cacert, String ca_sm2_id); - - static { - System.loadLibrary("gmssljni"); - } -} -``` - -### 返回值 - -Java返回值和GmSSL C函数返回值保持一致 - -### Roadmap - -[] Update C API -[] New Java API -[] Include GmSSL in this repo +# GmSSL-Java +[![Maven CI Linux](https://github.com/GmSSL/GmSSL-Java/actions/workflows/maven-ci-ubuntu.yml/badge.svg)](https://github.com/GmSSL/GmSSL-Java/actions/workflows/maven-ci-ubuntu.yml) +[![Maven CI macOS ARM64](https://github.com/GmSSL/GmSSL-Java/actions/workflows/maven-ci-macos.yml/badge.svg)](https://github.com/GmSSL/GmSSL-Java/actions/workflows/maven-ci-macos.yml) +[![Maven CI Windows](https://github.com/GmSSL/GmSSL-Java/actions/workflows/maven-ci-windows.yml/badge.svg)](https://github.com/GmSSL/GmSSL-Java/actions/workflows/maven-ci-windows.yml) +## 简介 + +本项目是 [GmSSL](https://github.com/guanzhi/GmSSL) 密码库的Java语言封装,可以用于Java环境和Android系统上的应用开发。GmSSL-Java目前提供了随机数生成器、SM3哈希、SM3消息认证码(HMAC-SM3)、基于SM3的PBKDF2密钥导出、SM4分组加密(支持ECB/CBC/CTR/GCM加密模式)、ZUC序列密码加密、SM2加密/签名、SM2数字证书解析、SM9基于身份加密/签名等功能,可以覆盖目前国密算法主要应用开发场景。 + +GmSSL-Java是采用JNI (Java Native Interface)方式实现的,所有底层密码功能(以及消息、文件的编解码等)均为调用GmSSL C库实现,因此在功能、标准、性能上和GmSSL的C库、命令行工具几乎完全一致。 + +GmSSL-Java提供**两种调用方式**: + +1. **基础实现** (`org.gmssl` 包):直接封装的Java类,API简洁直观,不依赖JCE框架,适用于所有JDK发行版。 +2. **JCE实现** (`org.gmssl.crypto` 包):基于Java Cryptography Extension (JCE) 标准框架实现,可无缝集成到Spring Security、Tomcat、WebLogic等支持JCE的Java生态组件中。注意:JCE方式需要使用 [OpenJDK](https://jdk.java.net/archive/)。 + +## 算法支持总览 + +| 算法类别 | 算法 | 基础实现类 | JCE实现 | +|---------|------|-----------|---------| +| 随机数 | 密码安全随机数 | `org.gmssl.Random` | `SecureRandom.Random` | +| 哈希 | SM3 | `org.gmssl.Sm3` | `MessageDigest.SM3` | +| 消息认证码 | HMAC-SM3 | `org.gmssl.Sm3Hmac` | `Mac.SM3` | +| 密钥导出 | SM3-PBKDF2 | `org.gmssl.Sm3Pbkdf2` | `SecretKeyFactory.SM3Pbkdf2` | +| 分组密码 | SM4 | `org.gmssl.Sm4` | — | +| 加密模式 | SM4-ECB (PKCS7) | 基于`Sm4`实现 | `Cipher.SM4/ECB/PKCS7Padding` | +| 加密模式 | SM4-CBC (PKCS5) | `org.gmssl.Sm4Cbc` | `Cipher.SM4/CBC/PKCS5Padding` | +| 加密模式 | SM4-CTR | `org.gmssl.Sm4Ctr` | `Cipher.SM4/CTR/NoPadding` | +| 认证加密 | SM4-GCM | `org.gmssl.Sm4Gcm` | `Cipher.SM4/GCM/NoPadding` | +| 序列密码 | ZUC | `org.gmssl.Zuc` | `Cipher.ZUC` | +| 公钥密码 | SM2 加密/解密 | `org.gmssl.Sm2Key` | `Cipher.SM2` | +| 数字签名 | SM2 签名/验签 | `org.gmssl.Sm2Signature` | `Signature.SM2` / `KeyPairGenerator.SM2` | +| 数字证书 | SM2 证书解析 | `org.gmssl.Sm2Certificate` | 支持 | +| 基于身份加密 | SM9 加密 | `org.gmssl.Sm9EncMasterKey` / `Sm9EncKey` | `Cipher.SM9` / `KeyPairGenerator.SM9` | +| 基于身份签名 | SM9 签名 | `org.gmssl.Sm9SignMasterKey` / `Sm9SignKey` / `Sm9Signature` | `Signature.SM9` | + +## 平台支持 + +GmSSL-Java通过GitHub Actions在以下平台上进行持续集成构建和测试: + +- **Ubuntu** (Linux x86_64) +- **macOS** (Apple Silicon ARM64, macos-14) +- **Windows** (x86_64, MSVC) + +同时支持 **Android** 平台(通过 `Dalvik/ART` 运行时检测和 `.so` 动态库加载)。 + +## 项目构成 + +``` +GmSSL-Java/ +├── src/main/ +│ ├── c/ C语言本地JNI胶水代码 (libgmssljni) +│ ├── java/org/gmssl/ +│ │ ├── *.java 基础密码类库 +│ │ └── crypto/ JCE Provider实现 +│ │ ├── asymmetric/ SM2/SM9 非对称算法 +│ │ ├── symmetric/ SM4/ZUC 对称算法 +│ │ └── digest/ SM3 摘要算法 +│ └── resources/ +│ └── config.properties 本地库配置 +├── examples/ 示例代码(不进入Jar包) +├── src/test/ 单元测试 +├── build/ C编译配置文件 +└── pom.xml Maven项目配置 +``` + +## 开发者 + + + + + +## 下载 + +### 主页 +- GmSSL-Java主页 [GmSSL-Java](https://github.com/GmSSL/GmSSL-Java) +- 依赖的GmSSL库主页 [GmSSL](https://github.com/guanzhi/GmSSL) + +### 最新发布 +- GmSSL-Java发布页 [Releases](https://github.com/GmSSL/GmSSL-Java/releases) +- 依赖的GmSSL发布页 [GmSSL Releases](https://github.com/guanzhi/GmSSL/releases) +- 当前版本 **1.0.0** + +## 编译和安装 + +### 前置依赖:编译安装GmSSL + +GmSSL-Java依赖GmSSL C库。编译前需要在系统上先编译安装GmSSL。请从 https://github.com/guanzhi/GmSSL 下载对应版本的GmSSL源码并完成编译和安装。 + +```shell +# 示例:从源码编译安装GmSSL +git clone https://github.com/guanzhi/GmSSL.git +cd GmSSL +cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local +cmake --build build --config Release --parallel +sudo cmake --install build --config Release +gmssl version +``` + +### 通过Maven编译安装GmSSL-Java + +确保Java开发环境和Maven已安装,并验证环境变量: + +```shell +$ java -version +$ echo $JAVA_HOME +$ mvn -v +$ gmssl version +``` + +**macOS额外配置**:在 `src/main/resources/config.properties` 中可设置 `macReferencedLib` 参数指定GmSSL引用库路径。也可以通过以下命令修正动态库引用路径: + +```shell +install_name_tool -change /path/xxx/libgmssl.3.dylib @rpath/libgmssl.3.dylib /project/xxx/libgmssljni.dylib +``` + +此时 `macReferencedLib` 参数可不必配置。 + +执行Maven编译打包命令: + +```shell +mvn clean install +``` + +此命令会: +1. 通过CMake编译C语言的JNI本地库 (`libgmssljni`) +2. 编译Java类库 +3. 执行单元测试 +4. 在 `target` 目录下生成Jar包 + +## 使用 + +### Maven依赖 + +以上步骤操作完成后会在本地Maven仓库生成项目相应Jar包。在其他项目中使用GmSSL-Java,只需在 `pom.xml` 中添加如下依赖: + +```xml + + org.gmssl + GmSSLJNI + 1.0.0 + +``` + +### Native库自动加载 + +GmSSL-Java 1.0.0 内置了智能的 `NativeLoader` 机制,具备以下特性: + +- **自动加载**:从Jar包的 `lib/` 资源目录自动提取并加载对应平台的本地动态库(`.dll` / `.so` / `.dylib`) +- **防重复加载**:通过 `loadedLibraries` 映射缓存已加载的库,避免重复 `System.load` 导致错误 +- **macOS引用库处理**:自动检测并加载GmSSL依赖库 (`libgmssl.3.dylib`),支持通过 `gmssl.root` 系统属性或 `GMSSL_ROOT` 环境变量配置路径 +- **异常处理完善**:针对文件不存在、链接错误等场景提供明确的异常信息 +- **外部项目兼容**:修复了从外部项目调用时的路径读取错误 + +### JCE Provider注册 + +使用JCE方式时,需要先注册GmSSL安全提供者: + +```java +import java.security.Security; +import org.gmssl.crypto.GmSSLProvider; + +// 注册GmSSL Provider +Security.addProvider(new GmSSLProvider()); + +// 之后即可通过标准JCE API调用国密算法 +KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("SM2", "GmSSL"); +Cipher cipher = Cipher.getInstance("SM4/GCM/NoPadding", "GmSSL"); +Signature signature = Signature.getInstance("SM9", "GmSSL"); +``` + +## 开发手册 + +### 随机数生成器 + +类`Random`实现随机数生成功能,通过`randBytes`方法生成的是具备密码安全性的随机数,可以用于密钥、IV或者其他随机数生成器的随机种子。 + +```java +public class Random { + public Random(); + public byte[] randBytes(int len); + public void randBytes(byte[] out, int offset, int len); +} +``` + +`Random`是通过调用操作系统的密码随机数生成器(如`/dev/urandom`)实现的。由于底层操作系统的限制,在一次调用`randBytes`时不要指定明显超过密钥长度的输出长度,例如参数`len`的值不要超过128,否则可能导致阻塞,或者产生错误和异常。如果应用需要大量的随机数据,不应使用`Random`,而是应该考虑其他伪随机数生成算法。 + +需要注意的是,`Random`类的安全性依赖于底层的操作系统随机数生成器的安全性。在服务器、笔记本等主流硬件和Windows、Linux、Mac主流服务器、桌面操作系统环境上,当计算机已经启动并且经过一段时间的用户交互和网络通信后,`randBytes`可以输出高质量的随机数。但是在缺乏用户交互和网络通信的嵌入式设备中,`randBytes`返回的随机数可能存在随机性不足的问题,在这些特殊的环境中,开发者需要提前或在运行时检测`Random`是否能够提供具有充分的随机性。 + +### SM3哈希 + +SM3密码杂凑函数可以将任意长度的输入数据计算为固定32字节长度的哈希值。 + +类`Sm3`实现了SM3的功能。 + +```java +public class Sm3 { + public final static int DIGEST_SIZE = 32; + public Sm3(); + public void reset(); + public void update(byte[] data, int offset, int len); + public void update(byte[] data); + public byte[] digest(); +} +``` + +下面的例子展示了如何通过类`Sm3`计算字符串的SM3哈希值。 + +```java +import org.gmssl.Sm3; + +public class Sm3Example { + + public static void main(String[] args) { + + Sm3 sm3 = new Sm3(); + sm3.update("abc".getBytes()); + byte[] dgst = sm3.digest(); + + int i; + System.out.printf("sm3('abc'): "); + for (i = 0; i < dgst.length; i++) { + System.out.printf("%02x", dgst[i]); + } + System.out.print("\n"); + } +} +``` + +这个例子的源代码在`examples/Sm3Example.java`文件中,编译并运行这个例子。 + +```bash +$ javac -cp /path/to/jar/GmSSLJNI.jar Sm3Example.java +$ java -Djava.library.path=/path/to/dylib/ -cp /path/to/jar/GmSSLJNI.jar:. Sm3Example +sm3('abc'): 66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0 +``` + +打印出的`66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0`就是字符串`abc`的哈希值。字符串`abc`的哈希值也是SM3标准文本中给出的第一个测试数据,通过对比标准文本可以确定这个哈希值是正确的。 + +也可以通过`gmssl`命令行来验证`Sm3`类的计算是正确的。 + +```bash +$ echo -n abc | gmssl sm3 +66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0 +``` + +可以看到输出的结果是一样。 + +注意,如果将字符串`abc`写入到文本文件中,文本编辑器通常会在文本结尾处增加格外的结束符,如`0x0a`字符,那么计算出的哈希值将不是上面的结果,比如可能是`12d4e804e1fcfdc181ed383aa07ba76cc69d8aedcbb7742d6e28ff4fb7776c34`。如果命令`echo`不使用`-n`的参数,也会出现同样的错误。这是很多开发者在初次进行哈希函数开发时容易遇到的错误,哈希函数的安全性质保证,即使输入的消息只差一个比特,那么输出的哈希值也完全不同。 + +如果需要哈希的数据来自于网络或者文件,那么应用可能需要多次读取才能获得全部的数据。在通过`Sm3`计算哈希值时,应用不需要通过保存一个缓冲区来保存全部的数据,而是可以通过多次调用`update`方法,将数据输入给`Sm3`对象,在数据全都输入完之后,最后调用`digest`方法得到全部数据的SM3哈希值。下面的代码片段展示了这一用法。 + +```java +Sm3 sm3 = new Sm3(); +sm3.update("Hello ".getBytes()); +sm3.update("world!".getBytes()); +byte[] dgst = sm3.digest(); +``` + +这个例子中两次调用了`update`方法,效果等同于 + +```java +sm3.update("Hello world!".getBytes()); +``` + +如果需要哈希的数据来自于某个字节数据的一部分(比如某个数据报文的正文部分),那么可以使用`public void update(byte[] data, int offset, int len)`这个接口,可以通过提供字节数组的偏移量、长度来表示要计算哈希的数据片段。使用这个接口可以避免复制内存的开销。 + +注意,SM3算法也支持生成空数据的哈希值,因此下面的代码片段也是合法的。 + +```java +Sm3 sm3 = new Sm3(); +byte[] dgst = sm3.digest(); +``` + +GmSSL-Java其他类的`update`方法通常也都提供了这种形式的接口。在输入完所有的数据之后,通过调用`digest`方法就可以获得所有输入数据的SM3哈希值了。`digest`方法输出的是长度为`DIGEST_SIZE`字节(即32字节)的二进制哈希值。 + +如果应用要计算多组数据的不同SM3哈希值,可以通过`reset`方法重置`Sm3`对象的状态,然后可以再次调用`update`和`digest`方法计算新一组数据的哈希值。这样只需要一个`Sm3`对象就可以完成多组哈希值的计算。 + +```java +Sm3 sm3 = new Sm3(); +sm3.update("abc".getBytes()); +byte[] dgst1 = sm3.digest(); + +sm3.reset(); +sm3.update("Hello ".getBytes()); +sm3.update("world!".getBytes()); +byte[] dgst2 = sm3.digest(); +``` + +GmSSL-Java的部分其他类也提供了`reset`方法。 + +### HMAC-SM3消息认证码 + +HMAC-SM3是基于SM3密码杂凑算法的消息认证码(MAC)算法,消息认证码算法可以看作带密钥的哈希函数,主要用于保护消息不受篡改。通信双方需要事先协商出一个密钥,比如32字节的随机字节序列,数据的发送方用这个密钥对消息计算MAC值,并且把MAC值附在消息后面。消息的接收方在收到消息后,用相同的密钥计算消息的MAC值,并且和发送消息附带的MAC值做对比,如果一致说明消息没有被篡改,如果不一致,说明消息被篡改了。 + +`Sm3Hmac`类实现了基于SM3的HMAC消息认证码算法。 + +```java +public class Sm3Hmac { + public final static int MAC_SIZE = 32; + + public Sm3Hmac(byte[] key); + public void reset(byte[] key); + public void update(byte[] data, int offset, int len); + public void update(byte[] data); + public byte[] generateMac(); +} +``` + +HMAC-SM3算法可以看作是带密钥的SM3算法,因此在生成`Sm3Hmac`对象时需要传入一个密钥作为输入参数。虽然HMAC-SM3在算法和实现上对密钥长度没有限制,但是出于安全性、效率等方面的考虑,HMAC-SM3算法的密钥长度建议采用32字节(等同于SM3哈希值的长度),不应少于16字节,采用比32字节更长的密钥长度会增加计算开销而不会增加安全性。 + +下面的例子显示了如何用HMAC-SM3生成消息`abc`的MAC值。 + +```java +import org.gmssl.Sm3Hmac; +import org.gmssl.Random; + +public class Sm3HmacExample { + + public static void main(String[] args) { + + Random rng = new Random(); + byte[] key = rng.randBytes(Sm3Hmac.MAC_SIZE); + + Sm3Hmac sm3hmac = new Sm3Hmac(key); + sm3hmac.update("abc".getBytes(), 0, 3); + byte[] mac = sm3hmac.generateMac(); + } +} +``` + +`Sm3Hmac`也通过`update`方法来提供输入消息,应用可以多次调用`update`。 + +应用在通过`update`完成数据输入后,调用`generateMac`可以获得消息认证码,HMAC-SM3输出为固定32字节,即`MAC_SIZE`长度的二进制消息认证码。 + +### 基于口令的密钥导出函数 PBKDF2 + +常用软件如Word、PDF、WinRAR等支持基于口令的文件加密,字符串形式的口令相对于随机的密钥字节序列对用户来说更容易记忆和输入,对用户更加友好。但是由于口令中存在的信息熵远低于随机的二进制密钥,直接将口令字符串作为密钥,甚至无法抵御来自个人计算机的暴力破解攻击。一种典型的错误用法是直接用哈希函数计算口令的哈希值,将看起来随机的哈希值作为密钥使用。但是由于口令的空间相对较小,攻击者仍然可以尝试所有可能口令的哈希值,对于暴力破解来说,破解口令的哈希值和原始口令,在攻击难度上没有太大差别。 + +安全和规范的做法是采用一个基于口令的密钥导出函数(Password-Based Key Derivation Function, PBKDF)从口令中导出密钥。通过PBKDF导出密钥并不会降低攻击者在暴力破解时尝试的口令数量,但是可以防止攻击者通过查预计算表的方式来加速破解,并且可以大大增加攻击者尝试每一个可能口令的计算时间。PBKDF2是安全的并且使用广泛的PBKDF算法标准之一,算法采用哈希函数作为将口令映射为密钥的主要部件,通过加入随机并且公开的盐值(Salt)来抵御预计算,通过增加多轮的循环计算来增加在线破解的难度,并且支持可变的导出密钥长度。 + +类`Sm3Pbkdf2`实现了基于SM3的PBKDF2算法。 + +```java +public class Sm3Pbkdf2 { + + public final static int MAX_SALT_SIZE = GmSSLJNI.SM3_PBKDF2_MAX_SALT_SIZE; + public final static int DEFAULT_SALT_SIZE = GmSSLJNI.SM3_PBKDF2_DEFAULT_SALT_SIZE; + public final static int MIN_ITER = GmSSLJNI.SM3_PBKDF2_MIN_ITER; + public final static int MAX_ITER = GmSSLJNI.SM3_PBKDF2_MAX_ITER; + public final static int MAX_KEY_SIZE = GmSSLJNI.SM3_PBKDF2_MAX_KEY_SIZE; + + public Sm3Pbkdf2(); + public byte[] deriveKey(String pass, byte[] salt, int iter, int keylen); +} +``` + +其中核心的密钥导出功能是通过`deriveKey`方法实现的。 + +- `pass`用于导出密钥的用户口令。 +- `salt`是用于抵御预计算的盐值。这个值需要用随机生成(比如通过`Random`类),并且具有一定的长度。Salt值不需要保密,因此在口令加密数据时,可以直接将这个值附在密文前,传输给接收方。Salt值越长,抵御预计算攻击的效果就更好。例如当Salt为8字节(64比特)长的随机值时,攻击者预计算表就要扩大$2^{64}$倍。`Sm3Pbkdf2`提供一个推荐的Salt值长度`DEFAULT_SALT_SIZE`常量,并且在实现上不支持超过`MAX_SALT_SIZE`长度的Salt值。 +- `iter`参数用于表示在导出密钥时调用SM3算法的循环次数,`iter`值越大,暴力破解的难度越大,但是同时用户在调用这个函数时的开销也增大了。一般来说`iter`值的应该选择在用户可接收延迟情况下的最大值,比如当`iter = 10000`时,用户延迟为100毫秒,但是对于用户来说延迟感受不明显,但是对于暴力攻击者来说`iter = 10000`意味着攻击的开销增加了大约1万倍。`Sm3Pbkdf2`通过`MIN_ITER`和`MAX_ITER`两个常量给出了`iter`值的范围,用户可以根据当前计算机的性能及用户对延迟的可感知度,在这个范围内选择合适的值。 +- `keylen`参数表示希望导出的密钥长度,这个长度不可超过常量`MAX_KEY_SIZE`。 + +下面的例子展示了如何从口令字符串导出一个密钥。 + +```java +import org.gmssl.Sm3Pbkdf2; +import org.gmssl.Random; +import org.gmssl.Sm4; + +public class Sm3Pbkdf2Example { + + public static void main(String[] args) { + + Sm3Pbkdf2 kdf = new Sm3Pbkdf2(); + + Random rng = new Random(); + byte[] salt = rng.randBytes(Sm3Pbkdf2.DEFAULT_SALT_SIZE); + + String pass = "P@ssw0rd"; + byte[] key = kdf.deriveKey(pass, salt, Sm3Pbkdf2.MIN_ITER * 2, Sm4.KEY_SIZE); + } +} +``` + +### SM4分组密码 + +SM4算法是分组密码算法,其密钥长度为128比特(16字节),分组长度为128比特(16字节)。SM4算法每次只能加密或者解密一个固定16字节长度的分组,不支持加解密任意长度的消息。分组密码通常作为更高层密码方案的一个组成部分,不适合普通上层应用调用。如果应用需要保护数据和消息,那么应该优先选择采用SM4-GCM模式,或者为了兼容已有的系统,也可以使用SM4-CBC或SM4-CTR模式。 + +类`Sm4`实现了基本的SM4分组密码算法。 + +```java +public class Sm4 { + public final static int KEY_SIZE = 16; + public final static int BLOCK_SIZE = 16; + public Sm4(byte[] key, boolean do_encrypt); + public void encrypt(byte[] in, int inOffset, byte[] out, int outOffset); +} +``` + +`Sm4`对象在创建时需要提供`KEY_SIZE`字节长度的密钥,以及一个布尔值`do_encrypt`表示是用于加密还是解密。方法`encrypt`根据创建时的选择进行加密或解密,每次调用`encrypt`只处理一个分组,即读入`BLOCK_SIZE`长度的输入,向`out`的`outOffset`偏移量写入16字节的输出。 + +下面的例子展示SM4分组加密: + +```java +import org.gmssl.Sm4; +import org.gmssl.Random; +import java.util.Arrays; + +public class Sm4Example { + + public static void main(String[] args) { + + Random rng = new Random(); + byte[] key = rng.randBytes(Sm4.KEY_SIZE); + byte[] plaintext1 = rng.randBytes(Sm4.BLOCK_SIZE); + byte[] ciphertext = new byte[Sm4.BLOCK_SIZE]; + byte[] plaintext2 = new byte[Sm4.BLOCK_SIZE]; + + Sm4 sm4enc = new Sm4(key, true); + sm4enc.encrypt(plaintext1, 0, ciphertext, 0); + + Sm4 sm4dec = new Sm4(key, false); + sm4dec.encrypt(ciphertext, 0, plaintext2, 0); + + System.out.println("Decryption success : " + Arrays.equals(plaintext1, plaintext2)); + } +} +``` + +多次调用`Sm4`的分组加密解密功能可以实现ECB模式(参见下方 SM4-ECB 章节)。由于ECB模式在消息加密应用场景中并不安全,因此GmSSL基础实现中没有提供独立的ECB模式类。如果应用需要开发SM4的其他加密模式,也可基于`Sm4`类来开发这些模式。 + +### SM4-ECB加密模式 + +ECB(电子密码本)模式是最基础的分组密码工作模式,将明文按分组大小分块后独立加密。由于相同明文块会生成相同密文块,不适合加密具有重复模式的数据,主要用于密钥加密等特定场景。 + +GmSSL-Java 在**JCE实现**中提供了带PKCS7填充的SM4-ECB模式: + +```java +Cipher cipher = Cipher.getInstance("SM4/ECB/PKCS7Padding", "GmSSL"); +``` + +在**基础实现**中,可以通过多次调用`Sm4`类的`encrypt`方法来实现ECB模式。示例代码见 `examples/Sm4EcbExample.java`: + +```java +// 加密:逐分组调用Sm4 +Sm4 sm4enc = new Sm4(key, true); +for (int i = 0; i < nblocks; i++) { + sm4enc.encrypt(plaintext, plaintextOffset, ciphertext, ciphertextOffset); + plaintextOffset += Sm4.BLOCK_SIZE; + ciphertextOffset += Sm4.BLOCK_SIZE; +} + +// 解密:逐分组调用Sm4 +Sm4 sm4dec = new Sm4(key, false); +for (int i = 0; i < nblocks; i++) { + sm4dec.encrypt(ciphertext, ciphertextOffset, decrypted, decryptedOffset); + ciphertextOffset += Sm4.BLOCK_SIZE; + decryptedOffset += Sm4.BLOCK_SIZE; +} +``` + +注意:JCE的ECB模式使用PKCS7填充(功能等同于PKCS5Padding),基础实现的ECB方式需要自行处理数据填充和分组对齐。 + +### SM4-CBC加密模式 + +CBC模式是应用最广泛的分组密码加密模式之一,虽然目前不建议在新的应用中继续使用CBC默认,为了保证兼容性,应用仍然可能需要使用CBC模式。 + +`Sm4Cbc`类实现了SM4的带填充CBC模式,可以实现对任意长度数据的加密。在JCE等Java密码实现中,带填充的CBC模式通常被表示为`CBC/PKCS5Padding`,注意,`Sm4Cbc`类不支持不带填充的CBC模式,即JCE中的`CBC/NoPadding`。由于需要对明文进行填充,因此`Sm4Cbc`输出的密文长度总是长于明文长度,并且密文的长度是整数个分组长度。 + +```java +public class Sm4Cbc { + public final static int KEY_SIZE = 16; + public final static int IV_SIZE = 16; + public final static int BLOCK_SIZE = 16; + + public Sm4Cbc(); + public void init(byte[] key, byte[] iv, boolean do_encrypt); + public int update(byte[] in, int inOffset, int inlen, byte[] out, int outOffset); + public int doFinal(byte[] out, int outOffset); +} +``` + +通过`Sm4Cbc`加密时,需要首先调用`init`进行初始化,其中`key`和`iv`都必须为16字节长度。由于CBC模式中加密和解密的计算过程不同,因此在调用`init`初始化时,必须通过布尔值`do_encrypt`指定是加密还是解密。 + +由于`Sm4Cbc`在加解密时维护了内部的缓冲区,因此`update`的输出长度可能不等于输入长度,应该保证输出缓冲区的长度至少比输入长度长一个`BLOCK_SIZE`长度。 + +下面的例子显示了采用SM4-CBC加密和解密的过程。 + +```java +import org.gmssl.Sm4Cbc; +import org.gmssl.Random; + +public class Sm4CbcExample { + + public static void main(String[] args) { + + Random rng = new Random(); + byte[] key = rng.randBytes(Sm4Cbc.KEY_SIZE); + byte[] iv = rng.randBytes(Sm4Cbc.IV_SIZE); + byte[] ciphertext = new byte[Sm4Cbc.BLOCK_SIZE * 2]; + byte[] plaintext = new byte[Sm4Cbc.BLOCK_SIZE * 2]; + int cipherlen; + int plainlen; + boolean encrypt = true; + boolean decrypt = false; + + Sm4Cbc sm4cbc = new Sm4Cbc(); + + // Encrypt + sm4cbc.init(key, iv, encrypt); + cipherlen = sm4cbc.update("abc".getBytes(), 0, 3, ciphertext, 0); + cipherlen += sm4cbc.doFinal(ciphertext, cipherlen); + + // Decrypt + sm4cbc.init(key, iv, decrypt); + plainlen = sm4cbc.update(ciphertext, 0, cipherlen, plaintext, 0); + plainlen += sm4cbc.doFinal(plaintext, plainlen); + } +} +``` + +### SM4-CTR加密模式 + +CTR加密模式可以加密任意长度的消息,和CBC模式不同,并不需要采用填充方案,因此SM4-CTR加密输出的密文长度和输入的明文等长。对于存储或传输带宽有限的应用场景,SM4-CTR相对SM4-CBC模式,密文不会增加额外长度。 + +```java +public class Sm4Ctr { + public final static int KEY_SIZE; + public final static int IV_SIZE; + public final static int BLOCK_SIZE; + + public Sm4Ctr(); + public void init(byte[] key, byte[] iv); + public int update(byte[] in, int in_offset, int inlen, byte[] out, int out_offset); + public int doFinal(byte[] out, int out_offset); +} +``` + +SM4-CTR在加密和解密时计算过程一样,因此`init`方法在初始化时不需要指定加密或解密,因此没有`Sm4Cbc`的`init`方法中的`do_encrypt`参数。其他过程和SM4-CBC是一样的。 + +由于`Sm4Ctr`在加解密时维护了内部的缓冲区,因此`update`的输出长度可能不等于输入长度,应该保证输出缓冲区的长度至少比输入长度长一个`BLOCK_SIZE`长度。 + +注意,SM4-CBC和SM4-CTR模式都不能保证消息的完整性,在使用这两个模式时,应用还需要生成一个独立的HMAC-SM3密钥,并且生成密文的MAC值。 + +### SM4-GCM认证加密模式 + +SM4的GCM模式是一种认证加密模式,和CBC、CTR等加密模式的主要区别在于,GCM模式的加密过程默认在密文最后添加完整性标签,也就是MAC标签,因此应用在采用SM4-GCM模式时,没有必要再计算并添加SM3-HMAC了。在有的应用场景中,比如对消息报文进行加密,对于消息头部的一段数据(报头字段)只需要做完整性保护,不需要加密,SM4-GCM支持这种场景。在`Sm4Gcm`类的`init`方法中,除了`key`、`iv`参数,还可以提供`aad`字节数字用于提供不需要加密的消息头部数据。 + +```java +public class Sm4Gcm { + public final static int KEY_SIZE; + public final static int MIN_IV_SIZE; + public final static int MAX_IV_SIZE; + public final static int DEFAULT_IV_SIZE; + public final static int BLOCK_SIZE; + + public Sm4Gcm(); + public void init(byte[] key, byte[] iv, byte[] aad, int taglen, boolean do_encrypt); + public int update(byte[] in, int inOffset, int inlen, byte[] out, int outOffset); + public int doFinal(byte[] out, int outOffset); +} +``` + +GCM模式和CBC、CTR、HMAC不同之处还在于可选的IV长度和MAC长度,其中IV的长度必须在`MIN_IV_SIZE`和`MAX_IV_SIZE`之间,长度为`DEFAULT_IV_SIZE`有最佳的计算效率。MAC的长度也是可选的,通过`init`方法中的`taglen`设定,其长度不应低于8字节,不应长于`BLOCK_SIZE = 16`字节。 + +下面例子展示SM4-GCM加密和解密的过程。 + +```java +import org.gmssl.Sm4Gcm; +import org.gmssl.Random; + +public class Sm4GcmExample { + + public static void main(String[] args) { + + Random rng = new Random(); + byte[] key = rng.randBytes(Sm4Gcm.KEY_SIZE); + byte[] iv = rng.randBytes(Sm4Gcm.DEFAULT_IV_SIZE); + byte[] aad = "Hello:".getBytes(); + int taglen = Sm4Gcm.MAX_TAG_SIZE; + byte[] ciphertext = new byte[64]; + byte[] plaintext = new byte[64]; + int cipherlen; + int plainlen; + boolean encrypt = true; + boolean decrypt = false; + + Sm4Gcm sm4gcm = new Sm4Gcm(); + + sm4gcm.init(key, iv, aad, taglen, encrypt); + cipherlen = sm4gcm.update("abc".getBytes(), 0, 3, ciphertext, 0); + cipherlen += sm4gcm.doFinal(ciphertext, cipherlen); + + sm4gcm.init(key, iv, aad, taglen, decrypt); + plainlen = sm4gcm.update(ciphertext, 0, cipherlen, plaintext, 0); + plainlen += sm4gcm.doFinal(plaintext, plainlen); + } +} +``` + +通过上面的例子可以看出,SM4-GCM加密模式中可以通过`init`指定了一个不需要加密的字段`aad`,注意`aad`是不会在`update`中输出的。由于GCM模式输出额外的完整性标签,因此`update`和`doFinal`输出的总密文长度会比总的输入明文长度多`taglen`个字节。 + +### Zuc序列密码 + +祖冲之密码算法(ZU Cipher, ZUC)是一种序列密码,密钥和IV长度均为16字节。作为序列密码ZUC可以加密可变长度的输入数据,并且输出的密文数据长度和输入数据等长,因此适合不允许密文膨胀的应用场景。在国密算法体系中,ZUC算法的设计晚于SM4,在32位通用处理器上通常比SM4-CBC明显要快。 + +在安全性方面,不建议在一组密钥和IV的情况下用ZUC算法加密大量的数据(比如GB级或TB级),避免序列密码超长输出时安全性降低。另外ZUC算法本身并不支持数据的完整性保护,因此在采用ZUC算法加密应用数据时,应考虑配合HMAC-SM3提供完整性保护。ZUC的标准中还包括针对移动通信底层数据报文加密的128-EEA3方案和用于消息完整性保护的128-EIA3算法,目前GmSSL-Java中不支持这两个算法。 + +`Zuc`类实现了ZUC加密、解密功能。 + +```java +public class Zuc { + public final static int KEY_SIZE = 16; + public final static int IV_SIZE = 16; + public final static int BLOCK_SIZE = 4; + + public Zuc(); + public void init(byte[] key, byte[] iv); + public int update(byte[] in, int inOffset, int inlen, byte[] out, int outOffset); + public int doFinal(byte[] out, int outOffset); +} +``` + +`Zuc`类的接口说明如下: + +- 序列密码通过生成密钥序列和输入数据进行异或操作的方式来加密或解密,因此序列密码的加密和解密的过程一致,因此`Zuc`的`init`方法中不需要额外的参数表明加密还是解密。 +- 由于CTR模式实际上是以分组密码实现了序列密码的能力,因此可以发现`Zuc`和`Sm4Ctr`的接口是完全一致的。 +- ZUC算法内部实现是以32比特字(4字节)为单位进行处理,因此`Zuc`实现加解密过程中也有内部的状态缓冲区,因此`update`的输出长度可能和输入长度不一致,调用方应该保证输出缓冲区长度比输入长度长`BLOCK_SIZE`个字节。注意,`BLOCK_SIZE`的实际值在未来也有可能会变化。 + +下面的例子展示了`Zuc`的加密和解密过程。 + +```java +import org.gmssl.Zuc; +import org.gmssl.Random; + +public class ZucExample { + + public static void main(String[] args) { + + Random rng = new Random(); + byte[] key = rng.randBytes(Zuc.KEY_SIZE); + byte[] iv = rng.randBytes(Zuc.IV_SIZE); + byte[] ciphertext = new byte[32]; + byte[] plaintext = new byte[32]; + int cipherlen; + int plainlen; + + Zuc zuc = new Zuc(); + + zuc.init(key, iv); + cipherlen = zuc.update("abc".getBytes(), 0, 3, ciphertext, 0); + cipherlen += zuc.doFinal(ciphertext, cipherlen); + + zuc.init(key, iv); + plainlen = zuc.update(ciphertext, 0, cipherlen, plaintext, 0); + plainlen += zuc.doFinal(plaintext, plainlen); + } +} +``` + +### SM2 + +SM2是国密标准中的椭圆曲线公钥密码,包含数字签名算法和公钥加密算法。SM2相关的功能由类`Sm2Key`和`Sm2Signature`实现,其中`Sm2Key`实现了SM2密钥对的生成、基础的加密和签名方案,`Sm2Signature`类实现了对任意长度消息签名的签名方案。 + +```java +public class Sm2Key { + public final static int MAX_PLAINTEXT_SIZE; + public final static String DEFAULT_ID; + + public Sm2Key(); + public void generateKey(); + + public void importPrivateKeyInfoDer(byte[] der); + public byte[] exportPrivateKeyInfoDer(); + public void importPublicKeyInfoDer(byte[] der); + public byte[] exportPublicKeyInfoDer(); + + public void importEncryptedPrivateKeyInfoPem(String pass, String file); + public void exportEncryptedPrivateKeyInfoPem(String pass, String file); + public void importPublicKeyInfoPem(String file); + public void exportPublicKeyInfoPem(String file); + + public byte[] computeZ(String id); + public byte[] sign(byte[] dgst); + public boolean verify(byte[] dgst, byte[] signature); + public byte[] encrypt(byte[] plaintext); + public byte[] decrypt(byte[] ciphertext); +} +``` + +需要注意的是,通过构造函数生成的新`Sm2Key`对象是一个空白的对象,可以通过`generateKey`方法生成一个新的密钥对,或者通过导入函数从外部导入密钥。`Sm2Key`一共提供了4个不同的导入方法: + +- `importPrivateKeyInfoDer` 从字节数组中导入SM2私钥,导入密钥后这个`Sm2Key`对象可以执行签名操作和解密操作,也可以执行验证签名和加密。 +- `importEncryptedPrivateKeyInfoPem` 从加密的PEM文件中导入SM2私钥,调用时需要提供PEM文件的路径和解密的口令(Password)。 +- `importPublicKeyInfoDer`从字节数组中导入SM2公钥,因为其中没有私钥,因此这个`Sm2Key`对象不能执行签名和解密操作,只能执行验证签名和加密操作。 +- `importPublicKeyInfoPem`从PEM文件中导入SM2公钥,只需要提供文件的路径,不需要提供口令。 + +上面四个导入函数也都有对应的导出函数。从字节数组中导入导出DER编码的公钥和私钥和JCE兼容,但是因为私钥需要以明文的方式写入到字节数组中,因此安全性比较低。从PEM文件中导入导出公钥私钥和`gmssl`命令行工具的默认密钥格式一致,并且在处理私钥时安全性更高。因此建议在默认情况下,在导入导出私钥时默认采用加密的PEM文件格式。 + +下面的代码片段展示了`Sm2Key`密钥对和公钥的DER导入导出。 + +```java +Sm2Key sm2_key = new Sm2Key(); +sm2_key.generateKey(); + +byte[] privateKeyInfo = sm2_key.exportPrivateKeyInfoDer(); +byte[] publicKeyInfo = sm2_key.exportPublicKeyInfoDer(); + +Sm2Key priKey = new Sm2Key(); +priKey.importPrivateKeyInfoDer(privateKeyInfo); + +Sm2Key pubKey = new Sm2Key(); +pubKey.importPublicKeyInfoDer(publicKeyInfo); +``` + +下面的代码片段展示了`Sm2Key`导出为加密的PEM私钥文件: + +```java +priKey.exportEncryptedPrivateKeyInfoPem("Password", "sm2.pem"); +priKey.importEncryptedPrivateKeyInfoPem("Password", "sm2.pem"); +``` + +用文本编辑器打开`sm2.pem`文件可以看到如下内容: + +``` +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBBjBhBgkqhkiG9w0BBQ0wVDA0BgkqhkiG9w0BBQwwJwQQxShg35gP7+BVnsLo +... +-----END ENCRYPTED PRIVATE KEY----- +``` + +下面的代码片段展示了`Sm2Key`导出为PEM公钥文件: + +```java +pubKey.exportPublicKeyInfoPem("sm2pub.pem"); +pubKey.importPublicKeyInfoPem("sm2pub.pem"); +``` + +用文本编辑器打开`sm2pub.pem`文件可以看到如下内容: + +``` +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEQ05FKjcbwu2LwLHp2bvacYUBUopR +... +-----END PUBLIC KEY----- +``` + +由于公钥文件是不加密的,因此这个公钥可以被支持SM2的第三方工具、库打开和访问。 + +`Sm2Key`类除了`generateKey`方法之外,提供了`computeZ`、`sign`、`verify`、`encrypt`、`decrypt`这几个密码计算相关的方法。 + +其中`computeZ`是由公钥和用户的字符串ID值计算出一个称为"Z值"的哈希值,用于对消息的签名。由于`Sm2Signature`类中提供了SM2消息签名的完整功能,因此这个`computeZ`方法只是用于实验验证。 + +```java +byte[] z = pubKey.computeZ(Sm2Key.DEFAULT_ID); +``` + +类`Sm2Key`的`sign`和`verify`方法实现了SM2签名的底层功能,这两个方法不支持对数据或消息的签名,只能实现对SM3哈希值的签名和验证。应用需要保证调用时提供的`dgst`参数的字节序列长度为32。 + +```java +Random rng = new Random(); +byte[] dgst = rng.randBytes(Sm3.DIGEST_SIZE); + +byte[] sig = priKey.sign(dgst); +boolean verify_ret = pubKey.verify(dgst, sig); +System.out.println("Verify result = " + verify_ret); +``` + +类`Sm2Key`的`encrypt`和`decrypt`方法实现了SM2加密和解密功能。注意,虽然SM2标准中没有限制加密消息的长度,但是公钥加密应该主要用于加密较短的对称密钥、主密钥等密钥数据,因此GmSSL库中限制了SM2加密消息的最大长度(`MAX_PLAINTEXT_SIZE = 255`字节)。如果需要加密应用层的消息,应该首先生成对称密钥,用SM4-GCM加密消息,再用SM2加密对称密钥。 + +```java +byte[] ciphertext = pubKey.encrypt("abc".getBytes()); +byte[] plaintext = priKey.decrypt(ciphertext); +``` + +类`Sm2Signature`提供了对任意长消息的签名、验签功能。 + +```java +public class Sm2Signature { + public final static String DEFAULT_ID; + + public Sm2Signature(Sm2Key key, String id, boolean do_sign); + public void reset(Sm2Key key, String id, boolean do_sign); + public void update(byte[] data, int offset, int len); + public void update(byte[] data); + public byte[] sign(); + public boolean verify(byte[] signature); +} +``` + +在生成`Sm2Signature`对象时,不仅需要提供`Sm2Key`,还需要提供签名方的字符串ID,以满足SM2签名的标准。如果提供的`Sm2Key`来自于导入的公钥,那么这个`Sm2Signature`对象只能进行签名验证操作,即在构造时`do_sign = false`,并且只能调用`verify`方法,不能调用`sign`方法。 + +```java +Sm2Signature sign = new Sm2Signature(priKey, Sm2Key.DEFAULT_ID, true); +sign.update("abc".getBytes()); +sig = sign.sign(); + +Sm2Signature verify = new Sm2Signature(pubKey, Sm2Key.DEFAULT_ID, false); +verify.update("abc".getBytes()); +verify_ret = verify.verify(sig); +System.out.println("Verify result = " + verify_ret); +``` + +不管是`Sm2Key`的`sign`还是`Sm2Signature`的`sign`方法输出的都是DER编码的签名值。这个签名值的第一个字节总是`0x30`,并且长度是可变的,常见的长度包括70字节、71字节、72字节,也可能短于70字节。一些SM2的实现不能输出DER编码的签名,只能输出固定64字节长度的签名值。可以通过签名值的长度以及首字节的值来判断SM2签名值的格式。 + +### SM2数字证书 + +类`Sm2Certificate`实现了SM2证书的导入、导出、解析和验证等功能。这里的"SM2证书"含义和"RSA证书"类似,是指证书中的公钥字段是SM2公钥,证书中签名字段是SM2签名,证书格式就是标准的X.509v3证书。由于GmSSL库目前只支持SM2签名算法,不支持ECDSA、RSA、DSA等签名算法,因此`Sm2Certificate`类无法支持其他公钥类型的证书。 + +类`Sm2Certificate`只支持SM2证书的解析和验证等功能,不支持SM2证书的签发和生成。如果应用需要实现证书申请(即生成CSR文件)或者自建CA签发证书功能,可以通过GmSSL库或者`gmssl`命令行工具实现。 + +```java +public class Sm2Certificate { + public Sm2Certificate(); + public byte[] getBytes(); + public void importPem(String file); + public void exportPem(String file); + public byte[] getSerialNumber(); + public String[] getIssuer(); + public String[] getSubject(); + public java.util.Date getNotBefore(); + public java.util.Date getNotAfter(); + public Sm2Key getSubjectPublicKey(); + public boolean verifyByCaCertificate(Sm2Certificate caCert, String sm2Id); +} +``` + +新生成的`Sm2Certificate`对象中的证书数据为空,必须通过导入证书数据才能实现真正的初始化。证书最常用的格式是PEM格式,这也是`Sm2Certificate`类默认支持的证书格式。PEM文件内容总是以`-----BEGIN CERTIFICATE-----`一行作为开头,以`-----END CERTIFICATE-----`一行作为结尾。 + +``` +-----BEGIN CERTIFICATE----- +MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG +... +-----END CERTIFICATE----- +``` + +通过`gmssl certparse`命令可以打印证书内容: + +```bash +$ gmssl certparse -in ROOTCA.pem +Certificate + tbsCertificate + version: v3 (2) + serialNumber: 69E2FEC0170AC67B + signature + algorithm: sm2sign-with-sm3 + issuer + countryName: CN + organizationName: NRCAC + commonName: ROOTCA + validity + notBefore: Sat Jul 14 11:11:59 2012 + notAfter: Mon Jul 7 11:11:59 2042 + subject + ... + subjectPulbicKeyInfo + algorithm: ecPublicKey + namedCurve: sm2p256v1 + extensions + ... +``` + +证书中持有者信息包含如下字段: + +- 证书格式的版本号 version +- 证书的序列号 serialNumber +- 证书的签名算法 signature +- 证书签发机构的名字 issuer(由countryName、organizationName、commonName等组成) +- 证书的有效期 validity(notBefore和notAfter) +- 证书持有者的名字 subject +- 证书持有者的公钥信息 subjectPulbicKeyInfo +- 多个扩展字段(BasicConstraints、KeyUsage等) + +证书验证时需要检查有效期(`getNotBefore` / `getNotAfter`)、签发机构(`getIssuer` + `verifyByCaCertificate`),并建议通过CRL或OCSP检查证书是否被作废。在完成所有证书检查之后,应用可以从证书中读取持有者身份信息(`getSubject`)和公钥(`getSubjectPublicKey`)。 + +### SM9 基于身份的密码 + +SM9算法属于基于身份的密码。基于身份的密码是一种"高级"的公钥密码方案,在具备常规公钥密码加密、签名等密码功能的同时,基于身份的密码体系不需要CA中心和数字证书体系。SM9方案的基本原理是,可以由用户的唯一身份ID(如对方的电子邮件地址、域名或ID号等),从系统的全局主密钥中导出对应的私钥或公钥,导出密钥的正确性是由算法保证的,因此在进行加密、验签的时候,只需要获得解密方或签名方的ID即可,不再需要对方的数字证书了。因此如果应用面对的是一个内部的封闭环境,所有参与用户都是系统内用户,那么采用SM9方案而不是SM2证书和CA的方案,可以简化系统的开发、设计和使用,并降低后续CA体系的维护成本。 + +对应数字证书体系中的CA中心,SM9体系中也存在一个权威中心,用于生成全局的主密钥(MasterKey),并且为系统中的每个用户生成、分配用户的私钥。SM9算法体系中包括SM9加密、SM9签名和SM9密钥交换协议,GmSSL-Java中实现了SM9加密和SM9签名,没有实现SM9密钥交换。其中SM9加密功能包含`Sm9EncMasterKey`类和`Sm9EncKey`类,分别实现了SM9加密主密钥和SM9加密用户密钥,SM9签名功能包含`Sm9SignMasterKey`类、`Sm9SignKey`类和`Sm9Signature`类,分别实现了SM9签名主密钥、SM9签名用户密钥和SM9签名功能。 + +和SM2算法中相同的密钥对既可以用于加密又可以用于签名不同,SM9中加密、签名的主密钥、用户密钥的组成是完全不同的,因此GmSSL中分别实现为不同的类。 + +SM9加密主密钥由类`Sm9EncMasterKey`实现。 + +```java +public class Sm9EncMasterKey { + + public final static int MAX_PLAINTEXT_SIZE; + + public Sm9EncMasterKey(); + public void generateMasterKey(); + public Sm9EncKey extractKey(String id); + public void importEncryptedMasterKeyInfoPem(String pass, String file); + public void exportEncryptedMasterKeyInfoPem(String pass, String file); + public void importPublicMasterKeyPem(String file); + public void exportPublicMasterKeyPem(String file); + public byte[] encrypt(byte[] plaintext, String id); +} +``` + +`Sm9EncMasterKey`的接口包括: + +- 主密钥的生成`generateMasterKey` +- 主密钥的导入`importEncryptedMasterKeyInfoPem`和导出`exportEncryptedMasterKeyInfoPem` +- 主公钥(主密钥的公钥部分)的导入`importPublicMasterKeyPem`和导出`exportPublicMasterKeyPem` +- 用户私钥的生成`extractKey` +- 数据加密`encrypt` + +这个类的用户包括两个不同角色,权威中心和用户。其中权威中心调用主密钥的生成、主密钥的导入导出、主公钥导出和用户私钥生成这几个接口,而用户调用主公钥导入和加密这两个接口。 + +类`Sm9EncKey`对象是由`Sm9EncMasterKey`的`extractKey`方法生成的。 + +```java +public class Sm9EncKey { + public Sm9EncKey(String id); + public String getId(); + public void exportEncryptedPrivateKeyInfoPem(String pass, String file); + public void importEncryptedPrivateKeyInfoPem(String pass, String file); + public byte[] decrypt(byte[] ciphertext); +} +``` + +类`Sm9EncKey`提供了解密、导入导出等接口,由于在SM9中用户密钥总是包含私钥的,因此导出的是经过口令加密的密钥。 + +下面的例子中给出了SM9加密方案的主密钥生成、用户密钥导出、加密、解密的整个过程。 + +```java +import org.gmssl.Sm9EncMasterKey; +import org.gmssl.Sm9EncKey; + +public class Sm9EncExample { + + public static void main(String[] args) { + + Sm9EncMasterKey enc_master_key = new Sm9EncMasterKey(); + enc_master_key.generateMasterKey(); + enc_master_key.exportPublicMasterKeyPem("sm9enc.mpk"); + + Sm9EncMasterKey enc_master_pub_key = new Sm9EncMasterKey(); + enc_master_pub_key.importPublicMasterKeyPem("sm9enc.mpk"); + + byte[] ciphertext = enc_master_pub_key.encrypt("abc".getBytes(), "Bob"); + + Sm9EncKey enc_key = enc_master_key.extractKey("Bob"); + byte[] plaintext = enc_key.decrypt(ciphertext); + } +} +``` + +SM9签名功能由`Sm9SignMasterKey`、`Sm9SignKey`和`Sm9Signature`几个类实现,前两者在接口上和SM9加密非常类似,只是这两个类不直接提供签名、验签的功能。 + +```java +public class Sm9SignMasterKey { + public Sm9SignMasterKey(); + public void generateMasterKey(); + public Sm9SignKey extractKey(String id); + public void importEncryptedMasterKeyInfoPem(String pass, String file); + public void exportEncryptedMasterKeyInfoPem(String pass, String file); + public void importPublicMasterKeyPem(String file); + public void exportPublicMasterKeyPem(String file); +} +``` + +```java +public class Sm9SignKey { + public Sm9SignKey(String id); + public String getId(); + public void exportEncryptedPrivateKeyInfoPem(String pass, String file); + public void importEncryptedPrivateKeyInfoPem(String pass, String file); +} +``` + +类`Sm9Signature`实现对数据的SM9签名和验证功能。SM9签名时需要提供`Sm9SignKey`类型的签名方私钥(其中包含签名者的ID),在验证签名时需要提供`Sm9SignMasterKey`格式的系统主公钥和签名方的ID。`Sm9Signature`和`Sm2Signature`提供类似的`update`、`sign`、`verify`接口,只是在验证的时候需要提供的不是公钥,而是系统的主公钥和签名方的ID。 + +```java +public class Sm9Signature { + public Sm9Signature(boolean do_sign); + public void reset(boolean do_sign); + public void update(byte[] data, int offset, int len); + public void update(byte[] data); + public byte[] sign(Sm9SignKey signKey); + public boolean verify(byte[] signature, Sm9SignMasterKey masterPublicKey, String id); +} +``` + +下面的例子展示了SM9签名的主密钥生成、用户私钥生成、签名、验证的过程。 + +```java +import org.gmssl.Sm9SignMasterKey; +import org.gmssl.Sm9SignKey; +import org.gmssl.Sm9Signature; + +public class Sm9SignExample { + + public static void main(String[] args) { + + Sm9SignMasterKey sign_master_key = new Sm9SignMasterKey(); + sign_master_key.generateMasterKey(); + + Sm9SignKey sign_key = sign_master_key.extractKey("Alice"); + + Sm9Signature sign = new Sm9Signature(true); + sign.update("abc".getBytes()); + byte[] sig = sign.sign(sign_key); + + sign_master_key.exportPublicMasterKeyPem("sm9sign.mpk"); + Sm9SignMasterKey sign_master_pub_key = new Sm9SignMasterKey(); + sign_master_pub_key.importPublicMasterKeyPem("sm9sign.mpk"); + + Sm9Signature verify = new Sm9Signature(false); + verify.update("abc".getBytes()); + boolean verify_ret = verify.verify(sig, sign_master_pub_key, "Alice"); + System.out.println("Verify result = " + verify_ret); + } +} +``` + +### GmSSLException + +GmSSL-Java在遇到错误和异常时,会抛出`GmSSLException`异常。 diff --git a/build.sh b/build.sh deleted file mode 100755 index c9fa8e9..0000000 --- a/build.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -mkdir build -cd build -cmake .. -make -cd .. -javac org/gmssl/GmSSLJNI.java - -cat << EOF > ROOTCA.pem ------BEGIN CERTIFICATE----- -MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG -EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw -MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO -UkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE -MPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT -V7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti -W/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ -MxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b -53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI -pDoiVhsLwg== ------END CERTIFICATE----- -EOF - -java -Djava.library.path=build org.gmssl.GmSSLJNI diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 0000000..89b6f5c --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,28 @@ + +all: + javac -cp ../build/GmSSLJNI.jar Sm3Example.java + java -Djava.library.path=../build -cp ../build/GmSSLJNI.jar:. Sm3Example + javac -cp ../build/GmSSLJNI.jar Sm3HmacExample.java + java -Djava.library.path=../build -cp ../build/GmSSLJNI.jar:. Sm3HmacExample + javac -cp ../build/GmSSLJNI.jar Sm3Pbkdf2Example.java + java -Djava.library.path=../build -cp ../build/GmSSLJNI.jar:. Sm3Pbkdf2Example + javac -cp ../build/GmSSLJNI.jar Sm4Example.java + java -Djava.library.path=../build -cp ../build/GmSSLJNI.jar:. Sm4Example + javac -cp ../build/GmSSLJNI.jar Sm4EcbExample.java + java -Djava.library.path=../build -cp ../build/GmSSLJNI.jar:. Sm4EcbExample + javac -cp ../build/GmSSLJNI.jar Sm4CbcExample.java + java -Djava.library.path=../build -cp ../build/GmSSLJNI.jar:. Sm4CbcExample + javac -cp ../build/GmSSLJNI.jar Sm4CtrExample.java + java -Djava.library.path=../build -cp ../build/GmSSLJNI.jar:. Sm4CtrExample + javac -cp ../build/GmSSLJNI.jar Sm4GcmExample.java + java -Djava.library.path=../build -cp ../build/GmSSLJNI.jar:. Sm4GcmExample + javac -cp ../build/GmSSLJNI.jar ZucExample.java + java -Djava.library.path=../build -cp ../build/GmSSLJNI.jar:. ZucExample + javac -cp ../build/GmSSLJNI.jar Sm2Example.java + java -Djava.library.path=../build -cp ../build/GmSSLJNI.jar:. Sm2Example + javac -cp ../build/GmSSLJNI.jar Sm9Example.java + java -Djava.library.path=../build -cp ../build/GmSSLJNI.jar:. Sm9Example + +clean: + rm -fr *.class + diff --git a/examples/Sm2Example.java b/examples/Sm2Example.java new file mode 100644 index 0000000..47f9c97 --- /dev/null +++ b/examples/Sm2Example.java @@ -0,0 +1,86 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import org.gmssl.Sm2Key; +import org.gmssl.Sm2Signature; +import org.gmssl.Sm3; +import org.gmssl.Random; + +public class Sm2Example { + + public static void main(String[] args) { + int i; + + Sm2Key sm2_key = new Sm2Key(); + + sm2_key.generateKey(); + + byte[] privateKeyInfo = sm2_key.exportPrivateKeyInfoDer(); + System.out.printf("PrivateKeyInfo: "); + for (i = 0; i < privateKeyInfo.length; i++) { + System.out.printf("%02x", privateKeyInfo[i]); + } + System.out.print("\n"); + + byte[] publicKeyInfo = sm2_key.exportPublicKeyInfoDer(); + System.out.printf("PrivateKeyInfo: "); + for (i = 0; i < publicKeyInfo.length; i++) { + System.out.printf("%02x", publicKeyInfo[i]); + } + System.out.print("\n"); + + + Sm2Key priKey = new Sm2Key(); + priKey.importPrivateKeyInfoDer(privateKeyInfo); + + Sm2Key pubKey = new Sm2Key(); + pubKey.importPublicKeyInfoDer(publicKeyInfo); + + priKey.exportEncryptedPrivateKeyInfoPem("Password", "sm2.pem"); + pubKey.exportPublicKeyInfoPem("sm2pub.pem"); + + priKey.importEncryptedPrivateKeyInfoPem("Password", "sm2.pem"); + pubKey.importPublicKeyInfoPem("sm2pub.pem"); + + + byte[] z = pubKey.computeZ(Sm2Key.DEFAULT_ID); + + System.out.printf("Z: "); + for (i = 0; i < z.length; i++) { + System.out.printf("%02x", z[i]); + } + System.out.print("\n"); + + + Random rng = new Random(); + byte[] dgst = rng.randBytes(Sm3.DIGEST_SIZE); + byte[] sig = priKey.sign(dgst); + boolean verify_ret = pubKey.verify(dgst, sig); + System.out.println("Verify result = " + verify_ret); + + byte[] ciphertext = pubKey.encrypt("abc".getBytes()); + byte[] plaintext = priKey.decrypt(ciphertext); + System.out.printf("Plaintext : "); + for (i = 0; i < plaintext.length; i++) { + System.out.printf("%02x", plaintext[i]); + } + System.out.print("\n"); + + Sm2Signature sign = new Sm2Signature(priKey, Sm2Key.DEFAULT_ID, true); + sign.update("abc".getBytes()); + sig = sign.sign(); + + Sm2Signature verify = new Sm2Signature(pubKey, Sm2Key.DEFAULT_ID, false); + verify.update("abc".getBytes()); + verify_ret = verify.verify(sig); + System.out.println("Verify result = " + verify_ret); + + } +} + diff --git a/examples/Sm3Example.java b/examples/Sm3Example.java new file mode 100644 index 0000000..4ba644f --- /dev/null +++ b/examples/Sm3Example.java @@ -0,0 +1,28 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import org.gmssl.Sm3; + +public class Sm3Example { + + public static void main(String[] args) { + + Sm3 sm3 = new Sm3(); + sm3.update("abc".getBytes()); + byte[] dgst = sm3.digest(); + + int i; + System.out.printf("sm3('abc'): "); + for (i = 0; i < dgst.length; i++) { + System.out.printf("%02x", dgst[i]); + } + System.out.print("\n"); + } +} + diff --git a/examples/Sm3HmacExample.java b/examples/Sm3HmacExample.java new file mode 100644 index 0000000..495c94d --- /dev/null +++ b/examples/Sm3HmacExample.java @@ -0,0 +1,32 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import org.gmssl.Sm3Hmac; +import org.gmssl.Random; + +public class Sm3HmacExample { + + public static void main(String[] args) { + + Random rng = new Random(); + byte[] key = rng.randBytes(Sm3Hmac.MAC_SIZE); + + Sm3Hmac sm3hmac = new Sm3Hmac(key); + sm3hmac.update("abc".getBytes(), 0, 3); + byte[] mac = sm3hmac.generateMac(); + + int i; + System.out.printf("sm3hmac('abc'): "); + for (i = 0; i < mac.length; i++) { + System.out.printf("%02x", mac[i]); + } + System.out.print("\n"); + } +} + diff --git a/examples/Sm3Pbkdf2Example.java b/examples/Sm3Pbkdf2Example.java new file mode 100644 index 0000000..79232cf --- /dev/null +++ b/examples/Sm3Pbkdf2Example.java @@ -0,0 +1,33 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import org.gmssl.Sm3Pbkdf2; +import org.gmssl.Random; + +public class Sm3Pbkdf2Example { + + public static void main(String[] args) { + + Sm3Pbkdf2 kdf = new Sm3Pbkdf2(); + + Random rng = new Random(); + byte[] salt = rng.randBytes(Sm3Pbkdf2.DEFAULT_SALT_SIZE); + + String pass = "P@ssw0rd"; + byte[] key = kdf.deriveKey(pass, salt, Sm3Pbkdf2.MIN_ITER * 2, 16); + + int i; + System.out.printf("pbkdf2(pass, salt, iter, keylen): "); + for (i = 0; i < key.length; i++) { + System.out.printf("%02x", key[i]); + } + System.out.print("\n"); + } +} + diff --git a/examples/Sm4CbcExample.java b/examples/Sm4CbcExample.java new file mode 100644 index 0000000..e2075d4 --- /dev/null +++ b/examples/Sm4CbcExample.java @@ -0,0 +1,73 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import org.gmssl.Sm4Cbc; +import org.gmssl.Random; + +public class Sm4CbcExample { + + public static void main(String[] args) { + + Random rng = new Random(); + byte[] key = rng.randBytes(Sm4Cbc.KEY_SIZE); + byte[] iv = rng.randBytes(Sm4Cbc.IV_SIZE); + + // Encrypted plaintext is "110101200106032443" + byte[] plaintext = "ID:110101200106032443".getBytes(); + int plaintextOffset = "ID:".length(); + int plaintextLen = plaintext.length - plaintextOffset; + + boolean encrypt = true; + boolean decrypt = false; + int i; + + System.out.println("SM4-CBC Example"); + + Sm4Cbc sm4cbc = new Sm4Cbc(); + + // Encrypt + + byte[] ciphertext = new byte[plaintextLen + Sm4Cbc.BLOCK_SIZE]; // Prepare large enough ciphertext buffer + int ciphertextOffset = 0; + int ciphertextLen; + + sm4cbc.init(key, iv, encrypt); + + ciphertextLen = sm4cbc.update(plaintext, plaintextOffset, plaintextLen, ciphertext, ciphertextOffset); + ciphertextOffset += ciphertextLen; + + ciphertextLen += sm4cbc.doFinal(ciphertext, ciphertextOffset); + + System.out.print("ciphertext : "); + for (i = 0; i < ciphertextLen; i++) { + System.out.printf("%02x", ciphertext[i]); + } + System.out.print("\n"); + + // Decrypt + + sm4cbc.init(key, iv, decrypt); + + byte[] decrypted = new byte[ciphertextLen + Sm4Cbc.BLOCK_SIZE]; // prepare large enough plaintext buffer + int decryptedOffset = 0; + int decryptedLen; + + ciphertextOffset = 0; + decryptedLen = sm4cbc.update(ciphertext, ciphertextOffset, ciphertextLen, decrypted, decryptedOffset); + decryptedOffset += decryptedLen; + + decryptedLen += sm4cbc.doFinal(decrypted, decryptedOffset); + + System.out.print("decrypted : "); + for (i = 0; i < decryptedLen; i++) { + System.out.printf("%02x", decrypted[i]); + } + System.out.print("\n"); + } +} diff --git a/examples/Sm4CtrExample.java b/examples/Sm4CtrExample.java new file mode 100644 index 0000000..4afdfff --- /dev/null +++ b/examples/Sm4CtrExample.java @@ -0,0 +1,50 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import org.gmssl.Sm4Ctr; +import org.gmssl.Random; + +public class Sm4CtrExample { + + public static void main(String[] args) { + + Random rng = new Random(); + byte[] key = rng.randBytes(Sm4Ctr.KEY_SIZE); + byte[] iv = rng.randBytes(Sm4Ctr.IV_SIZE); + byte[] ciphertext = new byte[64]; + byte[] plaintext = new byte[64]; + int cipherlen; + int plainlen; + int i; + + Sm4Ctr sm4ctr = new Sm4Ctr(); + + sm4ctr.init(key, iv); + cipherlen = sm4ctr.update("abc".getBytes(), 0, "abc".length(), ciphertext, 0); + cipherlen += sm4ctr.update("12345678".getBytes(), 0, "12345678".length(), ciphertext, cipherlen); + cipherlen += sm4ctr.update("xxyyyzzz".getBytes(), 0, "xxyyyzzz".length(), ciphertext, cipherlen); + cipherlen += sm4ctr.doFinal(ciphertext, cipherlen); + + System.out.print("ciphertext : "); + for (i = 0; i < cipherlen; i++) { + System.out.printf("%02x", ciphertext[i]); + } + System.out.print("\n"); + + sm4ctr.init(key, iv); + plainlen = sm4ctr.update(ciphertext, 0, cipherlen, plaintext, 0); + plainlen += sm4ctr.doFinal(plaintext, plainlen); + + System.out.print("plaintext : "); + for (i = 0; i < plainlen; i++) { + System.out.printf("%02x", plaintext[i]); + } + System.out.print("\n"); + } +} diff --git a/examples/Sm4EcbExample.java b/examples/Sm4EcbExample.java new file mode 100644 index 0000000..c112f8a --- /dev/null +++ b/examples/Sm4EcbExample.java @@ -0,0 +1,74 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import org.gmssl.Sm4; +import org.gmssl.Random; +import java.util.Arrays; + +public class Sm4EcbExample { + + public static void main(String[] args) { + + Random rng = new Random(); + byte[] key = rng.randBytes(Sm4.KEY_SIZE); + + int nblocks = 4; + byte[] plaintext = rng.randBytes(Sm4.BLOCK_SIZE * nblocks); + byte[] ciphertext = new byte[Sm4.BLOCK_SIZE * nblocks]; + byte[] decrypted = new byte[Sm4.BLOCK_SIZE * nblocks]; + int plaintextOffset, ciphertextOffset, decryptedOffset; + int i; + + System.out.println("SM4-ECB Example"); + + System.out.print("Plaintext : "); + for (i = 0; i < plaintext.length; i++) { + System.out.printf("%02x", plaintext[i]); + } + System.out.print("\n"); + + // Encrypt + + Sm4 sm4enc = new Sm4(key, true); + + plaintextOffset = 0; + ciphertextOffset = 0; + for (i = 0; i < nblocks; i++) { + sm4enc.encrypt(plaintext, plaintextOffset, ciphertext, ciphertextOffset); + plaintextOffset += Sm4.BLOCK_SIZE; + ciphertextOffset += Sm4.BLOCK_SIZE; + } + + System.out.print("Ciphertext : "); + for (i = 0; i < ciphertext.length; i++) { + System.out.printf("%02x", ciphertext[i]); + } + System.out.print("\n"); + + // Decrypt + + Sm4 sm4dec = new Sm4(key, false); + + ciphertextOffset = 0; + decryptedOffset = 0; + for (i = 0; i < nblocks; i++) { + sm4dec.encrypt(ciphertext, ciphertextOffset, decrypted, decryptedOffset); + ciphertextOffset += Sm4.BLOCK_SIZE; + decryptedOffset += Sm4.BLOCK_SIZE; + } + + System.out.print("Decrypted : "); + for (i = 0; i < decrypted.length; i++) { + System.out.printf("%02x", decrypted[i]); + } + System.out.print("\n"); + + System.out.println("Decryption success : " + Arrays.equals(plaintext, decrypted)); + } +} diff --git a/examples/Sm4Example.java b/examples/Sm4Example.java new file mode 100644 index 0000000..5bcec97 --- /dev/null +++ b/examples/Sm4Example.java @@ -0,0 +1,53 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import org.gmssl.Sm4; +import org.gmssl.Random; +import java.util.Arrays; + +public class Sm4Example { + + public static void main(String[] args) { + + Random rng = new Random(); + byte[] key = rng.randBytes(Sm4.KEY_SIZE); + byte[] plaintext1 = rng.randBytes(Sm4.BLOCK_SIZE); + byte[] ciphertext = new byte[Sm4.BLOCK_SIZE]; + byte[] plaintext2 = new byte[Sm4.BLOCK_SIZE]; + + Sm4 sm4enc = new Sm4(key, true); + sm4enc.encrypt(plaintext1, 0, ciphertext, 0); + + Sm4 sm4dec = new Sm4(key, false); + sm4dec.encrypt(ciphertext, 0, plaintext2, 0); + + System.out.println("Sm4 Example"); + + int i; + System.out.print("Plaintext : "); + for (i = 0; i < plaintext1.length; i++) { + System.out.printf("%02x", plaintext1[i]); + } + System.out.print("\n"); + + System.out.print("Ciphertext : "); + for (i = 0; i < ciphertext.length; i++) { + System.out.printf("%02x", ciphertext[i]); + } + System.out.print("\n"); + + System.out.print("Plaintext : "); + for (i = 0; i < plaintext2.length; i++) { + System.out.printf("%02x", plaintext2[i]); + } + System.out.print("\n"); + + System.out.println("Decryption success : " + Arrays.equals(plaintext1, plaintext2)); + } +} diff --git a/examples/Sm4GcmExample.java b/examples/Sm4GcmExample.java new file mode 100644 index 0000000..5184d97 --- /dev/null +++ b/examples/Sm4GcmExample.java @@ -0,0 +1,52 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import org.gmssl.Sm4Gcm; +import org.gmssl.Random; + +public class Sm4GcmExample { + + public static void main(String[] args) { + + Random rng = new Random(); + byte[] key = rng.randBytes(Sm4Gcm.KEY_SIZE); + byte[] iv = rng.randBytes(Sm4Gcm.DEFAULT_IV_SIZE); + byte[] aad = "Hello: ".getBytes(); + int taglen = Sm4Gcm.MAX_TAG_SIZE; + byte[] ciphertext = new byte[64]; + byte[] plaintext = new byte[64]; + int cipherlen; + int plainlen; + boolean encrypt = true; + boolean decrypt = false; + int i; + + Sm4Gcm sm4gcm = new Sm4Gcm(); + + sm4gcm.init(key, iv, aad, taglen, encrypt); + cipherlen = sm4gcm.update("abc".getBytes(), 0, 3, ciphertext, 0); + cipherlen += sm4gcm.doFinal(ciphertext, cipherlen); + + System.out.print("ciphertext : "); + for (i = 0; i < cipherlen; i++) { + System.out.printf("%02x", ciphertext[i]); + } + System.out.print("\n"); + + sm4gcm.init(key, iv, aad, taglen, decrypt); + plainlen = sm4gcm.update(ciphertext, 0, cipherlen, plaintext, 0); + plainlen += sm4gcm.doFinal(plaintext, plainlen); + + System.out.print("plaintext : "); + for (i = 0; i < plainlen; i++) { + System.out.printf("%02x", plaintext[i]); + } + System.out.print("\n"); + } +} diff --git a/examples/Sm9Example.java b/examples/Sm9Example.java new file mode 100644 index 0000000..2ce4c05 --- /dev/null +++ b/examples/Sm9Example.java @@ -0,0 +1,60 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import org.gmssl.Sm9EncMasterKey; +import org.gmssl.Sm9EncKey; +import org.gmssl.Sm9SignMasterKey; +import org.gmssl.Sm9SignKey; +import org.gmssl.Sm9Signature; + + +public class Sm9Example { + + public static void main(String[] args) { + + Sm9SignMasterKey sign_master_key = new Sm9SignMasterKey(); + sign_master_key.generateMasterKey(); + + Sm9SignKey sign_key = sign_master_key.extractKey("Alice"); + + Sm9Signature sign = new Sm9Signature(true); + sign.update("abc".getBytes()); + byte[] sig = sign.sign(sign_key); + + sign_master_key.exportPublicMasterKeyPem("sm9sign.mpk"); + Sm9SignMasterKey sign_master_pub_key = new Sm9SignMasterKey(); + sign_master_pub_key.importPublicMasterKeyPem("sm9sign.mpk"); + + Sm9Signature verify = new Sm9Signature(false); + verify.update("abc".getBytes()); + boolean verify_ret = verify.verify(sig, sign_master_pub_key, "Alice"); + System.out.println("Verify result = " + verify_ret); + + Sm9EncMasterKey enc_master_key = new Sm9EncMasterKey(); + enc_master_key.generateMasterKey(); + + enc_master_key.exportPublicMasterKeyPem("sm9enc.mpk"); + Sm9EncMasterKey enc_master_pub_key = new Sm9EncMasterKey(); + enc_master_pub_key.importPublicMasterKeyPem("sm9enc.mpk"); + + byte[] ciphertext = enc_master_pub_key.encrypt("abc".getBytes(), "Bob"); + + Sm9EncKey enc_key = enc_master_key.extractKey("Bob"); + byte[] plaintext = enc_key.decrypt(ciphertext); + int i; + System.out.printf("plaintext: "); + for (i = 0; i < plaintext.length; i++) { + System.out.printf("%02x", plaintext[i]); + } + System.out.print("\n"); + + + } +} + diff --git a/examples/ZucExample.java b/examples/ZucExample.java new file mode 100644 index 0000000..488d2b6 --- /dev/null +++ b/examples/ZucExample.java @@ -0,0 +1,48 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import org.gmssl.Zuc; +import org.gmssl.Random; + +public class ZucExample { + + public static void main(String[] args) { + + Random rng = new Random(); + byte[] key = rng.randBytes(Zuc.KEY_SIZE); + byte[] iv = rng.randBytes(Zuc.IV_SIZE); + byte[] ciphertext = new byte[32]; + byte[] plaintext = new byte[32]; + int cipherlen; + int plainlen; + int i; + + Zuc zuc = new Zuc(); + + zuc.init(key, iv); + cipherlen = zuc.update("abc".getBytes(), 0, 3, ciphertext, 0); + cipherlen += zuc.doFinal(ciphertext, cipherlen); + + System.out.print("ciphertext : "); + for (i = 0; i < cipherlen; i++) { + System.out.printf("%02x", ciphertext[i]); + } + System.out.print("\n"); + + zuc.init(key, iv); + plainlen = zuc.update(ciphertext, 0, cipherlen, plaintext, 0); + plainlen += zuc.doFinal(plaintext, plainlen); + + System.out.print("plaintext : "); + for (i = 0; i < plainlen; i++) { + System.out.printf("%02x", plaintext[i]); + } + System.out.print("\n"); + } +} diff --git a/jni/jni.h b/jni/jni.h deleted file mode 100755 index b56fb7f..0000000 --- a/jni/jni.h +++ /dev/null @@ -1,1961 +0,0 @@ -/* - * @(#)jni.h 1.62 06/02/02 - * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. - * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - */ - -/* - * We used part of Netscape's Java Runtime Interface (JRI) as the starting - * point of our design and implementation. - */ - -/****************************************************************************** - * Java Runtime Interface - * Copyright (c) 1996 Netscape Communications Corporation. All rights reserved. - *****************************************************************************/ - -#ifndef _JAVASOFT_JNI_H_ -#define _JAVASOFT_JNI_H_ - -#include -#include - -/* jni_md.h contains the machine-dependent typedefs for jbyte, jint - and jlong */ - -#include "jni_md.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * JNI Types - */ - -#ifndef JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H - -typedef unsigned char jboolean; -typedef unsigned short jchar; -typedef short jshort; -typedef float jfloat; -typedef double jdouble; - -typedef jint jsize; - -#ifdef __cplusplus - -class _jobject {}; -class _jclass : public _jobject {}; -class _jthrowable : public _jobject {}; -class _jstring : public _jobject {}; -class _jarray : public _jobject {}; -class _jbooleanArray : public _jarray {}; -class _jbyteArray : public _jarray {}; -class _jcharArray : public _jarray {}; -class _jshortArray : public _jarray {}; -class _jintArray : public _jarray {}; -class _jlongArray : public _jarray {}; -class _jfloatArray : public _jarray {}; -class _jdoubleArray : public _jarray {}; -class _jobjectArray : public _jarray {}; - -typedef _jobject *jobject; -typedef _jclass *jclass; -typedef _jthrowable *jthrowable; -typedef _jstring *jstring; -typedef _jarray *jarray; -typedef _jbooleanArray *jbooleanArray; -typedef _jbyteArray *jbyteArray; -typedef _jcharArray *jcharArray; -typedef _jshortArray *jshortArray; -typedef _jintArray *jintArray; -typedef _jlongArray *jlongArray; -typedef _jfloatArray *jfloatArray; -typedef _jdoubleArray *jdoubleArray; -typedef _jobjectArray *jobjectArray; - -#else - -struct _jobject; - -typedef struct _jobject *jobject; -typedef jobject jclass; -typedef jobject jthrowable; -typedef jobject jstring; -typedef jobject jarray; -typedef jarray jbooleanArray; -typedef jarray jbyteArray; -typedef jarray jcharArray; -typedef jarray jshortArray; -typedef jarray jintArray; -typedef jarray jlongArray; -typedef jarray jfloatArray; -typedef jarray jdoubleArray; -typedef jarray jobjectArray; - -#endif - -typedef jobject jweak; - -typedef union jvalue { - jboolean z; - jbyte b; - jchar c; - jshort s; - jint i; - jlong j; - jfloat f; - jdouble d; - jobject l; -} jvalue; - -struct _jfieldID; -typedef struct _jfieldID *jfieldID; - -struct _jmethodID; -typedef struct _jmethodID *jmethodID; - -/* Return values from jobjectRefType */ -typedef enum _jobjectType { - JNIInvalidRefType = 0, - JNILocalRefType = 1, - JNIGlobalRefType = 2, - JNIWeakGlobalRefType = 3 -} jobjectRefType; - - -#endif /* JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H */ - -/* - * jboolean constants - */ - -#define JNI_FALSE 0 -#define JNI_TRUE 1 - -/* - * possible return values for JNI functions. - */ - -#define JNI_OK 0 /* success */ -#define JNI_ERR (-1) /* unknown error */ -#define JNI_EDETACHED (-2) /* thread detached from the VM */ -#define JNI_EVERSION (-3) /* JNI version error */ -#define JNI_ENOMEM (-4) /* not enough memory */ -#define JNI_EEXIST (-5) /* VM already created */ -#define JNI_EINVAL (-6) /* invalid arguments */ - -/* - * used in ReleaseScalarArrayElements - */ - -#define JNI_COMMIT 1 -#define JNI_ABORT 2 - -/* - * used in RegisterNatives to describe native method name, signature, - * and function pointer. - */ - -typedef struct { - char *name; - char *signature; - void *fnPtr; -} JNINativeMethod; - -/* - * JNI Native Method Interface. - */ - -struct JNINativeInterface_; - -struct JNIEnv_; - -#ifdef __cplusplus -typedef JNIEnv_ JNIEnv; -#else -typedef const struct JNINativeInterface_ *JNIEnv; -#endif - -/* - * JNI Invocation Interface. - */ - -struct JNIInvokeInterface_; - -struct JavaVM_; - -#ifdef __cplusplus -typedef JavaVM_ JavaVM; -#else -typedef const struct JNIInvokeInterface_ *JavaVM; -#endif - -struct JNINativeInterface_ { - void *reserved0; - void *reserved1; - void *reserved2; - - void *reserved3; - -#if !TARGET_RT_MAC_CFM && defined(__ppc__) - void* cfm_vectors[225]; -#endif /* !TARGET_RT_MAC_CFM && defined(__ppc__) */ - - jint (JNICALL *GetVersion)(JNIEnv *env); - - jclass (JNICALL *DefineClass) - (JNIEnv *env, const char *name, jobject loader, const jbyte *buf, - jsize len); - jclass (JNICALL *FindClass) - (JNIEnv *env, const char *name); - - jmethodID (JNICALL *FromReflectedMethod) - (JNIEnv *env, jobject method); - jfieldID (JNICALL *FromReflectedField) - (JNIEnv *env, jobject field); - - jobject (JNICALL *ToReflectedMethod) - (JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic); - - jclass (JNICALL *GetSuperclass) - (JNIEnv *env, jclass sub); - jboolean (JNICALL *IsAssignableFrom) - (JNIEnv *env, jclass sub, jclass sup); - - jobject (JNICALL *ToReflectedField) - (JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic); - - jint (JNICALL *Throw) - (JNIEnv *env, jthrowable obj); - jint (JNICALL *ThrowNew) - (JNIEnv *env, jclass clazz, const char *msg); - jthrowable (JNICALL *ExceptionOccurred) - (JNIEnv *env); - void (JNICALL *ExceptionDescribe) - (JNIEnv *env); - void (JNICALL *ExceptionClear) - (JNIEnv *env); - void (JNICALL *FatalError) - (JNIEnv *env, const char *msg); - - jint (JNICALL *PushLocalFrame) - (JNIEnv *env, jint capacity); - jobject (JNICALL *PopLocalFrame) - (JNIEnv *env, jobject result); - - jobject (JNICALL *NewGlobalRef) - (JNIEnv *env, jobject lobj); - void (JNICALL *DeleteGlobalRef) - (JNIEnv *env, jobject gref); - void (JNICALL *DeleteLocalRef) - (JNIEnv *env, jobject obj); - jboolean (JNICALL *IsSameObject) - (JNIEnv *env, jobject obj1, jobject obj2); - jobject (JNICALL *NewLocalRef) - (JNIEnv *env, jobject ref); - jint (JNICALL *EnsureLocalCapacity) - (JNIEnv *env, jint capacity); - - jobject (JNICALL *AllocObject) - (JNIEnv *env, jclass clazz); - jobject (JNICALL *NewObject) - (JNIEnv *env, jclass clazz, jmethodID methodID, ...); - jobject (JNICALL *NewObjectV) - (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); - jobject (JNICALL *NewObjectA) - (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); - - jclass (JNICALL *GetObjectClass) - (JNIEnv *env, jobject obj); - jboolean (JNICALL *IsInstanceOf) - (JNIEnv *env, jobject obj, jclass clazz); - - jmethodID (JNICALL *GetMethodID) - (JNIEnv *env, jclass clazz, const char *name, const char *sig); - - jobject (JNICALL *CallObjectMethod) - (JNIEnv *env, jobject obj, jmethodID methodID, ...); - jobject (JNICALL *CallObjectMethodV) - (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); - jobject (JNICALL *CallObjectMethodA) - (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); - - jboolean (JNICALL *CallBooleanMethod) - (JNIEnv *env, jobject obj, jmethodID methodID, ...); - jboolean (JNICALL *CallBooleanMethodV) - (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); - jboolean (JNICALL *CallBooleanMethodA) - (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); - - jbyte (JNICALL *CallByteMethod) - (JNIEnv *env, jobject obj, jmethodID methodID, ...); - jbyte (JNICALL *CallByteMethodV) - (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); - jbyte (JNICALL *CallByteMethodA) - (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); - - jchar (JNICALL *CallCharMethod) - (JNIEnv *env, jobject obj, jmethodID methodID, ...); - jchar (JNICALL *CallCharMethodV) - (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); - jchar (JNICALL *CallCharMethodA) - (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); - - jshort (JNICALL *CallShortMethod) - (JNIEnv *env, jobject obj, jmethodID methodID, ...); - jshort (JNICALL *CallShortMethodV) - (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); - jshort (JNICALL *CallShortMethodA) - (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); - - jint (JNICALL *CallIntMethod) - (JNIEnv *env, jobject obj, jmethodID methodID, ...); - jint (JNICALL *CallIntMethodV) - (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); - jint (JNICALL *CallIntMethodA) - (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); - - jlong (JNICALL *CallLongMethod) - (JNIEnv *env, jobject obj, jmethodID methodID, ...); - jlong (JNICALL *CallLongMethodV) - (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); - jlong (JNICALL *CallLongMethodA) - (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); - - jfloat (JNICALL *CallFloatMethod) - (JNIEnv *env, jobject obj, jmethodID methodID, ...); - jfloat (JNICALL *CallFloatMethodV) - (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); - jfloat (JNICALL *CallFloatMethodA) - (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); - - jdouble (JNICALL *CallDoubleMethod) - (JNIEnv *env, jobject obj, jmethodID methodID, ...); - jdouble (JNICALL *CallDoubleMethodV) - (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); - jdouble (JNICALL *CallDoubleMethodA) - (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); - - void (JNICALL *CallVoidMethod) - (JNIEnv *env, jobject obj, jmethodID methodID, ...); - void (JNICALL *CallVoidMethodV) - (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); - void (JNICALL *CallVoidMethodA) - (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); - - jobject (JNICALL *CallNonvirtualObjectMethod) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); - jobject (JNICALL *CallNonvirtualObjectMethodV) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - va_list args); - jobject (JNICALL *CallNonvirtualObjectMethodA) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - const jvalue * args); - - jboolean (JNICALL *CallNonvirtualBooleanMethod) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); - jboolean (JNICALL *CallNonvirtualBooleanMethodV) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - va_list args); - jboolean (JNICALL *CallNonvirtualBooleanMethodA) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - const jvalue * args); - - jbyte (JNICALL *CallNonvirtualByteMethod) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); - jbyte (JNICALL *CallNonvirtualByteMethodV) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - va_list args); - jbyte (JNICALL *CallNonvirtualByteMethodA) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - const jvalue *args); - - jchar (JNICALL *CallNonvirtualCharMethod) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); - jchar (JNICALL *CallNonvirtualCharMethodV) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - va_list args); - jchar (JNICALL *CallNonvirtualCharMethodA) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - const jvalue *args); - - jshort (JNICALL *CallNonvirtualShortMethod) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); - jshort (JNICALL *CallNonvirtualShortMethodV) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - va_list args); - jshort (JNICALL *CallNonvirtualShortMethodA) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - const jvalue *args); - - jint (JNICALL *CallNonvirtualIntMethod) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); - jint (JNICALL *CallNonvirtualIntMethodV) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - va_list args); - jint (JNICALL *CallNonvirtualIntMethodA) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - const jvalue *args); - - jlong (JNICALL *CallNonvirtualLongMethod) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); - jlong (JNICALL *CallNonvirtualLongMethodV) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - va_list args); - jlong (JNICALL *CallNonvirtualLongMethodA) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - const jvalue *args); - - jfloat (JNICALL *CallNonvirtualFloatMethod) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); - jfloat (JNICALL *CallNonvirtualFloatMethodV) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - va_list args); - jfloat (JNICALL *CallNonvirtualFloatMethodA) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - const jvalue *args); - - jdouble (JNICALL *CallNonvirtualDoubleMethod) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); - jdouble (JNICALL *CallNonvirtualDoubleMethodV) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - va_list args); - jdouble (JNICALL *CallNonvirtualDoubleMethodA) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - const jvalue *args); - - void (JNICALL *CallNonvirtualVoidMethod) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); - void (JNICALL *CallNonvirtualVoidMethodV) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - va_list args); - void (JNICALL *CallNonvirtualVoidMethodA) - (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, - const jvalue * args); - - jfieldID (JNICALL *GetFieldID) - (JNIEnv *env, jclass clazz, const char *name, const char *sig); - - jobject (JNICALL *GetObjectField) - (JNIEnv *env, jobject obj, jfieldID fieldID); - jboolean (JNICALL *GetBooleanField) - (JNIEnv *env, jobject obj, jfieldID fieldID); - jbyte (JNICALL *GetByteField) - (JNIEnv *env, jobject obj, jfieldID fieldID); - jchar (JNICALL *GetCharField) - (JNIEnv *env, jobject obj, jfieldID fieldID); - jshort (JNICALL *GetShortField) - (JNIEnv *env, jobject obj, jfieldID fieldID); - jint (JNICALL *GetIntField) - (JNIEnv *env, jobject obj, jfieldID fieldID); - jlong (JNICALL *GetLongField) - (JNIEnv *env, jobject obj, jfieldID fieldID); - jfloat (JNICALL *GetFloatField) - (JNIEnv *env, jobject obj, jfieldID fieldID); - jdouble (JNICALL *GetDoubleField) - (JNIEnv *env, jobject obj, jfieldID fieldID); - - void (JNICALL *SetObjectField) - (JNIEnv *env, jobject obj, jfieldID fieldID, jobject val); - void (JNICALL *SetBooleanField) - (JNIEnv *env, jobject obj, jfieldID fieldID, jboolean val); - void (JNICALL *SetByteField) - (JNIEnv *env, jobject obj, jfieldID fieldID, jbyte val); - void (JNICALL *SetCharField) - (JNIEnv *env, jobject obj, jfieldID fieldID, jchar val); - void (JNICALL *SetShortField) - (JNIEnv *env, jobject obj, jfieldID fieldID, jshort val); - void (JNICALL *SetIntField) - (JNIEnv *env, jobject obj, jfieldID fieldID, jint val); - void (JNICALL *SetLongField) - (JNIEnv *env, jobject obj, jfieldID fieldID, jlong val); - void (JNICALL *SetFloatField) - (JNIEnv *env, jobject obj, jfieldID fieldID, jfloat val); - void (JNICALL *SetDoubleField) - (JNIEnv *env, jobject obj, jfieldID fieldID, jdouble val); - - jmethodID (JNICALL *GetStaticMethodID) - (JNIEnv *env, jclass clazz, const char *name, const char *sig); - - jobject (JNICALL *CallStaticObjectMethod) - (JNIEnv *env, jclass clazz, jmethodID methodID, ...); - jobject (JNICALL *CallStaticObjectMethodV) - (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); - jobject (JNICALL *CallStaticObjectMethodA) - (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); - - jboolean (JNICALL *CallStaticBooleanMethod) - (JNIEnv *env, jclass clazz, jmethodID methodID, ...); - jboolean (JNICALL *CallStaticBooleanMethodV) - (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); - jboolean (JNICALL *CallStaticBooleanMethodA) - (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); - - jbyte (JNICALL *CallStaticByteMethod) - (JNIEnv *env, jclass clazz, jmethodID methodID, ...); - jbyte (JNICALL *CallStaticByteMethodV) - (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); - jbyte (JNICALL *CallStaticByteMethodA) - (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); - - jchar (JNICALL *CallStaticCharMethod) - (JNIEnv *env, jclass clazz, jmethodID methodID, ...); - jchar (JNICALL *CallStaticCharMethodV) - (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); - jchar (JNICALL *CallStaticCharMethodA) - (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); - - jshort (JNICALL *CallStaticShortMethod) - (JNIEnv *env, jclass clazz, jmethodID methodID, ...); - jshort (JNICALL *CallStaticShortMethodV) - (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); - jshort (JNICALL *CallStaticShortMethodA) - (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); - - jint (JNICALL *CallStaticIntMethod) - (JNIEnv *env, jclass clazz, jmethodID methodID, ...); - jint (JNICALL *CallStaticIntMethodV) - (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); - jint (JNICALL *CallStaticIntMethodA) - (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); - - jlong (JNICALL *CallStaticLongMethod) - (JNIEnv *env, jclass clazz, jmethodID methodID, ...); - jlong (JNICALL *CallStaticLongMethodV) - (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); - jlong (JNICALL *CallStaticLongMethodA) - (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); - - jfloat (JNICALL *CallStaticFloatMethod) - (JNIEnv *env, jclass clazz, jmethodID methodID, ...); - jfloat (JNICALL *CallStaticFloatMethodV) - (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); - jfloat (JNICALL *CallStaticFloatMethodA) - (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); - - jdouble (JNICALL *CallStaticDoubleMethod) - (JNIEnv *env, jclass clazz, jmethodID methodID, ...); - jdouble (JNICALL *CallStaticDoubleMethodV) - (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); - jdouble (JNICALL *CallStaticDoubleMethodA) - (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); - - void (JNICALL *CallStaticVoidMethod) - (JNIEnv *env, jclass cls, jmethodID methodID, ...); - void (JNICALL *CallStaticVoidMethodV) - (JNIEnv *env, jclass cls, jmethodID methodID, va_list args); - void (JNICALL *CallStaticVoidMethodA) - (JNIEnv *env, jclass cls, jmethodID methodID, const jvalue * args); - - jfieldID (JNICALL *GetStaticFieldID) - (JNIEnv *env, jclass clazz, const char *name, const char *sig); - jobject (JNICALL *GetStaticObjectField) - (JNIEnv *env, jclass clazz, jfieldID fieldID); - jboolean (JNICALL *GetStaticBooleanField) - (JNIEnv *env, jclass clazz, jfieldID fieldID); - jbyte (JNICALL *GetStaticByteField) - (JNIEnv *env, jclass clazz, jfieldID fieldID); - jchar (JNICALL *GetStaticCharField) - (JNIEnv *env, jclass clazz, jfieldID fieldID); - jshort (JNICALL *GetStaticShortField) - (JNIEnv *env, jclass clazz, jfieldID fieldID); - jint (JNICALL *GetStaticIntField) - (JNIEnv *env, jclass clazz, jfieldID fieldID); - jlong (JNICALL *GetStaticLongField) - (JNIEnv *env, jclass clazz, jfieldID fieldID); - jfloat (JNICALL *GetStaticFloatField) - (JNIEnv *env, jclass clazz, jfieldID fieldID); - jdouble (JNICALL *GetStaticDoubleField) - (JNIEnv *env, jclass clazz, jfieldID fieldID); - - void (JNICALL *SetStaticObjectField) - (JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value); - void (JNICALL *SetStaticBooleanField) - (JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean value); - void (JNICALL *SetStaticByteField) - (JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte value); - void (JNICALL *SetStaticCharField) - (JNIEnv *env, jclass clazz, jfieldID fieldID, jchar value); - void (JNICALL *SetStaticShortField) - (JNIEnv *env, jclass clazz, jfieldID fieldID, jshort value); - void (JNICALL *SetStaticIntField) - (JNIEnv *env, jclass clazz, jfieldID fieldID, jint value); - void (JNICALL *SetStaticLongField) - (JNIEnv *env, jclass clazz, jfieldID fieldID, jlong value); - void (JNICALL *SetStaticFloatField) - (JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat value); - void (JNICALL *SetStaticDoubleField) - (JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble value); - - jstring (JNICALL *NewString) - (JNIEnv *env, const jchar *unicode, jsize len); - jsize (JNICALL *GetStringLength) - (JNIEnv *env, jstring str); - const jchar *(JNICALL *GetStringChars) - (JNIEnv *env, jstring str, jboolean *isCopy); - void (JNICALL *ReleaseStringChars) - (JNIEnv *env, jstring str, const jchar *chars); - - jstring (JNICALL *NewStringUTF) - (JNIEnv *env, const char *utf); - jsize (JNICALL *GetStringUTFLength) - (JNIEnv *env, jstring str); - const char* (JNICALL *GetStringUTFChars) - (JNIEnv *env, jstring str, jboolean *isCopy); - void (JNICALL *ReleaseStringUTFChars) - (JNIEnv *env, jstring str, const char* chars); - - - jsize (JNICALL *GetArrayLength) - (JNIEnv *env, jarray array); - - jobjectArray (JNICALL *NewObjectArray) - (JNIEnv *env, jsize len, jclass clazz, jobject init); - jobject (JNICALL *GetObjectArrayElement) - (JNIEnv *env, jobjectArray array, jsize index); - void (JNICALL *SetObjectArrayElement) - (JNIEnv *env, jobjectArray array, jsize index, jobject val); - - jbooleanArray (JNICALL *NewBooleanArray) - (JNIEnv *env, jsize len); - jbyteArray (JNICALL *NewByteArray) - (JNIEnv *env, jsize len); - jcharArray (JNICALL *NewCharArray) - (JNIEnv *env, jsize len); - jshortArray (JNICALL *NewShortArray) - (JNIEnv *env, jsize len); - jintArray (JNICALL *NewIntArray) - (JNIEnv *env, jsize len); - jlongArray (JNICALL *NewLongArray) - (JNIEnv *env, jsize len); - jfloatArray (JNICALL *NewFloatArray) - (JNIEnv *env, jsize len); - jdoubleArray (JNICALL *NewDoubleArray) - (JNIEnv *env, jsize len); - - jboolean * (JNICALL *GetBooleanArrayElements) - (JNIEnv *env, jbooleanArray array, jboolean *isCopy); - jbyte * (JNICALL *GetByteArrayElements) - (JNIEnv *env, jbyteArray array, jboolean *isCopy); - jchar * (JNICALL *GetCharArrayElements) - (JNIEnv *env, jcharArray array, jboolean *isCopy); - jshort * (JNICALL *GetShortArrayElements) - (JNIEnv *env, jshortArray array, jboolean *isCopy); - jint * (JNICALL *GetIntArrayElements) - (JNIEnv *env, jintArray array, jboolean *isCopy); - jlong * (JNICALL *GetLongArrayElements) - (JNIEnv *env, jlongArray array, jboolean *isCopy); - jfloat * (JNICALL *GetFloatArrayElements) - (JNIEnv *env, jfloatArray array, jboolean *isCopy); - jdouble * (JNICALL *GetDoubleArrayElements) - (JNIEnv *env, jdoubleArray array, jboolean *isCopy); - - void (JNICALL *ReleaseBooleanArrayElements) - (JNIEnv *env, jbooleanArray array, jboolean *elems, jint mode); - void (JNICALL *ReleaseByteArrayElements) - (JNIEnv *env, jbyteArray array, jbyte *elems, jint mode); - void (JNICALL *ReleaseCharArrayElements) - (JNIEnv *env, jcharArray array, jchar *elems, jint mode); - void (JNICALL *ReleaseShortArrayElements) - (JNIEnv *env, jshortArray array, jshort *elems, jint mode); - void (JNICALL *ReleaseIntArrayElements) - (JNIEnv *env, jintArray array, jint *elems, jint mode); - void (JNICALL *ReleaseLongArrayElements) - (JNIEnv *env, jlongArray array, jlong *elems, jint mode); - void (JNICALL *ReleaseFloatArrayElements) - (JNIEnv *env, jfloatArray array, jfloat *elems, jint mode); - void (JNICALL *ReleaseDoubleArrayElements) - (JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode); - - void (JNICALL *GetBooleanArrayRegion) - (JNIEnv *env, jbooleanArray array, jsize start, jsize l, jboolean *buf); - void (JNICALL *GetByteArrayRegion) - (JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf); - void (JNICALL *GetCharArrayRegion) - (JNIEnv *env, jcharArray array, jsize start, jsize len, jchar *buf); - void (JNICALL *GetShortArrayRegion) - (JNIEnv *env, jshortArray array, jsize start, jsize len, jshort *buf); - void (JNICALL *GetIntArrayRegion) - (JNIEnv *env, jintArray array, jsize start, jsize len, jint *buf); - void (JNICALL *GetLongArrayRegion) - (JNIEnv *env, jlongArray array, jsize start, jsize len, jlong *buf); - void (JNICALL *GetFloatArrayRegion) - (JNIEnv *env, jfloatArray array, jsize start, jsize len, jfloat *buf); - void (JNICALL *GetDoubleArrayRegion) - (JNIEnv *env, jdoubleArray array, jsize start, jsize len, jdouble *buf); - - void (JNICALL *SetBooleanArrayRegion) - (JNIEnv *env, jbooleanArray array, jsize start, jsize l, const jboolean *buf); - void (JNICALL *SetByteArrayRegion) - (JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf); - void (JNICALL *SetCharArrayRegion) - (JNIEnv *env, jcharArray array, jsize start, jsize len, const jchar *buf); - void (JNICALL *SetShortArrayRegion) - (JNIEnv *env, jshortArray array, jsize start, jsize len, const jshort *buf); - void (JNICALL *SetIntArrayRegion) - (JNIEnv *env, jintArray array, jsize start, jsize len, const jint *buf); - void (JNICALL *SetLongArrayRegion) - (JNIEnv *env, jlongArray array, jsize start, jsize len, const jlong *buf); - void (JNICALL *SetFloatArrayRegion) - (JNIEnv *env, jfloatArray array, jsize start, jsize len, const jfloat *buf); - void (JNICALL *SetDoubleArrayRegion) - (JNIEnv *env, jdoubleArray array, jsize start, jsize len, const jdouble *buf); - - jint (JNICALL *RegisterNatives) - (JNIEnv *env, jclass clazz, const JNINativeMethod *methods, - jint nMethods); - jint (JNICALL *UnregisterNatives) - (JNIEnv *env, jclass clazz); - - jint (JNICALL *MonitorEnter) - (JNIEnv *env, jobject obj); - jint (JNICALL *MonitorExit) - (JNIEnv *env, jobject obj); - - jint (JNICALL *GetJavaVM) - (JNIEnv *env, JavaVM **vm); - - void (JNICALL *GetStringRegion) - (JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf); - void (JNICALL *GetStringUTFRegion) - (JNIEnv *env, jstring str, jsize start, jsize len, char *buf); - - void * (JNICALL *GetPrimitiveArrayCritical) - (JNIEnv *env, jarray array, jboolean *isCopy); - void (JNICALL *ReleasePrimitiveArrayCritical) - (JNIEnv *env, jarray array, void *carray, jint mode); - - const jchar * (JNICALL *GetStringCritical) - (JNIEnv *env, jstring string, jboolean *isCopy); - void (JNICALL *ReleaseStringCritical) - (JNIEnv *env, jstring string, const jchar *cstring); - - jweak (JNICALL *NewWeakGlobalRef) - (JNIEnv *env, jobject obj); - void (JNICALL *DeleteWeakGlobalRef) - (JNIEnv *env, jweak ref); - - jboolean (JNICALL *ExceptionCheck) - (JNIEnv *env); - - jobject (JNICALL *NewDirectByteBuffer) - (JNIEnv* env, void* address, jlong capacity); - void* (JNICALL *GetDirectBufferAddress) - (JNIEnv* env, jobject buf); - jlong (JNICALL *GetDirectBufferCapacity) - (JNIEnv* env, jobject buf); - - /* New JNI 1.6 Features */ - - jobjectRefType (JNICALL *GetObjectRefType) - (JNIEnv* env, jobject obj); - - #if TARGET_RT_MAC_CFM && defined(__ppc__) - void* real_functions[228]; - #endif /* TARGET_RT_MAC_CFM && defined(__ppc__) */ -}; - -/* - * We use inlined functions for C++ so that programmers can write: - * - * env->FindClass("java/lang/String") - * - * in C++ rather than: - * - * (*env)->FindClass(env, "java/lang/String") - * - * in C. - */ - -struct JNIEnv_ { - const struct JNINativeInterface_ *functions; -#ifdef __cplusplus - - jint GetVersion() { - return functions->GetVersion(this); - } - jclass DefineClass(const char *name, jobject loader, const jbyte *buf, - jsize len) { - return functions->DefineClass(this, name, loader, buf, len); - } - jclass FindClass(const char *name) { - return functions->FindClass(this, name); - } - jmethodID FromReflectedMethod(jobject method) { - return functions->FromReflectedMethod(this,method); - } - jfieldID FromReflectedField(jobject field) { - return functions->FromReflectedField(this,field); - } - - jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) { - return functions->ToReflectedMethod(this, cls, methodID, isStatic); - } - - jclass GetSuperclass(jclass sub) { - return functions->GetSuperclass(this, sub); - } - jboolean IsAssignableFrom(jclass sub, jclass sup) { - return functions->IsAssignableFrom(this, sub, sup); - } - - jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic) { - return functions->ToReflectedField(this,cls,fieldID,isStatic); - } - - jint Throw(jthrowable obj) { - return functions->Throw(this, obj); - } - jint ThrowNew(jclass clazz, const char *msg) { - return functions->ThrowNew(this, clazz, msg); - } - jthrowable ExceptionOccurred() { - return functions->ExceptionOccurred(this); - } - void ExceptionDescribe() { - functions->ExceptionDescribe(this); - } - void ExceptionClear() { - functions->ExceptionClear(this); - } - void FatalError(const char *msg) { - functions->FatalError(this, msg); - } - - jint PushLocalFrame(jint capacity) { - return functions->PushLocalFrame(this,capacity); - } - jobject PopLocalFrame(jobject result) { - return functions->PopLocalFrame(this,result); - } - - jobject NewGlobalRef(jobject lobj) { - return functions->NewGlobalRef(this,lobj); - } - void DeleteGlobalRef(jobject gref) { - functions->DeleteGlobalRef(this,gref); - } - void DeleteLocalRef(jobject obj) { - functions->DeleteLocalRef(this, obj); - } - - jboolean IsSameObject(jobject obj1, jobject obj2) { - return functions->IsSameObject(this,obj1,obj2); - } - - jobject NewLocalRef(jobject ref) { - return functions->NewLocalRef(this,ref); - } - jint EnsureLocalCapacity(jint capacity) { - return functions->EnsureLocalCapacity(this,capacity); - } - - jobject AllocObject(jclass clazz) { - return functions->AllocObject(this,clazz); - } - jobject NewObject(jclass clazz, jmethodID methodID, ...) { - va_list args; - jobject result; - va_start(args, methodID); - result = functions->NewObjectV(this,clazz,methodID,args); - va_end(args); - return result; - } - jobject NewObjectV(jclass clazz, jmethodID methodID, - va_list args) { - return functions->NewObjectV(this,clazz,methodID,args); - } - jobject NewObjectA(jclass clazz, jmethodID methodID, - const jvalue *args) { - return functions->NewObjectA(this,clazz,methodID,args); - } - - jclass GetObjectClass(jobject obj) { - return functions->GetObjectClass(this,obj); - } - jboolean IsInstanceOf(jobject obj, jclass clazz) { - return functions->IsInstanceOf(this,obj,clazz); - } - - jmethodID GetMethodID(jclass clazz, const char *name, - const char *sig) { - return functions->GetMethodID(this,clazz,name,sig); - } - - jobject CallObjectMethod(jobject obj, jmethodID methodID, ...) { - va_list args; - jobject result; - va_start(args,methodID); - result = functions->CallObjectMethodV(this,obj,methodID,args); - va_end(args); - return result; - } - jobject CallObjectMethodV(jobject obj, jmethodID methodID, - va_list args) { - return functions->CallObjectMethodV(this,obj,methodID,args); - } - jobject CallObjectMethodA(jobject obj, jmethodID methodID, - const jvalue * args) { - return functions->CallObjectMethodA(this,obj,methodID,args); - } - - jboolean CallBooleanMethod(jobject obj, - jmethodID methodID, ...) { - va_list args; - jboolean result; - va_start(args,methodID); - result = functions->CallBooleanMethodV(this,obj,methodID,args); - va_end(args); - return result; - } - jboolean CallBooleanMethodV(jobject obj, jmethodID methodID, - va_list args) { - return functions->CallBooleanMethodV(this,obj,methodID,args); - } - jboolean CallBooleanMethodA(jobject obj, jmethodID methodID, - const jvalue * args) { - return functions->CallBooleanMethodA(this,obj,methodID, args); - } - - jbyte CallByteMethod(jobject obj, jmethodID methodID, ...) { - va_list args; - jbyte result; - va_start(args,methodID); - result = functions->CallByteMethodV(this,obj,methodID,args); - va_end(args); - return result; - } - jbyte CallByteMethodV(jobject obj, jmethodID methodID, - va_list args) { - return functions->CallByteMethodV(this,obj,methodID,args); - } - jbyte CallByteMethodA(jobject obj, jmethodID methodID, - const jvalue * args) { - return functions->CallByteMethodA(this,obj,methodID,args); - } - - jchar CallCharMethod(jobject obj, jmethodID methodID, ...) { - va_list args; - jchar result; - va_start(args,methodID); - result = functions->CallCharMethodV(this,obj,methodID,args); - va_end(args); - return result; - } - jchar CallCharMethodV(jobject obj, jmethodID methodID, - va_list args) { - return functions->CallCharMethodV(this,obj,methodID,args); - } - jchar CallCharMethodA(jobject obj, jmethodID methodID, - const jvalue * args) { - return functions->CallCharMethodA(this,obj,methodID,args); - } - - jshort CallShortMethod(jobject obj, jmethodID methodID, ...) { - va_list args; - jshort result; - va_start(args,methodID); - result = functions->CallShortMethodV(this,obj,methodID,args); - va_end(args); - return result; - } - jshort CallShortMethodV(jobject obj, jmethodID methodID, - va_list args) { - return functions->CallShortMethodV(this,obj,methodID,args); - } - jshort CallShortMethodA(jobject obj, jmethodID methodID, - const jvalue * args) { - return functions->CallShortMethodA(this,obj,methodID,args); - } - - jint CallIntMethod(jobject obj, jmethodID methodID, ...) { - va_list args; - jint result; - va_start(args,methodID); - result = functions->CallIntMethodV(this,obj,methodID,args); - va_end(args); - return result; - } - jint CallIntMethodV(jobject obj, jmethodID methodID, - va_list args) { - return functions->CallIntMethodV(this,obj,methodID,args); - } - jint CallIntMethodA(jobject obj, jmethodID methodID, - const jvalue * args) { - return functions->CallIntMethodA(this,obj,methodID,args); - } - - jlong CallLongMethod(jobject obj, jmethodID methodID, ...) { - va_list args; - jlong result; - va_start(args,methodID); - result = functions->CallLongMethodV(this,obj,methodID,args); - va_end(args); - return result; - } - jlong CallLongMethodV(jobject obj, jmethodID methodID, - va_list args) { - return functions->CallLongMethodV(this,obj,methodID,args); - } - jlong CallLongMethodA(jobject obj, jmethodID methodID, - const jvalue * args) { - return functions->CallLongMethodA(this,obj,methodID,args); - } - - jfloat CallFloatMethod(jobject obj, jmethodID methodID, ...) { - va_list args; - jfloat result; - va_start(args,methodID); - result = functions->CallFloatMethodV(this,obj,methodID,args); - va_end(args); - return result; - } - jfloat CallFloatMethodV(jobject obj, jmethodID methodID, - va_list args) { - return functions->CallFloatMethodV(this,obj,methodID,args); - } - jfloat CallFloatMethodA(jobject obj, jmethodID methodID, - const jvalue * args) { - return functions->CallFloatMethodA(this,obj,methodID,args); - } - - jdouble CallDoubleMethod(jobject obj, jmethodID methodID, ...) { - va_list args; - jdouble result; - va_start(args,methodID); - result = functions->CallDoubleMethodV(this,obj,methodID,args); - va_end(args); - return result; - } - jdouble CallDoubleMethodV(jobject obj, jmethodID methodID, - va_list args) { - return functions->CallDoubleMethodV(this,obj,methodID,args); - } - jdouble CallDoubleMethodA(jobject obj, jmethodID methodID, - const jvalue * args) { - return functions->CallDoubleMethodA(this,obj,methodID,args); - } - - void CallVoidMethod(jobject obj, jmethodID methodID, ...) { - va_list args; - va_start(args,methodID); - functions->CallVoidMethodV(this,obj,methodID,args); - va_end(args); - } - void CallVoidMethodV(jobject obj, jmethodID methodID, - va_list args) { - functions->CallVoidMethodV(this,obj,methodID,args); - } - void CallVoidMethodA(jobject obj, jmethodID methodID, - const jvalue * args) { - functions->CallVoidMethodA(this,obj,methodID,args); - } - - jobject CallNonvirtualObjectMethod(jobject obj, jclass clazz, - jmethodID methodID, ...) { - va_list args; - jobject result; - va_start(args,methodID); - result = functions->CallNonvirtualObjectMethodV(this,obj,clazz, - methodID,args); - va_end(args); - return result; - } - jobject CallNonvirtualObjectMethodV(jobject obj, jclass clazz, - jmethodID methodID, va_list args) { - return functions->CallNonvirtualObjectMethodV(this,obj,clazz, - methodID,args); - } - jobject CallNonvirtualObjectMethodA(jobject obj, jclass clazz, - jmethodID methodID, const jvalue * args) { - return functions->CallNonvirtualObjectMethodA(this,obj,clazz, - methodID,args); - } - - jboolean CallNonvirtualBooleanMethod(jobject obj, jclass clazz, - jmethodID methodID, ...) { - va_list args; - jboolean result; - va_start(args,methodID); - result = functions->CallNonvirtualBooleanMethodV(this,obj,clazz, - methodID,args); - va_end(args); - return result; - } - jboolean CallNonvirtualBooleanMethodV(jobject obj, jclass clazz, - jmethodID methodID, va_list args) { - return functions->CallNonvirtualBooleanMethodV(this,obj,clazz, - methodID,args); - } - jboolean CallNonvirtualBooleanMethodA(jobject obj, jclass clazz, - jmethodID methodID, const jvalue * args) { - return functions->CallNonvirtualBooleanMethodA(this,obj,clazz, - methodID, args); - } - - jbyte CallNonvirtualByteMethod(jobject obj, jclass clazz, - jmethodID methodID, ...) { - va_list args; - jbyte result; - va_start(args,methodID); - result = functions->CallNonvirtualByteMethodV(this,obj,clazz, - methodID,args); - va_end(args); - return result; - } - jbyte CallNonvirtualByteMethodV(jobject obj, jclass clazz, - jmethodID methodID, va_list args) { - return functions->CallNonvirtualByteMethodV(this,obj,clazz, - methodID,args); - } - jbyte CallNonvirtualByteMethodA(jobject obj, jclass clazz, - jmethodID methodID, const jvalue * args) { - return functions->CallNonvirtualByteMethodA(this,obj,clazz, - methodID,args); - } - - jchar CallNonvirtualCharMethod(jobject obj, jclass clazz, - jmethodID methodID, ...) { - va_list args; - jchar result; - va_start(args,methodID); - result = functions->CallNonvirtualCharMethodV(this,obj,clazz, - methodID,args); - va_end(args); - return result; - } - jchar CallNonvirtualCharMethodV(jobject obj, jclass clazz, - jmethodID methodID, va_list args) { - return functions->CallNonvirtualCharMethodV(this,obj,clazz, - methodID,args); - } - jchar CallNonvirtualCharMethodA(jobject obj, jclass clazz, - jmethodID methodID, const jvalue * args) { - return functions->CallNonvirtualCharMethodA(this,obj,clazz, - methodID,args); - } - - jshort CallNonvirtualShortMethod(jobject obj, jclass clazz, - jmethodID methodID, ...) { - va_list args; - jshort result; - va_start(args,methodID); - result = functions->CallNonvirtualShortMethodV(this,obj,clazz, - methodID,args); - va_end(args); - return result; - } - jshort CallNonvirtualShortMethodV(jobject obj, jclass clazz, - jmethodID methodID, va_list args) { - return functions->CallNonvirtualShortMethodV(this,obj,clazz, - methodID,args); - } - jshort CallNonvirtualShortMethodA(jobject obj, jclass clazz, - jmethodID methodID, const jvalue * args) { - return functions->CallNonvirtualShortMethodA(this,obj,clazz, - methodID,args); - } - - jint CallNonvirtualIntMethod(jobject obj, jclass clazz, - jmethodID methodID, ...) { - va_list args; - jint result; - va_start(args,methodID); - result = functions->CallNonvirtualIntMethodV(this,obj,clazz, - methodID,args); - va_end(args); - return result; - } - jint CallNonvirtualIntMethodV(jobject obj, jclass clazz, - jmethodID methodID, va_list args) { - return functions->CallNonvirtualIntMethodV(this,obj,clazz, - methodID,args); - } - jint CallNonvirtualIntMethodA(jobject obj, jclass clazz, - jmethodID methodID, const jvalue * args) { - return functions->CallNonvirtualIntMethodA(this,obj,clazz, - methodID,args); - } - - jlong CallNonvirtualLongMethod(jobject obj, jclass clazz, - jmethodID methodID, ...) { - va_list args; - jlong result; - va_start(args,methodID); - result = functions->CallNonvirtualLongMethodV(this,obj,clazz, - methodID,args); - va_end(args); - return result; - } - jlong CallNonvirtualLongMethodV(jobject obj, jclass clazz, - jmethodID methodID, va_list args) { - return functions->CallNonvirtualLongMethodV(this,obj,clazz, - methodID,args); - } - jlong CallNonvirtualLongMethodA(jobject obj, jclass clazz, - jmethodID methodID, const jvalue * args) { - return functions->CallNonvirtualLongMethodA(this,obj,clazz, - methodID,args); - } - - jfloat CallNonvirtualFloatMethod(jobject obj, jclass clazz, - jmethodID methodID, ...) { - va_list args; - jfloat result; - va_start(args,methodID); - result = functions->CallNonvirtualFloatMethodV(this,obj,clazz, - methodID,args); - va_end(args); - return result; - } - jfloat CallNonvirtualFloatMethodV(jobject obj, jclass clazz, - jmethodID methodID, - va_list args) { - return functions->CallNonvirtualFloatMethodV(this,obj,clazz, - methodID,args); - } - jfloat CallNonvirtualFloatMethodA(jobject obj, jclass clazz, - jmethodID methodID, - const jvalue * args) { - return functions->CallNonvirtualFloatMethodA(this,obj,clazz, - methodID,args); - } - - jdouble CallNonvirtualDoubleMethod(jobject obj, jclass clazz, - jmethodID methodID, ...) { - va_list args; - jdouble result; - va_start(args,methodID); - result = functions->CallNonvirtualDoubleMethodV(this,obj,clazz, - methodID,args); - va_end(args); - return result; - } - jdouble CallNonvirtualDoubleMethodV(jobject obj, jclass clazz, - jmethodID methodID, - va_list args) { - return functions->CallNonvirtualDoubleMethodV(this,obj,clazz, - methodID,args); - } - jdouble CallNonvirtualDoubleMethodA(jobject obj, jclass clazz, - jmethodID methodID, - const jvalue * args) { - return functions->CallNonvirtualDoubleMethodA(this,obj,clazz, - methodID,args); - } - - void CallNonvirtualVoidMethod(jobject obj, jclass clazz, - jmethodID methodID, ...) { - va_list args; - va_start(args,methodID); - functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); - va_end(args); - } - void CallNonvirtualVoidMethodV(jobject obj, jclass clazz, - jmethodID methodID, - va_list args) { - functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); - } - void CallNonvirtualVoidMethodA(jobject obj, jclass clazz, - jmethodID methodID, - const jvalue * args) { - functions->CallNonvirtualVoidMethodA(this,obj,clazz,methodID,args); - } - - jfieldID GetFieldID(jclass clazz, const char *name, - const char *sig) { - return functions->GetFieldID(this,clazz,name,sig); - } - - jobject GetObjectField(jobject obj, jfieldID fieldID) { - return functions->GetObjectField(this,obj,fieldID); - } - jboolean GetBooleanField(jobject obj, jfieldID fieldID) { - return functions->GetBooleanField(this,obj,fieldID); - } - jbyte GetByteField(jobject obj, jfieldID fieldID) { - return functions->GetByteField(this,obj,fieldID); - } - jchar GetCharField(jobject obj, jfieldID fieldID) { - return functions->GetCharField(this,obj,fieldID); - } - jshort GetShortField(jobject obj, jfieldID fieldID) { - return functions->GetShortField(this,obj,fieldID); - } - jint GetIntField(jobject obj, jfieldID fieldID) { - return functions->GetIntField(this,obj,fieldID); - } - jlong GetLongField(jobject obj, jfieldID fieldID) { - return functions->GetLongField(this,obj,fieldID); - } - jfloat GetFloatField(jobject obj, jfieldID fieldID) { - return functions->GetFloatField(this,obj,fieldID); - } - jdouble GetDoubleField(jobject obj, jfieldID fieldID) { - return functions->GetDoubleField(this,obj,fieldID); - } - - void SetObjectField(jobject obj, jfieldID fieldID, jobject val) { - functions->SetObjectField(this,obj,fieldID,val); - } - void SetBooleanField(jobject obj, jfieldID fieldID, - jboolean val) { - functions->SetBooleanField(this,obj,fieldID,val); - } - void SetByteField(jobject obj, jfieldID fieldID, - jbyte val) { - functions->SetByteField(this,obj,fieldID,val); - } - void SetCharField(jobject obj, jfieldID fieldID, - jchar val) { - functions->SetCharField(this,obj,fieldID,val); - } - void SetShortField(jobject obj, jfieldID fieldID, - jshort val) { - functions->SetShortField(this,obj,fieldID,val); - } - void SetIntField(jobject obj, jfieldID fieldID, - jint val) { - functions->SetIntField(this,obj,fieldID,val); - } - void SetLongField(jobject obj, jfieldID fieldID, - jlong val) { - functions->SetLongField(this,obj,fieldID,val); - } - void SetFloatField(jobject obj, jfieldID fieldID, - jfloat val) { - functions->SetFloatField(this,obj,fieldID,val); - } - void SetDoubleField(jobject obj, jfieldID fieldID, - jdouble val) { - functions->SetDoubleField(this,obj,fieldID,val); - } - - jmethodID GetStaticMethodID(jclass clazz, const char *name, - const char *sig) { - return functions->GetStaticMethodID(this,clazz,name,sig); - } - - jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID, - ...) { - va_list args; - jobject result; - va_start(args,methodID); - result = functions->CallStaticObjectMethodV(this,clazz,methodID,args); - va_end(args); - return result; - } - jobject CallStaticObjectMethodV(jclass clazz, jmethodID methodID, - va_list args) { - return functions->CallStaticObjectMethodV(this,clazz,methodID,args); - } - jobject CallStaticObjectMethodA(jclass clazz, jmethodID methodID, - const jvalue *args) { - return functions->CallStaticObjectMethodA(this,clazz,methodID,args); - } - - jboolean CallStaticBooleanMethod(jclass clazz, - jmethodID methodID, ...) { - va_list args; - jboolean result; - va_start(args,methodID); - result = functions->CallStaticBooleanMethodV(this,clazz,methodID,args); - va_end(args); - return result; - } - jboolean CallStaticBooleanMethodV(jclass clazz, - jmethodID methodID, va_list args) { - return functions->CallStaticBooleanMethodV(this,clazz,methodID,args); - } - jboolean CallStaticBooleanMethodA(jclass clazz, - jmethodID methodID, const jvalue *args) { - return functions->CallStaticBooleanMethodA(this,clazz,methodID,args); - } - - jbyte CallStaticByteMethod(jclass clazz, - jmethodID methodID, ...) { - va_list args; - jbyte result; - va_start(args,methodID); - result = functions->CallStaticByteMethodV(this,clazz,methodID,args); - va_end(args); - return result; - } - jbyte CallStaticByteMethodV(jclass clazz, - jmethodID methodID, va_list args) { - return functions->CallStaticByteMethodV(this,clazz,methodID,args); - } - jbyte CallStaticByteMethodA(jclass clazz, - jmethodID methodID, const jvalue *args) { - return functions->CallStaticByteMethodA(this,clazz,methodID,args); - } - - jchar CallStaticCharMethod(jclass clazz, - jmethodID methodID, ...) { - va_list args; - jchar result; - va_start(args,methodID); - result = functions->CallStaticCharMethodV(this,clazz,methodID,args); - va_end(args); - return result; - } - jchar CallStaticCharMethodV(jclass clazz, - jmethodID methodID, va_list args) { - return functions->CallStaticCharMethodV(this,clazz,methodID,args); - } - jchar CallStaticCharMethodA(jclass clazz, - jmethodID methodID, const jvalue *args) { - return functions->CallStaticCharMethodA(this,clazz,methodID,args); - } - - jshort CallStaticShortMethod(jclass clazz, - jmethodID methodID, ...) { - va_list args; - jshort result; - va_start(args,methodID); - result = functions->CallStaticShortMethodV(this,clazz,methodID,args); - va_end(args); - return result; - } - jshort CallStaticShortMethodV(jclass clazz, - jmethodID methodID, va_list args) { - return functions->CallStaticShortMethodV(this,clazz,methodID,args); - } - jshort CallStaticShortMethodA(jclass clazz, - jmethodID methodID, const jvalue *args) { - return functions->CallStaticShortMethodA(this,clazz,methodID,args); - } - - jint CallStaticIntMethod(jclass clazz, - jmethodID methodID, ...) { - va_list args; - jint result; - va_start(args,methodID); - result = functions->CallStaticIntMethodV(this,clazz,methodID,args); - va_end(args); - return result; - } - jint CallStaticIntMethodV(jclass clazz, - jmethodID methodID, va_list args) { - return functions->CallStaticIntMethodV(this,clazz,methodID,args); - } - jint CallStaticIntMethodA(jclass clazz, - jmethodID methodID, const jvalue *args) { - return functions->CallStaticIntMethodA(this,clazz,methodID,args); - } - - jlong CallStaticLongMethod(jclass clazz, - jmethodID methodID, ...) { - va_list args; - jlong result; - va_start(args,methodID); - result = functions->CallStaticLongMethodV(this,clazz,methodID,args); - va_end(args); - return result; - } - jlong CallStaticLongMethodV(jclass clazz, - jmethodID methodID, va_list args) { - return functions->CallStaticLongMethodV(this,clazz,methodID,args); - } - jlong CallStaticLongMethodA(jclass clazz, - jmethodID methodID, const jvalue *args) { - return functions->CallStaticLongMethodA(this,clazz,methodID,args); - } - - jfloat CallStaticFloatMethod(jclass clazz, - jmethodID methodID, ...) { - va_list args; - jfloat result; - va_start(args,methodID); - result = functions->CallStaticFloatMethodV(this,clazz,methodID,args); - va_end(args); - return result; - } - jfloat CallStaticFloatMethodV(jclass clazz, - jmethodID methodID, va_list args) { - return functions->CallStaticFloatMethodV(this,clazz,methodID,args); - } - jfloat CallStaticFloatMethodA(jclass clazz, - jmethodID methodID, const jvalue *args) { - return functions->CallStaticFloatMethodA(this,clazz,methodID,args); - } - - jdouble CallStaticDoubleMethod(jclass clazz, - jmethodID methodID, ...) { - va_list args; - jdouble result; - va_start(args,methodID); - result = functions->CallStaticDoubleMethodV(this,clazz,methodID,args); - va_end(args); - return result; - } - jdouble CallStaticDoubleMethodV(jclass clazz, - jmethodID methodID, va_list args) { - return functions->CallStaticDoubleMethodV(this,clazz,methodID,args); - } - jdouble CallStaticDoubleMethodA(jclass clazz, - jmethodID methodID, const jvalue *args) { - return functions->CallStaticDoubleMethodA(this,clazz,methodID,args); - } - - void CallStaticVoidMethod(jclass cls, jmethodID methodID, ...) { - va_list args; - va_start(args,methodID); - functions->CallStaticVoidMethodV(this,cls,methodID,args); - va_end(args); - } - void CallStaticVoidMethodV(jclass cls, jmethodID methodID, - va_list args) { - functions->CallStaticVoidMethodV(this,cls,methodID,args); - } - void CallStaticVoidMethodA(jclass cls, jmethodID methodID, - const jvalue * args) { - functions->CallStaticVoidMethodA(this,cls,methodID,args); - } - - jfieldID GetStaticFieldID(jclass clazz, const char *name, - const char *sig) { - return functions->GetStaticFieldID(this,clazz,name,sig); - } - jobject GetStaticObjectField(jclass clazz, jfieldID fieldID) { - return functions->GetStaticObjectField(this,clazz,fieldID); - } - jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID) { - return functions->GetStaticBooleanField(this,clazz,fieldID); - } - jbyte GetStaticByteField(jclass clazz, jfieldID fieldID) { - return functions->GetStaticByteField(this,clazz,fieldID); - } - jchar GetStaticCharField(jclass clazz, jfieldID fieldID) { - return functions->GetStaticCharField(this,clazz,fieldID); - } - jshort GetStaticShortField(jclass clazz, jfieldID fieldID) { - return functions->GetStaticShortField(this,clazz,fieldID); - } - jint GetStaticIntField(jclass clazz, jfieldID fieldID) { - return functions->GetStaticIntField(this,clazz,fieldID); - } - jlong GetStaticLongField(jclass clazz, jfieldID fieldID) { - return functions->GetStaticLongField(this,clazz,fieldID); - } - jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID) { - return functions->GetStaticFloatField(this,clazz,fieldID); - } - jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID) { - return functions->GetStaticDoubleField(this,clazz,fieldID); - } - - void SetStaticObjectField(jclass clazz, jfieldID fieldID, - jobject value) { - functions->SetStaticObjectField(this,clazz,fieldID,value); - } - void SetStaticBooleanField(jclass clazz, jfieldID fieldID, - jboolean value) { - functions->SetStaticBooleanField(this,clazz,fieldID,value); - } - void SetStaticByteField(jclass clazz, jfieldID fieldID, - jbyte value) { - functions->SetStaticByteField(this,clazz,fieldID,value); - } - void SetStaticCharField(jclass clazz, jfieldID fieldID, - jchar value) { - functions->SetStaticCharField(this,clazz,fieldID,value); - } - void SetStaticShortField(jclass clazz, jfieldID fieldID, - jshort value) { - functions->SetStaticShortField(this,clazz,fieldID,value); - } - void SetStaticIntField(jclass clazz, jfieldID fieldID, - jint value) { - functions->SetStaticIntField(this,clazz,fieldID,value); - } - void SetStaticLongField(jclass clazz, jfieldID fieldID, - jlong value) { - functions->SetStaticLongField(this,clazz,fieldID,value); - } - void SetStaticFloatField(jclass clazz, jfieldID fieldID, - jfloat value) { - functions->SetStaticFloatField(this,clazz,fieldID,value); - } - void SetStaticDoubleField(jclass clazz, jfieldID fieldID, - jdouble value) { - functions->SetStaticDoubleField(this,clazz,fieldID,value); - } - - jstring NewString(const jchar *unicode, jsize len) { - return functions->NewString(this,unicode,len); - } - jsize GetStringLength(jstring str) { - return functions->GetStringLength(this,str); - } - const jchar *GetStringChars(jstring str, jboolean *isCopy) { - return functions->GetStringChars(this,str,isCopy); - } - void ReleaseStringChars(jstring str, const jchar *chars) { - functions->ReleaseStringChars(this,str,chars); - } - - jstring NewStringUTF(const char *utf) { - return functions->NewStringUTF(this,utf); - } - jsize GetStringUTFLength(jstring str) { - return functions->GetStringUTFLength(this,str); - } - const char* GetStringUTFChars(jstring str, jboolean *isCopy) { - return functions->GetStringUTFChars(this,str,isCopy); - } - void ReleaseStringUTFChars(jstring str, const char* chars) { - functions->ReleaseStringUTFChars(this,str,chars); - } - - jsize GetArrayLength(jarray array) { - return functions->GetArrayLength(this,array); - } - - jobjectArray NewObjectArray(jsize len, jclass clazz, - jobject init) { - return functions->NewObjectArray(this,len,clazz,init); - } - jobject GetObjectArrayElement(jobjectArray array, jsize index) { - return functions->GetObjectArrayElement(this,array,index); - } - void SetObjectArrayElement(jobjectArray array, jsize index, - jobject val) { - functions->SetObjectArrayElement(this,array,index,val); - } - - jbooleanArray NewBooleanArray(jsize len) { - return functions->NewBooleanArray(this,len); - } - jbyteArray NewByteArray(jsize len) { - return functions->NewByteArray(this,len); - } - jcharArray NewCharArray(jsize len) { - return functions->NewCharArray(this,len); - } - jshortArray NewShortArray(jsize len) { - return functions->NewShortArray(this,len); - } - jintArray NewIntArray(jsize len) { - return functions->NewIntArray(this,len); - } - jlongArray NewLongArray(jsize len) { - return functions->NewLongArray(this,len); - } - jfloatArray NewFloatArray(jsize len) { - return functions->NewFloatArray(this,len); - } - jdoubleArray NewDoubleArray(jsize len) { - return functions->NewDoubleArray(this,len); - } - - jboolean * GetBooleanArrayElements(jbooleanArray array, jboolean *isCopy) { - return functions->GetBooleanArrayElements(this,array,isCopy); - } - jbyte * GetByteArrayElements(jbyteArray array, jboolean *isCopy) { - return functions->GetByteArrayElements(this,array,isCopy); - } - jchar * GetCharArrayElements(jcharArray array, jboolean *isCopy) { - return functions->GetCharArrayElements(this,array,isCopy); - } - jshort * GetShortArrayElements(jshortArray array, jboolean *isCopy) { - return functions->GetShortArrayElements(this,array,isCopy); - } - jint * GetIntArrayElements(jintArray array, jboolean *isCopy) { - return functions->GetIntArrayElements(this,array,isCopy); - } - jlong * GetLongArrayElements(jlongArray array, jboolean *isCopy) { - return functions->GetLongArrayElements(this,array,isCopy); - } - jfloat * GetFloatArrayElements(jfloatArray array, jboolean *isCopy) { - return functions->GetFloatArrayElements(this,array,isCopy); - } - jdouble * GetDoubleArrayElements(jdoubleArray array, jboolean *isCopy) { - return functions->GetDoubleArrayElements(this,array,isCopy); - } - - void ReleaseBooleanArrayElements(jbooleanArray array, - jboolean *elems, - jint mode) { - functions->ReleaseBooleanArrayElements(this,array,elems,mode); - } - void ReleaseByteArrayElements(jbyteArray array, - jbyte *elems, - jint mode) { - functions->ReleaseByteArrayElements(this,array,elems,mode); - } - void ReleaseCharArrayElements(jcharArray array, - jchar *elems, - jint mode) { - functions->ReleaseCharArrayElements(this,array,elems,mode); - } - void ReleaseShortArrayElements(jshortArray array, - jshort *elems, - jint mode) { - functions->ReleaseShortArrayElements(this,array,elems,mode); - } - void ReleaseIntArrayElements(jintArray array, - jint *elems, - jint mode) { - functions->ReleaseIntArrayElements(this,array,elems,mode); - } - void ReleaseLongArrayElements(jlongArray array, - jlong *elems, - jint mode) { - functions->ReleaseLongArrayElements(this,array,elems,mode); - } - void ReleaseFloatArrayElements(jfloatArray array, - jfloat *elems, - jint mode) { - functions->ReleaseFloatArrayElements(this,array,elems,mode); - } - void ReleaseDoubleArrayElements(jdoubleArray array, - jdouble *elems, - jint mode) { - functions->ReleaseDoubleArrayElements(this,array,elems,mode); - } - - void GetBooleanArrayRegion(jbooleanArray array, - jsize start, jsize len, jboolean *buf) { - functions->GetBooleanArrayRegion(this,array,start,len,buf); - } - void GetByteArrayRegion(jbyteArray array, - jsize start, jsize len, jbyte *buf) { - functions->GetByteArrayRegion(this,array,start,len,buf); - } - void GetCharArrayRegion(jcharArray array, - jsize start, jsize len, jchar *buf) { - functions->GetCharArrayRegion(this,array,start,len,buf); - } - void GetShortArrayRegion(jshortArray array, - jsize start, jsize len, jshort *buf) { - functions->GetShortArrayRegion(this,array,start,len,buf); - } - void GetIntArrayRegion(jintArray array, - jsize start, jsize len, jint *buf) { - functions->GetIntArrayRegion(this,array,start,len,buf); - } - void GetLongArrayRegion(jlongArray array, - jsize start, jsize len, jlong *buf) { - functions->GetLongArrayRegion(this,array,start,len,buf); - } - void GetFloatArrayRegion(jfloatArray array, - jsize start, jsize len, jfloat *buf) { - functions->GetFloatArrayRegion(this,array,start,len,buf); - } - void GetDoubleArrayRegion(jdoubleArray array, - jsize start, jsize len, jdouble *buf) { - functions->GetDoubleArrayRegion(this,array,start,len,buf); - } - - void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, - const jboolean *buf) { - functions->SetBooleanArrayRegion(this,array,start,len,buf); - } - void SetByteArrayRegion(jbyteArray array, jsize start, jsize len, - const jbyte *buf) { - functions->SetByteArrayRegion(this,array,start,len,buf); - } - void SetCharArrayRegion(jcharArray array, jsize start, jsize len, - const jchar *buf) { - functions->SetCharArrayRegion(this,array,start,len,buf); - } - void SetShortArrayRegion(jshortArray array, jsize start, jsize len, - const jshort *buf) { - functions->SetShortArrayRegion(this,array,start,len,buf); - } - void SetIntArrayRegion(jintArray array, jsize start, jsize len, - const jint *buf) { - functions->SetIntArrayRegion(this,array,start,len,buf); - } - void SetLongArrayRegion(jlongArray array, jsize start, jsize len, - const jlong *buf) { - functions->SetLongArrayRegion(this,array,start,len,buf); - } - void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, - const jfloat *buf) { - functions->SetFloatArrayRegion(this,array,start,len,buf); - } - void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, - const jdouble *buf) { - functions->SetDoubleArrayRegion(this,array,start,len,buf); - } - - jint RegisterNatives(jclass clazz, const JNINativeMethod *methods, - jint nMethods) { - return functions->RegisterNatives(this,clazz,methods,nMethods); - } - jint UnregisterNatives(jclass clazz) { - return functions->UnregisterNatives(this,clazz); - } - - jint MonitorEnter(jobject obj) { - return functions->MonitorEnter(this,obj); - } - jint MonitorExit(jobject obj) { - return functions->MonitorExit(this,obj); - } - - jint GetJavaVM(JavaVM **vm) { - return functions->GetJavaVM(this,vm); - } - - void GetStringRegion(jstring str, jsize start, jsize len, jchar *buf) { - functions->GetStringRegion(this,str,start,len,buf); - } - void GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf) { - functions->GetStringUTFRegion(this,str,start,len,buf); - } - - void * GetPrimitiveArrayCritical(jarray array, jboolean *isCopy) { - return functions->GetPrimitiveArrayCritical(this,array,isCopy); - } - void ReleasePrimitiveArrayCritical(jarray array, void *carray, jint mode) { - functions->ReleasePrimitiveArrayCritical(this,array,carray,mode); - } - - const jchar * GetStringCritical(jstring string, jboolean *isCopy) { - return functions->GetStringCritical(this,string,isCopy); - } - void ReleaseStringCritical(jstring string, const jchar *cstring) { - functions->ReleaseStringCritical(this,string,cstring); - } - - jweak NewWeakGlobalRef(jobject obj) { - return functions->NewWeakGlobalRef(this,obj); - } - void DeleteWeakGlobalRef(jweak ref) { - functions->DeleteWeakGlobalRef(this,ref); - } - - jboolean ExceptionCheck() { - return functions->ExceptionCheck(this); - } - - jobject NewDirectByteBuffer(void* address, jlong capacity) { - return functions->NewDirectByteBuffer(this, address, capacity); - } - void* GetDirectBufferAddress(jobject buf) { - return functions->GetDirectBufferAddress(this, buf); - } - jlong GetDirectBufferCapacity(jobject buf) { - return functions->GetDirectBufferCapacity(this, buf); - } - jobjectRefType GetObjectRefType(jobject obj) { - return functions->GetObjectRefType(this, obj); - } - -#endif /* __cplusplus */ -}; - -typedef struct JavaVMOption { - char *optionString; - void *extraInfo; -} JavaVMOption; - -typedef struct JavaVMInitArgs { - jint version; - - jint nOptions; - JavaVMOption *options; - jboolean ignoreUnrecognized; -} JavaVMInitArgs; - -typedef struct JavaVMAttachArgs { - jint version; - - char *name; - jobject group; -} JavaVMAttachArgs; - -/* These will be VM-specific. */ - -#define JDK1_2 -#define JDK1_4 - -/* End VM-specific. */ - -struct JNIInvokeInterface_ { - void *reserved0; - void *reserved1; - void *reserved2; - -#if !TARGET_RT_MAC_CFM && defined(__ppc__) - void* cfm_vectors[4]; -#endif /* !TARGET_RT_MAC_CFM && defined(__ppc__) */ - - jint (JNICALL *DestroyJavaVM)(JavaVM *vm); - - jint (JNICALL *AttachCurrentThread)(JavaVM *vm, void **penv, void *args); - - jint (JNICALL *DetachCurrentThread)(JavaVM *vm); - - jint (JNICALL *GetEnv)(JavaVM *vm, void **penv, jint version); - - jint (JNICALL *AttachCurrentThreadAsDaemon)(JavaVM *vm, void **penv, void *args); - -#if TARGET_RT_MAC_CFM && defined(__ppc__) - void* real_functions[5]; -#endif /* TARGET_RT_MAC_CFM && defined(__ppc__) */ -}; - -struct JavaVM_ { - const struct JNIInvokeInterface_ *functions; -#ifdef __cplusplus - - jint DestroyJavaVM() { - return functions->DestroyJavaVM(this); - } - jint AttachCurrentThread(void **penv, void *args) { - return functions->AttachCurrentThread(this, penv, args); - } - jint DetachCurrentThread() { - return functions->DetachCurrentThread(this); - } - - jint GetEnv(void **penv, jint version) { - return functions->GetEnv(this, penv, version); - } - jint AttachCurrentThreadAsDaemon(void **penv, void *args) { - return functions->AttachCurrentThreadAsDaemon(this, penv, args); - } -#endif -}; - -#ifdef _JNI_IMPLEMENTATION_ -#define _JNI_IMPORT_OR_EXPORT_ JNIEXPORT -#else -#define _JNI_IMPORT_OR_EXPORT_ JNIIMPORT -#endif -_JNI_IMPORT_OR_EXPORT_ jint JNICALL -JNI_GetDefaultJavaVMInitArgs(void *args); - -_JNI_IMPORT_OR_EXPORT_ jint JNICALL -JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args); - -_JNI_IMPORT_OR_EXPORT_ jint JNICALL -JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *); - -/* Defined by native libraries. */ -JNIEXPORT jint JNICALL -JNI_OnLoad(JavaVM *vm, void *reserved); - -JNIEXPORT void JNICALL -JNI_OnUnload(JavaVM *vm, void *reserved); - -#define JNI_VERSION_1_1 0x00010001 -#define JNI_VERSION_1_2 0x00010002 -#define JNI_VERSION_1_4 0x00010004 -#define JNI_VERSION_1_6 0x00010006 - -#ifdef __cplusplus -} /* extern "C" */ -#endif /* __cplusplus */ - -#endif /* !_JAVASOFT_JNI_H_ */ - - - diff --git a/jni/jni_md.h b/jni/jni_md.h deleted file mode 100755 index a3289ee..0000000 --- a/jni/jni_md.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * @(#)jni_md.h 1.19 05/11/17 - * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. - * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - */ - -#ifndef _JAVASOFT_JNI_MD_H_ -#define _JAVASOFT_JNI_MD_H_ - -#define JNIEXPORT __attribute__((visibility("default"))) -#define JNIIMPORT -#define JNICALL - -#if __LP64__ -typedef int jint; -#else -typedef long jint; -#endif -typedef long long jlong; -typedef signed char jbyte; - -#endif /* !_JAVASOFT_JNI_MD_H_ */ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..c891fd9 --- /dev/null +++ b/pom.xml @@ -0,0 +1,191 @@ + + 4.0.0 + org.gmssl + GmSSLJNI + 1.0.0 + GmSSL-Java + jar + GmSSL Java SDK + + + UTF-8 + 11 + 11 + false + + Debug + + + gmssljni + + + + + + junit + junit + 4.13.1 + test + + + + + + + com.googlecode.cmake-maven-project + cmake-maven-plugin + 3.23.2-b1 + + + cmake-generate + + generate + + + + + src/main/c + + + + ${project.build.directory}/build + + + + + + ${cmake.compile.config} + ${libName} + + + + + + + + + + cmake-compile + + compile + + + + + ${cmake.compile.config} + + + + + + + ${project.build.directory}/build + + + + + + + + + + org.apache.maven.plugins + maven-clean-plugin + 3.3.1 + + false + + + ${basedir} + + ROOTCA.pem + + false + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + + + ${gmssl.root} + + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.1 + + + copy-resources + + compile + + copy-resources + + + ${project.build.outputDirectory} + + + ${project.build.directory}/build/${cmake.compile.config} + + *.dll + *.so + *.dylib + + lib + + + + + + copy-rootca + compile + + copy-resources + + + ${basedir} + + + ${project.build.directory}/build + + ROOTCA.pem + + + + + + + + + + + + + central-maven2 + Maven Mirror + https://repo1.maven.org/maven2/ + + true + + + false + + + + + diff --git a/src/main/c/CMakeLists.txt b/src/main/c/CMakeLists.txt new file mode 100644 index 0000000..68ab72e --- /dev/null +++ b/src/main/c/CMakeLists.txt @@ -0,0 +1,81 @@ +cmake_minimum_required(VERSION 3.11) +project(gmssljni C) + +if(DEFINED GMSSL_ROOT AND (GMSSL_ROOT STREQUAL "" OR GMSSL_ROOT MATCHES "^\\$\\{.*\\}$")) + unset(GMSSL_ROOT) +endif() +if(NOT DEFINED GMSSL_ROOT AND DEFINED ENV{GMSSL_ROOT} AND NOT "$ENV{GMSSL_ROOT}" STREQUAL "") + set(GMSSL_ROOT "$ENV{GMSSL_ROOT}") +endif() + +if(GMSSL_ROOT) + unset(GMSSL_EXECUTABLE CACHE) + find_program(GMSSL_EXECUTABLE NAMES gmssl HINTS "${GMSSL_ROOT}/bin" NO_DEFAULT_PATH) +else() + find_program(GMSSL_EXECUTABLE NAMES gmssl) +endif() +if(GMSSL_EXECUTABLE) + get_filename_component(GMSSL_BIN_DIR "${GMSSL_EXECUTABLE}" DIRECTORY) + get_filename_component(GMSSL_PARENT_DIR "${GMSSL_BIN_DIR}" DIRECTORY) +else() + message(FATAL_ERROR "gmssl not found! Install GmSSL or pass -DGMSSL_ROOT=/path/to/gmssl") +endif() + +set(GMSSL_INCLUDE_DIR "${GMSSL_PARENT_DIR}/include") +set(GMSSL_LIBRARY_DIR "${GMSSL_PARENT_DIR}/lib") + +if(NOT EXISTS "${GMSSL_INCLUDE_DIR}/gmssl/sm4.h") + message(FATAL_ERROR "GmSSL headers at ${GMSSL_INCLUDE_DIR} are incompatible: gmssl/sm4.h is missing. Use guanzhi/GmSSL release v3.2.0 or a compatible release.") +endif() + +unset(GMSSL_LIBRARY CACHE) +find_library(GMSSL_LIBRARY NAMES gmssl libgmssl HINTS "${GMSSL_LIBRARY_DIR}" NO_DEFAULT_PATH) +if(NOT GMSSL_LIBRARY) + message(FATAL_ERROR "GmSSL library not found under ${GMSSL_LIBRARY_DIR}") +endif() + +if(NOT DEFINED ENV{libSubFolder} OR "$ENV{libSubFolder}" STREQUAL "") + set(LIB_SUB_FOLDER "${CMAKE_BUILD_TYPE}") +else() + set(LIB_SUB_FOLDER "$ENV{libSubFolder}") +endif() +if(NOT LIB_SUB_FOLDER) + set(LIB_SUB_FOLDER "Debug") +endif() + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${LIB_SUB_FOLDER}") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${LIB_SUB_FOLDER}") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${LIB_SUB_FOLDER}") +foreach(CONFIG_TYPE Debug Release RelWithDebInfo MinSizeRel) + string(TOUPPER "${CONFIG_TYPE}" CONFIG_TYPE_UPPER) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CONFIG_TYPE_UPPER} "${CMAKE_BINARY_DIR}/${CONFIG_TYPE}") + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${CONFIG_TYPE_UPPER} "${CMAKE_BINARY_DIR}/${CONFIG_TYPE}") + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CONFIG_TYPE_UPPER} "${CMAKE_BINARY_DIR}/${CONFIG_TYPE}") +endforeach() + +find_package(JNI REQUIRED) + +add_library(gmssljni-native SHARED gmssljni.c) +target_include_directories(gmssljni-native PRIVATE ${JNI_INCLUDE_DIRS} "${GMSSL_INCLUDE_DIR}") +target_link_libraries(gmssljni-native PRIVATE "${GMSSL_LIBRARY}") + +if(WIN32) + set_target_properties(gmssljni-native PROPERTIES OUTPUT_NAME lib$ENV{libName}) +else() + set_target_properties(gmssljni-native PROPERTIES OUTPUT_NAME $ENV{libName}) +endif() + +set(certfile +"-----BEGIN CERTIFICATE-----\n" +"MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG\n" +"EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw\n" +"MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO\n" +"UkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE\n" +"MPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT\n" +"V7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti\n" +"W/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ\n" +"MxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b\n" +"53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI\n" +"pDoiVhsLwg==\n" +"-----END CERTIFICATE-----\n") +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/ROOTCA.pem ${certfile}) diff --git a/gmssljni.c b/src/main/c/gmssljni.c similarity index 89% rename from gmssljni.c rename to src/main/c/gmssljni.c index e001abf..64cd42c 100644 --- a/gmssljni.c +++ b/src/main/c/gmssljni.c @@ -1,3 +1,12 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + #include #include #include @@ -9,12 +18,14 @@ #include #include #include -#include #include #include +#include #include #include "gmssljni.h" +#define SM2_SIGNATURE_CTX_SIZE (sizeof(SM2_SIGN_CTX) > sizeof(SM2_VERIFY_CTX) ? sizeof(SM2_SIGN_CTX) : sizeof(SM2_VERIFY_CTX)) + static int check_buf(const jbyte *buf, jint bufsiz, jint offset, jint outlen) { @@ -343,6 +354,61 @@ JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_sm3_1hmac_1finish( return ret; } +/* + * Class: org_gmssl_GmSSLJNI + * Method: sm3_pbkdf2 + * Signature: (Ljava/lang/String;[BII)[B + */ +JNIEXPORT jbyteArray JNICALL Java_org_gmssl_GmSSLJNI_sm3_1pbkdf2( + JNIEnv *env, jclass this, + jstring pass, jbyteArray salt, jint iter, jint keylen) +{ + jbyteArray ret = NULL; + uint8_t keybuf[256]; + const char *pass_str = NULL; + jbyte *saltbuf = NULL; + jlong saltlen; + + if (!(pass_str = (*env)->GetStringUTFChars(env, pass, 0))) { + error_print(); + goto end; + } + if (iter < PBKDF2_MIN_ITER || iter > PBKDF2_MAX_ITER) { + error_print(); + goto end; + } + if (!(saltbuf = (*env)->GetByteArrayElements(env, salt, NULL))) { + error_print(); + goto end; + } + saltlen = (*env)->GetArrayLength(env, salt); + if (saltlen < 1 || saltlen > PBKDF2_MAX_SALT_SIZE) { + error_print(); + goto end; + } + if (keylen < 1 || keylen > sizeof(keybuf)) { + error_print(); + goto end; + } + + if (sm3_pbkdf2(pass_str, strlen(pass_str), + (const uint8_t *)saltbuf, saltlen, iter, keylen, keybuf) != 1) { + error_print(); + goto end; + } + + if (!(ret = (*env)->NewByteArray(env, keylen))) { + error_print(); + goto end; + } + (*env)->SetByteArrayRegion(env, ret, 0, keylen, (jbyte *)keybuf); + +end: + if (pass_str) (*env)->ReleaseStringUTFChars(env, pass, pass_str); + if (saltbuf) (*env)->ReleaseByteArrayElements(env, salt, saltbuf, JNI_ABORT); + return ret; +} + /* * Class: org_gmssl_GmSSLJNI * Method: sm4_key_new @@ -784,6 +850,7 @@ JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_sm4_1cbc_1decrypt_1finish( return ret; } + /* * Class: org_gmssl_GmSSLJNI * Method: sm4_ctr_ctx_new @@ -925,6 +992,7 @@ JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_sm4_1ctr_1encrypt_1finish( jint ret = -1; jbyte *outbuf = NULL; size_t outlen; + jint mode = JNI_ABORT; if (!sm4_ctr_ctx) { error_print(); @@ -943,9 +1011,11 @@ JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_sm4_1ctr_1encrypt_1finish( error_print(); goto end; } + + mode = 0; ret = (jint)outlen; end: - if (outbuf) (*env)->ReleaseByteArrayElements(env, out, outbuf, JNI_ABORT); + if (outbuf) (*env)->ReleaseByteArrayElements(env, out, outbuf, mode); return ret; } @@ -982,7 +1052,7 @@ JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_sm4_1ctr_1decrypt_1init( error_print(); goto end; } - if (sm4_ctr_decrypt_init((SM4_CTR_CTX *)sm4_ctr_ctx, (uint8_t *)keybuf, (uint8_t *)ivbuf) != 1) { + if (sm4_ctr_encrypt_init((SM4_CTR_CTX *)sm4_ctr_ctx, (uint8_t *)keybuf, (uint8_t *)ivbuf) != 1) { error_print(); goto end; } @@ -1032,7 +1102,7 @@ JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_sm4_1ctr_1decrypt_1update( error_print(); goto end; } - if (sm4_ctr_decrypt_update((SM4_CTR_CTX *)sm4_ctr_ctx, (uint8_t *)inbuf + in_offset, (size_t)inlen, + if (sm4_ctr_encrypt_update((SM4_CTR_CTX *)sm4_ctr_ctx, (uint8_t *)inbuf + in_offset, (size_t)inlen, (uint8_t *)outbuf + out_offset, &outlen) != 1) { error_print(); goto end; @@ -1071,7 +1141,7 @@ JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_sm4_1ctr_1decrypt_1finish( error_print(); goto end; } - if (sm4_ctr_decrypt_finish((SM4_CTR_CTX *)sm4_ctr_ctx, + if (sm4_ctr_encrypt_finish((SM4_CTR_CTX *)sm4_ctr_ctx, (uint8_t *)outbuf + offset, &outlen) != 1) { error_print(); goto end; @@ -1419,6 +1489,173 @@ JNIEXPORT jlong JNICALL Java_org_gmssl_GmSSLJNI_sm2_1key_1generate( return sm2_key; } +/* + * Class: org_gmssl_GmSSLJNI + * Method: zuc_ctx_new + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_org_gmssl_GmSSLJNI_zuc_1ctx_1new( + JNIEnv *env, jclass this) +{ + jlong zuc_ctx; + + if (!(zuc_ctx = (jlong)malloc(sizeof(ZUC_CTX)))) { + error_print(); + return 0; + } + memset((ZUC_CTX *)zuc_ctx, 0, sizeof(ZUC_CTX)); + return zuc_ctx; +} + +/* + * Class: org_gmssl_GmSSLJNI + * Method: zuc_ctx_free + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_org_gmssl_GmSSLJNI_zuc_1ctx_1free( + JNIEnv *env, jclass this, + jlong zuc_ctx) +{ + if (zuc_ctx) { + gmssl_secure_clear((ZUC_CTX *)zuc_ctx, sizeof(ZUC_CTX)); + free((ZUC_CTX *)zuc_ctx); + } +} + +/* + * Class: org_gmssl_GmSSLJNI + * Method: zuc_encrypt_init + * Signature: (J[B[B)I + */ +JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_zuc_1encrypt_1init( + JNIEnv *env, jclass this, + jlong zuc_ctx, jbyteArray key, jbyteArray iv) +{ + jint ret = -1; + jbyte *keybuf = NULL; + jbyte *ivbuf = NULL; + + if (!zuc_ctx) { + error_print(); + return -1; + } + if (!(keybuf = (*env)->GetByteArrayElements(env, key, NULL))) { + error_print(); + return -1; + } + if ((*env)->GetArrayLength(env, key) < ZUC_KEY_SIZE) { + error_print(); + goto end; + } + if (!(ivbuf = (*env)->GetByteArrayElements(env, iv, NULL))) { + error_print(); + goto end; + } + if ((*env)->GetArrayLength(env, iv) < ZUC_IV_SIZE) { + error_print(); + goto end; + } + if (zuc_encrypt_init((ZUC_CTX *)zuc_ctx, (uint8_t *)keybuf, (uint8_t *)ivbuf) != 1) { + error_print(); + goto end; + } + ret = 1; +end: + (*env)->ReleaseByteArrayElements(env, key, keybuf, JNI_ABORT); + if (ivbuf) (*env)->ReleaseByteArrayElements(env, iv, ivbuf, JNI_ABORT); + return ret; +} + +/* + * Class: org_gmssl_GmSSLJNI + * Method: zuc_encrypt_update + * Signature: (J[BII[BI)I + */ +JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_zuc_1encrypt_1update( + JNIEnv *env, jclass this, + jlong zuc_ctx, + jbyteArray in, jint in_offset, jint inlen, + jbyteArray out, jint out_offset) +{ + jint ret = -1; + jbyte *inbuf = NULL; + jbyte *outbuf = NULL; + size_t outlen; + jint mode = JNI_ABORT; + + if (!zuc_ctx) { + error_print(); + return -1; + } + if (!(inbuf = (*env)->GetByteArrayElements(env, in, NULL))) { + error_print(); + return -1; + } + if (check_buf(inbuf, (*env)->GetArrayLength(env, in), in_offset, inlen) != 1) { + error_print(); + goto end; + } + if (!(outbuf = (*env)->GetByteArrayElements(env, out, NULL))) { + error_print(); + goto end; + } + outlen = inlen + 4; // ZUC block size is sizeof(uint32_t) + if (check_buf(outbuf, (*env)->GetArrayLength(env, out), out_offset, outlen) != 1 + || outlen < inlen) { + error_print(); + goto end; + } + if (zuc_encrypt_update((ZUC_CTX *)zuc_ctx, (uint8_t *)inbuf + in_offset, (size_t)inlen, + (uint8_t *)outbuf + out_offset, &outlen) != 1) { + error_print(); + goto end; + } + mode = 0; + ret = (jint)outlen; +end: + (*env)->ReleaseByteArrayElements(env, in, inbuf, JNI_ABORT); + if (outbuf) (*env)->ReleaseByteArrayElements(env, out, outbuf, mode); + return ret; +} + +/* + * Class: org_gmssl_GmSSLJNI + * Method: zuc_encrypt_finish + * Signature: (J[BI)I + */ +JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_zuc_1encrypt_1finish( + JNIEnv *env, jclass this, + jlong zuc_ctx, jbyteArray out, jint offset) +{ + jint ret = -1; + jbyte *outbuf = NULL; + size_t outlen; + jint mode = JNI_ABORT; + + if (!zuc_ctx) { + error_print(); + return -1; + } + if (!(outbuf = (*env)->GetByteArrayElements(env, out, 0))) { + error_print(); + goto end; + } + if ((*env)->GetArrayLength(env, out) < offset + 4) { // ZUC block size is sizeof(uint32) == 4 + error_print(); + goto end; + } + if (zuc_encrypt_finish((ZUC_CTX *)zuc_ctx, + (uint8_t *)outbuf + offset, &outlen) != 1) { + error_print(); + goto end; + } + mode = 0; + ret = (jint)outlen; +end: + if (outbuf) (*env)->ReleaseByteArrayElements(env, out, outbuf, mode); + return ret; +} + /* * Class: org_gmssl_GmSSLJNI * Method: sm2_key_free @@ -1434,6 +1671,146 @@ JNIEXPORT void JNICALL Java_org_gmssl_GmSSLJNI_sm2_1key_1free( } } +/* + * Class: org_gmssl_GmSSLJNI + * Method: sm2_private_key_info_to_der + * Signature: (J)[B + */ +JNIEXPORT jbyteArray JNICALL Java_org_gmssl_GmSSLJNI_sm2_1private_1key_1info_1to_1der( + JNIEnv *env, jclass this, + jlong sm2_key) +{ + jbyteArray ret = NULL; + uint8_t outbuf[1024]; + uint8_t *p = outbuf; + size_t outlen = 0; + + if (sm2_private_key_info_to_der((SM2_KEY *)sm2_key, &p, &outlen) != 1) { + error_print(); + return NULL; + } + if (!(ret = (*env)->NewByteArray(env, outlen))) { + error_print(); + gmssl_secure_clear(outbuf, sizeof(outbuf)); + return NULL; + } + (*env)->SetByteArrayRegion(env, ret, 0, outlen, (jbyte *)outbuf); + gmssl_secure_clear(outbuf, sizeof(outbuf)); + return ret; +} + +/* + * Class: org_gmssl_GmSSLJNI + * Method: sm2_private_key_info_from_der + * Signature: ([B)J + */ +JNIEXPORT jlong JNICALL Java_org_gmssl_GmSSLJNI_sm2_1private_1key_1info_1from_1der( + JNIEnv *env, jclass this, + jbyteArray der) +{ + jlong ret = 0; + SM2_KEY *sm2_key = NULL; + jbyte *derbuf = NULL; + size_t derlen; + const uint8_t *attrs; + size_t attrslen; + const uint8_t *cp; + + if (!(derbuf = (*env)->GetByteArrayElements(env, der, NULL))) { + error_print(); + return 0; + } + derlen = (*env)->GetArrayLength(env, der); + + if (!(sm2_key = (SM2_KEY *)malloc(sizeof(SM2_KEY)))) { + error_print(); + goto end; + } + cp = (const uint8_t *)derbuf; + if (sm2_private_key_info_from_der(sm2_key, &attrs, &attrslen, &cp, &derlen) != 1) { + error_print(); + goto end; + } + ret = (jlong)sm2_key; + sm2_key = NULL; +end: + (*env)->ReleaseByteArrayElements(env, der, derbuf, JNI_ABORT); + if (sm2_key) { + gmssl_secure_clear(sm2_key, sizeof(SM2_KEY)); + free(sm2_key); + } + return ret; +} + +/* + * Class: org_gmssl_GmSSLJNI + * Method: sm2_public_key_info_to_der + * Signature: (J)[B + */ +JNIEXPORT jbyteArray JNICALL Java_org_gmssl_GmSSLJNI_sm2_1public_1key_1info_1to_1der( + JNIEnv *env, jclass this, + jlong sm2_key) +{ + jbyteArray ret = NULL; + uint8_t outbuf[1024]; + uint8_t *p = outbuf; + size_t outlen = 0; + + if (sm2_public_key_info_to_der((SM2_KEY *)sm2_key, &p, &outlen) != 1) { + error_print(); + return NULL; + } + if (!(ret = (*env)->NewByteArray(env, outlen))) { + error_print(); + gmssl_secure_clear(outbuf, sizeof(outbuf)); + return NULL; + } + (*env)->SetByteArrayRegion(env, ret, 0, outlen, (jbyte *)outbuf); + gmssl_secure_clear(outbuf, sizeof(outbuf)); + return ret; +} + +/* + * Class: org_gmssl_GmSSLJNI + * Method: sm2_public_key_info_from_der + * Signature: ([B)J + */ +JNIEXPORT jlong JNICALL Java_org_gmssl_GmSSLJNI_sm2_1public_1key_1info_1from_1der( + JNIEnv *env, jclass this, + jbyteArray der) +{ + jlong ret = 0; + SM2_KEY *sm2_key = NULL; + jbyte *derbuf = NULL; + size_t derlen; + const uint8_t *cp; + + if (!(derbuf = (*env)->GetByteArrayElements(env, der, NULL))) { + error_print(); + return 0; + } + derlen = (*env)->GetArrayLength(env, der); // return jsize which is int! + + if (!(sm2_key = (SM2_KEY *)malloc(sizeof(SM2_KEY)))) { + error_print(); + goto end; + } + cp = (const uint8_t *)derbuf; + if (sm2_public_key_info_from_der(sm2_key, &cp, &derlen) != 1) { + error_print(); + goto end; + } + ret = (jlong)sm2_key; + sm2_key = NULL; +end: + (*env)->ReleaseByteArrayElements(env, der, derbuf, JNI_ABORT); + if (sm2_key) { + gmssl_secure_clear(sm2_key, sizeof(SM2_KEY)); + free(sm2_key); + } + return ret; +} + // FIXME: ReleaseStringUTFChars ?? no mode? /* @@ -1812,11 +2189,11 @@ JNIEXPORT jlong JNICALL Java_org_gmssl_GmSSLJNI_sm2_1sign_1ctx_1new( { jlong sm2_sign_ctx; - if (!(sm2_sign_ctx = (jlong)malloc(sizeof(SM2_SIGN_CTX)))) { + if (!(sm2_sign_ctx = (jlong)malloc(SM2_SIGNATURE_CTX_SIZE))) { error_print(); return 0; } - memset((SM2_SIGN_CTX *)sm2_sign_ctx, 0, sizeof(SM2_SIGN_CTX)); + memset((void *)sm2_sign_ctx, 0, SM2_SIGNATURE_CTX_SIZE); return sm2_sign_ctx; } @@ -1830,7 +2207,7 @@ JNIEXPORT void JNICALL Java_org_gmssl_GmSSLJNI_sm2_1sign_1ctx_1free( jlong sm2_sign_ctx) { if (sm2_sign_ctx) { - gmssl_secure_clear((SM2_SIGN_CTX *)sm2_sign_ctx, sizeof(SM2_SIGN_CTX)); + gmssl_secure_clear((void *)sm2_sign_ctx, SM2_SIGNATURE_CTX_SIZE); free((SM2_SIGN_CTX *)sm2_sign_ctx); } } @@ -1957,7 +2334,7 @@ JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_sm2_1verify_1init( error_print(); return -1; } - if (sm2_verify_init((SM2_SIGN_CTX *)sm2_sign_ctx, (SM2_KEY *)sm2_pub, id_str, strlen(id_str)) != 1) { + if (sm2_verify_init((SM2_VERIFY_CTX *)sm2_sign_ctx, (SM2_KEY *)sm2_pub, id_str, strlen(id_str)) != 1) { error_print(); goto end; } @@ -1992,7 +2369,7 @@ JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_sm2_1verify_1update( error_print(); goto end; } - if (sm2_verify_update((SM2_SIGN_CTX *)sm2_sign_ctx, (uint8_t *)buf + offset, (size_t)length) != 1) { + if (sm2_verify_update((SM2_VERIFY_CTX *)sm2_sign_ctx, (uint8_t *)buf + offset, (size_t)length) != 1) { error_print(); goto end; } @@ -2024,7 +2401,7 @@ JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_sm2_1verify_1finish( return -1; } siglen = (*env)->GetArrayLength(env, sig); - if ((ret = sm2_verify_finish((SM2_SIGN_CTX *)sm2_sign_ctx, (uint8_t *)sigbuf, (size_t)siglen)) < 0) { + if ((ret = sm2_verify_finish((SM2_VERIFY_CTX *)sm2_sign_ctx, (uint8_t *)sigbuf, (size_t)siglen)) < 0) { error_print(); goto end; } @@ -3439,6 +3816,7 @@ JNIEXPORT jlong JNICALL Java_org_gmssl_GmSSLJNI_cert_1get_1subject_1public_1key( jbyte *certbuf; jsize certlen; SM2_KEY *sm2_pub = NULL; + X509_KEY x509_key; if (!(certbuf = (*env)->GetByteArrayElements(env, cert, NULL))) { error_print(); @@ -3450,10 +3828,18 @@ JNIEXPORT jlong JNICALL Java_org_gmssl_GmSSLJNI_cert_1get_1subject_1public_1key( goto end; } memset(sm2_pub, 0, sizeof(SM2_KEY)); - if (x509_cert_get_subject_public_key((uint8_t *)certbuf, certlen, sm2_pub) != 1) { + memset(&x509_key, 0, sizeof(x509_key)); + if (x509_cert_get_subject_public_key((uint8_t *)certbuf, certlen, &x509_key) != 1) { error_print(); goto end; } + if (x509_key.algor != OID_ec_public_key || x509_key.algor_param != OID_sm2) { + error_print(); + x509_key_cleanup(&x509_key); + goto end; + } + memcpy(sm2_pub, &x509_key.u.sm2_key, sizeof(SM2_KEY)); + x509_key_cleanup(&x509_key); ret = (jlong)sm2_pub; sm2_pub = NULL; end: @@ -3503,4 +3889,3 @@ JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_cert_1verify_1by_1ca_1cert( if (id_str) (*env)->ReleaseStringUTFChars(env, ca_sm2_id, id_str); return ret; } - diff --git a/gmssljni.h b/src/main/c/gmssljni.h similarity index 89% rename from gmssljni.h rename to src/main/c/gmssljni.h index 90e4239..f9dbe2a 100644 --- a/gmssljni.h +++ b/src/main/c/gmssljni.h @@ -13,10 +13,24 @@ extern "C" { #define org_gmssl_GmSSLJNI_SM3_HMAC_SIZE 32L #undef org_gmssl_GmSSLJNI_SM3_HMAC_MIN_KEY_SIZE #define org_gmssl_GmSSLJNI_SM3_HMAC_MIN_KEY_SIZE 16L +#undef org_gmssl_GmSSLJNI_SM3_PBKDF2_MIN_ITER +#define org_gmssl_GmSSLJNI_SM3_PBKDF2_MIN_ITER 10000L +#undef org_gmssl_GmSSLJNI_SM3_PBKDF2_MAX_ITER +#define org_gmssl_GmSSLJNI_SM3_PBKDF2_MAX_ITER 16777216L +#undef org_gmssl_GmSSLJNI_SM3_PBKDF2_MAX_SALT_SIZE +#define org_gmssl_GmSSLJNI_SM3_PBKDF2_MAX_SALT_SIZE 64L +#undef org_gmssl_GmSSLJNI_SM3_PBKDF2_DEFAULT_SALT_SIZE +#define org_gmssl_GmSSLJNI_SM3_PBKDF2_DEFAULT_SALT_SIZE 8L +#undef org_gmssl_GmSSLJNI_SM3_PBKDF2_MAX_KEY_SIZE +#define org_gmssl_GmSSLJNI_SM3_PBKDF2_MAX_KEY_SIZE 256L #undef org_gmssl_GmSSLJNI_SM4_KEY_SIZE #define org_gmssl_GmSSLJNI_SM4_KEY_SIZE 16L #undef org_gmssl_GmSSLJNI_SM4_BLOCK_SIZE #define org_gmssl_GmSSLJNI_SM4_BLOCK_SIZE 16L +#undef org_gmssl_GmSSLJNI_SM4_CBC_IV_SIZE +#define org_gmssl_GmSSLJNI_SM4_CBC_IV_SIZE 16L +#undef org_gmssl_GmSSLJNI_SM4_CTR_IV_SIZE +#define org_gmssl_GmSSLJNI_SM4_CTR_IV_SIZE 16L #undef org_gmssl_GmSSLJNI_SM4_GCM_MIN_IV_SIZE #define org_gmssl_GmSSLJNI_SM4_GCM_MIN_IV_SIZE 1L #undef org_gmssl_GmSSLJNI_SM4_GCM_MAX_IV_SIZE @@ -137,6 +151,14 @@ JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_sm3_1hmac_1update JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_sm3_1hmac_1finish (JNIEnv *, jclass, jlong, jbyteArray); +/* + * Class: org_gmssl_GmSSLJNI + * Method: sm3_pbkdf2 + * Signature: (Ljava/lang/String;[BII)[B + */ +JNIEXPORT jbyteArray JNICALL Java_org_gmssl_GmSSLJNI_sm3_1pbkdf2 + (JNIEnv *, jclass, jstring, jbyteArray, jint, jint); + /* * Class: org_gmssl_GmSSLJNI * Method: sm4_key_new @@ -369,6 +391,46 @@ JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_sm4_1gcm_1decrypt_1update JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_sm4_1gcm_1decrypt_1finish (JNIEnv *, jclass, jlong, jbyteArray, jint); +/* + * Class: org_gmssl_GmSSLJNI + * Method: zuc_ctx_new + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_org_gmssl_GmSSLJNI_zuc_1ctx_1new + (JNIEnv *, jclass); + +/* + * Class: org_gmssl_GmSSLJNI + * Method: zuc_ctx_free + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_org_gmssl_GmSSLJNI_zuc_1ctx_1free + (JNIEnv *, jclass, jlong); + +/* + * Class: org_gmssl_GmSSLJNI + * Method: zuc_encrypt_init + * Signature: (J[B[B)I + */ +JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_zuc_1encrypt_1init + (JNIEnv *, jclass, jlong, jbyteArray, jbyteArray); + +/* + * Class: org_gmssl_GmSSLJNI + * Method: zuc_encrypt_update + * Signature: (J[BII[BI)I + */ +JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_zuc_1encrypt_1update + (JNIEnv *, jclass, jlong, jbyteArray, jint, jint, jbyteArray, jint); + +/* + * Class: org_gmssl_GmSSLJNI + * Method: zuc_encrypt_finish + * Signature: (J[BI)I + */ +JNIEXPORT jint JNICALL Java_org_gmssl_GmSSLJNI_zuc_1encrypt_1finish + (JNIEnv *, jclass, jlong, jbyteArray, jint); + /* * Class: org_gmssl_GmSSLJNI * Method: sm2_key_generate @@ -385,6 +447,38 @@ JNIEXPORT jlong JNICALL Java_org_gmssl_GmSSLJNI_sm2_1key_1generate JNIEXPORT void JNICALL Java_org_gmssl_GmSSLJNI_sm2_1key_1free (JNIEnv *, jclass, jlong); +/* + * Class: org_gmssl_GmSSLJNI + * Method: sm2_private_key_info_to_der + * Signature: (J)[B + */ +JNIEXPORT jbyteArray JNICALL Java_org_gmssl_GmSSLJNI_sm2_1private_1key_1info_1to_1der + (JNIEnv *, jclass, jlong); + +/* + * Class: org_gmssl_GmSSLJNI + * Method: sm2_private_key_info_from_der + * Signature: ([B)J + */ +JNIEXPORT jlong JNICALL Java_org_gmssl_GmSSLJNI_sm2_1private_1key_1info_1from_1der + (JNIEnv *, jclass, jbyteArray); + +/* + * Class: org_gmssl_GmSSLJNI + * Method: sm2_public_key_info_to_der + * Signature: (J)[B + */ +JNIEXPORT jbyteArray JNICALL Java_org_gmssl_GmSSLJNI_sm2_1public_1key_1info_1to_1der + (JNIEnv *, jclass, jlong); + +/* + * Class: org_gmssl_GmSSLJNI + * Method: sm2_public_key_info_from_der + * Signature: ([B)J + */ +JNIEXPORT jlong JNICALL Java_org_gmssl_GmSSLJNI_sm2_1public_1key_1info_1from_1der + (JNIEnv *, jclass, jbyteArray); + /* * Class: org_gmssl_GmSSLJNI * Method: sm2_private_key_info_encrypt_to_pem diff --git a/src/main/java/org/gmssl/GmSSLException.java b/src/main/java/org/gmssl/GmSSLException.java new file mode 100644 index 0000000..393db56 --- /dev/null +++ b/src/main/java/org/gmssl/GmSSLException.java @@ -0,0 +1,21 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +package org.gmssl; + +/** + * @author gmssl + */ +public class GmSSLException extends RuntimeException { + + public GmSSLException(String reason) { + super(reason); + } + +} diff --git a/org/gmssl/GmSSLJNI.java b/src/main/java/org/gmssl/GmSSLJNI.java similarity index 90% rename from org/gmssl/GmSSLJNI.java rename to src/main/java/org/gmssl/GmSSLJNI.java index bddadf9..72903df 100644 --- a/org/gmssl/GmSSLJNI.java +++ b/src/main/java/org/gmssl/GmSSLJNI.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 The GmSSL Project. All Rights Reserved. + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the License); you may * not use this file except in compliance with the License. @@ -11,13 +11,20 @@ public class GmSSLJNI { - public final static String GMSSL_JNI_VERSION = "GmSSL JNI 2.0.0"; + public final static String GMSSL_JNI_VERSION = "GmSSL JNI 1.0.0"; public final static int SM3_DIGEST_SIZE = 32; public final static int SM3_HMAC_SIZE = 32; public final static int SM3_HMAC_MIN_KEY_SIZE = 16; + public final static int SM3_PBKDF2_MIN_ITER = 10000; // from + public final static int SM3_PBKDF2_MAX_ITER = 16777216; // 2^24 + public final static int SM3_PBKDF2_MAX_SALT_SIZE = 64; // from + public final static int SM3_PBKDF2_DEFAULT_SALT_SIZE = 8; // from + public final static int SM3_PBKDF2_MAX_KEY_SIZE = 256; // from gmssljni.c:sm3_pbkdf2():sizeof(keybuf) public final static int SM4_KEY_SIZE = 16; public final static int SM4_BLOCK_SIZE = 16; + public final static int SM4_CBC_IV_SIZE = 16; + public final static int SM4_CTR_IV_SIZE = 16; public final static int SM4_GCM_MIN_IV_SIZE = 1; public final static int SM4_GCM_MAX_IV_SIZE = 64; public final static int SM4_GCM_DEFAULT_IV_SIZE = 12; @@ -41,6 +48,7 @@ public class GmSSLJNI { public final static native int sm3_hmac_init(long sm3_hmac_ctx, byte[] key); public final static native int sm3_hmac_update(long sm3_hmac_ctx, byte[] data, int offset, int datalen); public final static native int sm3_hmac_finish(long sm3_hmac_ctx, byte[] hmac); + public final static native byte[] sm3_pbkdf2(String pass, byte[] salt, int iter, int keylen); public final static native long sm4_key_new(); public final static native void sm4_key_free(long sm4_key); public final static native int sm4_set_encrypt_key(long sm4_key, byte[] key); @@ -70,8 +78,19 @@ public class GmSSLJNI { public final static native int sm4_gcm_decrypt_init(long sm4_gcm_ctx, byte[] key, byte[] iv, byte[] aad, int taglen); public final static native int sm4_gcm_decrypt_update(long sm4_gcm_ctx, byte[] in, int in_offset, int inlen, byte[] out, int out_offset); public final static native int sm4_gcm_decrypt_finish(long sm4_gcm_ctx, byte[] out, int out_offset); + public final static native long zuc_ctx_new(); + public final static native void zuc_ctx_free(long zuc_ctx); + public final static native int zuc_encrypt_init(long zuc_ctx, byte[] key, byte[] iv); + public final static native int zuc_encrypt_update(long zuc_ctx, byte[] in, int in_offset, int inlen, byte[] out, int out_offset); + public final static native int zuc_encrypt_finish(long zuc_ctx, byte[] out, int out_offset); public final static native long sm2_key_generate(); public final static native void sm2_key_free(long sm2_key); + + public final static native byte[] sm2_private_key_info_to_der(long sm2_key); + public final static native long sm2_private_key_info_from_der(byte[] der); + public final static native byte[] sm2_public_key_info_to_der(long sm2_key); + public final static native long sm2_public_key_info_from_der(byte[] der); + public final static native int sm2_private_key_info_encrypt_to_pem(long sm2_key, String pass, String file); public final static native long sm2_private_key_info_decrypt_from_pem(String pass, String file); public final static native int sm2_public_key_info_to_pem(long sm2_key, String file); @@ -174,6 +193,12 @@ public static void main(String[] args) { sm3_hmac_finish(sm3_hmac_ctx, hmac); print_bytes("sm3_hmac('abc')", hmac); + String password = "P@ssw0rd"; + byte[] salt = new byte[SM3_PBKDF2_MAX_SALT_SIZE]; + rand_bytes(salt, 0, salt.length); + byte[] derived_key = sm3_pbkdf2(password, salt, SM3_PBKDF2_MIN_ITER, 16); + print_bytes("sm2_pbkdf2", derived_key); + long sm4_key = sm4_key_new(); sm4_set_encrypt_key(sm4_key, key); byte[] block = new byte[SM4_BLOCK_SIZE]; @@ -332,6 +357,6 @@ public static void main(String[] args) { } static { - System.loadLibrary("gmssljni"); + NativeLoader.load(NativeLoader.GMSSLJNILIB_NAME); } } diff --git a/src/main/java/org/gmssl/NativeLoader.java b/src/main/java/org/gmssl/NativeLoader.java new file mode 100644 index 0000000..df14f82 --- /dev/null +++ b/src/main/java/org/gmssl/NativeLoader.java @@ -0,0 +1,166 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +/** + * @author yongfei.li + * @email 290836576@qq.com + * @date 2023/09/07 + * @description Native lib load util + */ +public class NativeLoader { + + /* custom jni library prefix path relative to project resources */ + private static final String RESOURCELIB_PREFIXPATH = "lib"; + + static final String GMSSLJNILIB_NAME = "libgmssljni"; + + private static final Map loadedLibraries = new HashMap<>(); + + private static final Properties PROPERTIES = new Properties(); + + static { + try (InputStream input = NativeLoader.class.getClassLoader().getResourceAsStream("config.properties")) { + if (input == null) { + throw new GmSSLException("can't find config file: config.properties"); + } + PROPERTIES.load(input); + } catch (IOException e) { + e.printStackTrace(); + throw new GmSSLException("can't load config file: config.properties"); + } + } + + /** + * load jni lib from resources path,the parameter does not contain the path and suffix. + * + * @param library libraryName + */ + public static void load(String library) { + if (loadedLibraries.containsKey(library)) { + return; + } + Path tempFile = null; + String resourceLibPath = RESOURCELIB_PREFIXPATH + "/" + library + "." + libExtension(); + try (InputStream inputStream = NativeLoader.class.getClassLoader().getResourceAsStream(resourceLibPath)) { + if (inputStream == null) { + throw new GmSSLException("lib file not found in classpath: " + resourceLibPath); + } + tempFile = Files.createTempFile(library, "." + libExtension()); + tempFile.toFile().deleteOnExit(); + Files.copy(inputStream, tempFile, StandardCopyOption.REPLACE_EXISTING); + checkReferencedLib(); + System.load(tempFile.toAbsolutePath().toString()); + loadedLibraries.put(library, tempFile); + }catch (IOException e){ + throw new GmSSLException("lib file not found:"+ e.getMessage()); + }catch (UnsatisfiedLinkError e){ + throw new GmSSLException("Failed to load native library:"+ e.getMessage()); + } catch (Exception e) { + throw new GmSSLException("Unable to load lib!"); + } + } + + /** + * Get the operating system type. + * + * @return operating system name + */ + static String osType() { + String os = "unknown"; + String vmName = System.getProperty("java.vm.name"); + if ("dalvik".equalsIgnoreCase(vmName) || "art".equalsIgnoreCase(vmName)) { + os = "android"; + } + String osName = System.getProperty("os.name").toLowerCase(); + if (osName.startsWith("windows")) { + os = "win"; + } else if (osName.startsWith("linux")) { + os = "linux"; + } else if (osName.startsWith("mac os x") || osName.startsWith("darwin")) { + os = "osx"; + } else { + System.err.println("Unsupported OS: " + osName); + } + return os; + } + + /** + * Get the library extension name based on the operating system type. + * + * @return extension name + */ + static String libExtension() { + String osType = osType(); + String libExtension = null; + switch (osType) { + case "win": + libExtension = "dll"; + break; + case "osx": + libExtension = "dylib"; + break; + case "linux": + case "android": + libExtension = "so"; + break; + default: + throw new IllegalArgumentException("Unsupported OS type!"); + } + return libExtension; + } + + + /** + * In macOS systems, the execution of library calls relies on loading gmssl.3.dylib from the installed gmssl library, + * in order to correct the @rpath path issue. Alternatively, you can manually execute the command + * "install_name_tool -change @rpath/libgmssl.3.dylib /usr/local/lib/libgmssl.3.dylib xxx/lib/libgmssljni.dylib" to fix the library reference path issue. + * This has already been loaded and manual execution is unnecessary. + */ + private static void checkReferencedLib() { + String gmsslRoot = System.getProperty("gmssl.root"); + if (gmsslRoot == null || gmsslRoot.isEmpty()) { + gmsslRoot = System.getenv("GMSSL_ROOT"); + } + if (gmsslRoot == null || gmsslRoot.isEmpty()) { + return; + } + + String os = osType(); + if ("osx".equals(os)) { + // Pre-load libgmssl.3.dylib so that @rpath resolution works + String macReferencedLib = PROPERTIES.getProperty("macReferencedLib"); + if (macReferencedLib == null || macReferencedLib.isEmpty()) { + Path libPath = Paths.get(gmsslRoot, "lib", "libgmssl.3.dylib"); + macReferencedLib = libPath.toString(); + } + File libFile = new File(macReferencedLib); + if (libFile.exists()) { + System.load(macReferencedLib); + } + } else if ("win".equals(os)) { + // Pre-load gmssl.dll so that the JNI DLL can resolve its dependency + Path libPath = Paths.get(gmsslRoot, "bin", "gmssl.dll"); + File libFile = libPath.toFile(); + if (libFile.exists()) { + System.load(libPath.toString()); + } + } + } + +} diff --git a/src/main/java/org/gmssl/Random.java b/src/main/java/org/gmssl/Random.java new file mode 100644 index 0000000..d3a0112 --- /dev/null +++ b/src/main/java/org/gmssl/Random.java @@ -0,0 +1,37 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +package org.gmssl; + +public class Random { + + public Random() { + } + + public byte[] randBytes(int len) { + byte[] out = new byte[len]; + if (GmSSLJNI.rand_bytes(out, 0, len) != 1) { + throw new GmSSLException(""); + } + return out; + } + + public void randBytes(byte[] out, int offset, int len) { + if (out == null + || offset < 0 + || len < 0 + || offset + len <= 0 + || out.length < offset + len) { + throw new GmSSLException(""); + } + if (GmSSLJNI.rand_bytes(out, offset, len) != 1) { + throw new GmSSLException(""); + } + } +} diff --git a/src/main/java/org/gmssl/Sm2Certificate.java b/src/main/java/org/gmssl/Sm2Certificate.java new file mode 100644 index 0000000..64d3a55 --- /dev/null +++ b/src/main/java/org/gmssl/Sm2Certificate.java @@ -0,0 +1,112 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +package org.gmssl; + +public class Sm2Certificate { + + private byte[] cert = null; + + public Sm2Certificate() { + this.cert = null; + } + + public byte[] getBytes() { + if (this.cert == null) { + throw new GmSSLException(""); + } + return this.cert; + } + + public void importPem(String file) { + if ((this.cert = GmSSLJNI.cert_from_pem(file)) == null) { + throw new GmSSLException(""); + } + } + + public void exportPem(String file) { + if (this.cert == null) { + throw new GmSSLException(""); + } + if (GmSSLJNI.cert_to_pem(this.cert, file) != 1) { + throw new GmSSLException(""); + } + } + + public byte[] getSerialNumber() { + if (this.cert == null) { + throw new GmSSLException(""); + } + byte[] serial; + if ((serial = GmSSLJNI.cert_get_serial_number(this.cert)) == null) { + throw new GmSSLException(""); + } + return serial; + } + + public String[] getIssuer() { + if (this.cert == null) { + throw new GmSSLException(""); + } + String[] issuer; + if ((issuer = GmSSLJNI.cert_get_issuer(this.cert)) == null) { + throw new GmSSLException(""); + } + return issuer; + } + + public String[] getSubject() { + if (this.cert == null) { + throw new GmSSLException(""); + } + String[] subject; + if ((subject = GmSSLJNI.cert_get_subject(this.cert)) == null) { + throw new GmSSLException(""); + } + return subject; + } + + public java.util.Date getNotBefore() { + if (this.cert == null) { + throw new GmSSLException(""); + } + return new java.util.Date(GmSSLJNI.cert_get_not_before(this.cert)); + } + + public java.util.Date getNotAfter() { + if (this.cert == null) { + throw new GmSSLException(""); + } + return new java.util.Date(GmSSLJNI.cert_get_not_after(this.cert)); + } + + public Sm2Key getSubjectPublicKey() { + if (this.cert == null) { + throw new GmSSLException(""); + } + long pub_key; + if ((pub_key = GmSSLJNI.cert_get_subject_public_key(this.cert)) == 0) { + throw new GmSSLException(""); + } + boolean has_private_key = false; + return new Sm2Key(pub_key, has_private_key); + } + + public boolean verifyByCaCertificate(Sm2Certificate caCert, String sm2Id) { + if (this.cert == null) { + throw new GmSSLException(""); + } + int ret = GmSSLJNI.cert_verify_by_ca_cert(this.cert, caCert.getBytes(), sm2Id); + if (ret == 1) { + return true; + } else { + return false; + } + } +} diff --git a/src/main/java/org/gmssl/Sm2Key.java b/src/main/java/org/gmssl/Sm2Key.java new file mode 100644 index 0000000..08d70fc --- /dev/null +++ b/src/main/java/org/gmssl/Sm2Key.java @@ -0,0 +1,233 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +package org.gmssl; + + +public class Sm2Key { + + public final static int MAX_PLAINTEXT_SIZE = GmSSLJNI.SM2_MAX_PLAINTEXT_SIZE; + public final static String DEFAULT_ID = GmSSLJNI.SM2_DEFAULT_ID; + + private long sm2_key = 0; + private boolean has_private_key = false; + + public Sm2Key() { + this.sm2_key = 0; + } + + Sm2Key(long sm2_key, boolean has_private_key) { + this.sm2_key = sm2_key; + this.has_private_key = has_private_key; + } + + long getPrivateKey() { + if (this.sm2_key == 0) { + throw new GmSSLException(""); + } + if (this.has_private_key == false) { + throw new GmSSLException(""); + } + return this.sm2_key; + } + + long getPublicKey() { + if (this.sm2_key == 0) { + throw new GmSSLException(""); + } + return this.sm2_key; + } + + public void generateKey() { + if (this.sm2_key != 0) { + GmSSLJNI.sm2_key_free(this.sm2_key); + } + if ((sm2_key = GmSSLJNI.sm2_key_generate()) == 0) { + throw new GmSSLException(""); + } + this.has_private_key = true; + } + + public void importPrivateKeyInfoDer(byte[] der) { + if (der == null) { + throw new GmSSLException(""); + } + if (this.sm2_key != 0) { + GmSSLJNI.sm2_key_free(this.sm2_key); + } + if ((this.sm2_key = GmSSLJNI.sm2_private_key_info_from_der(der)) == 0) { + throw new GmSSLException(""); + } + this.has_private_key = true; + } + + public byte[] exportPrivateKeyInfoDer() { + if (this.sm2_key == 0) { + throw new GmSSLException(""); + } + if (this.has_private_key == false) { + throw new GmSSLException(""); + } + byte[] der; + if ((der = GmSSLJNI.sm2_private_key_info_to_der(this.sm2_key)) == null) { + throw new GmSSLException(""); + } + return der; + } + + public void importPublicKeyInfoDer(byte[] der) { + if (der == null) { + throw new GmSSLException(""); + } + if (this.sm2_key != 0) { + GmSSLJNI.sm2_key_free(this.sm2_key); + } + if ((this.sm2_key = GmSSLJNI.sm2_public_key_info_from_der(der)) == 0) { + throw new GmSSLException(""); + } + this.has_private_key = false; + } + + public byte[] exportPublicKeyInfoDer() { + if (this.sm2_key == 0) { + throw new GmSSLException(""); + } + byte[] der; + if ((der = GmSSLJNI.sm2_public_key_info_to_der(this.sm2_key)) == null) { + throw new GmSSLException(""); + } + return der; + } + + public void importEncryptedPrivateKeyInfoPem(String pass, String file) { + if (this.sm2_key != 0) { + GmSSLJNI.sm2_key_free(this.sm2_key); + } + if ((sm2_key = GmSSLJNI.sm2_private_key_info_decrypt_from_pem(pass, file)) == 0) { + throw new GmSSLException(""); + } + this.has_private_key = true; + } + + public void exportEncryptedPrivateKeyInfoPem(String pass, String file) { + if (this.sm2_key == 0) { + throw new GmSSLException(""); + } + if (this.has_private_key == false) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm2_private_key_info_encrypt_to_pem(this.sm2_key, pass, file) != 1) { + throw new GmSSLException(""); + } + } + + public void importPublicKeyInfoPem(String file) { + if (this.sm2_key != 0) { + GmSSLJNI.sm2_key_free(this.sm2_key); + } + if ((this.sm2_key = GmSSLJNI.sm2_public_key_info_from_pem(file)) == 0) { + throw new GmSSLException(""); + } + this.has_private_key = false; + } + + public void exportPublicKeyInfoPem(String file) { + if (this.sm2_key == 0) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm2_public_key_info_to_pem(this.sm2_key, file) != 1) { + throw new GmSSLException(""); + } + } + + public byte[] computeZ(String id) { + if (this.sm2_key == 0) { + throw new GmSSLException(""); + } + byte[] z = new byte[Sm3.DIGEST_SIZE]; + if (GmSSLJNI.sm2_compute_z(this.sm2_key, id, z) != 1) { + throw new GmSSLException(""); + } + return z; + } + + public byte[] sign(byte[] dgst) { + if (this.sm2_key == 0) { + throw new GmSSLException(""); + } + if (this.has_private_key == false) { + throw new GmSSLException(""); + } + + if (dgst == null || dgst.length != Sm3.DIGEST_SIZE) { + throw new GmSSLException(""); + } + + byte[] sig; + if ((sig = GmSSLJNI.sm2_sign(this.sm2_key, dgst)) == null) { + throw new GmSSLException(""); + } + return sig; + } + + public boolean verify(byte[] dgst, byte[] signature) { + if (this.sm2_key == 0) { + throw new GmSSLException(""); + } + if (dgst == null + || dgst.length != Sm3.DIGEST_SIZE + || signature == null) { + throw new GmSSLException(""); + } + + int ret; + if ((ret = GmSSLJNI.sm2_verify(this.sm2_key, dgst, signature)) < 0) { + throw new GmSSLException(""); + } + if (ret > 0) { + return true; + } else { + return false; + } + } + + public byte[] encrypt(byte[] plaintext) { + if (this.sm2_key == 0) { + throw new GmSSLException(""); + } + if (plaintext == null + || plaintext.length > this.MAX_PLAINTEXT_SIZE) { + throw new GmSSLException(""); + } + + byte[] ciphertext; + if ((ciphertext = GmSSLJNI.sm2_encrypt(this.sm2_key, plaintext)) == null) { + throw new GmSSLException(""); + } + return ciphertext; + } + + public byte[] decrypt(byte[] ciphertext) { + if (this.sm2_key == 0) { + throw new GmSSLException(""); + } + if (this.has_private_key == false) { + throw new GmSSLException(""); + } + if (ciphertext == null) { + throw new GmSSLException(""); + } + + byte[] plaintext; + if ((plaintext = GmSSLJNI.sm2_decrypt(this.sm2_key, ciphertext)) == null) { + throw new GmSSLException(""); + } + return plaintext; + } +} diff --git a/src/main/java/org/gmssl/Sm2Signature.java b/src/main/java/org/gmssl/Sm2Signature.java new file mode 100644 index 0000000..984fef5 --- /dev/null +++ b/src/main/java/org/gmssl/Sm2Signature.java @@ -0,0 +1,114 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +package org.gmssl; + +public class Sm2Signature { + + public final static String DEFAULT_ID = GmSSLJNI.SM2_DEFAULT_ID; + + private long sm2_sign_ctx = 0; + private boolean inited = false; + private boolean do_sign = true; + + public Sm2Signature(Sm2Key key, String id, boolean do_sign) { + + if ((this.sm2_sign_ctx = GmSSLJNI.sm2_sign_ctx_new()) == 0) { + throw new GmSSLException(""); + } + + if (do_sign == true) { + if (GmSSLJNI.sm2_sign_init(this.sm2_sign_ctx, key.getPrivateKey(), id) != 1) { + throw new GmSSLException(""); + } + } else { + if (GmSSLJNI.sm2_verify_init(sm2_sign_ctx, key.getPublicKey(), id) != 1) { + throw new GmSSLException(""); + } + } + + this.inited = true; + this.do_sign = do_sign; + } + + public void reset(Sm2Key key, String id, boolean do_sign) { + if (do_sign == true) { + if (GmSSLJNI.sm2_sign_init(this.sm2_sign_ctx, key.getPrivateKey(), id) != 1) { + throw new GmSSLException(""); + } + } else { + if (GmSSLJNI.sm2_verify_init(sm2_sign_ctx, key.getPublicKey(), id) != 1) { + throw new GmSSLException(""); + } + } + this.inited = true; + this.do_sign = do_sign; + } + + public void update(byte[] data, int offset, int len) { + + if (this.inited == false) { + throw new GmSSLException(""); + } + + if (data == null + || offset < 0 + || len < 0 + || offset + len <= 0 + || data.length < offset + len) { + throw new GmSSLException(""); + } + + if (this.do_sign == true) { + if (GmSSLJNI.sm2_sign_update(this.sm2_sign_ctx, data, offset, len) != 1) { + throw new GmSSLException(""); + } + } else { + if (GmSSLJNI.sm2_verify_update(this.sm2_sign_ctx, data, offset, len) != 1) { + throw new GmSSLException(""); + } + } + } + + public void update(byte[] data) { + update(data, 0, data.length); + } + + public byte[] sign() { + if (this.inited == false) { + throw new GmSSLException(""); + } + if (this.do_sign == false) { + throw new GmSSLException(""); + } + this.inited = false; + + byte[] sig; + if ((sig = GmSSLJNI.sm2_sign_finish(this.sm2_sign_ctx)) == null) { + throw new GmSSLException(""); + } + return sig; + } + + public boolean verify(byte[] signature) { + if (this.sm2_sign_ctx == 0) { + throw new GmSSLException(""); + } + if (this.do_sign == true) { + throw new GmSSLException(""); + } + this.inited = false; + + int ret; + if ((ret = GmSSLJNI.sm2_verify_finish(sm2_sign_ctx, signature)) != 1) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/gmssl/Sm3.java b/src/main/java/org/gmssl/Sm3.java new file mode 100644 index 0000000..e9d7eca --- /dev/null +++ b/src/main/java/org/gmssl/Sm3.java @@ -0,0 +1,65 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +package org.gmssl; + +public class Sm3 implements AutoCloseable{ + + public final static int DIGEST_SIZE = GmSSLJNI.SM3_DIGEST_SIZE; + + private long sm3_ctx = 0; + + public Sm3() { + if ((sm3_ctx = GmSSLJNI.sm3_ctx_new()) == 0) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm3_init(sm3_ctx) != 1) { + throw new GmSSLException(""); + } + } + + public void reset() { + if (GmSSLJNI.sm3_init(sm3_ctx) != 1) { + throw new GmSSLException(""); + } + } + + public void update(byte[] data, int offset, int len) { + if (data == null + || offset < 0 + || len < 0 + || offset + len <= 0 + || data.length < offset + len) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm3_update(sm3_ctx, data, offset, len) != 1) { + throw new GmSSLException(""); + } + } + + public void update(byte[] data) { + this.update(data, 0, data.length); + } + + public byte[] digest() { + byte[] dgst = new byte[DIGEST_SIZE]; + if (GmSSLJNI.sm3_finish(sm3_ctx, dgst) != 1) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm3_init(sm3_ctx) != 1) { + throw new GmSSLException(""); + } + return dgst; + } + + @Override + public void close() throws Exception { + GmSSLJNI.sm3_ctx_free(sm3_ctx); + } +} diff --git a/src/main/java/org/gmssl/Sm3Hmac.java b/src/main/java/org/gmssl/Sm3Hmac.java new file mode 100644 index 0000000..71f5fd5 --- /dev/null +++ b/src/main/java/org/gmssl/Sm3Hmac.java @@ -0,0 +1,71 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +package org.gmssl; + +public class Sm3Hmac { + + public final static int MAC_SIZE = GmSSLJNI.SM3_HMAC_SIZE; + + private byte[] key; + + private long sm3_hmac_ctx = 0; + + public Sm3Hmac(byte[] key) { + if (key == null) { + throw new GmSSLException(""); + } + if ((this.sm3_hmac_ctx = GmSSLJNI.sm3_hmac_ctx_new()) == 0) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm3_hmac_init(this.sm3_hmac_ctx, key) != 1) { + throw new GmSSLException(""); + } + this.key = key; + } + + public void reset(byte[] key) { + if (key == null) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm3_hmac_init(this.sm3_hmac_ctx, key) != 1) { + throw new GmSSLException(""); + } + this.key = key; + } + + public void update(byte[] data, int offset, int len) { + if (data == null + || offset < 0 + || len < 0 + || offset + len <= 0 + || data.length < offset + len) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm3_hmac_update(this.sm3_hmac_ctx, data, offset, len) != 1) { + throw new GmSSLException(""); + } + } + + public void update(byte[] data) { + this.update(data, 0, data.length); + } + + public byte[] generateMac() { + byte[] mac = new byte[this.MAC_SIZE]; + if (GmSSLJNI.sm3_hmac_finish(this.sm3_hmac_ctx, mac) != 1) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm3_hmac_init(this.sm3_hmac_ctx, this.key) != 1) { + throw new GmSSLException(""); + } + return mac; + } +} + diff --git a/src/main/java/org/gmssl/Sm3Pbkdf2.java b/src/main/java/org/gmssl/Sm3Pbkdf2.java new file mode 100644 index 0000000..4891300 --- /dev/null +++ b/src/main/java/org/gmssl/Sm3Pbkdf2.java @@ -0,0 +1,42 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +package org.gmssl; + +public class Sm3Pbkdf2 { + + public final static int MAX_SALT_SIZE = GmSSLJNI.SM3_PBKDF2_MAX_SALT_SIZE; + public final static int DEFAULT_SALT_SIZE = GmSSLJNI.SM3_PBKDF2_DEFAULT_SALT_SIZE; + public final static int MIN_ITER = GmSSLJNI.SM3_PBKDF2_MIN_ITER; + public final static int MAX_ITER = GmSSLJNI.SM3_PBKDF2_MAX_ITER; + public final static int MAX_KEY_SIZE = GmSSLJNI.SM3_PBKDF2_MAX_KEY_SIZE; + + public Sm3Pbkdf2() { + } + + public byte[] deriveKey(String pass, byte[] salt, int iter, int keylen) { + if (pass == null) { + throw new GmSSLException(""); + } + if (salt == null || salt.length > MAX_SALT_SIZE) { + throw new GmSSLException(""); + } + if (iter < MIN_ITER || iter > MAX_ITER) { + throw new GmSSLException(""); + } + if (keylen < 0 || keylen > MAX_KEY_SIZE) { + throw new GmSSLException(""); + } + byte[] key = GmSSLJNI.sm3_pbkdf2(pass, salt, iter, keylen); + if (key == null) { + throw new GmSSLException(""); + } + return key; + } +} diff --git a/src/main/java/org/gmssl/Sm4.java b/src/main/java/org/gmssl/Sm4.java new file mode 100644 index 0000000..bf9de4f --- /dev/null +++ b/src/main/java/org/gmssl/Sm4.java @@ -0,0 +1,61 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +package org.gmssl; + +public class Sm4 { + + public final static int KEY_SIZE = GmSSLJNI.SM4_KEY_SIZE; + public final static int BLOCK_SIZE = GmSSLJNI.SM4_BLOCK_SIZE; + + private long sm4_key = 0; + + public Sm4(byte[] key, boolean do_encrypt) { + + if (key == null) { + throw new GmSSLException(""); + } + if (key.length != this.KEY_SIZE) { + throw new GmSSLException(""); + } + + if ((sm4_key = GmSSLJNI.sm4_key_new()) == 0) { + throw new GmSSLException(""); + } + + if (do_encrypt == true) { + if (GmSSLJNI.sm4_set_encrypt_key(sm4_key, key) != 1) { + throw new GmSSLException(""); + } + } else { + if (GmSSLJNI.sm4_set_decrypt_key(sm4_key, key) != 1) { + throw new GmSSLException(""); + } + } + } + + public void encrypt(byte[] in, int in_offset, byte[] out, int out_offset) { + if (in == null + || in_offset < 0 + || in_offset + this.BLOCK_SIZE <= 0 + || in_offset + this.BLOCK_SIZE > in.length) { + throw new GmSSLException(""); + } + if (out == null + || out_offset < 0 + || out_offset + this.BLOCK_SIZE <= 0 + || out_offset + this.BLOCK_SIZE > in.length) { + throw new GmSSLException(""); + } + + if (GmSSLJNI.sm4_encrypt(sm4_key, in, in_offset, out, out_offset) != 1) { + throw new GmSSLException(""); + } + } +} diff --git a/src/main/java/org/gmssl/Sm4Cbc.java b/src/main/java/org/gmssl/Sm4Cbc.java new file mode 100644 index 0000000..0e45a1e --- /dev/null +++ b/src/main/java/org/gmssl/Sm4Cbc.java @@ -0,0 +1,112 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +package org.gmssl; + + +public class Sm4Cbc { + + public final static int KEY_SIZE = GmSSLJNI.SM4_KEY_SIZE; + public final static int IV_SIZE = GmSSLJNI.SM4_BLOCK_SIZE; + public final static int BLOCK_SIZE = GmSSLJNI.SM4_BLOCK_SIZE; + + private long sm4_cbc_ctx = 0; + private boolean do_encrypt = true; + private boolean inited = false; + + public Sm4Cbc() { + if ((this.sm4_cbc_ctx = GmSSLJNI.sm4_cbc_ctx_new()) == 0) { + throw new GmSSLException(""); + } + this.inited = false; + } + + public void init(byte[] key, byte[] iv, boolean do_encrypt) { + + if (key == null + || key.length != this.KEY_SIZE + || iv == null + || iv.length != this.IV_SIZE) { + throw new GmSSLException(""); + } + + if (do_encrypt == true) { + if (GmSSLJNI.sm4_cbc_encrypt_init(this.sm4_cbc_ctx, key, iv) != 1) { + throw new GmSSLException(""); + } + } else { + if (GmSSLJNI.sm4_cbc_decrypt_init(this.sm4_cbc_ctx, key, iv) != 1) { + throw new GmSSLException(""); + } + } + + this.do_encrypt = do_encrypt; + this.inited = true; + } + + public int update(byte[] in, int in_offset, int inlen, byte[] out, int out_offset) { + + if (this.inited == false) { + throw new GmSSLException(""); + } + + if (in == null + || in_offset < 0 + || inlen < 0 + || in_offset + inlen <= 0 + || in.length < in_offset + inlen) { + throw new GmSSLException(""); + } + if (out == null + || out_offset < 0 + || out.length < out_offset) { + throw new GmSSLException(""); + } + + int outlen; + if (this.do_encrypt) { + if ((outlen = GmSSLJNI.sm4_cbc_encrypt_update(this.sm4_cbc_ctx, in, in_offset, inlen, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + } else { + if ((outlen = GmSSLJNI.sm4_cbc_decrypt_update(this.sm4_cbc_ctx, in, in_offset, inlen, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + } + + return outlen; + } + + public int doFinal(byte[] out, int out_offset) { + + if (this.inited == false) { + throw new GmSSLException(""); + } + + if (out == null + || out_offset < 0 + || out.length < out_offset) { + throw new GmSSLException(""); + } + + int outlen; + if (this.do_encrypt) { + if ((outlen = GmSSLJNI.sm4_cbc_encrypt_finish(this.sm4_cbc_ctx, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + } else { + if ((outlen = GmSSLJNI.sm4_cbc_decrypt_finish(this.sm4_cbc_ctx, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + } + + this.inited = false; + return outlen; + } +} diff --git a/src/main/java/org/gmssl/Sm4Ctr.java b/src/main/java/org/gmssl/Sm4Ctr.java new file mode 100644 index 0000000..b41f8c8 --- /dev/null +++ b/src/main/java/org/gmssl/Sm4Ctr.java @@ -0,0 +1,90 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +package org.gmssl; + + +public class Sm4Ctr { + + public final static int KEY_SIZE = GmSSLJNI.SM4_KEY_SIZE; + public final static int IV_SIZE = GmSSLJNI.SM4_BLOCK_SIZE; + public final static int BLOCK_SIZE = GmSSLJNI.SM4_BLOCK_SIZE; + + private long sm4_ctr_ctx = 0; + private boolean inited = false; + + public Sm4Ctr() { + if ((this.sm4_ctr_ctx = GmSSLJNI.sm4_ctr_ctx_new()) == 0) { + throw new GmSSLException(""); + } + this.inited = false; + } + + public void init(byte[] key, byte[] iv) { + + if (key == null + || key.length != this.KEY_SIZE + || iv == null + || iv.length != this.IV_SIZE) { + throw new GmSSLException(""); + } + + if (GmSSLJNI.sm4_ctr_encrypt_init(this.sm4_ctr_ctx, key, iv) != 1) { + throw new GmSSLException(""); + } + + this.inited = true; + } + + public int update(byte[] in, int in_offset, int inlen, byte[] out, int out_offset) { + + if (this.inited == false) { + throw new GmSSLException(""); + } + + if (in == null + || in_offset < 0 + || inlen < 0 + || in_offset + inlen <= 0 + || in.length < in_offset + inlen) { + throw new GmSSLException(""); + } + if (out == null + || out_offset < 0 + || out.length < out_offset) { + throw new GmSSLException(""); + } + + int outlen; + if ((outlen = GmSSLJNI.sm4_ctr_encrypt_update(this.sm4_ctr_ctx, in, in_offset, inlen, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + return outlen; + } + + public int doFinal(byte[] out, int out_offset) { + + if (this.inited == false) { + throw new GmSSLException(""); + } + + if (out == null + || out_offset < 0 + || out.length < out_offset) { + throw new GmSSLException(""); + } + + int outlen; + if ((outlen = GmSSLJNI.sm4_ctr_encrypt_finish(this.sm4_ctr_ctx, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + this.inited = false; + return outlen; + } +} diff --git a/src/main/java/org/gmssl/Sm4Gcm.java b/src/main/java/org/gmssl/Sm4Gcm.java new file mode 100644 index 0000000..927422b --- /dev/null +++ b/src/main/java/org/gmssl/Sm4Gcm.java @@ -0,0 +1,120 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +package org.gmssl; + + +public class Sm4Gcm { + + public final static int KEY_SIZE = GmSSLJNI.SM4_KEY_SIZE; + public final static int MIN_IV_SIZE = GmSSLJNI.SM4_GCM_MIN_IV_SIZE; + public final static int MAX_IV_SIZE = GmSSLJNI.SM4_GCM_MAX_IV_SIZE; + public final static int DEFAULT_IV_SIZE = GmSSLJNI.SM4_GCM_DEFAULT_IV_SIZE; + public final static int MIN_TAG_SIZE = 8; + public final static int MAX_TAG_SIZE = GmSSLJNI.SM4_GCM_MAX_TAG_SIZE; + public final static int BLOCK_SIZE = GmSSLJNI.SM4_BLOCK_SIZE; + + private long sm4_gcm_ctx = 0; + private boolean do_encrypt = true; + private boolean inited = false; + + + public Sm4Gcm() { + if ((this.sm4_gcm_ctx = GmSSLJNI.sm4_gcm_ctx_new()) == 0) { + throw new GmSSLException(""); + } + this.inited = false; + } + + public void init(byte[] key, byte[] iv, byte[] aad, int taglen, boolean do_encrypt) { + + if (key == null + || key.length != this.KEY_SIZE + || iv == null + || iv.length < this.MIN_IV_SIZE + || iv.length > this.MAX_IV_SIZE + || taglen < this.MIN_TAG_SIZE + || taglen > this.MAX_TAG_SIZE) { + throw new GmSSLException(""); + } + + if (do_encrypt == true) { + if (GmSSLJNI.sm4_gcm_encrypt_init(this.sm4_gcm_ctx, key, iv, aad, taglen) != 1) { + throw new GmSSLException(""); + } + } else { + if (GmSSLJNI.sm4_gcm_decrypt_init(this.sm4_gcm_ctx, key, iv, aad, taglen) != 1) { + throw new GmSSLException(""); + } + } + + this.do_encrypt = do_encrypt; + this.inited = true; + } + + public int update(byte[] in, int in_offset, int inlen, byte[] out, int out_offset) { + + if (this.inited == false) { + throw new GmSSLException(""); + } + + if (in == null + || in_offset < 0 + || inlen < 0 + || in_offset + inlen <= 0 + || in.length < in_offset + inlen) { + throw new GmSSLException(""); + } + if (out == null + || out_offset < 0 + || out.length < out_offset) { + throw new GmSSLException(""); + } + + int outlen; + if (this.do_encrypt) { + if ((outlen = GmSSLJNI.sm4_gcm_encrypt_update(this.sm4_gcm_ctx, in, in_offset, inlen, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + } else { + if ((outlen = GmSSLJNI.sm4_gcm_decrypt_update(this.sm4_gcm_ctx, in, in_offset, inlen, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + } + + return outlen; + } + + public int doFinal(byte[] out, int out_offset) { + + if (this.inited == false) { + throw new GmSSLException(""); + } + + if (out == null + || out_offset < 0 + || out.length < out_offset) { + throw new GmSSLException(""); + } + + int outlen; + if (this.do_encrypt) { + if ((outlen = GmSSLJNI.sm4_gcm_encrypt_finish(this.sm4_gcm_ctx, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + } else { + if ((outlen = GmSSLJNI.sm4_gcm_decrypt_finish(this.sm4_gcm_ctx, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + } + + this.inited = false; + return outlen; + } +} diff --git a/src/main/java/org/gmssl/Sm9EncKey.java b/src/main/java/org/gmssl/Sm9EncKey.java new file mode 100644 index 0000000..c527d95 --- /dev/null +++ b/src/main/java/org/gmssl/Sm9EncKey.java @@ -0,0 +1,63 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +package org.gmssl; + +public class Sm9EncKey { + + private long sm9_enc_key = 0; + private String id; + + Sm9EncKey(long key, String id) { + this.sm9_enc_key = key; + this.id = id; + } + + public Sm9EncKey(String id) { + this.sm9_enc_key = 0; + this.id = id; + } + + public void importEncryptedPrivateKeyInfoPem(String pass, String file) { + if (this.sm9_enc_key != 0) { + GmSSLJNI.sm9_enc_key_free(this.sm9_enc_key); + } + if ((this.sm9_enc_key = GmSSLJNI.sm9_enc_key_info_decrypt_from_pem(pass, file)) == 0) { + throw new GmSSLException(""); + } + } + + public void exportEncryptedPrivateKeyInfoPem(String pass, String file) { + if (this.sm9_enc_key == 0) { + throw new GmSSLException("Key not initialized"); + } + if (GmSSLJNI.sm9_enc_key_info_encrypt_to_pem(this.sm9_enc_key, pass, file) != 1) { + throw new GmSSLException(""); + } + } + + public String getId() { + return this.id; + } + + public byte[] decrypt(byte[] ciphertext) { + if (this.sm9_enc_key == 0) { + throw new GmSSLException(""); + } + if (ciphertext == null) { + throw new GmSSLException(""); + } + + byte[] plaintext; + if ((plaintext = GmSSLJNI.sm9_decrypt(this.sm9_enc_key, this.id, ciphertext)) == null) { + throw new GmSSLException(""); + } + return plaintext; + } +} diff --git a/src/main/java/org/gmssl/Sm9EncMasterKey.java b/src/main/java/org/gmssl/Sm9EncMasterKey.java new file mode 100644 index 0000000..1e77f67 --- /dev/null +++ b/src/main/java/org/gmssl/Sm9EncMasterKey.java @@ -0,0 +1,121 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +package org.gmssl; + + +public class Sm9EncMasterKey { + + public final static int MAX_PLAINTEXT_SIZE = GmSSLJNI.SM9_MAX_PLAINTEXT_SIZE; + + private long master_key = 0; + private boolean has_private_key = false; + + public Sm9EncMasterKey() { + this.master_key = 0; + } + + public void generateMasterKey() { + if (this.master_key != 0) { + GmSSLJNI.sm9_enc_master_key_free(this.master_key); + } + if ((this.master_key = GmSSLJNI.sm9_enc_master_key_generate()) == 0) { + throw new GmSSLException(""); + } + this.has_private_key = true; + } + + public long getMasterKey() { + if (this.master_key == 0) { + throw new GmSSLException(""); + } + if (this.has_private_key == false) { + throw new GmSSLException(""); + } + return this.master_key; + } + + public long getPublicMasterKey() { + if (this.master_key == 0) { + throw new GmSSLException(""); + } + return this.master_key; + } + + public Sm9EncKey extractKey(String id) { + if (this.master_key == 0) { + throw new GmSSLException(""); + } + if (this.has_private_key == false) { + throw new GmSSLException(""); + } + long key; + if ((key = GmSSLJNI.sm9_enc_master_key_extract_key(this.master_key, id)) == 0) { + throw new GmSSLException(""); + } + return new Sm9EncKey(key, id); + } + + public void importEncryptedMasterKeyInfoPem(String pass, String file) { + if (this.master_key != 0) { + GmSSLJNI.sm9_enc_master_key_free(this.master_key); + } + if ((this.master_key = GmSSLJNI.sm9_enc_master_key_info_decrypt_from_pem(pass, file)) == 0) { + throw new GmSSLException(""); + } + this.has_private_key = true; + } + + public void exportEncryptedMasterKeyInfoPem(String pass, String file) { + if (this.master_key == 0) { + throw new GmSSLException(""); + } + if (this.has_private_key == false) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm9_enc_master_key_info_encrypt_to_pem(this.master_key, pass, file) != 1) { + throw new GmSSLException(""); + } + } + + public void importPublicMasterKeyPem(String file) { + if (this.master_key != 0) { + GmSSLJNI.sm9_enc_master_key_free(this.master_key); + } + if ((this.master_key = GmSSLJNI.sm9_enc_master_public_key_from_pem(file)) == 0) { + throw new GmSSLException(""); + } + this.has_private_key = false; + } + + public void exportPublicMasterKeyPem(String file) { + if (this.master_key == 0) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm9_enc_master_public_key_to_pem(this.master_key, file) != 1) { + throw new GmSSLException(""); + } + } + + public byte[] encrypt(byte[] plaintext, String id) { + if (this.master_key == 0) { + throw new GmSSLException(""); + } + if (plaintext == null + || plaintext.length > this.MAX_PLAINTEXT_SIZE) { + throw new GmSSLException(""); + } + + byte[] ciphertext; + if ((ciphertext = GmSSLJNI.sm9_encrypt(this.master_key, id, plaintext)) == null) { + throw new GmSSLException(""); + } + return ciphertext; + } +} diff --git a/src/main/java/org/gmssl/Sm9SignKey.java b/src/main/java/org/gmssl/Sm9SignKey.java new file mode 100644 index 0000000..5aae198 --- /dev/null +++ b/src/main/java/org/gmssl/Sm9SignKey.java @@ -0,0 +1,55 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +package org.gmssl; + +public class Sm9SignKey { + + private long sm9_sign_key = 0; + private String id; + + Sm9SignKey(long key, String id) { + this.sm9_sign_key = key; + this.id = id; + } + + public Sm9SignKey(String id) { + this.sm9_sign_key = 0; + this.id = id; + } + + long getKey() { + if (this.sm9_sign_key == 0) { + throw new GmSSLException(""); + } + return this.sm9_sign_key; + } + + public String getId() { + return this.id; + } + + public void exportEncryptedPrivateKeyInfoPem(String pass, String file) { + if (this.sm9_sign_key == 0) { + throw new GmSSLException("Key not initialized"); + } + if (GmSSLJNI.sm9_sign_key_info_encrypt_to_pem(this.sm9_sign_key, pass, file) != 1) { + throw new GmSSLException(""); + } + } + + public void importEncryptedPrivateKeyInfoPem(String pass, String file) { + if (this.sm9_sign_key != 0) { + GmSSLJNI.sm9_sign_key_free(this.sm9_sign_key); + } + if ((this.sm9_sign_key = GmSSLJNI.sm9_sign_key_info_decrypt_from_pem(pass, file)) == 0) { + throw new GmSSLException("Import key failure"); + } + } +} diff --git a/src/main/java/org/gmssl/Sm9SignMasterKey.java b/src/main/java/org/gmssl/Sm9SignMasterKey.java new file mode 100644 index 0000000..de5e535 --- /dev/null +++ b/src/main/java/org/gmssl/Sm9SignMasterKey.java @@ -0,0 +1,103 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +package org.gmssl; + + +public class Sm9SignMasterKey { + + private long master_key = 0; + private boolean has_private_key = false; + + public Sm9SignMasterKey() { + this.master_key = 0; + } + + public void generateMasterKey() { + if (this.master_key != 0) { + GmSSLJNI.sm9_sign_master_key_free(this.master_key); + } + if ((this.master_key = GmSSLJNI.sm9_sign_master_key_generate()) == 0) { + throw new GmSSLException(""); + } + this.has_private_key = true; + } + + public long getMasterKey() { + if (this.master_key == 0) { + throw new GmSSLException(""); + } + if (this.has_private_key == false) { + throw new GmSSLException(""); + } + return this.master_key; + } + + public long getPublicMasterKey() { + if (this.master_key == 0) { + throw new GmSSLException(""); + } + return this.master_key; + } + + public Sm9SignKey extractKey(String id) { + if (this.master_key == 0) { + throw new GmSSLException(""); + } + if (this.has_private_key == false) { + throw new GmSSLException(""); + } + long key; + if ((key = GmSSLJNI.sm9_sign_master_key_extract_key(this.master_key, id)) == 0) { + throw new GmSSLException(""); + } + return new Sm9SignKey(key, id); + } + + public void importEncryptedMasterKeyInfoPem(String pass, String file) { + if (this.master_key != 0) { + GmSSLJNI.sm9_sign_master_key_free(this.master_key); + } + if ((this.master_key = GmSSLJNI.sm9_sign_master_key_info_decrypt_from_pem(pass, file)) == 0) { + throw new GmSSLException(""); + } + this.has_private_key = true; + } + + public void exportEncryptedMasterKeyInfoPem(String pass, String file) { + if (this.master_key == 0) { + throw new GmSSLException(""); + } + if (this.has_private_key == false) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm9_sign_master_key_info_encrypt_to_pem(this.master_key, pass, file) != 1) { + throw new GmSSLException(""); + } + } + + public void importPublicMasterKeyPem(String file) { + if (this.master_key != 0) { + GmSSLJNI.sm9_sign_master_key_free(this.master_key); + } + if ((this.master_key = GmSSLJNI.sm9_sign_master_public_key_from_pem(file)) == 0) { + throw new GmSSLException(""); + } + this.has_private_key = false; + } + + public void exportPublicMasterKeyPem(String file) { + if (this.master_key == 0) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm9_sign_master_public_key_to_pem(this.master_key, file) != 1) { + throw new GmSSLException(""); + } + } +} diff --git a/src/main/java/org/gmssl/Sm9Signature.java b/src/main/java/org/gmssl/Sm9Signature.java new file mode 100644 index 0000000..ec21294 --- /dev/null +++ b/src/main/java/org/gmssl/Sm9Signature.java @@ -0,0 +1,108 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +package org.gmssl; + + +public class Sm9Signature { + + private long sm9_sign_ctx = 0; + private boolean inited = false; + private boolean do_sign = true; + + public Sm9Signature(boolean do_sign) { + if ((this.sm9_sign_ctx = GmSSLJNI.sm9_sign_ctx_new()) == 0) { + throw new GmSSLException(""); + } + if (do_sign == true) { + if (GmSSLJNI.sm9_sign_init(this.sm9_sign_ctx) != 1) { + throw new GmSSLException(""); + } + } else { + if (GmSSLJNI.sm9_verify_init(this.sm9_sign_ctx) != 1) { + throw new GmSSLException(""); + } + } + this.inited = true; + this.do_sign = do_sign; + } + + public void reset(boolean do_sign) { + if (do_sign == true) { + if (GmSSLJNI.sm9_sign_init(this.sm9_sign_ctx) != 1) { + throw new GmSSLException(""); + } + } else { + if (GmSSLJNI.sm9_verify_init(this.sm9_sign_ctx) != 1) { + throw new GmSSLException(""); + } + } + this.inited = true; + this.do_sign = do_sign; + } + + public void update(byte[] data, int offset, int len) { + if (this.inited == false) { + throw new GmSSLException(""); + } + if (data == null + || offset < 0 + || len < 0 + || offset + len <= 0 + || data.length < offset + len) { + throw new GmSSLException(""); + } + if (this.do_sign == true) { + if (GmSSLJNI.sm9_sign_update(this.sm9_sign_ctx, data, offset, len) != 1) { + throw new GmSSLException(""); + } + } else { + if (GmSSLJNI.sm9_verify_update(this.sm9_sign_ctx, data, offset, len) != 1) { + throw new GmSSLException(""); + } + } + } + + public void update(byte[] data) { + update(data, 0, data.length); + } + + public byte[] sign(Sm9SignKey signKey) { + if (this.inited == false) { + throw new GmSSLException(""); + } + if (this.do_sign == false) { + throw new GmSSLException(""); + } + + byte[] signature; + if ((signature = GmSSLJNI.sm9_sign_finish(this.sm9_sign_ctx, signKey.getKey())) == null) { + throw new GmSSLException(""); + } + this.inited = false; + return signature; + } + + public boolean verify(byte[] signature, Sm9SignMasterKey masterPublicKey, String id) { + if (this.inited == false) { + throw new GmSSLException(""); + } + if (this.do_sign == true) { + throw new GmSSLException(""); + } + int ret; + ret = GmSSLJNI.sm9_verify_finish(sm9_sign_ctx, signature, masterPublicKey.getPublicMasterKey(), id); + this.inited = false; + if (ret == 1) { + return true; + } else { + return false; + } + } +} diff --git a/src/main/java/org/gmssl/Zuc.java b/src/main/java/org/gmssl/Zuc.java new file mode 100644 index 0000000..b580c32 --- /dev/null +++ b/src/main/java/org/gmssl/Zuc.java @@ -0,0 +1,92 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +package org.gmssl; + + +public class Zuc { + + public final static int KEY_SIZE = GmSSLJNI.ZUC_KEY_SIZE; + public final static int IV_SIZE = GmSSLJNI.ZUC_IV_SIZE; + public final static int BLOCK_SIZE = 4; + + private long zuc_ctx = 0; + private boolean inited = false; + + public Zuc() { + if ((this.zuc_ctx = GmSSLJNI.zuc_ctx_new()) == 0) { + throw new GmSSLException(""); + } + this.inited = false; + } + + public void init(byte[] key, byte[] iv) { + + if (key == null + || key.length != this.KEY_SIZE + || iv == null + || iv.length != this.IV_SIZE) { + throw new GmSSLException(""); + } + + if (GmSSLJNI.zuc_encrypt_init(this.zuc_ctx, key, iv) != 1) { + throw new GmSSLException(""); + } + + this.inited = true; + } + + public int update(byte[] in, int in_offset, int inlen, byte[] out, int out_offset) { + + if (this.inited == false) { + throw new GmSSLException(""); + } + + if (in == null + || in_offset < 0 + || inlen < 0 + || in_offset + inlen <= 0 + || in.length < in_offset + inlen) { + throw new GmSSLException(""); + } + if (out == null + || out_offset < 0 + || out.length < out_offset) { + throw new GmSSLException(""); + } + + int outlen; + if ((outlen = GmSSLJNI.zuc_encrypt_update(this.zuc_ctx, in, in_offset, inlen, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + + return outlen; + } + + public int doFinal(byte[] out, int out_offset) { + + if (this.inited == false) { + throw new GmSSLException(""); + } + + if (out == null + || out_offset < 0 + || out.length < out_offset) { + throw new GmSSLException(""); + } + + int outlen; + if ((outlen = GmSSLJNI.zuc_encrypt_finish(this.zuc_ctx, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + + this.inited = false; + return outlen; + } +} diff --git a/src/main/java/org/gmssl/crypto/CipherPaddingEnum.java b/src/main/java/org/gmssl/crypto/CipherPaddingEnum.java new file mode 100644 index 0000000..a11b709 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/CipherPaddingEnum.java @@ -0,0 +1,23 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/07/27 + * @description + * + */ +public enum CipherPaddingEnum { + NoPadding, + ZeroPadding, + PKCS5Padding, + PKCS7Padding; +} diff --git a/src/main/java/org/gmssl/crypto/GmSSLProvider.java b/src/main/java/org/gmssl/crypto/GmSSLProvider.java new file mode 100644 index 0000000..0b4cb7b --- /dev/null +++ b/src/main/java/org/gmssl/crypto/GmSSLProvider.java @@ -0,0 +1,42 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto; + +import java.security.Provider; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/07/27 + * @description + * GmSSL-Java currently provides functionality for random number generation, SM3 hash, SM3 message authentication code (HMAC-SM3), + * SM4 encryption (including block encryption and CBC/CTR/GCM encryption modes), ZUC encryption, SM2 encryption/signature, SM9 encryption/signature, and SM2 certificate parsing. + * These features cover the main application development scenarios for the current Chinese cryptographic algorithms. + */ +public class GmSSLProvider extends Provider { + + public GmSSLProvider() { + super("GmSSL", "1.0.0", "GmSSL Provider"); + + put("SecureRandom.Random", "org.gmssl.crypto.Random"); + put("Cipher.SM2", "org.gmssl.crypto.asymmetric.SM2Cipher"); + put("KeyPairGenerator.SM2", "org.gmssl.crypto.asymmetric.SM2KeyPairGenerator"); + put("Signature.SM2", "org.gmssl.crypto.asymmetric.SM2Signature"); + put("MessageDigest.SM3", "org.gmssl.crypto.digest.SM3Digest"); + put("Mac.SM3", "org.gmssl.crypto.digest.SM3Hmac"); + put("SecretKeyFactory.SM3Pbkdf2", "org.gmssl.crypto.digest.SM3Pbkdf2"); + put("Cipher.SM4", "org.gmssl.crypto.symmetric.SM4Cipher"); + put("Cipher.SM9", "org.gmssl.crypto.asymmetric.SM9Cipher"); + put("Signature.SM9", "org.gmssl.crypto.asymmetric.SM9Signature"); + put("KeyPairGenerator.SM9", "org.gmssl.crypto.asymmetric.SM9KeyPairGeneratorSpi"); + put("Cipher.ZUC", "org.gmssl.crypto.symmetric.ZucCipher"); + } + + +} diff --git a/src/main/java/org/gmssl/crypto/PKCS7PaddingScheme.java b/src/main/java/org/gmssl/crypto/PKCS7PaddingScheme.java new file mode 100644 index 0000000..79e01a5 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/PKCS7PaddingScheme.java @@ -0,0 +1,51 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto; + +import java.util.Arrays; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/07/27 + * @description PKCS#7 + * + */ +public class PKCS7PaddingScheme implements PaddingScheme{ + @Override + public String getPaddingName() { + return "PKCS#7"; + } + + @Override + public byte[] pad(byte[] input, int blockSize) { + int paddingLength = blockSize - (input.length % blockSize); + byte[] padding = new byte[paddingLength]; + Arrays.fill(padding, (byte) paddingLength); + byte[] result = new byte[input.length + padding.length]; + System.arraycopy(input, 0, result, 0, input.length); + System.arraycopy(padding, 0, result, input.length, padding.length); + return result; + } + + @Override + public byte[] unpad(byte[] input) { + int paddingSize = input[input.length - 1]; + if (paddingSize <= 0 || paddingSize > input.length) { + throw new IllegalArgumentException("Invalid pkcs#7 padding!"); + } + for (int i = input.length - paddingSize; i < input.length; i++) { + if (input[i] != paddingSize) { + throw new IllegalArgumentException("Invalid pkcs#7 padding!"); + } + } + return Arrays.copyOfRange(input, 0, input.length - paddingSize); + } + +} diff --git a/src/main/java/org/gmssl/crypto/PaddingScheme.java b/src/main/java/org/gmssl/crypto/PaddingScheme.java new file mode 100644 index 0000000..1c18535 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/PaddingScheme.java @@ -0,0 +1,40 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/07/27 + * @description + * + */ +public interface PaddingScheme { + + /** + * get padding name + * @return paddingName + */ + String getPaddingName(); + + /** + * Pad according to fixed block size + * @param input Data to be padded + * @param blockSize block size + * @return padded data + */ + byte[] pad(byte[] input, int blockSize); + + /** + * Unpad according to fixed block size + * @param input Data to be unpadded + * @return unpadded data + */ + byte[] unpad(byte[] input); +} diff --git a/src/main/java/org/gmssl/crypto/Random.java b/src/main/java/org/gmssl/crypto/Random.java new file mode 100644 index 0000000..a19079a --- /dev/null +++ b/src/main/java/org/gmssl/crypto/Random.java @@ -0,0 +1,74 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; + +import java.security.SecureRandomSpi; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/07/27 + * @description + * + */ +public class Random extends SecureRandomSpi { + + public Random() { + super(); + } + + @Override + protected void engineSetSeed(byte[] seed) { + if (seed == null || seed.length == 0) { + throw new IllegalArgumentException("Seed cannot be null or empty"); + } + //rand_seed + throw new GmSSLException("The current method is not supported."); + } + + @Override + protected void engineNextBytes(byte[] bytes) { + if (bytes == null) { + throw new IllegalArgumentException("Output buffer cannot be null"); + } + randBytes(bytes,0, bytes.length); + } + + @Override + protected byte[] engineGenerateSeed(int numBytes) { + if (numBytes <= 0) { + throw new IllegalArgumentException("Number of bytes must be positive"); + } + return randBytes(numBytes); + } + + public byte[] randBytes(int len) { + byte[] out = new byte[len]; + if (GmSSLJNI.rand_bytes(out, 0, len) != 1) { + throw new GmSSLException("Failed to generate seed"); + } + return out; + } + + public void randBytes(byte[] out, int offset, int len) { + if (out == null + || offset < 0 + || len < 0 + || offset + len <= 0 + || out.length < offset + len) { + throw new GmSSLException(""); + } + if (GmSSLJNI.rand_bytes(out, offset, len) != 1) { + throw new GmSSLException(""); + } + } +} diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM2Certificate.java b/src/main/java/org/gmssl/crypto/asymmetric/SM2Certificate.java new file mode 100644 index 0000000..a4cab9f --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM2Certificate.java @@ -0,0 +1,127 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; + +import java.security.*; +import java.security.cert.*; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description + * The certificate format is the standard X.509v3 certificate. Currently, only the SM2 signature algorithm is supported. + * This includes functions for parsing and verifying SM2 certificates. However, issuing and generating SM2 certificates are not supported. + * If the application needs to implement certificate request (i.e., generating CSR files) or self-built CA certificate issuance, + * these functionalities can be achieved using the GmSSL library or the gmssl command-line tool. + */ +public class SM2Certificate{ + + private byte[] cert; + + public byte[] getEncoded() throws CertificateEncodingException { + if (this.cert == null) { + throw new GmSSLException(""); + } + return this.cert; + } + + public String toString() { + return null; + } + + public PublicKey getPublicKey() { + if (this.cert == null) { + throw new GmSSLException(""); + } + long pub_key; + if ((pub_key = GmSSLJNI.cert_get_subject_public_key(this.cert)) == 0) { + throw new GmSSLException(""); + } + boolean has_private_key = false; + return new SM2PublicKey(pub_key, has_private_key); + } + + public void importPem(String file) { + if ((this.cert = GmSSLJNI.cert_from_pem(file)) == null) { + throw new GmSSLException(""); + } + } + + public void exportPem(String file) { + if (this.cert == null) { + throw new GmSSLException(""); + } + if (GmSSLJNI.cert_to_pem(this.cert, file) != 1) { + throw new GmSSLException(""); + } + } + + public boolean verifyByCaCertificate(SM2Certificate caCert, String sm2Id) throws CertificateEncodingException { + if (this.cert == null) { + throw new GmSSLException(""); + } + int ret = GmSSLJNI.cert_verify_by_ca_cert(this.cert, caCert.getEncoded(), sm2Id); + if (ret == 1) { + return true; + } else { + return false; + } + } + + public byte[] getSerialNumber() { + if (this.cert == null) { + throw new GmSSLException(""); + } + byte[] serial; + if ((serial = GmSSLJNI.cert_get_serial_number(this.cert)) == null) { + throw new GmSSLException(""); + } + return serial; + } + + public java.util.Date getNotBefore() { + if (this.cert == null) { + throw new GmSSLException(""); + } + return new java.util.Date(GmSSLJNI.cert_get_not_before(this.cert)); + } + + public java.util.Date getNotAfter() { + if (this.cert == null) { + throw new GmSSLException(""); + } + return new java.util.Date(GmSSLJNI.cert_get_not_after(this.cert)); + } + + public String[] getIssuer() { + if (this.cert == null) { + throw new GmSSLException(""); + } + String[] issuer; + if ((issuer = GmSSLJNI.cert_get_issuer(this.cert)) == null) { + throw new GmSSLException(""); + } + return issuer; + } + + public String[] getSubject() { + if (this.cert == null) { + throw new GmSSLException(""); + } + String[] subject; + if ((subject = GmSSLJNI.cert_get_subject(this.cert)) == null) { + throw new GmSSLException(""); + } + return subject; + } +} diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM2Cipher.java b/src/main/java/org/gmssl/crypto/asymmetric/SM2Cipher.java new file mode 100644 index 0000000..2abc8c0 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM2Cipher.java @@ -0,0 +1,201 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; + +import javax.crypto.*; +import java.nio.ByteBuffer; +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description + * The SM2Cipher class implements encryption and decryption methods. When calling the encrypt method, ensure that the length of the plaintext input does not exceed the MAX_PLAINTEXT_SIZE limit. + * If you need to encrypt a message at the reference layer, first generate a symmetric key, encrypt the message using SM4-GCM, and then encrypt the symmetric key using SM2. + */ +public class SM2Cipher extends CipherSpi { + + private int mode; + private SM2Key key; + private SecureRandom random; + private ByteBuffer buffer; + + /** + * SM2 uses the C1C2C3 encryption mode. + * @param mode the cipher mode + * + * @throws NoSuchAlgorithmException + */ + @Override + protected void engineSetMode(String mode) throws NoSuchAlgorithmException { + + } + + /** + * SM2 has adopted the corresponding padding rule and does not involve specific padding modes. + * @param padding the padding mechanism + * + * @throws NoSuchPaddingException + */ + @Override + protected void engineSetPadding(String padding) throws NoSuchPaddingException { + + } + + /** + * SM2 does not have a fixed block size. + * @return + */ + @Override + protected int engineGetBlockSize() { + return 0; + } + + @Override + protected int engineGetOutputSize(int inputLen) { + return 0; + } + + @Override + protected byte[] engineGetIV() { + return null; + } + + @Override + protected AlgorithmParameters engineGetParameters() { + return null; + } + + @Override + protected void engineInit(int mode, Key key, SecureRandom secureRandom) throws InvalidKeyException { + if (!(key instanceof SM2Key)) { + throw new InvalidKeyException("Invalid key type"); + } + this.key = (SM2Key)key; + this.mode = mode; + this.random = (secureRandom != null) ? secureRandom : new SecureRandom(); + this.buffer = ByteBuffer.allocate(1024); + } + + @Override + protected void engineInit(int mode, Key key, AlgorithmParameterSpec algorithmParameterSpec, SecureRandom secureRandom) throws InvalidKeyException, InvalidAlgorithmParameterException { + engineInit(mode, key, random); + } + + @Override + protected void engineInit(int i, Key key, AlgorithmParameters algorithmParameters, SecureRandom secureRandom) throws InvalidKeyException, InvalidAlgorithmParameterException { + engineInit(mode, key, random); + } + + /** + * + * @param input the input buffer + * @param inputOffset the offset in input where the input + * starts + * @param inputLen the input length + * + * @return null + * The SM2 algorithm typically does not return any data during the engineUpdate phase. + */ + @Override + protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { + if (input == null || inputOffset < 0 || inputLen < 0 || inputOffset + inputLen > input.length) { + throw new IllegalArgumentException("Invalid input parameters"); + } + buffer.put(input, inputOffset, inputLen); + return null; + } + + /** + * The method does not perform any encryption or decryption; it only stores the input data. The returned result is meaningless, and the final result is output through `doFinal`. + * @param input the input buffer + * @param inputOffset the offset in input where the input + * starts + * @param inputLen the input length + * @param output the buffer for the result + * @param outputOffset the offset in output where the result + * is stored + * + * @return + * @throws ShortBufferException + */ + @Override + protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException { + if (input == null || inputOffset < 0 || inputLen < 0 || inputOffset + inputLen > input.length) { + throw new IllegalArgumentException("Invalid input parameters"); + } + buffer.put(input, inputOffset, inputLen); + return 0; + } + + @Override + protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) throws IllegalBlockSizeException, BadPaddingException { + if(null != input){ + buffer.put(input, inputOffset, inputLen); + } + byte[] data = new byte[buffer.position()]; + buffer.flip(); + buffer.get(data); + buffer.clear(); + + if (mode == Cipher.ENCRYPT_MODE) { + return encrypt(data); + } else if (mode == Cipher.DECRYPT_MODE) { + return decrypt(data); + } else { + throw new GmSSLException("Cipher not initialized properly"); + } + } + + @Override + protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { + byte[] result = engineDoFinal(input, inputOffset, inputLen); + System.arraycopy(result, 0, output, outputOffset, result.length); + return result.length; + } + + public byte[] encrypt(byte[] plaintext) { + if (this.key.sm2_key == 0) { + throw new GmSSLException(""); + } + if (plaintext == null + || plaintext.length > this.key.MAX_PLAINTEXT_SIZE) { + throw new GmSSLException(""); + } + + byte[] ciphertext; + if ((ciphertext = GmSSLJNI.sm2_encrypt(this.key.sm2_key, plaintext)) == null) { + throw new GmSSLException(""); + } + return ciphertext; + } + + public byte[] decrypt(byte[] ciphertext) { + if (this.key.sm2_key == 0) { + throw new GmSSLException(""); + } + if (this.key.has_private_key == false) { + throw new GmSSLException(""); + } + if (ciphertext == null) { + throw new GmSSLException(""); + } + + byte[] plaintext; + if ((plaintext = GmSSLJNI.sm2_decrypt(this.key.sm2_key, ciphertext)) == null) { + throw new GmSSLException(""); + } + return plaintext; + } +} diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM2Key.java b/src/main/java/org/gmssl/crypto/asymmetric/SM2Key.java new file mode 100644 index 0000000..ac006e2 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM2Key.java @@ -0,0 +1,62 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; + +import java.security.Key; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description SM2Key + * + */ +public abstract class SM2Key implements Key { + + public final static int MAX_PLAINTEXT_SIZE = GmSSLJNI.SM2_MAX_PLAINTEXT_SIZE; + + protected long sm2_key; + protected boolean has_private_key; + + protected SM2Key() { + this.sm2_key = 0; + this.has_private_key = false; + } + + protected SM2Key(long sm2_key, boolean has_private_key) { + this.sm2_key = sm2_key; + this.has_private_key = has_private_key; + } + + @Override + public String getAlgorithm() { + return "SM2"; + } + + long getPrivateKey() { + if (this.sm2_key == 0) { + throw new GmSSLException(""); + } + if (this.has_private_key == false) { + throw new GmSSLException(""); + } + return this.sm2_key; + } + + long getPublicKey() { + if (this.sm2_key == 0) { + throw new GmSSLException(""); + } + return this.sm2_key; + } + +} diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM2KeyPairGenerator.java b/src/main/java/org/gmssl/crypto/asymmetric/SM2KeyPairGenerator.java new file mode 100644 index 0000000..3fe591e --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM2KeyPairGenerator.java @@ -0,0 +1,49 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; + +import java.security.*; +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description + * + */ +public class SM2KeyPairGenerator extends KeyPairGeneratorSpi { + + private long sm2_key = 0; + private boolean has_private_key = false; + + @Override + public void initialize(int keysize, SecureRandom random) { + generateKey(); + } + + @Override + public KeyPair generateKeyPair() { + PublicKey publicKey = new SM2PublicKey(sm2_key, has_private_key); + PrivateKey privateKey = new SM2PrivateKey(sm2_key, has_private_key); + return new KeyPair(publicKey, privateKey); + } + + private void generateKey() { + if (this.sm2_key != 0) { + GmSSLJNI.sm2_key_free(this.sm2_key); + } + if ((sm2_key = GmSSLJNI.sm2_key_generate()) == 0) { + throw new GmSSLException(""); + } + this.has_private_key = true; + } + +} diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM2PrivateKey.java b/src/main/java/org/gmssl/crypto/asymmetric/SM2PrivateKey.java new file mode 100644 index 0000000..bd5ac98 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM2PrivateKey.java @@ -0,0 +1,104 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; + +import java.security.PrivateKey; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description + * + */ +public class SM2PrivateKey extends SM2Key implements PrivateKey{ + + protected SM2PrivateKey() { + super(); + } + + public SM2PrivateKey(byte[] der) { + importPrivateKeyInfoDer(der); + } + + public SM2PrivateKey(String password, String file) { + importEncryptedPrivateKeyInfoPem(password, file); + } + + protected SM2PrivateKey(long sm2_key, boolean has_private_key) { + super(sm2_key,has_private_key); + } + + public String getAlgorithm() { + return "SM2"; + } + + @Override + public String getFormat() { + return null; + } + + @Override + public byte[] getEncoded() { + return exportPrivateKeyInfoDer(); + } + + public void importPrivateKeyInfoDer(byte[] der) { + if (der == null) { + throw new GmSSLException(""); + } + if (this.sm2_key != 0) { + GmSSLJNI.sm2_key_free(this.sm2_key); + } + if ((this.sm2_key = GmSSLJNI.sm2_private_key_info_from_der(der)) == 0) { + throw new GmSSLException(""); + } + this.has_private_key = true; + } + + public byte[] exportPrivateKeyInfoDer() { + if (this.sm2_key == 0) { + throw new GmSSLException(""); + } + if (this.has_private_key == false) { + throw new GmSSLException(""); + } + byte[] der; + if ((der = GmSSLJNI.sm2_private_key_info_to_der(this.sm2_key)) == null) { + throw new GmSSLException(""); + } + return der; + } + + public void importEncryptedPrivateKeyInfoPem(String pass, String file) { + if (this.sm2_key != 0) { + GmSSLJNI.sm2_key_free(this.sm2_key); + } + if ((sm2_key = GmSSLJNI.sm2_private_key_info_decrypt_from_pem(pass, file)) == 0) { + throw new GmSSLException(""); + } + this.has_private_key = true; + } + + public void exportEncryptedPrivateKeyInfoPem(String pass, String file) { + if (this.sm2_key == 0) { + throw new GmSSLException(""); + } + if (this.has_private_key == false) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm2_private_key_info_encrypt_to_pem(this.sm2_key, pass, file) != 1) { + throw new GmSSLException(""); + } + } + +} diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM2PublicKey.java b/src/main/java/org/gmssl/crypto/asymmetric/SM2PublicKey.java new file mode 100644 index 0000000..5d301f8 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM2PublicKey.java @@ -0,0 +1,111 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; +import org.gmssl.Sm3; + +import java.security.PublicKey; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description + * + */ +public class SM2PublicKey extends SM2Key implements PublicKey{ + + protected SM2PublicKey() { + super(); + } + + public SM2PublicKey(byte[] der) { + importPublicKeyInfoDer(der); + } + + public SM2PublicKey(String file) { + importPublicKeyInfoPem(file); + } + + protected SM2PublicKey(long sm2_key, boolean has_private_key) { + super(sm2_key,has_private_key); + } + + @Override + public String getAlgorithm() { + return "SM2"; + } + + @Override + public String getFormat() { + return null; + } + + @Override + public byte[] getEncoded() { + return exportPublicKeyInfoDer(); + } + + public void importPublicKeyInfoDer(byte[] der) { + if (der == null) { + throw new GmSSLException(""); + } + if (this.sm2_key != 0) { + GmSSLJNI.sm2_key_free(this.sm2_key); + } + if ((this.sm2_key = GmSSLJNI.sm2_public_key_info_from_der(der)) == 0) { + throw new GmSSLException(""); + } + this.has_private_key = false; + } + + public byte[] exportPublicKeyInfoDer() { + if (this.sm2_key == 0) { + throw new GmSSLException(""); + } + byte[] der; + if ((der = GmSSLJNI.sm2_public_key_info_to_der(this.sm2_key)) == null) { + throw new GmSSLException(""); + } + return der; + } + + public void importPublicKeyInfoPem(String file) { + if (this.sm2_key != 0) { + GmSSLJNI.sm2_key_free(this.sm2_key); + } + if ((this.sm2_key = GmSSLJNI.sm2_public_key_info_from_pem(file)) == 0) { + throw new GmSSLException(""); + } + this.has_private_key = false; + } + + public void exportPublicKeyInfoPem(String file) { + if (this.sm2_key == 0) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm2_public_key_info_to_pem(this.sm2_key, file) != 1) { + throw new GmSSLException(""); + } + } + + public byte[] computeZ(String id) { + if (this.sm2_key == 0) { + throw new GmSSLException(""); + } + byte[] z = new byte[Sm3.DIGEST_SIZE]; + if (GmSSLJNI.sm2_compute_z(this.sm2_key, id, z) != 1) { + throw new GmSSLException(""); + } + return z; + } + +} diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM2Signature.java b/src/main/java/org/gmssl/crypto/asymmetric/SM2Signature.java new file mode 100644 index 0000000..3d6bd8b --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM2Signature.java @@ -0,0 +1,162 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; + +import java.security.*; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description SM2Signature + * It provides signing and verification functionality for messages of arbitrary length. + */ +public class SM2Signature extends SignatureSpi { + + public final static String DEFAULT_ID = GmSSLJNI.SM2_DEFAULT_ID; + + private long sm2_sign_ctx = 0; + private boolean inited = false; + + private boolean do_sign = true; + + public SM2Signature() { + super(); + } + + @Override + protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { + if (!(publicKey instanceof SM2PublicKey)) { + throw new GmSSLException("Invalid publicKey type"); + } + init(); + initVerify((SM2PublicKey) publicKey,DEFAULT_ID); + } + + @Override + protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { + if (!(privateKey instanceof SM2PrivateKey)) { + throw new GmSSLException("Invalid privateKey type"); + } + init(); + initSign((SM2PrivateKey) privateKey,DEFAULT_ID); + } + + @Override + protected void engineUpdate(byte b) throws SignatureException { + byte[] data= new byte[]{b}; + update(data, 0, data.length); + } + + @Override + protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { + update(b, off, len); + } + + @Override + protected byte[] engineSign() throws SignatureException { + byte[] data = sign(); + return data; + } + + @Override + protected boolean engineVerify(byte[] sigBytes) throws SignatureException { + boolean verifyResult= verify(sigBytes); + return verifyResult; + } + + @Override + protected void engineSetParameter(String param, Object value) throws InvalidParameterException { + } + + @Override + protected Object engineGetParameter(String param) throws InvalidParameterException { + return null; + } + + private void init(){ + if ((this.sm2_sign_ctx = GmSSLJNI.sm2_sign_ctx_new()) == 0) { + throw new GmSSLException(""); + } + this.inited = true; + } + + private void initSign(SM2PrivateKey privateKey,String id){ + if (GmSSLJNI.sm2_sign_init(this.sm2_sign_ctx, privateKey.getPrivateKey(), id) != 1) { + throw new GmSSLException(""); + } + this.do_sign = true; + } + + private void initVerify(SM2PublicKey publicKey,String id){ + if (GmSSLJNI.sm2_verify_init(sm2_sign_ctx, publicKey.getPublicKey(), id) != 1) { + throw new GmSSLException(""); + } + this.do_sign = false; + } + + private void update(byte[] data, int offset, int len) { + if (this.inited == false) { + throw new GmSSLException(""); + } + + if (data == null + || offset < 0 + || len < 0 + || offset + len <= 0 + || data.length < offset + len) { + throw new GmSSLException(""); + } + + if (this.do_sign == true) { + if (GmSSLJNI.sm2_sign_update(this.sm2_sign_ctx, data, offset, len) != 1) { + throw new GmSSLException(""); + } + } else { + if (GmSSLJNI.sm2_verify_update(this.sm2_sign_ctx, data, offset, len) != 1) { + throw new GmSSLException(""); + } + } + } + + private byte[] sign() { + if (this.inited == false) { + throw new GmSSLException(""); + } + if (this.do_sign == false) { + throw new GmSSLException(""); + } + + byte[] sig; + if ((sig = GmSSLJNI.sm2_sign_finish(this.sm2_sign_ctx)) == null) { + throw new GmSSLException(""); + } + this.inited = false; + return sig; + } + + private boolean verify(byte[] signature) { + if (this.sm2_sign_ctx == 0) { + throw new GmSSLException(""); + } + if (this.do_sign == true) { + throw new GmSSLException(""); + } + this.inited = false; + int ret; + if ((ret = GmSSLJNI.sm2_verify_finish(sm2_sign_ctx, signature)) != 1) { + return false; + } + return true; + } + +} diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM9Cipher.java b/src/main/java/org/gmssl/crypto/asymmetric/SM9Cipher.java new file mode 100644 index 0000000..5f1564c --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM9Cipher.java @@ -0,0 +1,180 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import org.gmssl.GmSSLException; + +import javax.crypto.*; +import java.nio.ByteBuffer; +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description SM9Cipher + * The SM9 algorithm belongs to an identity-based encryption (IBE) system. + * Since IBE does not require a Certificate Authority (CA) or a digital certificate infrastructure, + * if the application operates in a closed internal environment where all participating users are within the system, adopting the SM9 solution is a better choice. + */ +public class SM9Cipher extends CipherSpi { + + private int opmode; + + private Key key; + + private ByteBuffer buffer; + + private String id; + + /** + * + * @param mode the cipher mode + * @throws NoSuchAlgorithmException + * @description + * SM9 is an identity-based encryption and signature algorithm that does not support traditional block cipher modes. + */ + @Override + protected void engineSetMode(String mode) throws NoSuchAlgorithmException { + } + + /** + * + * @param padding the padding mechanism + * @throws NoSuchPaddingException + * @description + * SM9 is an identity-based encryption and signature algorithm that does not support common padding modes. + */ + @Override + protected void engineSetPadding(String padding) throws NoSuchPaddingException { + } + + /** + * SM9 is a public key encryption algorithm that does not have a fixed block size and does not use blocks. + * @return + */ + @Override + protected int engineGetBlockSize() { + return 0; + } + + @Override + protected int engineGetOutputSize(int inputLen) { + return 0; + } + + @Override + protected byte[] engineGetIV() { + return new byte[0]; + } + + @Override + protected AlgorithmParameters engineGetParameters() { + return null; + } + + @Override + protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException { + if (!(key instanceof SM9PrivateKey)) { + throw new GmSSLException("Invalid privateKey type"); + } + this.opmode = opmode; + this.key = key; + SM9PrivateKey privateKey = (SM9PrivateKey)key; + SM9UserKey userKey = (SM9UserKey)privateKey.getSecretKey(); + this.id = userKey.getId(); + this.buffer = ByteBuffer.allocate(1024); + } + + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { + if (!(key instanceof SM9PublicKey)) { + throw new GmSSLException("Invalid publicKey type"); + } + this.opmode = opmode; + this.key = key; + this.id = ((SM9EncMasterKeyGenParameterSpec)params).getId(); + this.buffer = ByteBuffer.allocate(1024); + } + + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { + } + + /** + * SM9 encryption and decryption are completed during the engineDoFinal phase. During the update phase, data is only cached, and no partial encryption or decryption results are returned. + * @param input the input buffer + * @param inputOffset the offset in input where the input + * starts + * @param inputLen the input length + * + * @return + */ + @Override + protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { + buffer.put(input, inputOffset, inputLen); + return null; + } + + /** + * SM9 encryption and decryption are completed during the engineDoFinal phase. During the update phase, data is only cached, and no partial encryption or decryption results are returned. + * @param input the input buffer + * @param inputOffset the offset in input where the input + * starts + * @param inputLen the input length + * @param output the buffer for the result + * @param outputOffset the offset in output where the result + * is stored + * + * @return + * @throws ShortBufferException + */ + @Override + protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException { + buffer.put(input, inputOffset, inputLen); + return 0; + } + + @Override + protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) throws IllegalBlockSizeException, BadPaddingException { + buffer.put(input, inputOffset, inputLen); + byte[] data = new byte[buffer.position()]; + buffer.flip(); + buffer.get(data); + buffer.clear(); + + if (opmode == Cipher.ENCRYPT_MODE) { + return encrypt(data); + } else if (opmode == Cipher.DECRYPT_MODE) { + return decrypt(data); + } else { + throw new GmSSLException("Cipher not initialized properly"); + } + } + + @Override + protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { + byte[] result = engineDoFinal(input, inputOffset, inputLen); + System.arraycopy(result, 0, output, outputOffset, result.length); + return result.length; + } + + private byte[] encrypt(byte[] plaintext) { + SM9PublicKey encMasterKey = (SM9PublicKey) key; + byte[] ciphertext = encMasterKey.encrypt(plaintext,id); + return ciphertext; + } + + private byte[] decrypt(byte[] ciphertext) { + SM9PrivateKey privateKey = (SM9PrivateKey)key; + byte[] plaintext = privateKey.decrypt(ciphertext); + return plaintext; + } +} diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM9EncMasterKey.java b/src/main/java/org/gmssl/crypto/asymmetric/SM9EncMasterKey.java new file mode 100644 index 0000000..f549cda --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM9EncMasterKey.java @@ -0,0 +1,139 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description + * + */ +public class SM9EncMasterKey extends SM9MasterKey{ + + public final static int MAX_PLAINTEXT_SIZE = GmSSLJNI.SM9_MAX_PLAINTEXT_SIZE; + + public SM9EncMasterKey(){ + publicKey = new SM9EncPublicKey(); + privateKey = new SM9EncPrivateKey(); + } + + class SM9EncPublicKey extends SM9PublicKey { + + public long getPublicKey() { + if (master_key == 0) { + throw new GmSSLException(""); + } + return master_key; + } + + public void importPublicKeyPem(String file) { + if (master_key != 0) { + GmSSLJNI.sm9_enc_master_key_free(master_key); + } + if ((master_key = GmSSLJNI.sm9_enc_master_public_key_from_pem(file)) == 0) { + throw new GmSSLException(""); + } + has_private_key = false; + } + + public void exportPublicKeyPem(String file) { + if (master_key == 0) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm9_enc_master_public_key_to_pem(master_key, file) != 1) { + throw new GmSSLException(""); + } + } + + public byte[] encrypt(byte[] plaintext, String id) { + if (master_key == 0) { + throw new GmSSLException(""); + } + if (plaintext == null + || plaintext.length > MAX_PLAINTEXT_SIZE) { + throw new GmSSLException(""); + } + + byte[] ciphertext; + if ((ciphertext = GmSSLJNI.sm9_encrypt(master_key, id, plaintext)) == null) { + throw new GmSSLException(""); + } + return ciphertext; + } + + } + + class SM9EncPrivateKey extends SM9PrivateKey{ + public void importEncryptedPrivateKeyInfoPem(String pass, String file) { + if (master_key != 0) { + GmSSLJNI.sm9_enc_master_key_free(master_key); + } + if ((master_key = GmSSLJNI.sm9_enc_master_key_info_decrypt_from_pem(pass, file)) == 0) { + throw new GmSSLException(""); + } + has_private_key = true; + } + + public void exportEncryptedPrivateKeyInfoPem(String pass, String file) { + if (master_key == 0) { + throw new GmSSLException(""); + } + if (has_private_key == false) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm9_enc_master_key_info_encrypt_to_pem(master_key, pass, file) != 1) { + throw new GmSSLException(""); + } + } + + public SM9EncMasterKey getSecretKey() { + return SM9EncMasterKey.this; + } + + } + + public void generateMasterKey() { + if (master_key != 0) { + GmSSLJNI.sm9_enc_master_key_free(master_key); + } + if ((master_key = GmSSLJNI.sm9_enc_master_key_generate()) == 0) { + throw new GmSSLException(""); + } + has_private_key = true; + } + + public long getSecretKey() { + if (master_key == 0) { + throw new GmSSLException(""); + } + if (has_private_key == false) { + throw new GmSSLException(""); + } + return master_key; + } + + public SM9UserKey extractKey(String id) { + if (this.master_key == 0) { + throw new GmSSLException(""); + } + if (this.has_private_key == false) { + throw new GmSSLException(""); + } + long key; + if ((key = GmSSLJNI.sm9_enc_master_key_extract_key(this.master_key, id)) == 0) { + throw new GmSSLException(""); + } + return new SM9EncUserKey(key, id); + } + +} diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM9EncMasterKeyGenParameterSpec.java b/src/main/java/org/gmssl/crypto/asymmetric/SM9EncMasterKeyGenParameterSpec.java new file mode 100644 index 0000000..da982ad --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM9EncMasterKeyGenParameterSpec.java @@ -0,0 +1,42 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import org.gmssl.GmSSLJNI; + +import java.security.spec.AlgorithmParameterSpec; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description + * + */ +public class SM9EncMasterKeyGenParameterSpec implements AlgorithmParameterSpec { + + private String id; + + protected SM9EncMasterKeyGenParameterSpec() { + + } + + public SM9EncMasterKeyGenParameterSpec(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } +} + diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM9EncUserKey.java b/src/main/java/org/gmssl/crypto/asymmetric/SM9EncUserKey.java new file mode 100644 index 0000000..4a417da --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM9EncUserKey.java @@ -0,0 +1,66 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; +import org.gmssl.Sm9SignKey; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description + * + */ +public class SM9EncUserKey extends SM9UserKey{ + protected SM9EncUserKey(long sm9_key, String id) { + super(sm9_key, id); + this.privateKey = new SM9EncPrivateKey(); + } + + class SM9EncPrivateKey extends SM9PrivateKey { + public void importEncryptedPrivateKeyInfoPem(String pass, String file) { + if (sm9_key != 0) { + GmSSLJNI.sm9_enc_key_free(sm9_key); + } + if ((sm9_key = GmSSLJNI.sm9_enc_key_info_decrypt_from_pem(pass, file)) == 0) { + throw new GmSSLException(""); + } + } + + public void exportEncryptedPrivateKeyInfoPem(String pass, String file) { + if (sm9_key == 0) { + throw new GmSSLException("Key not initialized"); + } + if (GmSSLJNI.sm9_enc_key_info_encrypt_to_pem(sm9_key, pass, file) != 1) { + throw new GmSSLException(""); + } + } + + public byte[] decrypt(byte[] ciphertext) { + if (sm9_key == 0) { + throw new GmSSLException(""); + } + if (ciphertext == null) { + throw new GmSSLException(""); + } + + byte[] plaintext; + if ((plaintext = GmSSLJNI.sm9_decrypt(sm9_key, id, ciphertext)) == null) { + throw new GmSSLException(""); + } + return plaintext; + } + public SM9EncUserKey getSecretKey() { + return SM9EncUserKey.this; + } + } + +} diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM9KeyPairGeneratorSpi.java b/src/main/java/org/gmssl/crypto/asymmetric/SM9KeyPairGeneratorSpi.java new file mode 100644 index 0000000..a3962cc --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM9KeyPairGeneratorSpi.java @@ -0,0 +1,55 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.KeyPairGeneratorSpi; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description + * + */ +public class SM9KeyPairGeneratorSpi extends KeyPairGeneratorSpi { + + private SM9MasterKey masterKey; + + @Override + public void initialize(int keysize, SecureRandom random) { + + } + + @Override + public void initialize(AlgorithmParameterSpec params, SecureRandom random) throws InvalidAlgorithmParameterException { + if (params == null) { + throw new InvalidAlgorithmParameterException("The parameter must not be null"); + } + if (params instanceof SM9SignMasterKeyGenParameterSpec) { + SM9SignMasterKeyGenParameterSpec spec = (SM9SignMasterKeyGenParameterSpec) params; + this.masterKey = new SM9SignMasterKey(); + } else if (params instanceof SM9EncMasterKeyGenParameterSpec) { + SM9EncMasterKeyGenParameterSpec spec = (SM9EncMasterKeyGenParameterSpec) params; + this.masterKey = new SM9EncMasterKey(); + } else { + throw new InvalidAlgorithmParameterException(""); + } + masterKey.generateMasterKey(); + } + + @Override + public KeyPair generateKeyPair() { + return new KeyPair(masterKey.publicKey, masterKey.privateKey); + } + +} diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM9MasterKey.java b/src/main/java/org/gmssl/crypto/asymmetric/SM9MasterKey.java new file mode 100644 index 0000000..3ad7fe3 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM9MasterKey.java @@ -0,0 +1,43 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import java.security.spec.KeySpec; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description + * + */ +public abstract class SM9MasterKey implements KeySpec { + + protected long master_key; + + protected boolean has_private_key; + + protected SM9PublicKey publicKey; + + protected SM9PrivateKey privateKey; + + public abstract void generateMasterKey(); + + public abstract long getSecretKey(); + + public abstract SM9UserKey extractKey(String id); + + public SM9PublicKey getPublicKey() { + return this.publicKey; + } + + public SM9PrivateKey getPrivateKey() { + return this.privateKey; + } +} diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM9PrivateKey.java b/src/main/java/org/gmssl/crypto/asymmetric/SM9PrivateKey.java new file mode 100644 index 0000000..d34c9de --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM9PrivateKey.java @@ -0,0 +1,51 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import java.security.PrivateKey; +import java.security.spec.KeySpec; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description + * + */ +public abstract class SM9PrivateKey implements PrivateKey { + + @Override + public String getAlgorithm() { + return null; + } + + @Override + public String getFormat() { + return null; + } + + @Override + public byte[] getEncoded() { + return new byte[0]; + } + + public abstract KeySpec getSecretKey(); + + public abstract void importEncryptedPrivateKeyInfoPem(String pass, String file); + + public abstract void exportEncryptedPrivateKeyInfoPem(String pass, String file); + + public byte[] decrypt(byte[] ciphertext) { + return null; + } + + public byte[] sign(long sign_ctx) { + return null; + } +} diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM9PublicKey.java b/src/main/java/org/gmssl/crypto/asymmetric/SM9PublicKey.java new file mode 100644 index 0000000..474776c --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM9PublicKey.java @@ -0,0 +1,50 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import java.security.PublicKey; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description + * + */ +public abstract class SM9PublicKey implements PublicKey { + + @Override + public String getAlgorithm() { + return null; + } + + @Override + public String getFormat() { + return null; + } + + @Override + public byte[] getEncoded() { + return new byte[0]; + } + + public abstract long getPublicKey(); + + public abstract void importPublicKeyPem(String file); + + public abstract void exportPublicKeyPem(String file); + + public byte[] encrypt(byte[] plaintext, String id){ + return null; + }; + + public Boolean verify(byte[] signature, String id,long sign_ctx){ + return null; + } +} diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM9SignMasterKey.java b/src/main/java/org/gmssl/crypto/asymmetric/SM9SignMasterKey.java new file mode 100644 index 0000000..6b4dbe7 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM9SignMasterKey.java @@ -0,0 +1,129 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description + * + */ +public class SM9SignMasterKey extends SM9MasterKey{ + + public SM9SignMasterKey(){ + publicKey = new SM9SignPublicKey(); + privateKey = new SM9SignPrivateKey(); + } + + class SM9SignPublicKey extends SM9PublicKey { + public long getPublicKey() { + if (master_key == 0) { + throw new GmSSLException(""); + } + return master_key; + } + + public void importPublicKeyPem(String file) { + if (master_key != 0) { + GmSSLJNI.sm9_sign_master_key_free(master_key); + } + if ((master_key = GmSSLJNI.sm9_sign_master_public_key_from_pem(file)) == 0) { + throw new GmSSLException(""); + } + has_private_key = false; + } + + public void exportPublicKeyPem(String file) { + if (master_key == 0) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm9_sign_master_public_key_to_pem(master_key, file) != 1) { + throw new GmSSLException(""); + } + } + + public Boolean verify(byte[] signature, String id,long sm9_sign_ctx) { + int ret; + ret = GmSSLJNI.sm9_verify_finish(sm9_sign_ctx, signature, master_key, id); + if (ret == 1) { + return true; + } else { + return false; + } + } + + } + + class SM9SignPrivateKey extends SM9PrivateKey{ + public void importEncryptedPrivateKeyInfoPem(String pass, String file) { + if (master_key != 0) { + GmSSLJNI.sm9_sign_master_key_free(master_key); + } + if ((master_key = GmSSLJNI.sm9_sign_master_key_info_decrypt_from_pem(pass, file)) == 0) { + throw new GmSSLException(""); + } + has_private_key = true; + } + + public void exportEncryptedPrivateKeyInfoPem(String pass, String file) { + if (master_key == 0) { + throw new GmSSLException(""); + } + if (has_private_key == false) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm9_sign_master_key_info_encrypt_to_pem(master_key, pass, file) != 1) { + throw new GmSSLException(""); + } + } + + public SM9SignMasterKey getSecretKey() { + return SM9SignMasterKey.this; + } + + } + public void generateMasterKey() { + if (this.master_key != 0) { + GmSSLJNI.sm9_sign_master_key_free(this.master_key); + } + if ((this.master_key = GmSSLJNI.sm9_sign_master_key_generate()) == 0) { + throw new GmSSLException(""); + } + this.has_private_key = true; + } + + public long getSecretKey() { + if (this.master_key == 0) { + throw new GmSSLException(""); + } + if (this.has_private_key == false) { + throw new GmSSLException(""); + } + return this.master_key; + } + + public SM9UserKey extractKey(String id) { + if (this.master_key == 0) { + throw new GmSSLException(""); + } + if (this.has_private_key == false) { + throw new GmSSLException(""); + } + long key; + if ((key = GmSSLJNI.sm9_sign_master_key_extract_key(this.master_key, id)) == 0) { + throw new GmSSLException(""); + } + return new SM9SignUserKey(key, id); + } + +} diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM9SignMasterKeyGenParameterSpec.java b/src/main/java/org/gmssl/crypto/asymmetric/SM9SignMasterKeyGenParameterSpec.java new file mode 100644 index 0000000..51749c8 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM9SignMasterKeyGenParameterSpec.java @@ -0,0 +1,39 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import java.security.spec.AlgorithmParameterSpec; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description + * + */ +public class SM9SignMasterKeyGenParameterSpec implements AlgorithmParameterSpec { + + private String id; + + protected SM9SignMasterKeyGenParameterSpec() { + + } + + public SM9SignMasterKeyGenParameterSpec(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } +} diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM9SignUserKey.java b/src/main/java/org/gmssl/crypto/asymmetric/SM9SignUserKey.java new file mode 100644 index 0000000..815d338 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM9SignUserKey.java @@ -0,0 +1,62 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; +import org.gmssl.Sm9SignKey; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description + * + */ +public class SM9SignUserKey extends SM9UserKey{ + protected SM9SignUserKey(long sm9_key, String id) { + super(sm9_key, id); + this.privateKey = new SM9SignPrivateKey(); + } + + class SM9SignPrivateKey extends SM9PrivateKey { + + public void exportEncryptedPrivateKeyInfoPem(String pass, String file) { + if (sm9_key == 0) { + throw new GmSSLException("Key not initialized"); + } + if (GmSSLJNI.sm9_sign_key_info_encrypt_to_pem(sm9_key, pass, file) != 1) { + throw new GmSSLException(""); + } + } + + public void importEncryptedPrivateKeyInfoPem(String pass, String file) { + if (sm9_key != 0) { + GmSSLJNI.sm9_sign_key_free(sm9_key); + } + if ((sm9_key = GmSSLJNI.sm9_sign_key_info_decrypt_from_pem(pass, file)) == 0) { + throw new GmSSLException("Import key failure"); + } + } + + public byte[] sign(long sm9_sign_ctx) { + byte[] signature; + if ((signature = GmSSLJNI.sm9_sign_finish(sm9_sign_ctx, sm9_key)) == null) { + throw new GmSSLException(""); + } + return signature; + } + + public SM9SignUserKey getSecretKey() { + return SM9SignUserKey.this; + } + + } + +} diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM9Signature.java b/src/main/java/org/gmssl/crypto/asymmetric/SM9Signature.java new file mode 100644 index 0000000..704328e --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM9Signature.java @@ -0,0 +1,160 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; + +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description + * + */ +public class SM9Signature extends SignatureSpi { + + private long sm9_sign_ctx; + + private boolean inited; + + private boolean do_sign; + + private Key key; + + private String id; + + public SM9Signature() { + super(); + } + + @Override + protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { + if (!(publicKey instanceof SM9PublicKey)) { + throw new GmSSLException("Invalid publicKey type"); + } + this.key = publicKey; + init(); + initVerify(); + } + + @Override + protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { + if (!(privateKey instanceof SM9PrivateKey)) { + throw new GmSSLException("Invalid privateKey type"); + } + this.key = privateKey; + init(); + initSign(); + } + + @Override + protected void engineUpdate(byte b) throws SignatureException { + byte[] data= new byte[]{b}; + update(data, 0, data.length); + } + + @Override + protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { + update(b, off, len); + } + + @Override + protected byte[] engineSign() { + SM9PrivateKey sm9_private_key = (SM9PrivateKey)key; + byte[] signature = sm9_private_key.sign(sm9_sign_ctx); + this.inited = false; + return signature; + } + + @Override + protected boolean engineVerify(byte[] sigBytes) { + SM9PublicKey sm9_public_key = (SM9PublicKey)key; + boolean verify = sm9_public_key.verify(sigBytes,id,sm9_sign_ctx); + this.inited = false; + return verify; + } + + @Deprecated + @Override + protected void engineSetParameter(String param, Object value) throws InvalidParameterException { + + } + + @Deprecated + @Override + protected Object engineGetParameter(String param) throws InvalidParameterException { + return null; + } + + @Override + protected void engineSetParameter(AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException { + this.id = ((SM9SignMasterKeyGenParameterSpec)params).getId(); + } + + private void init(){ + if ((this.sm9_sign_ctx = GmSSLJNI.sm9_sign_ctx_new()) == 0) { + throw new GmSSLException(""); + } + this.inited = true; + } + + private void initSign() { + if (GmSSLJNI.sm9_sign_init(this.sm9_sign_ctx) != 1) { + throw new GmSSLException(""); + } + this.do_sign = true; + } + + private void initVerify() { + if (GmSSLJNI.sm9_verify_init(this.sm9_sign_ctx) != 1) { + throw new GmSSLException(""); + } + this.do_sign = false; + } + + public void reset(boolean do_sign) { + if (do_sign == true) { + if (GmSSLJNI.sm9_sign_init(this.sm9_sign_ctx) != 1) { + throw new GmSSLException(""); + } + } else { + if (GmSSLJNI.sm9_verify_init(this.sm9_sign_ctx) != 1) { + throw new GmSSLException(""); + } + } + this.inited = true; + this.do_sign = do_sign; + } + + public void update(byte[] data, int offset, int len) { + if (this.inited == false) { + throw new GmSSLException(""); + } + if (data == null + || offset < 0 + || len < 0 + || offset + len <= 0 + || data.length < offset + len) { + throw new GmSSLException(""); + } + if (this.do_sign == true) { + if (GmSSLJNI.sm9_sign_update(this.sm9_sign_ctx, data, offset, len) != 1) { + throw new GmSSLException(""); + } + } else { + if (GmSSLJNI.sm9_verify_update(this.sm9_sign_ctx, data, offset, len) != 1) { + throw new GmSSLException(""); + } + } + } +} diff --git a/src/main/java/org/gmssl/crypto/asymmetric/SM9UserKey.java b/src/main/java/org/gmssl/crypto/asymmetric/SM9UserKey.java new file mode 100644 index 0000000..7e219b5 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/asymmetric/SM9UserKey.java @@ -0,0 +1,40 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.asymmetric; + +import java.security.spec.KeySpec; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/08/11 + * @description + * + */ +public abstract class SM9UserKey implements KeySpec { + + protected long sm9_key; + + protected String id; + + protected SM9PrivateKey privateKey; + + protected SM9UserKey(long key, String id) { + this.sm9_key = key; + this.id = id; + } + + public String getId() { + return this.id; + } + + public SM9PrivateKey getPrivateKey() { + return this.privateKey; + } +} diff --git a/src/main/java/org/gmssl/crypto/digest/SM3Digest.java b/src/main/java/org/gmssl/crypto/digest/SM3Digest.java new file mode 100644 index 0000000..15273e4 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/digest/SM3Digest.java @@ -0,0 +1,101 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.digest; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; + +import java.security.MessageDigestSpi; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/09/07 + * @description + * The SM3 cryptographic hash function can compute input data of arbitrary length into a fixed hash value of 32 bytes. + */ +public class SM3Digest extends MessageDigestSpi { + + private final static int DIGEST_SIZE = GmSSLJNI.SM3_DIGEST_SIZE; + + private long sm3_ctx = 0; + + public SM3Digest() { + init(); + } + + /** + * You can call the update method multiple times. After all the data has been input, finally call the digest method to obtain the SM3 hash value of the entire data. + * @param input the input byte to be processed. + */ + @Override + protected void engineUpdate(byte input) { + byte[] data = new byte[]{input}; + this.update(data, 0, data.length); + } + + @Override + protected void engineUpdate(byte[] input, int offset, int len) { + this.update(input, offset, len); + } + + @Override + protected byte[] engineDigest() { + return this.digest(); + } + + /** + * If you need to calculate different SM3 hash values for multiple sets of data, you can use the reset method to reset, + * and then call the update and digest methods again to compute the hash value of a new set of data. + */ + @Override + protected void engineReset() { + this.reset(); + } + + private void init(){ + if ((sm3_ctx = GmSSLJNI.sm3_ctx_new()) == 0) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm3_init(sm3_ctx) != 1) { + throw new GmSSLException(""); + } + } + + private void update(byte[] data, int offset, int len) { + if (data == null + || offset < 0 + || len < 0 + || offset + len <= 0 + || data.length < offset + len) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm3_update(sm3_ctx, data, offset, len) != 1) { + throw new GmSSLException(""); + } + } + + private byte[] digest() { + byte[] dgst = new byte[DIGEST_SIZE]; + if (GmSSLJNI.sm3_finish(sm3_ctx, dgst) != 1) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm3_init(sm3_ctx) != 1) { + throw new GmSSLException(""); + } + return dgst; + } + + private void reset() { + if (GmSSLJNI.sm3_init(sm3_ctx) != 1) { + throw new GmSSLException(""); + } + } + +} diff --git a/src/main/java/org/gmssl/crypto/digest/SM3Hmac.java b/src/main/java/org/gmssl/crypto/digest/SM3Hmac.java new file mode 100644 index 0000000..11d6243 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/digest/SM3Hmac.java @@ -0,0 +1,144 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.digest; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; + +import javax.crypto.MacSpi; +import javax.crypto.SecretKey; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.spec.AlgorithmParameterSpec; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/09/07 + * @description + * HMAC-SM3 is a Message Authentication Code (MAC) algorithm based on the SM3 cryptographic hash algorithm. + * A MAC algorithm can be viewed as a keyed hash function, primarily used to protect messages from tampering. + * Both communicating parties need to agree on a key in advance, such as a 32-byte random byte sequence. + * The data sender uses this key to compute the MAC value of the message and appends the MAC value to the message. + * Upon receiving the message, the recipient uses the same key to compute the MAC value of the message and compares it with the MAC value attached to the sent message. + * If they match, it indicates that the message has not been tampered with; if they do not match, it indicates that the message has been altered. + */ +public class SM3Hmac extends MacSpi { + + public final static int MAC_SIZE = GmSSLJNI.SM3_HMAC_SIZE; + + private Key key; + + private long sm3_hmac_ctx = 0; + + public SM3Hmac() { + super(); + ctx(); + } + + @Override + protected int engineGetMacLength() { + return MAC_SIZE; + } + + /** + * The HMAC-SM3 algorithm can be seen as the SM3 algorithm with a key, so when creating an Sm3Hmac object, a key must be passed as an input parameter. + * Although HMAC-SM3 does not have any restrictions on key length in terms of the algorithm and implementation, for considerations of security and efficiency, the key length for the HMAC-SM3 algorithm is recommended to be 32 bytes (equivalent to the length of the SM3 hash value) and should not be less than 16 bytes. + * Using a key length longer than 32 bytes would increase computational overhead without enhancing security. + * @param key the (secret) key. + * @param params the algorithm parameters. + * + * @throws InvalidKeyException + * @throws InvalidAlgorithmParameterException + */ + @Override + protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException { + if (!(key instanceof SecretKey)) { + throw new GmSSLException("Invalid key for HMAC-SM3"); + } + this.key = key; + init(); + } + + /** + * You can call update multiple times and ultimately execute doFinal. The HMAC-SM3 output is a fixed 32 bytes, which is a binary message authentication code of length MAC_SIZE. + * @param input the input byte to be processed. + */ + @Override + protected void engineUpdate(byte input) { + byte[] data = new byte[]{input}; + this.update(data, 0, data.length); + } + + @Override + protected void engineUpdate(byte[] input, int offset, int len) { + this.update(input, offset, len); + } + + @Override + protected byte[] engineDoFinal() { + return generateMac(); + } + + @Override + protected void engineReset() { + this.reset(this.key); + } + + private void ctx(){ + if ((this.sm3_hmac_ctx = GmSSLJNI.sm3_hmac_ctx_new()) == 0) { + throw new GmSSLException(""); + } + } + + private void init() { + if (GmSSLJNI.sm3_hmac_init(this.sm3_hmac_ctx, key.getEncoded()) != 1) { + throw new GmSSLException(""); + } + } + + private void update(byte[] data, int offset, int len) { + if (data == null + || offset < 0 + || len < 0 + || offset + len <= 0 + || data.length < offset + len) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm3_hmac_update(this.sm3_hmac_ctx, data, offset, len) != 1) { + throw new GmSSLException(""); + } + } + + private void update(byte[] data) { + this.update(data, 0, data.length); + } + + private byte[] generateMac() { + byte[] mac = new byte[this.MAC_SIZE]; + if (GmSSLJNI.sm3_hmac_finish(this.sm3_hmac_ctx, mac) != 1) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm3_hmac_init(this.sm3_hmac_ctx, this.key.getEncoded()) != 1) { + throw new GmSSLException(""); + } + return mac; + } + + private void reset(Key key) { + if (key == null) { + throw new GmSSLException(""); + } + if (GmSSLJNI.sm3_hmac_init(this.sm3_hmac_ctx, key.getEncoded()) != 1) { + throw new GmSSLException(""); + } + this.key = key; + } +} diff --git a/src/main/java/org/gmssl/crypto/digest/SM3Pbkdf2.java b/src/main/java/org/gmssl/crypto/digest/SM3Pbkdf2.java new file mode 100644 index 0000000..1e29f1a --- /dev/null +++ b/src/main/java/org/gmssl/crypto/digest/SM3Pbkdf2.java @@ -0,0 +1,97 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.digest; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; + +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactorySpi; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; +import java.security.InvalidKeyException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/09/07 + * @description + * PBKDF2 is one of the secure and widely used PBKDF algorithm standards. The algorithm uses a hash function as the primary component to map passwords to keys. + * It employs a random and public salt value (Salt) to resist precomputation attacks, increases the difficulty of online cracking by adding multiple rounds of iterative computation, and supports variable derived key lengths. + */ +public class SM3Pbkdf2 extends SecretKeyFactorySpi { + + public final static int MAX_SALT_SIZE = GmSSLJNI.SM3_PBKDF2_MAX_SALT_SIZE; + public final static int DEFAULT_SALT_SIZE = GmSSLJNI.SM3_PBKDF2_DEFAULT_SALT_SIZE; + public final static int MIN_ITER = GmSSLJNI.SM3_PBKDF2_MIN_ITER; + public final static int MAX_ITER = GmSSLJNI.SM3_PBKDF2_MAX_ITER; + public final static int MAX_KEY_SIZE = GmSSLJNI.SM3_PBKDF2_MAX_KEY_SIZE; + + public final static String ALGORITHM = "SM3Pbkdf2"; + + public SM3Pbkdf2() { + super(); + } + + /** + * + * @param keySpec PBEKeySpec the specification (key material) of the secret key + * pass is the user password used for deriving the key. + * salt is the value used to resist precomputation attacks. This value should be randomly generated (for example, using the Random class) and should have a certain length. + * The iter parameter represents the number of times the SM3 algorithm is called iteratively when deriving the key. A larger iter value increases the difficulty of brute-force attacks but also increases the computational overhead for users calling this function. + * The keylen parameter indicates the desired length of the derived key, which must not exceed the constant MAX_KEY_SIZE. + * @return + * @throws InvalidKeySpecException + */ + @Override + protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException { + if (!(keySpec instanceof PBEKeySpec)) { + throw new GmSSLException("Invalid KeySpec"); + } + PBEKeySpec pbeKeySpec = (PBEKeySpec) keySpec; + char[] password = pbeKeySpec.getPassword(); + byte[] salt = pbeKeySpec.getSalt(); + int iterations = pbeKeySpec.getIterationCount(); + int derivedKeyLength = pbeKeySpec.getKeyLength(); + byte[] key = deriveKey(new String(password), salt, iterations, derivedKeyLength); + return new SecretKeySpec(key, ALGORITHM); + } + + @Override + protected KeySpec engineGetKeySpec(SecretKey key, Class keySpec) throws InvalidKeySpecException { + throw new GmSSLException("Not supported"); + } + + @Override + protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException { + throw new GmSSLException("Not supported"); + } + + private byte[] deriveKey(String pass, byte[] salt, int iter, int keylen) { + if (pass == null) { + throw new GmSSLException(""); + } + if (salt == null || salt.length > MAX_SALT_SIZE) { + throw new GmSSLException(""); + } + if (iter < MIN_ITER || iter > MAX_ITER) { + throw new GmSSLException(""); + } + if (keylen < 0 || keylen > MAX_KEY_SIZE) { + throw new GmSSLException(""); + } + byte[] key = GmSSLJNI.sm3_pbkdf2(pass, salt, iter, keylen); + if (key == null) { + throw new GmSSLException(""); + } + return key; + } +} diff --git a/src/main/java/org/gmssl/crypto/symmetric/SM4CBC.java b/src/main/java/org/gmssl/crypto/symmetric/SM4CBC.java new file mode 100644 index 0000000..5f2a4b9 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/symmetric/SM4CBC.java @@ -0,0 +1,201 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.symmetric; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Arrays; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/07/27 + * @description + * + */ +public class SM4CBC extends SM4Engine { + + public final static int IV_SIZE = GmSSLJNI.SM4_BLOCK_SIZE; + + private long sm4_cbc_ctx; + + private byte[] iv; + + private boolean do_encrypt = true; + + private boolean inited; + + private int offset; + + private byte[] outputByteArray; + + protected SM4CBC() { + super(); + ctx(); + } + + @Override + protected byte[] engineGetIV() { + return iv; + } + + @Override + protected void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException { + throw new GmSSLException("Initialization method not supported!"); + } + + @Override + protected void init(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random){ + if (!(params instanceof IvParameterSpec)) { + throw new GmSSLException("need the IvParameterSpec parameter"); + } + this.iv = ((IvParameterSpec) params).getIV(); + this.do_encrypt = (opmode == Cipher.ENCRYPT_MODE); + init(key.getEncoded(), iv, do_encrypt); + + outputByteArray = new byte[BLOCK_SIZE]; + } + + @Override + protected byte[] processUpdate(byte[] input, int inputOffset, int inputLen) { + int newOutputLength = BLOCK_SIZE + offset + inputLen; + if (outputByteArray.length < newOutputLength) { + int newSize = Math.max(outputByteArray.length * 3 / 2 + BLOCK_SIZE, newOutputLength); + outputByteArray = Arrays.copyOf(outputByteArray, newSize); + } + + int outLen = processUpdate(input, inputOffset, inputLen, outputByteArray, offset); + return Arrays.copyOfRange(outputByteArray,offset-outLen,offset); + } + + @Override + protected int processUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset){ + int outLen = update(input, inputOffset, inputLen, output, outputOffset); + this.offset+=outLen; + return outLen; + } + + @Override + protected byte[] processBlock(byte[] input, int inputOffset, int inputLen) { + if(null!=input){ + processUpdate(input, inputOffset, inputLen); + } + int outLen = doFinal(outputByteArray, this.offset); + outLen = outLen + this.offset; + this.offset = 0; + outputByteArray = Arrays.copyOfRange(outputByteArray,0,outLen); + return outputByteArray; + } + + @Override + protected int processBlock(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) { + int outLen = 0; + if(null!=input){ + outLen=this.processUpdate(input, inputOffset, inputLen, output, outputOffset); + } + outLen += doFinal(output, this.offset); + this.offset = 0; + return outLen; + } + + private void ctx() { + if ((this.sm4_cbc_ctx = GmSSLJNI.sm4_cbc_ctx_new()) == 0) { + throw new GmSSLException(""); + } + this.inited = false; + } + + private void init(byte[] key, byte[] iv, boolean do_encrypt) { + if (key == null + || key.length != this.KEY_SIZE + || iv == null + || iv.length != this.IV_SIZE) { + throw new GmSSLException(""); + } + + if (do_encrypt) { + if (GmSSLJNI.sm4_cbc_encrypt_init(this.sm4_cbc_ctx, key, iv) != 1) { + throw new GmSSLException(""); + } + } else { + if (GmSSLJNI.sm4_cbc_decrypt_init(this.sm4_cbc_ctx, key, iv) != 1) { + throw new GmSSLException(""); + } + } + + this.do_encrypt = do_encrypt; + this.inited = true; + } + + private int update(byte[] in, int in_offset, int inlen, byte[] out, int out_offset) { + if (this.inited == false) { + throw new GmSSLException(""); + } + + if (in == null + || in_offset < 0 + || inlen < 0 + || in_offset + inlen <= 0 + || in.length < in_offset + inlen) { + throw new GmSSLException(""); + } + if (out == null + || out_offset < 0 + || out.length < out_offset) { + throw new GmSSLException(""); + } + + int outlen; + if (this.do_encrypt) { + if ((outlen = GmSSLJNI.sm4_cbc_encrypt_update(this.sm4_cbc_ctx, in, in_offset, inlen, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + } else { + if ((outlen = GmSSLJNI.sm4_cbc_decrypt_update(this.sm4_cbc_ctx, in, in_offset, inlen, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + } + + return outlen; + } + + private int doFinal(byte[] out, int out_offset) { + if (this.inited == false) { + throw new GmSSLException(""); + } + + if (out == null + || out_offset < 0 + || out.length < out_offset) { + throw new GmSSLException(""); + } + + int outlen; + if (this.do_encrypt) { + if ((outlen = GmSSLJNI.sm4_cbc_encrypt_finish(this.sm4_cbc_ctx, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + } else { + if ((outlen = GmSSLJNI.sm4_cbc_decrypt_finish(this.sm4_cbc_ctx, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + } + + this.inited = false; + return outlen; + } + +} diff --git a/src/main/java/org/gmssl/crypto/symmetric/SM4CTR.java b/src/main/java/org/gmssl/crypto/symmetric/SM4CTR.java new file mode 100644 index 0000000..01732f3 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/symmetric/SM4CTR.java @@ -0,0 +1,177 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.symmetric; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.IvParameterSpec; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Arrays; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/07/27 + * @description + * + */ +public class SM4CTR extends SM4Engine { + + public final static int IV_SIZE = GmSSLJNI.SM4_BLOCK_SIZE; + + private byte[] iv; + + private long sm4_ctr_ctx = 0; + private boolean inited = false; + + private int offset; + + private byte[] outputByteArray; + + protected SM4CTR() { + super(); + ctx(); + } + + @Override + protected void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException { + throw new GmSSLException("Initialization method not supported!"); + } + + @Override + protected void init(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) { + if (!(params instanceof IvParameterSpec)) { + throw new GmSSLException("need the IvParameterSpec parameter"); + } + this.iv = ((IvParameterSpec) params).getIV(); + init(key.getEncoded(), iv); + + outputByteArray = new byte[BLOCK_SIZE]; + } + + @Override + protected byte[] engineGetIV() { + return iv; + } + + @Override + protected byte[] processUpdate(byte[] input, int inputOffset, int inputLen) { + int newOutputLength = BLOCK_SIZE + offset + inputLen; + if (outputByteArray.length < newOutputLength) { + int newSize = Math.max(outputByteArray.length * 3 / 2 + BLOCK_SIZE, newOutputLength); + outputByteArray = Arrays.copyOf(outputByteArray, newSize); + } + + int outLen = processUpdate(input, inputOffset, inputLen, outputByteArray, offset); + return Arrays.copyOfRange(outputByteArray,offset-outLen,offset); + } + + @Override + protected int processUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) { + int outLen = update(input, inputOffset, inputLen, output, outputOffset); + this.offset += outLen; + return outLen; + } + + @Override + protected byte[] processBlock(byte[] input, int inputOffset, int inputLen) throws IllegalBlockSizeException, BadPaddingException { + if(null!=input){ + processUpdate(input, inputOffset, inputLen); + } + int outLen = doFinal(outputByteArray, this.offset); + outLen = outLen + this.offset; + this.offset = 0; + outputByteArray = Arrays.copyOfRange(outputByteArray,0,outLen); + return outputByteArray; + } + + @Override + protected int processBlock(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { + int outLen = 0; + if(null!=input){ + outLen=this.processUpdate(input, inputOffset, inputLen, output, outputOffset); + } + outLen += doFinal(output, this.offset); + this.offset = 0; + return outLen; + } + + public void ctx(){ + if ((this.sm4_ctr_ctx = GmSSLJNI.sm4_ctr_ctx_new()) == 0) { + throw new GmSSLException(""); + } + this.inited = false; + } + + public void init(byte[] key, byte[] iv) { + if (key == null + || key.length != this.KEY_SIZE + || iv == null + || iv.length != this.IV_SIZE) { + throw new GmSSLException(""); + } + + if (GmSSLJNI.sm4_ctr_encrypt_init(this.sm4_ctr_ctx, key, iv) != 1) { + throw new GmSSLException(""); + } + + this.inited = true; + } + + public int update(byte[] in, int in_offset, int inlen, byte[] out, int out_offset) { + if (this.inited == false) { + throw new GmSSLException(""); + } + + if (in == null + || in_offset < 0 + || inlen < 0 + || in_offset + inlen <= 0 + || in.length < in_offset + inlen) { + throw new GmSSLException(""); + } + if (out == null + || out_offset < 0 + || out.length < out_offset) { + throw new GmSSLException(""); + } + + int outlen; + if ((outlen = GmSSLJNI.sm4_ctr_encrypt_update(this.sm4_ctr_ctx, in, in_offset, inlen, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + return outlen; + } + + public int doFinal(byte[] out, int out_offset){ + if (this.inited == false) { + throw new GmSSLException(""); + } + + if (out == null + || out_offset < 0 + || out.length < out_offset) { + throw new GmSSLException(""); + } + + int outlen; + if ((outlen = GmSSLJNI.sm4_ctr_encrypt_finish(this.sm4_ctr_ctx, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + this.inited = false; + return outlen; + } +} diff --git a/src/main/java/org/gmssl/crypto/symmetric/SM4Cipher.java b/src/main/java/org/gmssl/crypto/symmetric/SM4Cipher.java new file mode 100644 index 0000000..2902c4c --- /dev/null +++ b/src/main/java/org/gmssl/crypto/symmetric/SM4Cipher.java @@ -0,0 +1,110 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.symmetric; + +import org.gmssl.GmSSLJNI; + +import javax.crypto.*; +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/07/27 + * @description + * CBC、CTR、ECB、GCM + */ +public class SM4Cipher extends CipherSpi { + + public static final int KEY_SIZE = GmSSLJNI.SM4_KEY_SIZE; + + public static final int BLOCK_SIZE = GmSSLJNI.SM4_BLOCK_SIZE; + + private SM4Engine sm4Engine; + + public SM4Cipher() { + super(); + } + + @Override + protected void engineSetMode(String mode) throws NoSuchAlgorithmException { + this.sm4Engine = SM4CipherFactory.createCipher(mode); + } + + @Override + protected void engineSetPadding(String padding) throws NoSuchPaddingException { + SM4CipherFactory.setPaddingScheme(sm4Engine, padding); + } + + @Override + protected int engineGetBlockSize() { + return SM4Engine.BLOCK_SIZE; + } + + @Override + protected int engineGetOutputSize(int inputLen) { + return 0; + } + + @Override + protected byte[] engineGetIV() { + return sm4Engine.engineGetIV(); + } + + @Override + protected AlgorithmParameters engineGetParameters() { + return null; + } + + @Override + protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException { + sm4Engine.init(opmode,key,random); + } + + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { + sm4Engine.init(opmode, key, params, random); + } + + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { + + } + + @Override + protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { + byte[] result = sm4Engine.processUpdate(input,inputOffset,inputLen); + return result; + } + + @Override + protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException { + int outLen = sm4Engine.processUpdate(input, inputOffset, inputLen, output, outputOffset); + return outLen; + } + + @Override + protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) throws IllegalBlockSizeException, BadPaddingException { + byte[] result = sm4Engine.processBlock(input, inputOffset, inputLen); + return result; + } + + @Override + protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { + int outLen = sm4Engine.processBlock(input, inputOffset, inputLen, output, outputOffset); + return outLen; + } + + @Override + protected void engineUpdateAAD(byte[] src, int offset, int len) { + sm4Engine.processUpdateAAD(src, offset, len); + } + +} diff --git a/src/main/java/org/gmssl/crypto/symmetric/SM4CipherFactory.java b/src/main/java/org/gmssl/crypto/symmetric/SM4CipherFactory.java new file mode 100644 index 0000000..cde6769 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/symmetric/SM4CipherFactory.java @@ -0,0 +1,66 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.symmetric; + +import org.gmssl.crypto.CipherPaddingEnum; +import org.gmssl.crypto.PKCS7PaddingScheme; + +import java.security.NoSuchAlgorithmException; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/07/27 + * @description + * + */ +public class SM4CipherFactory { + + /** + * Create an SM4 encryption and decryption engine that supports ECB, CBC, CTR, and GCM modes. + * @param mode + * @return + */ + public static SM4Engine createCipher(String mode){ + SM4Engine cipher; + try { + switch (mode.toUpperCase()) { + case "ECB": + cipher = new SM4ECB(); + break; + case "CBC": + cipher = new SM4CBC(); + break; + case "CTR": + cipher = new SM4CTR(); + break; + case "GCM": + cipher = new SM4GCM(); + break; + default: + throw new NoSuchAlgorithmException("Unsupported mode: " + mode); + } + }catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + return cipher; + } + + /** + * Currently, only the PKCS7Padding padding mode for ECB algorithm mode is set. Other algorithms (SM4/CBC/PKCS5Padding, SM4/CTR/NoPadding, SM4/GCM/NoPadding) have their default padding implemented. + * @param engine + * @param padding + */ + public static void setPaddingScheme(SM4Engine engine, String padding) { + if(CipherPaddingEnum.PKCS7Padding.name().equals(padding)){ + engine.paddingScheme=new PKCS7PaddingScheme(); + } + } + +} diff --git a/src/main/java/org/gmssl/crypto/symmetric/SM4ECB.java b/src/main/java/org/gmssl/crypto/symmetric/SM4ECB.java new file mode 100644 index 0000000..27352c5 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/symmetric/SM4ECB.java @@ -0,0 +1,181 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.symmetric; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; +import org.gmssl.Sm4; + +import javax.crypto.*; +import java.nio.ByteBuffer; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Arrays; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/07/27 + * @description + * + */ +public class SM4ECB extends SM4Engine { + + private Key key; + private long sm4_key; + + private boolean do_encrypt; + + private ByteBuffer buffer; + + @Override + protected void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException { + if (!(key instanceof SecretKey)) { + throw new GmSSLException("Invalid KeySpec"); + } + this.do_encrypt = (opmode == Cipher.ENCRYPT_MODE); + this.key = key; + // 初始化缓冲区 + this.buffer = ByteBuffer.allocate(2048); + init(); + } + + @Override + protected void init(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) { + throw new GmSSLException("Initialization method not supported!"); + } + + @Override + protected byte[] engineGetIV() { + return null; + } + + /** + * Mainly used for caching data; it will not immediately generate encryption or decryption results + * @param input + * @param inputOffset + * @param inputLen + * @return null + * Return a non-actual value; actual encryption or decryption operations are performed in processBlock + */ + @Override + protected byte[] processUpdate(byte[] input, int inputOffset, int inputLen) { + putBytes(input, inputOffset, inputLen); + return null; + } + + @Override + protected byte[] processBlock(byte[] input, int inputOffset, int inputLen) throws IllegalBlockSizeException, BadPaddingException { + if(null!=input){ + putBytes(input, inputOffset, inputLen); + } + byte[] data = new byte[buffer.position()]; + buffer.flip(); + buffer.get(data); + + byte[] outPutByteArray = new byte[buffer.position()]; + if(do_encrypt){ + data = this.paddingScheme.pad(data,this.BLOCK_SIZE); + outPutByteArray = new byte[data.length]; + for (int i = 0; i < data.length; i += this.BLOCK_SIZE) { + encrypt(data,i,outPutByteArray,i); + } + }else{ + for (int i = 0; i < data.length; i += this.BLOCK_SIZE) { + encrypt(data,i,outPutByteArray,i); + } + outPutByteArray=this.paddingScheme.unpad(outPutByteArray); + } + + buffer.clear(); + return outPutByteArray; + } + + /** + * Mainly used for caching data; it will not immediately generate encryption or decryption results + * @param input + * @param inputOffset + * @param inputLen + * @param output + * @param outputOffset + * @return 0 + * Return a non-actual value; actual encryption or decryption operations are performed in processBlock + */ + @Override + protected int processUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) { + putBytes(input, inputOffset, inputLen); + return 0; + } + + /** + * + * @param input + * @param inputOffset + * @param inputLen + * @param output + * @param outputOffset + * @return actual encryption or decryption bytes length,not the whole length of the output data + * + * @throws IllegalBlockSizeException + * @throws BadPaddingException + */ + @Override + protected int processBlock(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws IllegalBlockSizeException, BadPaddingException { + byte[] outPutByteArray = processBlock(input, inputOffset, inputLen); + System.arraycopy(outPutByteArray, 0,output, outputOffset, outPutByteArray.length); + return outPutByteArray.length; + } + + private void putBytes(byte[] input, int inputOffset, int inputLen){ + if(buffer.remaining() in.length) { + throw new GmSSLException(""); + } + if (out == null + || out_offset < 0 + || out_offset + this.BLOCK_SIZE <= 0 + || out_offset + this.BLOCK_SIZE > in.length) { + throw new GmSSLException(""); + } + + if (GmSSLJNI.sm4_encrypt(sm4_key, in, in_offset, out, out_offset) != 1) { + throw new GmSSLException(""); + } + } +} diff --git a/src/main/java/org/gmssl/crypto/symmetric/SM4Engine.java b/src/main/java/org/gmssl/crypto/symmetric/SM4Engine.java new file mode 100644 index 0000000..52bfe1c --- /dev/null +++ b/src/main/java/org/gmssl/crypto/symmetric/SM4Engine.java @@ -0,0 +1,53 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.symmetric; + +import org.gmssl.GmSSLJNI; +import org.gmssl.crypto.PaddingScheme; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.ShortBufferException; +import java.nio.ByteBuffer; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/07/27 + * @description + * + */ +public abstract class SM4Engine { + + public static final int KEY_SIZE = SM4Cipher.KEY_SIZE; + + public static final int BLOCK_SIZE = SM4Cipher.BLOCK_SIZE; + + protected PaddingScheme paddingScheme; + + protected abstract void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException; + + protected abstract void init(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random); + + protected abstract byte[] engineGetIV(); + + protected abstract byte[] processUpdate(byte[] input, int inputOffset, int inputLen); + + protected abstract int processUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException; + + protected abstract int processBlock(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException; + + protected abstract byte[] processBlock(byte[] input, int inputOffset, int inputLen) throws IllegalBlockSizeException, BadPaddingException; + protected void processUpdateAAD(byte[] src, int offset, int len){}; + +} diff --git a/src/main/java/org/gmssl/crypto/symmetric/SM4GCM.java b/src/main/java/org/gmssl/crypto/symmetric/SM4GCM.java new file mode 100644 index 0000000..a10f8b4 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/symmetric/SM4GCM.java @@ -0,0 +1,231 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.symmetric; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.GCMParameterSpec; +import java.nio.ByteBuffer; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Arrays; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/07/27 + * @description + * + */ +public class SM4GCM extends SM4Engine { + + public final static int MIN_IV_SIZE = GmSSLJNI.SM4_GCM_MIN_IV_SIZE; + public final static int MAX_IV_SIZE = GmSSLJNI.SM4_GCM_MAX_IV_SIZE; + public final static int DEFAULT_IV_SIZE = GmSSLJNI.SM4_GCM_DEFAULT_IV_SIZE; + public final static int MIN_TAG_SIZE = 8; + public final static int MAX_TAG_SIZE = GmSSLJNI.SM4_GCM_MAX_TAG_SIZE; + + private long sm4_gcm_ctx = 0; + private boolean do_encrypt = true; + private boolean inited = false; + + private byte[] iv; + + private ByteBuffer aad; + + private Key key; + + private int tLen; + + private int offset; + + private byte[] outputByteArray; + + protected SM4GCM(){ + super(); + ctx(); + } + + @Override + protected void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException { + throw new GmSSLException("Initialization method not supported!"); + } + + @Override + protected void init(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) { + if (!(params instanceof GCMParameterSpec)) { + throw new GmSSLException("need the GCMParameterSpec parameter"); + } + this.key = key; + this.iv = ((GCMParameterSpec) params).getIV(); + this.tLen = ((GCMParameterSpec) params).getTLen(); + this.do_encrypt = (opmode == Cipher.ENCRYPT_MODE); + + outputByteArray = new byte[BLOCK_SIZE+tLen]; + aad=ByteBuffer.allocate(BLOCK_SIZE+tLen); + } + + @Override + protected byte[] engineGetIV() { + return iv; + } + + @Override + protected byte[] processUpdate(byte[] input, int inputOffset, int inputLen) { + int newOutputLength = BLOCK_SIZE + offset + tLen + inputLen; + if (outputByteArray.length < newOutputLength) { + int newSize = Math.max(outputByteArray.length * 3 / 2 + BLOCK_SIZE + tLen, newOutputLength); + outputByteArray = Arrays.copyOf(outputByteArray, newSize); + } + + int outLen = processUpdate(input, inputOffset, inputLen, outputByteArray, offset); + return Arrays.copyOfRange(outputByteArray,offset-outLen,offset); + } + + @Override + protected int processUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) { + int outLen = update(input, inputOffset, inputLen, output, outputOffset); + this.offset+=outLen; + return outLen; + } + + @Override + protected byte[] processBlock(byte[] input, int inputOffset, int inputLen) throws IllegalBlockSizeException, BadPaddingException { + if(null!=input){ + processUpdate(input, inputOffset, inputLen); + } + int outLen = doFinal(outputByteArray, this.offset); + outLen = outLen + this.offset; + this.offset = 0; + outputByteArray = Arrays.copyOfRange(outputByteArray,0,outLen); + return outputByteArray; + } + + @Override + protected int processBlock(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { + int outLen = 0; + if(null!=input){ + outLen=this.processUpdate(input, inputOffset, inputLen, output, outputOffset); + } + outLen += doFinal(output, this.offset); + this.offset = 0; + return outLen; + } + + @Override + protected void processUpdateAAD(byte[] src, int offset, int len) { + if(aad.remaining() this.MAX_IV_SIZE + || taglen < this.MIN_TAG_SIZE + || taglen > this.MAX_TAG_SIZE) { + throw new GmSSLException(""); + } + + if (do_encrypt == true) { + if (GmSSLJNI.sm4_gcm_encrypt_init(this.sm4_gcm_ctx, key, iv, aad, taglen) != 1) { + throw new GmSSLException(""); + } + } else { + if (GmSSLJNI.sm4_gcm_decrypt_init(this.sm4_gcm_ctx, key, iv, aad, taglen) != 1) { + throw new GmSSLException(""); + } + } + + this.do_encrypt = do_encrypt; + this.inited = true; + } + + private int update(byte[] in, int in_offset, int inlen, byte[] out, int out_offset){ + if (this.inited == false) { + throw new GmSSLException(""); + } + + if (in == null + || in_offset < 0 + || inlen < 0 + || in_offset + inlen <= 0 + || in.length < in_offset + inlen) { + throw new GmSSLException(""); + } + if (out == null + || out_offset < 0 + || out.length < out_offset) { + throw new GmSSLException(""); + } + + int outlen; + if (this.do_encrypt) { + if ((outlen = GmSSLJNI.sm4_gcm_encrypt_update(this.sm4_gcm_ctx, in, in_offset, inlen, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + } else { + if ((outlen = GmSSLJNI.sm4_gcm_decrypt_update(this.sm4_gcm_ctx, in, in_offset, inlen, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + } + + return outlen; + } + + private int doFinal(byte[] out, int out_offset) { + + if (this.inited == false) { + throw new GmSSLException(""); + } + + if (out == null + || out_offset < 0 + || out.length < out_offset) { + throw new GmSSLException(""); + } + + int outlen; + if (this.do_encrypt) { + if ((outlen = GmSSLJNI.sm4_gcm_encrypt_finish(this.sm4_gcm_ctx, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + } else { + if ((outlen = GmSSLJNI.sm4_gcm_decrypt_finish(this.sm4_gcm_ctx, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + } + + this.inited = false; + return outlen; + } +} diff --git a/src/main/java/org/gmssl/crypto/symmetric/ZucCipher.java b/src/main/java/org/gmssl/crypto/symmetric/ZucCipher.java new file mode 100644 index 0000000..5f43828 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/symmetric/ZucCipher.java @@ -0,0 +1,221 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.symmetric; + +import org.gmssl.GmSSLException; +import org.gmssl.GmSSLJNI; + +import javax.crypto.*; +import javax.crypto.spec.IvParameterSpec; +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Arrays; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/07/27 + * @description + * The Zu Chongzhi Cipher Algorithm (ZU Cipher, ZUC) is a stream cipher with both the key and IV lengths set to 16 bytes. + * As a stream cipher, ZUC can encrypt input data of variable length, and the ciphertext output has the same length as the input data. + * This makes it suitable for applications that do not allow ciphertext expansion. + * In terms of security, it is not recommended to use the ZUC algorithm to encrypt large amounts of data (such as GB or TB levels) with a single key and IV, to avoid a decrease in security when the stream cipher produces extremely long outputs. + */ +public class ZucCipher extends CipherSpi { + + public final static int IV_SIZE = GmSSLJNI.ZUC_IV_SIZE; + public final static int BLOCK_SIZE = 4; + + private long zuc_ctx; + private boolean inited; + + private byte[] iv; + + private int offset; + + private byte[] outputByteArray; + + public ZucCipher(){ + ctx(); + } + + /** + * As a stream cipher, ZUC generates a pseudo-random sequence for each encryption or decryption operation, which is then XORed bit-by-bit with the plaintext to achieve encryption or decryption. + * Therefore, ZUC does not require the use of specific modes like block ciphers; instead, it directly generates the encryption key stream and performs the encryption operation bit-by-bit. + * @param mode the cipher mode + * + */ + @Override + protected void engineSetMode(String mode){ + } + + /** + * ZUC does not require a padding mode and can directly handle plaintext of any length. + * @param padding the padding mechanism + * + */ + @Override + protected void engineSetPadding(String padding){ + } + + @Override + protected int engineGetBlockSize() { + return BLOCK_SIZE; + } + + @Override + protected int engineGetOutputSize(int inputLen) { + return 0; + } + + @Override + protected byte[] engineGetIV() { + return iv; + } + + @Override + protected AlgorithmParameters engineGetParameters() { + return null; + } + + @Override + protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException { + throw new GmSSLException("Initialization method not supported!"); + } + + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidAlgorithmParameterException { + if (params instanceof IvParameterSpec) { + IvParameterSpec ivSpec = (IvParameterSpec) params; + this.iv = ivSpec.getIV(); + } else { + throw new InvalidAlgorithmParameterException("Unsupported parameters"); + } + init(key.getEncoded(), iv); + + outputByteArray = new byte[BLOCK_SIZE]; + } + + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { + throw new GmSSLException("Initialization method not supported!"); + } + + @Override + protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { + int newOutputLength = BLOCK_SIZE + offset + inputLen; + if (outputByteArray.length < newOutputLength) { + int newSize = Math.max(outputByteArray.length * 3 / 2 + BLOCK_SIZE, newOutputLength); + outputByteArray = Arrays.copyOf(outputByteArray, newSize); + } + + int outLen = engineUpdate(input, inputOffset, inputLen, outputByteArray, offset); + return Arrays.copyOfRange(outputByteArray,offset-outLen,offset); + } + + @Override + protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset){ + int outLen = update(input, inputOffset, inputLen, output, outputOffset); + this.offset+=outLen; + return outLen; + } + + @Override + protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) throws IllegalBlockSizeException, BadPaddingException { + if(null!=input){ + engineUpdate(input, inputOffset, inputLen); + } + int outLen = doFinal(outputByteArray, this.offset); + outLen = outLen + this.offset; + this.offset = 0; + outputByteArray = Arrays.copyOfRange(outputByteArray,0,outLen); + return outputByteArray; + } + + @Override + protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { + int outLen = 0; + if(null!=input){ + outLen=this.engineUpdate(input, inputOffset, inputLen, output, outputOffset); + } + outLen += doFinal(output, this.offset); + this.offset = 0; + return outLen; + } + + private void ctx(){ + if ((this.zuc_ctx = GmSSLJNI.zuc_ctx_new()) == 0) { + throw new GmSSLException(""); + } + this.inited = false; + } + + private void init(byte[] key, byte[] iv){ + if (key == null + || key.length != ZucKey.KEY_SIZE + || iv == null + || iv.length != this.IV_SIZE) { + throw new GmSSLException(""); + } + + if (GmSSLJNI.zuc_encrypt_init(this.zuc_ctx, key, iv) != 1) { + throw new GmSSLException(""); + } + + this.inited = true; + } + + private int update(byte[] in, int in_offset, int inlen, byte[] out, int out_offset) { + + if (this.inited == false) { + throw new GmSSLException(""); + } + + if (in == null + || in_offset < 0 + || inlen < 0 + || in_offset + inlen <= 0 + || in.length < in_offset + inlen) { + throw new GmSSLException(""); + } + if (out == null + || out_offset < 0 + || out.length < out_offset) { + throw new GmSSLException(""); + } + + int outlen; + if ((outlen = GmSSLJNI.zuc_encrypt_update(this.zuc_ctx, in, in_offset, inlen, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + + return outlen; + } + + private int doFinal(byte[] out, int out_offset) { + + if (this.inited == false) { + throw new GmSSLException(""); + } + + if (out == null + || out_offset < 0 + || out.length < out_offset) { + throw new GmSSLException(""); + } + + int outlen; + if ((outlen = GmSSLJNI.zuc_encrypt_finish(this.zuc_ctx, out, out_offset)) < 0) { + throw new GmSSLException(""); + } + + this.inited = false; + return outlen; + } +} diff --git a/src/main/java/org/gmssl/crypto/symmetric/ZucKey.java b/src/main/java/org/gmssl/crypto/symmetric/ZucKey.java new file mode 100644 index 0000000..f305f91 --- /dev/null +++ b/src/main/java/org/gmssl/crypto/symmetric/ZucKey.java @@ -0,0 +1,46 @@ +/* + * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl.crypto.symmetric; + +import org.gmssl.GmSSLJNI; + +import javax.crypto.SecretKey; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2024/07/27 + * @description + * + */ +public class ZucKey implements SecretKey { + + public final static int KEY_SIZE = GmSSLJNI.ZUC_KEY_SIZE; + + private byte[] key; + + public ZucKey(byte[] key){ + this.key = key; + } + + @Override + public String getAlgorithm() { + return "ZUC"; + } + + @Override + public String getFormat() { + return null; + } + + @Override + public byte[] getEncoded() { + return key; + } +} diff --git a/src/main/resources/config.properties b/src/main/resources/config.properties new file mode 100644 index 0000000..fb89108 --- /dev/null +++ b/src/main/resources/config.properties @@ -0,0 +1,3 @@ +# The address of the gmssl library referenced in the generated library call chain in the macOS environment. +# The call chain situation can be viewed using the otool -L tool. +macReferencedLib=/usr/local/lib/libgmssl.3.dylib \ No newline at end of file diff --git a/src/main/resources/lib/libgmssljni.dll b/src/main/resources/lib/libgmssljni.dll new file mode 100644 index 0000000..0d4e190 Binary files /dev/null and b/src/main/resources/lib/libgmssljni.dll differ diff --git a/src/test/java/org/gmssl/HexUtil.java b/src/test/java/org/gmssl/HexUtil.java new file mode 100644 index 0000000..26b43cf --- /dev/null +++ b/src/test/java/org/gmssl/HexUtil.java @@ -0,0 +1,49 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl; + + +import java.math.BigInteger; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2023/09/07 + * @description Hex uitl + */ +public class HexUtil { + + /** + * convert byte array to hex string + * @param btArr + * @return String + */ + public static String byteToHex(byte[] btArr) { + BigInteger bigInteger = new BigInteger(1, btArr); + return bigInteger.toString(16); + } + + /** + * convert hex string to byte array + * @param hexString + * @return byte[] + */ + public static byte[] hexToByte(String hexString) { + byte[] byteArray = new BigInteger(hexString, 16) + .toByteArray(); + if (byteArray[0] == 0) { + byte[] output = new byte[byteArray.length - 1]; + System.arraycopy( + byteArray, 1, output, + 0, output.length); + return output; + } + return byteArray; + } +} \ No newline at end of file diff --git a/src/test/java/org/gmssl/JceTest.java b/src/test/java/org/gmssl/JceTest.java new file mode 100644 index 0000000..39f2f35 --- /dev/null +++ b/src/test/java/org/gmssl/JceTest.java @@ -0,0 +1,406 @@ +package org.gmssl; + +import org.gmssl.crypto.asymmetric.*; +import org.gmssl.crypto.digest.SM3Hmac; +import org.gmssl.crypto.digest.SM3Pbkdf2; +import org.gmssl.crypto.symmetric.*; +import org.junit.Before; +import org.junit.Test; + +import javax.crypto.*; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; +import java.math.BigInteger; +import java.security.*; +import java.util.Arrays; + +/** + * @author yongfeili + * @date 2024/8/2 + * @description you must need to use openjdk! + * https://jdk.java.net/archive/ + * https://stackoverflow.com/questions/1756801/how-to-sign-a-custom-jce-security-provider + */ +public class JceTest { + + @Before + public void beforeTest(){ + Security.addProvider(new org.gmssl.crypto.GmSSLProvider()); + } + + @Test + public void SM2_test() throws Exception{ + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("SM2", "GmSSL"); + keyPairGen.initialize(256); + KeyPair keyPair = keyPairGen.generateKeyPair(); + byte[] pub= keyPair.getPublic().getEncoded(); + System.out.println(byteToHex(pub)); + byte[] pri= keyPair.getPrivate().getEncoded(); + // export private key + SM2PrivateKey SM2PrivateKey= (SM2PrivateKey)keyPair.getPrivate(); + SM2PrivateKey.exportEncryptedPrivateKeyInfoPem("123456", "D:\\private.key.pem"); + System.out.println(byteToHex(pri)); + + //Test "Z-value" hash + SM2PublicKey sm2PublicKey = new SM2PublicKey(pub); + byte[] zHash = sm2PublicKey.computeZ("Hello, GmSSL"); + System.out.println("zHash:"+byteToHex(zHash)); + + Cipher cipher = Cipher.getInstance("SM2", "GmSSL"); + // Test encryption + cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic()); + byte[] plaintext = "Hello, GmSSL".getBytes(); + byte[] ciphertext = cipher.doFinal(plaintext); + System.out.println("Ciphertext: " + byteToHex(ciphertext)); + // Test decryption + cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate()); + byte[] decrypted = cipher.doFinal(ciphertext); + System.out.println("Decrypted: " + new String(decrypted)); + + Signature signature = Signature.getInstance("SM2", "GmSSL"); + // Test signature + signature.initSign(keyPair.getPrivate()); + byte[] signatureText = "Hello, GmSSL".getBytes(); + signature.update(signatureText); + byte[] signatureByte = signature.sign(); + System.out.println("Signature:"+byteToHex(signatureByte)); + // Test signature verification + signature.initVerify(keyPair.getPublic()); + signature.update(signatureText); + boolean signatureResult = signature.verify(signatureByte); + System.out.println("SignatureResult:"+signatureResult); + + + Signature signatureImport = Signature.getInstance("SM2", "GmSSL"); + // import private key + String privateKeyInfoHex="308193020100301306072a8648ce3d020106082a811ccf5501822d0479307702010104207fef3e258348873c47117c15093266e9dad99e131f1778e53d362b2b70649f85a00a06082a811ccf5501822da14403420004f94c0abb6cd00c6f0918cb9c54162213501d5cc278f5d3fcf63886f4e1dc6322b1b110e33a25216f258c4cce5fd52ab320d3b086ee5390f7387218c92578c3ab"; + byte[] privateKeyInfo = hexToByte(privateKeyInfoHex); + signatureImport.initSign(new SM2PrivateKey(privateKeyInfo)); + signatureImport.update(signatureText); + byte[] signatureByteImport = signatureImport.sign(); + System.out.println("Signature:"+byteToHex(signatureByteImport)); + // export public key + String publicKeyInfoHex = "3059301306072a8648ce3d020106082a811ccf5501822d03420004f94c0abb6cd00c6f0918cb9c54162213501d5cc278f5d3fcf63886f4e1dc6322b1b110e33a25216f258c4cce5fd52ab320d3b086ee5390f7387218c92578c3ab"; + byte[] publicKeyInfo = hexToByte(publicKeyInfoHex); + signatureImport.initVerify(new SM2PublicKey(publicKeyInfo)); + signatureImport.update(signatureText); + boolean signatureResultImport = signatureImport.verify(signatureByteImport); + System.out.println("SignatureResult:"+signatureResultImport); + } + + @Test + public void sm2_certificate_test() throws Exception{ + SM2Certificate sm2Cert = new SM2Certificate(); + //sm2Cert.importPem("D:\\cert.pem"); + //System.out.println("NotAfter:"+sm2Cert.getNotAfter()); + } + + @Test + public void SM3_test() throws Exception{ + String text="Hello, GmSSL"; + // hash + MessageDigest sm3Digest = MessageDigest.getInstance("SM3","GmSSL"); + sm3Digest.update("abc".getBytes()); + sm3Digest.reset(); + sm3Digest.update(text.getBytes()); + byte[] digest = sm3Digest.digest(); + System.out.println("digest:"+byteToHex(digest)); + + //HMAC Message Authentication Code Algorithm Based on SM3 + Mac hmac = Mac.getInstance("SM3", "GmSSL"); + hmac.init(new SecretKeySpec(new Random().randBytes(SM3Hmac.MAC_SIZE), "SM3")); + hmac.update(text.getBytes()); + byte[] hmacFinal = hmac.doFinal(); + System.out.println("hmac:"+byteToHex(hmacFinal)); + + //Password-Based Key Derivation Function PBKDF2 + char[] password = "P@ssw0rd".toCharArray(); + byte[] salt = new Random().randBytes(SM3Pbkdf2.DEFAULT_SALT_SIZE); + int iterations = SM3Pbkdf2.MIN_ITER * 2; + int keyLength = SM3Pbkdf2.MAX_KEY_SIZE; + PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, keyLength); + SecretKeyFactory skf = SecretKeyFactory.getInstance("SM3Pbkdf2"); + SecretKey key = skf.generateSecret(spec); + byte[] keyBytes = key.getEncoded(); + System.out.println("DerivedKey: " + byteToHex(keyBytes)); + } + + @Test + public void SM4_ECB_test() throws Exception{ + String text="Hello, GmSSL!"; + SecureRandom secureRandom = SecureRandom.getInstance("Random", "GmSSL"); + // encryption + Cipher sm4Cipher = Cipher.getInstance("SM4/ECB/PKCS7Padding", "GmSSL"); + SecretKeySpec sm4Key = new SecretKeySpec(secureRandom.generateSeed(SM4ECB.KEY_SIZE), "SM4"); + sm4Cipher.init(Cipher.ENCRYPT_MODE, sm4Key); + sm4Cipher.update(text.getBytes()); + sm4Cipher.update("cipher.".getBytes(),0, 6); + byte[] ciphertext = sm4Cipher.doFinal(); + System.out.println("Ciphertext: " + byteToHex(ciphertext)); + // decryption + sm4Cipher.init(Cipher.DECRYPT_MODE, sm4Key); + byte[] plaintext = sm4Cipher.doFinal(ciphertext); + System.out.println("plaintext: " + new String(plaintext)); + } + + @Test + public void SM4_CBC_test1() throws Exception{ + String text="Hello,GmSSL!"; + SecureRandom secureRandom = SecureRandom.getInstance("Random", "GmSSL"); + byte[] randomBytes = new byte[32]; + secureRandom.nextBytes(randomBytes); + System.out.println("Generated Random Bytes: " + byteToHex(randomBytes)); + // encryption + Cipher sm4cbcCipher = Cipher.getInstance("SM4/CBC/PKCS5Padding", "GmSSL"); + byte[] key = secureRandom.generateSeed(SM4CBC.KEY_SIZE); + byte[] iv = secureRandom.generateSeed(SM4CBC.IV_SIZE); + sm4cbcCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "SM4"), new IvParameterSpec(iv)); + sm4cbcCipher.update(text.getBytes()); + sm4cbcCipher.update(text.getBytes()); + sm4cbcCipher.update(text.getBytes()); + byte[] ciphertext = sm4cbcCipher.doFinal(); + System.out.println("Ciphertext: " + byteToHex(ciphertext)); + // decryption + sm4cbcCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "SM4"), new IvParameterSpec(iv)); + sm4cbcCipher.update(ciphertext); + byte[] plaintext2 = sm4cbcCipher.doFinal(); + String plaintextStr=new String(plaintext2); + System.out.println("plaintext: " + plaintextStr); + } + + @Test + public void SM4_CBC_test2() throws Exception{ + String text="Hello,GmSSL!"; + SecureRandom secureRandom = SecureRandom.getInstance("Random", "GmSSL"); + byte[] randomBytes = new byte[32]; + secureRandom.nextBytes(randomBytes); + System.out.println("Generated Random Bytes: " + byteToHex(randomBytes)); + // encryption + Cipher sm4cbcCipher = Cipher.getInstance("SM4/CBC/PKCS5Padding", "GmSSL"); + byte[] key = secureRandom.generateSeed(SM4CBC.KEY_SIZE); + byte[] iv = secureRandom.generateSeed(SM4CBC.IV_SIZE); + sm4cbcCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "SM4"), new IvParameterSpec(iv)); + byte[] ciphertext = new byte[100]; + int cipherlen = sm4cbcCipher.update(text.getBytes(), 0, text.getBytes().length, ciphertext, 0); + cipherlen += sm4cbcCipher.update(text.getBytes(), 0, text.getBytes().length, ciphertext, cipherlen); + cipherlen += sm4cbcCipher.update(text.getBytes(), 0, text.getBytes().length, ciphertext, cipherlen); + cipherlen += sm4cbcCipher.doFinal(text.getBytes(), 0, text.getBytes().length,ciphertext, cipherlen); + byte[] ciphertext1 = Arrays.copyOfRange(ciphertext,0,cipherlen); + System.out.println("Ciphertext: " + byteToHex(ciphertext1)); + // decryption + byte[] plaintext = new byte[ciphertext1.length + SM4CBC.BLOCK_SIZE]; + sm4cbcCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "SM4"), new IvParameterSpec(iv)); + int plainLen = sm4cbcCipher.doFinal(ciphertext1, 0,ciphertext1.length, plaintext,0); + byte[] plaintext1 =Arrays.copyOfRange(plaintext,0,plainLen); + String plaintextStr=new String(plaintext1); + System.out.println("plaintext: " + plaintextStr); + } + + @Test + public void SM4_CTR_test1() throws Exception{ + String text="Hello, GmSSL!"; + SecureRandom secureRandom = SecureRandom.getInstance("Random", "GmSSL"); + Cipher sm4Cipher = Cipher.getInstance("SM4/CTR/NoPadding", "GmSSL"); + byte[] key = secureRandom.generateSeed(SM4CTR.KEY_SIZE); + byte[] iv = secureRandom.generateSeed(SM4CTR.IV_SIZE); + // encryption + sm4Cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "SM4"), new IvParameterSpec(iv)); + byte[] ciphertext = new byte[100]; + int cipherlen = sm4Cipher.update(text.getBytes(), 0, text.getBytes().length, ciphertext, 0); + cipherlen += sm4Cipher.update(text.getBytes(), 0, text.getBytes().length, ciphertext, cipherlen); + cipherlen += sm4Cipher.doFinal(text.getBytes(), 0, text.getBytes().length, ciphertext, cipherlen); + byte[] ciphertext1 = Arrays.copyOfRange(ciphertext,0,cipherlen); + System.out.println("Ciphertext: " + byteToHex(ciphertext1)); + // decryption + byte[] plaintext = new byte[ciphertext1.length+SM4CTR.BLOCK_SIZE]; + sm4Cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "SM4"), new IvParameterSpec(iv)); + int plainLen = sm4Cipher.doFinal(ciphertext1, 0, ciphertext1.length, plaintext, 0); + byte[] plaintext1 = Arrays.copyOfRange(plaintext,0,plainLen); + System.out.println("plaintext: " + new String(plaintext1)); + } + + @Test + public void SM4_CTR_test2() throws Exception{ + String text="Hello, GmSSL!"; + SecureRandom secureRandom = SecureRandom.getInstance("Random", "GmSSL"); + Cipher sm4Cipher = Cipher.getInstance("SM4/CTR/NoPadding", "GmSSL"); + byte[] key = secureRandom.generateSeed(SM4CTR.KEY_SIZE); + byte[] iv = secureRandom.generateSeed(SM4CTR.IV_SIZE); + // encryption + sm4Cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "SM4"), new IvParameterSpec(iv)); + sm4Cipher.update(text.getBytes()); + sm4Cipher.update(text.getBytes()); + sm4Cipher.update(text.getBytes()); + byte[] ciphertext = sm4Cipher.doFinal(); + System.out.println("Ciphertext: " + byteToHex(ciphertext)); + // decryption + sm4Cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "SM4"), new IvParameterSpec(iv)); + sm4Cipher.update(ciphertext); + byte[] plaintext1=sm4Cipher.doFinal(); + System.out.println("plaintext: " + new String(plaintext1)); + } + + @Test + public void SM4_GCM_test1() throws Exception { + String text="Hello, GmSSL!"; + SecureRandom secureRandom = SecureRandom.getInstance("Random", "GmSSL"); + Cipher sm4Cipher = Cipher.getInstance("SM4/GCM/NoPadding", "GmSSL"); + byte[] key = secureRandom.generateSeed(SM4GCM.KEY_SIZE); + byte[] iv = secureRandom.generateSeed(SM4GCM.DEFAULT_IV_SIZE); + byte[] aad = "Hello: ".getBytes(); + // encryption + sm4Cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "SM4"), new GCMParameterSpec(SM4GCM.MAX_TAG_SIZE,iv)); + sm4Cipher.updateAAD(aad); + byte[] ciphertext = new byte[100]; + int cipherlen = sm4Cipher.update(text.getBytes(), 0, text.getBytes().length, ciphertext, 0); + cipherlen += sm4Cipher.update(text.getBytes(), 0, text.getBytes().length, ciphertext, cipherlen); + cipherlen += sm4Cipher.update(text.getBytes(), 0, text.getBytes().length, ciphertext, cipherlen); + cipherlen += sm4Cipher.doFinal(text.getBytes(), 0, text.getBytes().length, ciphertext, cipherlen); + byte[] ciphertext1 = Arrays.copyOfRange(ciphertext,0,cipherlen); + System.out.println("Ciphertext: " + byteToHex(ciphertext1)); + // decryption + byte[] plaintext = new byte[ciphertext1.length+SM4GCM.MAX_TAG_SIZE]; + sm4Cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "SM4"), new GCMParameterSpec(SM4GCM.MAX_TAG_SIZE,iv)); + sm4Cipher.updateAAD(aad); + int plainlen =sm4Cipher.doFinal(ciphertext1, 0, ciphertext1.length, plaintext, 0); + byte[] plaintext1 = Arrays.copyOfRange(plaintext,0,plainlen); + System.out.println("plaintext: " + new String(plaintext1)); + } + + @Test + public void SM4_GCM_test2() throws Exception { + String text="Hello,GmSSL!"; + SecureRandom secureRandom = SecureRandom.getInstance("Random", "GmSSL"); + Cipher sm4Cipher = Cipher.getInstance("SM4/GCM/NoPadding", "GmSSL"); + byte[] key = secureRandom.generateSeed(SM4GCM.KEY_SIZE); + byte[] iv = secureRandom.generateSeed(SM4GCM.DEFAULT_IV_SIZE); + byte[] aad = "Hello: ".getBytes(); + // encryption + sm4Cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "SM4"), new GCMParameterSpec(SM4GCM.MAX_TAG_SIZE,iv)); + sm4Cipher.updateAAD(aad); + sm4Cipher.updateAAD(aad); + sm4Cipher.update(text.getBytes()); + sm4Cipher.update(text.getBytes()); + sm4Cipher.update(text.getBytes()); + byte[] ciphertext = sm4Cipher.doFinal(); + System.out.println("Ciphertext: " + byteToHex(ciphertext)); + // decryption + sm4Cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "SM4"), new GCMParameterSpec(SM4GCM.MAX_TAG_SIZE,iv)); + sm4Cipher.updateAAD(aad); + sm4Cipher.updateAAD(aad); + sm4Cipher.update(ciphertext); + byte[] plaintext1=sm4Cipher.doFinal(); + System.out.println("plaintext: " + new String(plaintext1)); + } + + @Test + public void SM9_cipher_test() throws Exception{ + String text="Hello, GmSSL"; + SM9EncMasterKeyGenParameterSpec sm9EncMasterKeyGenParameterSpec = new SM9EncMasterKeyGenParameterSpec("bob"); + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("SM9", "GmSSL"); + keyPairGen.initialize(sm9EncMasterKeyGenParameterSpec); + keyPairGen.generateKeyPair(); + // encryption + PublicKey publicKey = keyPairGen.genKeyPair().getPublic(); + // export public key + SM9PublicKey SM9PublicKey = (SM9PublicKey)publicKey; + SM9PublicKey.exportPublicKeyPem("SM9Public.enc.mpk"); + Cipher sm9Cipher = Cipher.getInstance("SM9", "GmSSL"); + sm9Cipher.init(Cipher.ENCRYPT_MODE, publicKey,sm9EncMasterKeyGenParameterSpec); + byte[] ciphertext = sm9Cipher.doFinal(text.getBytes()); + System.out.println("Ciphertext: " + byteToHex(ciphertext)); + // decryption + SM9PrivateKey privateKey= (SM9PrivateKey) keyPairGen.genKeyPair().getPrivate(); + // export private key + privateKey.exportEncryptedPrivateKeyInfoPem("123456", "SM9Private.enc.mpk"); + SM9MasterKey masterKey = (SM9MasterKey)privateKey.getSecretKey(); + SM9UserKey userKey= masterKey.extractKey(sm9EncMasterKeyGenParameterSpec.getId()); + sm9Cipher.init(Cipher.DECRYPT_MODE, userKey.getPrivateKey()); + byte[] plaintext = sm9Cipher.doFinal(ciphertext); + System.out.println("plaintext: " + new String(plaintext)); + } + + @Test + public void SM9_sign_test() throws Exception{ + String text="Hello, GmSSL"; + SM9SignMasterKeyGenParameterSpec sm9SignMasterKeyGenParameterSpec = new SM9SignMasterKeyGenParameterSpec("alice"); + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("SM9", "GmSSL"); + keyPairGen.initialize(sm9SignMasterKeyGenParameterSpec); + keyPairGen.generateKeyPair(); + + Signature signature = Signature.getInstance("SM9", "GmSSL"); + // Signature + SM9PrivateKey privateKey= (SM9PrivateKey) keyPairGen.genKeyPair().getPrivate(); + // export private key + privateKey.exportEncryptedPrivateKeyInfoPem("123456", "SM9Private.sign.mpk"); + SM9MasterKey masterKey = (SM9MasterKey)privateKey.getSecretKey(); + SM9UserKey userKey= masterKey.extractKey(sm9SignMasterKeyGenParameterSpec.getId()); + signature.initSign(userKey.getPrivateKey()); + byte[] signatureText = text.getBytes(); + signature.update(signatureText); + byte[] signatureByte = signature.sign(); + System.out.println("Signature:"+byteToHex(signatureByte)); + // Verify + signature.setParameter(sm9SignMasterKeyGenParameterSpec); + PublicKey publicKey= keyPairGen.genKeyPair().getPublic(); + // export public key + SM9PublicKey SM9PublicKey = (SM9PublicKey)publicKey; + SM9PublicKey.exportPublicKeyPem("SM9Public.sign.mpk"); + signature.initVerify(publicKey); + signature.update(signatureText); + boolean signatureResult = signature.verify(signatureByte); + System.out.println("SignatureResult:"+signatureResult); + } + + @Test + public void ZUC_test() throws Exception{ + String text="Hello,GmSSL!"; + SecureRandom secureRandom = SecureRandom.getInstance("Random", "GmSSL"); + Cipher cipher = Cipher.getInstance("ZUC","GmSSL"); + SecretKey key = new ZucKey(secureRandom.generateSeed(ZucKey.KEY_SIZE)); + IvParameterSpec ivParameterSpec = new IvParameterSpec(secureRandom.generateSeed(ZucCipher.IV_SIZE)); + // encryption + cipher.init(Cipher.ENCRYPT_MODE, key, ivParameterSpec); + byte[] ciphertext = new byte[100]; + int cipherlen = cipher.update(text.getBytes(), 0, text.getBytes().length, ciphertext, 0); + cipherlen += cipher.update(text.getBytes(), 0, text.getBytes().length, ciphertext, cipherlen); + cipherlen += cipher.doFinal(text.getBytes(), 0, text.getBytes().length, ciphertext, cipherlen); + byte[] ciphertext1 = Arrays.copyOfRange(ciphertext,0,cipherlen); + System.out.println("Ciphertext: " + byteToHex(ciphertext1)); + // decryption + cipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec); + cipher.update(ciphertext1); + byte[] plaintext1 = cipher.doFinal(); + System.out.println("plaintext: " + new String(plaintext1)); + } + + /** + * convert byte array to hex string + * @param btArr + * @return String + */ + private String byteToHex(byte[] btArr) { + BigInteger bigInteger = new BigInteger(1, btArr); + return bigInteger.toString(16); + } + + /** + * convert hex string to byte array + * @param hexString + * @return byte[] + */ + private byte[] hexToByte(String hexString) { + byte[] byteArray = new BigInteger(hexString, 16) + .toByteArray(); + if (byteArray[0] == 0) { + byte[] output = new byte[byteArray.length - 1]; + System.arraycopy( + byteArray, 1, output, + 0, output.length); + return output; + } + return byteArray; + } +} diff --git a/src/test/java/org/gmssl/Sm2Test.java b/src/test/java/org/gmssl/Sm2Test.java new file mode 100644 index 0000000..70ec8e3 --- /dev/null +++ b/src/test/java/org/gmssl/Sm2Test.java @@ -0,0 +1,131 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2023/09/07 + * @description Sm2 unit test + */ +public class Sm2Test { + + Sm2Key sm2_key; + byte[] privateKeyInfo; + byte[] publicKeyInfo; + Sm2Key priKey,pubKey; + byte[] dgst; + + @Before + public void beforeTest(){ + sm2_key = new Sm2Key(); + sm2_key.generateKey(); + + //byte[] privateKeyInfo=sm2_key.exportPrivateKeyInfoDer(); + String privateKeyInfoHex="308193020100301306072a8648ce3d020106082a811ccf5501822d0479307702010104207fef3e258348873c47117c15093266e9dad99e131f1778e53d362b2b70649f85a00a06082a811ccf5501822da14403420004f94c0abb6cd00c6f0918cb9c54162213501d5cc278f5d3fcf63886f4e1dc6322b1b110e33a25216f258c4cce5fd52ab320d3b086ee5390f7387218c92578c3ab"; + privateKeyInfo = HexUtil.hexToByte(privateKeyInfoHex); + + //byte[] publicKeyInfo = sm2_key.exportPublicKeyInfoDer(); + String publicKeyInfoHex = "3059301306072a8648ce3d020106082a811ccf5501822d03420004f94c0abb6cd00c6f0918cb9c54162213501d5cc278f5d3fcf63886f4e1dc6322b1b110e33a25216f258c4cce5fd52ab320d3b086ee5390f7387218c92578c3ab"; + publicKeyInfo = HexUtil.hexToByte(publicKeyInfoHex); + + priKey = new Sm2Key(); + priKey.importPrivateKeyInfoDer(privateKeyInfo); + priKey.exportEncryptedPrivateKeyInfoPem("Password", "sm2.pem"); + priKey.importEncryptedPrivateKeyInfoPem("Password", "sm2.pem"); + + pubKey = new Sm2Key(); + pubKey.importPublicKeyInfoDer(publicKeyInfo); + pubKey.exportPublicKeyInfoPem("sm2.pem"); + pubKey.importPublicKeyInfoPem("sm2.pem"); + + //byte[] dgst = rng.randBytes(Sm3.DIGEST_SIZE); + String dgstHex="372a28b963da9733515640f163dd017ae8544cafa78097d5765e4169348c030b"; + dgst=HexUtil.hexToByte(dgstHex); + + } + + @Test + public void computeZTest(){ + byte[] z = pubKey.computeZ(Sm2Key.DEFAULT_ID); + + String hexZ= HexUtil.byteToHex(z); + //System.out.println("z:"+hexZ); + Assert.assertNotNull("data is empty exception!",hexZ); + } + + @Test + public void signTest(){ + byte[] sig = priKey.sign(dgst); + String sigHex = HexUtil.byteToHex(sig); + //System.out.println("sigHex : "+sigHex); + Assert.assertNotNull("data is empty exception!",sig); + } + + @Test + public void verifySignTest(){ + String sigHex="3046022100c2a92338bf430b0bd1ed68ea9910168cbd6bbb6f8de0992e1350e894296273b1022100e4814ac9ea6dab86334f47b2de6122923a0abbb7ec0687a2a1974773eb9a9542"; + byte[] sig=HexUtil.hexToByte(sigHex); + + boolean verify_ret = pubKey.verify(dgst, sig); + //System.out.println("Verify result = " + verify_ret); + Assert.assertTrue("Verification of the signature failed!",verify_ret); + } + + @Test + public void encryptTest(){ + String testStr="gmssl"; + + byte[] ciphertext = pubKey.encrypt(testStr.getBytes()); + //System.out.println("ciphertext : "+HexUtil.byteToHex(ciphertext)); + Assert.assertNotNull("data is empty exception!",ciphertext); + } + + @Test + public void decryptTest(){ + String ciphertextHex="306e022100d5d193d99876b6b2a2456356c09db06074aceb3ad6ae736d415d6988bdd4392902207ffbef363ae9584703d10b799609aff0fcb7a026b04aeec14021c9e12d22d2470420bbcc0a0bd07ffde6d0f5d5ee6e81eb47debbd9c6c0fca55107b1891cea29f742040526af292b75"; + byte[] ciphertext = HexUtil.hexToByte(ciphertextHex); + + byte[] plaintext = priKey.decrypt(ciphertext); + String plaintextStr= new String(plaintext); + //System.out.printf("Plaintext : "+plaintextStr); + Assert.assertEquals("The original value is not equal to the expected value after decryption!","gmssl",plaintextStr); + } + + @Test + public void signatureTest(){ + String signatureContentStr="gmssl"; + + Sm2Signature sign = new Sm2Signature(priKey, Sm2Key.DEFAULT_ID, true); + sign.update(signatureContentStr.getBytes()); + byte[] sig = sign.sign(); + String sigHex = HexUtil.byteToHex(sig); + //System.out.println("signatureContentHex : "+sigHex); + Assert.assertNotNull("data is empty exception!",sig); + } + + @Test + public void verifySignatureTest(){ + String signatureContentStr = "gmssl"; + String signatureContentHex = "3046022100cf526564d0964225f857856bc6ef181df5fcf1c87d630ccf6b992d4371772ed3022100915a309279e90ed00a02e84617991aaf1baa70586cc6cce395e52b7105bb73fa"; + byte[] sig=HexUtil.hexToByte(signatureContentHex); + + Sm2Signature verify = new Sm2Signature(pubKey, Sm2Key.DEFAULT_ID, false); + verify.update(signatureContentStr.getBytes()); + boolean verify_ret = verify.verify(sig); + //System.out.println("Verify result = " + verify_ret); + Assert.assertTrue("Verification of the signature failed!",verify_ret); + } + +} diff --git a/src/test/java/org/gmssl/Sm3HmacTest.java b/src/test/java/org/gmssl/Sm3HmacTest.java new file mode 100644 index 0000000..ba3e943 --- /dev/null +++ b/src/test/java/org/gmssl/Sm3HmacTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl; + +import org.junit.Assert; +import org.junit.Test; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2023/09/07 + * @description Sm3Hmac unit test + */ +public class Sm3HmacTest { + + @Test + public void macTest(){ + String testStr="gmssl"; + Random rng = new Random(); + byte[] key = rng.randBytes(Sm3Hmac.MAC_SIZE); + + Sm3Hmac sm3hmac = new Sm3Hmac(key); + sm3hmac.update(testStr.getBytes(), 0, 3); + byte[] mac = sm3hmac.generateMac(); + + String maxHex= HexUtil.byteToHex(mac); + //System.out.println(maxHex); + Assert.assertNotNull("data is empty exception!",maxHex); + } + +} diff --git a/src/test/java/org/gmssl/Sm3Pbkdf2Test.java b/src/test/java/org/gmssl/Sm3Pbkdf2Test.java new file mode 100644 index 0000000..f1aae21 --- /dev/null +++ b/src/test/java/org/gmssl/Sm3Pbkdf2Test.java @@ -0,0 +1,42 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl; + + +import org.junit.Assert; +import org.junit.Test; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2023/10/20 + * @description Sm3Pbkdf2 unit test + */ +public class Sm3Pbkdf2Test { + + /** + * PBKDF2 (Password-Based Key Derivation Function 2) is a cryptographic algorithm used to derive a key from a password. + * It employs a pseudorandom function to generate the key, and the length of the derived key can be arbitrarily chosen. However, PBKDF2 allows for multiple iterations of the computation to further enhance security. + * By incorporating a salt value (random data) along with the plaintext password, PBKDF2 generates a salted key, which greatly improves resistance against attacks like rainbow table attacks. + */ + @Test + public void deriveKeyTest(){ + Sm3Pbkdf2 kdf = new Sm3Pbkdf2(); + + Random rng = new Random(); + byte[] salt = rng.randBytes(Sm3Pbkdf2.DEFAULT_SALT_SIZE); + + String pass = "P@ssw0rd"; + byte[] key = kdf.deriveKey(pass, salt, Sm3Pbkdf2.MIN_ITER * 2, 16); + String keyHexStr = HexUtil.byteToHex(key); + //System.out.println(keyHexStr); + Assert.assertNotNull("data is empty exception!",keyHexStr); + } + +} diff --git a/src/test/java/org/gmssl/Sm3Test.java b/src/test/java/org/gmssl/Sm3Test.java new file mode 100644 index 0000000..a869dbf --- /dev/null +++ b/src/test/java/org/gmssl/Sm3Test.java @@ -0,0 +1,34 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl; + +import org.junit.Assert; +import org.junit.Test; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2023/09/07 + * @description Sm3 unit test + */ +public class Sm3Test { + + @Test + public void digestTest(){ + String testStr="gmssl"; + Sm3 sm3 = new Sm3(); + sm3.update(testStr.getBytes()); + byte[] dgst = sm3.digest(); + + String dgstHex= HexUtil.byteToHex(dgst); + //System.out.println(dgstHex); + Assert.assertNotNull("data is empty exception!",dgstHex); + } + +} diff --git a/src/test/java/org/gmssl/Sm4CbcTest.java b/src/test/java/org/gmssl/Sm4CbcTest.java new file mode 100644 index 0000000..7e7a30c --- /dev/null +++ b/src/test/java/org/gmssl/Sm4CbcTest.java @@ -0,0 +1,68 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; + + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2023/09/07 + * @description Sm4Cbc unit test + */ +public class Sm4CbcTest { + + Sm4Cbc sm4Cbc; + byte[] key , iv ; + + @Before + public void beforeTest(){ + sm4Cbc = new Sm4Cbc(); + key = new byte[]{-73, -55, -122, -95, 0, -4, 51, -38, 125, -31, 38, 12, 112, 8, -50, -92}; + iv = new byte[]{88, 121, -51, 88, 32, -85, 98, 56, 108, 18, 102, -73, -122, -59, -97, -25}; + } + + @Test + public void encryptTest(){ + String testStr="gmssl"; + + byte[] plaintext = testStr.getBytes(); + byte[] ciphertext = new byte[plaintext.length+Sm4Cbc.BLOCK_SIZE]; + sm4Cbc.init(key, iv, true); + int cipherlen = sm4Cbc.update(plaintext, 0, plaintext.length, ciphertext, 0); + cipherlen += sm4Cbc.doFinal(ciphertext, cipherlen); + byte[] ciphertext1 =Arrays.copyOfRange(ciphertext,0,cipherlen); + //System.out.println("cipher:"+HexUtil.byteToHex(ciphertext1)); + Assert.assertNotNull("data is empty exception!",HexUtil.byteToHex(ciphertext1)); + } + + @Test + public void decryptTest(){ + String cipherHex="ccedec05b742098b33e0fc8c5c006365"; + byte[] ciphertext=HexUtil.hexToByte(cipherHex); + sm4Cbc.init(key, iv, false); + byte[] decrypted = new byte[ciphertext.length + Sm4Cbc.BLOCK_SIZE]; // prepare large enough plaintext buffer + int decryptedOffset = 0; + int decryptedLen; + int ciphertextOffset = 0; + decryptedLen = sm4Cbc.update(ciphertext, ciphertextOffset, ciphertext.length, decrypted, decryptedOffset); + decryptedOffset += decryptedLen; + decryptedLen += sm4Cbc.doFinal(decrypted, decryptedOffset); + byte[] plaintext =Arrays.copyOfRange(decrypted,0,decryptedLen); + String plaintextStr=new String(plaintext); + //System.out.println(plaintextStr); + Assert.assertEquals("original value is not equal to the expected value after decryption!","gmssl",plaintextStr); + } + +} diff --git a/src/test/java/org/gmssl/Sm4CtrTest.java b/src/test/java/org/gmssl/Sm4CtrTest.java new file mode 100644 index 0000000..7dd030d --- /dev/null +++ b/src/test/java/org/gmssl/Sm4CtrTest.java @@ -0,0 +1,64 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2023/09/07 + * @description Sm4Ctr unit test + */ +public class Sm4CtrTest { + + private Sm4Ctr sm4Ctr; + + byte[] key ,iv ; + + @Before + public void beforeTest(){ + sm4Ctr = new Sm4Ctr(); + key=new byte[]{99, -49, -44, -61, 104, 76, -65, 88, 55, 54, 48, -81, 99, -10, 50, 22}; + iv=new byte[]{-127, 39, -104, -97, 61, -119, 85, -18, -14, -79, 47, -92, -113, 92, 28, -34}; + } + + @Test + public void encryptTest(){ + String ciphertext_1="gmssl",ciphertext_2="_",ciphertext_3="v3"; + byte[] ciphertext = new byte[64]; + sm4Ctr.init(key, iv); + int cipherlen = sm4Ctr.update(ciphertext_1.getBytes(), 0, ciphertext_1.length(), ciphertext, 0); + cipherlen += sm4Ctr.update(ciphertext_2.getBytes(), 0, ciphertext_2.length(), ciphertext, cipherlen); + cipherlen += sm4Ctr.update(ciphertext_3.getBytes(), 0, ciphertext_3.length(), ciphertext, cipherlen); + cipherlen += sm4Ctr.doFinal(ciphertext, cipherlen); + byte[] ciphertextEnd = Arrays.copyOfRange(ciphertext,0,cipherlen); + //System.out.println(HexUtil.byteToHex(ciphertextEnd)); + Assert.assertNotNull("data is empty exception!",HexUtil.byteToHex(ciphertextEnd)); + } + + @Test + public void decryptTest(){ + String plainText="gmssl_v3"; + String ciphertext="912c3317275d8e5f"; + byte[] ciphertextByte=HexUtil.hexToByte(ciphertext); + byte[] plaintext = new byte[64]; + + sm4Ctr.init(key, iv); + int plainlen = sm4Ctr.update(ciphertextByte, 0, ciphertext.length()/2, plaintext, 0); + plainlen += sm4Ctr.doFinal(plaintext, plainlen); + plaintext=Arrays.copyOfRange(plaintext,0,plainlen); + //System.out.println(new String(plaintext)); + Assert.assertEquals("original value is not equal to the expected value after decryption!",plainText,new String(plaintext)); + } + +} diff --git a/src/test/java/org/gmssl/Sm4EcbTest.java b/src/test/java/org/gmssl/Sm4EcbTest.java new file mode 100644 index 0000000..3f5c324 --- /dev/null +++ b/src/test/java/org/gmssl/Sm4EcbTest.java @@ -0,0 +1,122 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2023/09/25 + * @description Sm4Ecb unit test + */ +public class Sm4EcbTest { + + byte[] key; + + @Before + public void beforeTest(){ + key=new byte[]{74, 97, -73, 5, -31, 1, -88, -21, -7, -2, -65, 98, 70, 5, -54, 15}; + } + + @Test + public void encryptTest(){ + String test_plaintext="gmssl"; + byte[] paddingPlaintext=pkcs7padding(test_plaintext.getBytes(),Sm4.BLOCK_SIZE); + byte[] encrypted = encrypt(paddingPlaintext,key); + //System.out.println("encrypted data:"+HexUtil.byteToHex(encrypted)); + Assert.assertNotNull("data is empty exception!",encrypted); + } + + @Test + public void decryptTest(){ + String test_hex_chipertext="31acce3f0317026c30accba2be9d326f"; + String test_plaintext="gmssl"; + byte[] encrypted =HexUtil.hexToByte(test_hex_chipertext); + byte[] plaintextArray = decrypt(encrypted,key); + byte[] unpaddingPlaintextArray = pkcs7UnPadding(plaintextArray); + String plaintext=new String(unpaddingPlaintextArray); + //System.out.println("chipertext:"+plaintext); + Assert.assertEquals("original value is not equal to the expected value after decryption!",plaintext,test_plaintext); + } + + + /** + * The purpose of PKCS7Padding is to pad the data to the block size required by the encryption algorithm, ensuring that the data length meets the requirements of the encryption algorithm. + * In special cases where the data length is already a multiple of the block size, according to the PKCS7 rule, padding is still added at the end. + * This is done to ensure consistent handling of padding during encryption and decryption processes. + * @param byteArray + * @param blockSize + * @return byte[] padding array + */ + private static byte[] pkcs7padding(byte[] byteArray, int blockSize) { + int paddingLength = blockSize - (byteArray.length % blockSize); + byte[] padding = new byte[paddingLength]; + Arrays.fill(padding, (byte) paddingLength); + byte[] result = new byte[byteArray.length + padding.length]; + System.arraycopy(byteArray, 0, result, 0, byteArray.length); + System.arraycopy(padding, 0, result, byteArray.length, padding.length); + return result; + } + + /** + * unPadding the byteArray + * @param byteArray + * @return byte[] unPadding byteArray + * @throws IllegalArgumentException + */ + private static byte[] pkcs7UnPadding(byte[] byteArray) throws IllegalArgumentException { + int paddingSize = byteArray[byteArray.length - 1]; + if (paddingSize <= 0 || paddingSize > byteArray.length) { + throw new IllegalArgumentException("Invalid pkcs#7 padding!"); + } + for (int i = byteArray.length - paddingSize; i < byteArray.length; i++) { + if (byteArray[i] != paddingSize) { + throw new IllegalArgumentException("Invalid pkcs#7 padding!"); + } + } + return Arrays.copyOfRange(byteArray, 0, byteArray.length - paddingSize); + } + + + /** + * Encrypt data by block + * @param data data to be encrypted + * @param key + * @return byte[] encrypted data + */ + private static byte[] encrypt(byte[] data, byte[] key) { + byte[] ciphertext = new byte[data.length]; + Sm4 sm4 = new Sm4(key, true); + for (int i = 0; i < data.length; i += Sm4.BLOCK_SIZE) { + sm4.encrypt(data, i, ciphertext, i); + } + return ciphertext; + } + + /** + * Decrypt data by block + * @param data data to be decrypted + * @param key + * @return byte[] decrypted data + */ + private static byte[] decrypt(byte[] data, byte[] key) { + byte[] plaintext=new byte[data.length]; + Sm4 sm4 = new Sm4(key, false); + for (int i = 0; i < data.length; i += Sm4.BLOCK_SIZE) { + sm4.encrypt(data, i, plaintext, i); + } + return plaintext; + } + +} diff --git a/src/test/java/org/gmssl/Sm4GcmTest.java b/src/test/java/org/gmssl/Sm4GcmTest.java new file mode 100644 index 0000000..ade9919 --- /dev/null +++ b/src/test/java/org/gmssl/Sm4GcmTest.java @@ -0,0 +1,77 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2023/09/26 + * @description Sm4Gcm unit test. + * In SM4 GCM mode, GCM provides both encryption and authentication functionalities. + * Encryption is performed using the SM4 algorithm, while authentication is provided by the GCM mode through message authentication codes. + * Additionally, GCM offers additional data integrity checks to detect if the data has been tampered with. + */ +public class Sm4GcmTest { + + Sm4Gcm sm4gcm; + byte[] key ,iv ; + int taglen; + byte[] aad; + + @Before + public void beforeTest(){ + sm4gcm=new Sm4Gcm(); + key=new byte[]{52, -63, -74, 123, 75, -42, -109, -94, -108, -35, 117, -70, 95, 126, -71, 6}; + iv=new byte[]{-97, -42, 38, -65, 37, -75, -26, -119, -19, 124, -116, -27}; + taglen=Sm4Gcm.MAX_TAG_SIZE; + aad = "Hello: ".getBytes(); + } + + /** + * sm4Gcm encrypt + * GCM operates on fixed size blocks (usually 128 bits), unlike other encryption modes such as CBC or ECB that require padding. + */ + @Test + public void encryptTest(){ + String testStr="gmssl"; + byte[] palaintextByte=testStr.getBytes(); + int blockLength= (int)Math.ceil((palaintextByte.length+taglen)/(double)Sm4.BLOCK_SIZE); + byte[] tempCiphertextByte=new byte[blockLength*Sm4.BLOCK_SIZE]; + int cipherlen; + sm4gcm.init(key, iv, aad, taglen, true); + cipherlen = sm4gcm.update(palaintextByte, 0, palaintextByte.length, tempCiphertextByte, 0); + cipherlen += sm4gcm.doFinal(tempCiphertextByte, cipherlen); + byte[] ciphertextByte = Arrays.copyOfRange(tempCiphertextByte,0,cipherlen); + //System.out.println("ciphertext:"+HexUtil.byteToHex(ciphertextByte)); + Assert.assertNotNull("data is empty exception!",ciphertextByte); + } + + @Test + public void decryptTest(){ + String test_plaintext="gmssl"; + String test_hex_ciphertext="b4a20037dc223f3e3474304dbb464a86423fa6c6db"; + byte[] ciphertextByte=HexUtil.hexToByte(test_hex_ciphertext); + byte[] tempPlaintextByte = new byte[ciphertextByte.length+taglen]; + sm4gcm.init(key, iv, aad, taglen, false); + int plainlen = sm4gcm.update(ciphertextByte, 0, ciphertextByte.length, tempPlaintextByte, 0); + plainlen += sm4gcm.doFinal(tempPlaintextByte, plainlen); + byte[] plaintextByte = Arrays.copyOfRange(tempPlaintextByte,0,plainlen); + String plaintext=new String(plaintextByte); + //System.out.println("plaintext:"+plaintext); + Assert.assertEquals("original value is not equal to the expected value after decryption!",plaintext,test_plaintext); + } + +} + diff --git a/src/test/java/org/gmssl/Sm4Test.java b/src/test/java/org/gmssl/Sm4Test.java new file mode 100644 index 0000000..817062f --- /dev/null +++ b/src/test/java/org/gmssl/Sm4Test.java @@ -0,0 +1,54 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * @author yongfeili + * @email 290836576@qq.com + * @date 2023/10/20 + * @description Sm4 unit test + */ +public class Sm4Test { + + byte[] key; + + @Before + public void beforeTest(){ + key=new byte[]{49, 50, 51, 52, 53, 54, 55, 56, 56, 55, 54, 53, 52, 51, 50, 49}; + } + + @Test + public void encryptTest(){ + String plaintextStr="1234567887654321"; + byte[] plaintext=plaintextStr.getBytes(); + byte[] ciphertext=new byte[Sm4.BLOCK_SIZE]; + Sm4 sm4enc = new Sm4(key, true); + sm4enc.encrypt(plaintext, 0, ciphertext, 0); + String ciphertextHex = HexUtil.byteToHex(ciphertext); + //System.out.println(ciphertextHex); + Assert.assertNotNull("data is empty exception!",ciphertextHex); + } + + @Test + public void decryptTest(){ + String ciphertextHex="4a7dc8fc6f7fb9bac989bbf8a5f194a7"; + byte[] ciphertext = HexUtil.hexToByte(ciphertextHex); + byte[] plaintext = new byte[ciphertext.length]; + Sm4 sm4dec = new Sm4(key, false); + sm4dec.encrypt(ciphertext, 0, plaintext, 0); + String plaintext1=new String(plaintext); + //System.out.println(plaintext1); + Assert.assertEquals("original value is not equal to the expected value after decryption!","1234567887654321",plaintext1); + } + +} diff --git a/src/test/java/org/gmssl/Sm9Test.java b/src/test/java/org/gmssl/Sm9Test.java new file mode 100644 index 0000000..dbee0f0 --- /dev/null +++ b/src/test/java/org/gmssl/Sm9Test.java @@ -0,0 +1,151 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +import java.io.*; +import java.util.Map; + +/** + * @author yongfei.li + * @email 290836576@qq.com + * @date 2023/10/20 + * @description sm9 unit test + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class Sm9Test { + + @Test + public void signTest() { + String singContentStr = "gmssl"; + byte[] singContent = singContentStr.getBytes(); + + Sm9SignMasterKey sign_master_key = new Sm9SignMasterKey(); + sign_master_key.generateMasterKey(); + Sm9SignKey sign_key = sign_master_key.extractKey("testKey"); + Sm9Signature sign = new Sm9Signature(true); + sign.update(singContent); + byte[] sig = sign.sign(sign_key); + sign_master_key.exportPublicMasterKeyPem("sm9sign.mpk"); + + String hexSig = HexUtil.byteToHex(sig); + //System.out.println(hexSig); + writeFile("sm9SignData.txt",hexSig); + Assert.assertNotNull("data is empty exception!",hexSig); + } + + @Test + public void verifyTest(){ + String hexSig=readFile("sm9SignData.txt"); + byte[] sig=HexUtil.hexToByte(hexSig); + String singContentStr = "gmssl"; + byte[] singContent = singContentStr.getBytes(); + + Sm9SignMasterKey sign_master_pub_key = new Sm9SignMasterKey(); + sign_master_pub_key.importPublicMasterKeyPem("sm9sign.mpk"); + Sm9Signature verify = new Sm9Signature(false); + verify.update(singContent); + boolean verify_ret = verify.verify(sig, sign_master_pub_key, "testKey"); + + //System.out.println("Verify result = " + verify_ret); + Assert.assertTrue("Verification of the signature failed!",verify_ret); + + } + + /** + * The encryption test method will generate a file, which will be used by the decryption test method , + * the encryption test method needs to be run before the decryption test method. + */ + @Test + public void _encryptTest(){ + String plaintextStr = "gmssl"; + byte[] plaintext = plaintextStr.getBytes(); + + Sm9EncMasterKey enc_master_key = new Sm9EncMasterKey(); + enc_master_key.generateMasterKey(); + enc_master_key.exportEncryptedMasterKeyInfoPem("password","sm9enc.mpk"); + + Sm9EncMasterKey enc_master_pub_key = new Sm9EncMasterKey(); + enc_master_pub_key.importEncryptedMasterKeyInfoPem("password","sm9enc.mpk"); + byte[] ciphertext = enc_master_pub_key.encrypt(plaintext, "testKey"); + + String ciphertextHex=HexUtil.byteToHex(ciphertext); + //System.out.println(ciphertextHex); + writeFile("sm9EncryptData.txt",ciphertextHex); + Assert.assertNotNull("data is empty exception!",ciphertextHex); + } + + @Test + public void decryptTest(){ + String ciphertextHex=readFile("sm9EncryptData.txt"); + byte[] ciphertext=HexUtil.hexToByte(ciphertextHex); + + Sm9EncMasterKey enc_master_key = new Sm9EncMasterKey(); + enc_master_key.importEncryptedMasterKeyInfoPem("password","sm9enc.mpk"); + Sm9EncKey enc_key = enc_master_key.extractKey("testKey"); + byte[] plaintext = enc_key.decrypt(ciphertext); + + String plaintextStr = new String(plaintext); + //System.out.print(plaintextStr); + Assert.assertEquals("The original value is not equal to the expected value after decryption!","gmssl",plaintextStr); + } + + /** + * Write string data to a temporary file. + * @param fileName + * @param data + */ + private void writeFile(String fileName,String data){ + File tempFile = new File("./"+ fileName); + try { + if(tempFile.exists()){ + tempFile.delete(); + }else { + tempFile.createNewFile(); + } + } catch (IOException e) { + e.printStackTrace(); + } + + try (BufferedWriter writer = new BufferedWriter(new FileWriter("./" + fileName))) { + writer.write(data); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Read string data from a temporary file. + * @param fileName + * @return String data + */ + private String readFile(String fileName){ + FileReader fileReader = null; + StringBuilder data= new StringBuilder(); + try { + fileReader = new FileReader(new File( "./"+fileName)); + BufferedReader bufferedReader = new BufferedReader(fileReader); + String line; + while ((line = bufferedReader.readLine()) != null) { + data.append(line); + } + bufferedReader.close(); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + return data.toString(); + } + +} diff --git a/src/test/java/org/gmssl/ZucTest.java b/src/test/java/org/gmssl/ZucTest.java new file mode 100644 index 0000000..d5c8e1a --- /dev/null +++ b/src/test/java/org/gmssl/ZucTest.java @@ -0,0 +1,83 @@ +/* + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.gmssl; + + +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Arrays; + +/** + * @author yongfei.li + * @email 290836576@qq.com + * @date 2023/09/12 + * @description zuc unit test + */ +public class ZucTest { + + byte[] key , iv; + Zuc zuc; + + + @Before + public void beforeTest(){ + zuc = new Zuc(); + key=new byte[]{-58, -106, -55, 98, -75, 49, -74, -101, -50, 1, -79, 43, -33, -86, -57, -106}; + iv=new byte[]{-119, 19, 24, 45, 83, 17, -89, 102, -72, -104, 91, -31, -25, -109, -28, 30}; + } + + /** + * encryption unit test + */ + @Test + public void encryptTest(){ + String plaintextStr = "gmss"; + byte[] plaintext = plaintextStr.getBytes(); + + int ciphertextLen = 2 * Zuc.BLOCK_SIZE * (int)Math.ceil((plaintext.length)/(double)Zuc.BLOCK_SIZE); + byte[] ciphertext = new byte[Math.max(16,ciphertextLen)]; + int cipherlen; + + zuc.init(key, iv); + cipherlen = zuc.update(plaintext, 0, plaintext.length, ciphertext, 0); + cipherlen += zuc.doFinal(ciphertext, cipherlen); + + ciphertext = Arrays.copyOfRange(ciphertext,0,cipherlen); + String ciphertextHex= HexUtil.byteToHex(ciphertext); + //System.out.println(ciphertextHex); + Assert.assertNotNull("data is empty exception!",ciphertextHex); + } + + /** + * decryption unit test + */ + @Test + public void decryptTest(){ + String ciphertextHex = "91a99db164"; + + int plainlen; + byte[] ciphertext=HexUtil.hexToByte(ciphertextHex); + int plaintextLen = 2 * Zuc.BLOCK_SIZE * (int)Math.ceil((ciphertext.length)/(double)Zuc.BLOCK_SIZE); + byte[] plaintext = new byte[Math.max(16,plaintextLen)]; + + zuc.init(key, iv); + plainlen = zuc.update(ciphertext, 0, ciphertext.length, plaintext, 0); + plainlen += zuc.doFinal(plaintext, plainlen); + + plaintext=Arrays.copyOfRange(plaintext,0,plainlen); + String plaintextStr = new String(plaintext); + //System.out.println(plaintextStr); + Assert.assertEquals("original value is not equal to the expected value after decryption!","gmssl",plaintextStr); + } + + +}