大家都知道用C++編寫的程序如果用於windows使用則編譯為xxx.dll文件,如果是Linux使用則編譯為libxxx.so文件。下面將java調用dll/so文件的方法粘出來方便下次使用。此處使用的jna的方式
jna可以從官方下載,也可以從maven里面去引用,下面是引用代碼:
<dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> <version>4.2.2</version> </dependency>
先把C++的一段代碼粘出來參考。

#pragma once #include <stdint.h> #ifdef __linux #define _APICALL #define DLL_PUBLIC __attribute__ ((visibility ("default"))) #else #define DLL_PUBLIC #define _APICALL __stdcall #endif // __linux static const int8_t CRYPTTYPE_SM2 = 1; // 非對稱加密,SM2官方推薦模式(256bit加密強度) static const int8_t CRYPTTYPE_SM4CBC = 2; // 對稱分組加密,SM4 CBC模式(128bit加密強度) enum //錯誤碼 { GMC_ERR_OK = 0, //成功 GMC_ERR_CryptoTypeNotSupport = 1, //不支持該密碼系統 GMC_ERR_PubKeyLost = 2, //公鑰未設置 GMC_ERR_PriKeyLost = 3, //私鑰未設置 GMC_ERR_OperationNotSupportThisCryptoType = 4, //操作不支持目前的密碼系統類型。 GMC_ERR_SMALLMEMSIZE = 5, //相關內存長度過小 GMC_ERR_BADALLOCATE = 6, //內存分配失敗 GMC_ERR_KEY_LENGTH = 7, //KEY長度不符合 GMC_ERR_KEY_FORMAT = 8, //KEY格式不符合 GMC_ERR_PARAM = 87, //參數錯誤 GMC_ERR_OPENSSL_INTERERR = 100, //openssl 內部錯誤 GMC_ERR_CIPHER = 1000, //CIPHER相關錯誤 GMC_ERR_CIPHERTYPE_NOTSUPPORT = 1001, //不支持的cipher類型 GMC_ERR_CIPHER_NOTINIT = 1002, //未初始化 GMC_ERR_CIPHER_NOTSETKEY = 1003, //沒有設置KEY GMC_ERR_SM2 = 2000, //sm2相關錯誤碼 GMC_ERR_SM2_NOTINIT =2001, //未初始化 GMC_ERR_SM2_NOTSETKEY = 2002, //未設置 公鑰或者私鑰 }; extern "C" { /* 函數:GMC_New_CTX 功能:初始化獲取一個指定類型的密碼體系。 參數: type:密碼系統類型 返回:成功返回上下文指針,失敗返回NULL。如果返回失敗這表明不支持type指定的類型。 Note:在相關使用完成后需要調用GMC_Delete_CTX函數進行釋放資源。 */ DLL_PUBLIC void* _APICALL GMC_New_CTX(int8_t type); /* 函數:GMC_Delete_CTX 功能:釋放指定的密碼體系上下文 參數: pCtx:指定密碼體系上下文 返回:無 */ DLL_PUBLIC void _APICALL GMC_Delete_CTX(void *pCtx); /* 函數:GMC_set_Key 功能:用於對稱加密設置密鑰 參數: pKey:密鑰(16進制數據的字符串化形式) 返回指:成功返回0,失敗見錯誤碼 */ DLL_PUBLIC long _APICALL GMC_set_Key(void *pCtx,const char *pszKey); /* 函數:GMC_ECKEY_set_PublicKey 功能:橢圓曲線加密系統公鑰設置 參數: pszXKey:X坐標(16進制數據的字符串化形式) pszYKey:Y坐標(16進制數據的字符串化形式) 返回:成功返回0,失敗見錯誤碼 */ DLL_PUBLIC long _APICALL GMC_ECKEY_set_PublicKey(void *pCtx,const char *pszXKey, const char *pszYKey); /* 函數:GMC_ECKEY_set_PrivateKey 功能:橢圓曲線加解密系統私鑰設置 參數: pszXKey:私鑰(16進制數據的字符串化形式) 返回:成功返回0,失敗見錯誤碼 */ DLL_PUBLIC long _APICALL GMC_ECKEY_set_PrivateKey(void *pCtx,const char *pszKey); /* 函數:GMC_Encrypt 功能:加密指定數據 參數: pData:被加密數據地址 cbData:被加密數據長度 pEncrypttedData[out]:用於保存加密數據的地址(可以與pData內存重疊) pcbEncryptted[int][out]:in:pEncrypttedData內存的大小,out:加密后數據的長度 返回:見錯誤碼 note:參數pEncrypttedData可以為NULL,如果為NULl則pcbEncryptted參數返回需要長度。 */ DLL_PUBLIC long _APICALL GMC_Encrypt(void *pCtx,const unsigned char *pData, uint32_t cbData, unsigned char *pEncrypttedData, uint32_t *pcbEncryptted); /* 函數:GMC_Decrypt 功能:解密指定數據 參數: pData:被解密數據地址 cbData:被解密數據長度 pDecrypttedData[out]:用於保存解密數據的地址(可以與pData內存重疊) pcbDecryptted[int][out]:in:pDecrypttedData內存的大小,out:解密后數據的長度 返回:見錯誤碼 note:參數pDecrypttedData可以為NULL,如果為NULl則pcbDecryptted參數返回需要長度。 */ DLL_PUBLIC long _APICALL GMC_Decrypt(void *pCtx,const unsigned char *pData, uint32_t cbData, unsigned char *pDecrypttedData, uint32_t *pcbDecryptted); /* 函數:GMC_GetLastErrMsg 功能:獲取最后一次錯誤消息 參數:無 返回值:錯誤消息字串,返回值指針值必定不為NULL */ DLL_PUBLIC const char * _APICALL GMC_GetLastErrMsg(); }
然后就就介紹java的調用方法。什么引用jna就不說了。
一、創建一個接口並繼承於com.sun.jna.Library(這里只實現了C++的部分方法)
public interface gmcrypto extends Library { gmcrypto INSTANCE = (gmcrypto) Native.loadLibrary("gmcrypto", gmcrypto.class); /** * 初始化獲取一個指定類型的密碼體系。 * @param type */ IntByReference GMC_New_CTX(long type); /** * 獲取上次的錯誤信息 * @return */ String GMC_GetLastErrMsg(); /** * 加密,注意加密前先獲取長度 * @param intByReference * @param pData * @param cbData * @param pEncrypttedData * @param pcbEncryptted * @return */ long GMC_Encrypt(IntByReference intByReference, byte[] pData, int cbData, byte[] pEncrypttedData, IntByReference pcbEncryptted); /** * 釋放資源上下文 * @param intByReference */ void GMC_Delete_CTX(IntByReference intByReference); }
二、在普通主方法中就可以調用了
public static void main2(String[] args) { IntByReference gm = gmcrypto.INSTANCE.GMC_New_CTX(1); if (gm == null) { System.err.println("指定密碼體系:失敗"); } else { /****************解密******************/ byte[] inByte = Base64.getDecoder().decode("MHYCIFnXBM5gF7OF4VYVmPh+exzQi9ik8dZBAFYs0hKrr8WRAiEArIjXQyOR1vdraQcdv9kG9/NGwVCEJ/UKIGw6gKcrTc0EIABJ7041HF7OLvzcSLvPVWDz3zjKWxOBu91someJ7D1+BA2tOEJsGFtH5rYu2Sxn"); long res = gmcrypto.INSTANCE.GMC_ECKEY_set_PrivateKey(gm, "C51F66571D5C472E383939D3C8944599D50452F4D8909B0C989C68888C0A1509"); System.out.println("設置私鑰結果:" + res); IntByReference total = new IntByReference(1); res = gmcrypto.INSTANCE.GMC_Decrypt(gm, inByte, inByte.length, null, total); System.out.println(res + "\t" + total.getValue()); byte[] outByte = new byte[total.getValue()]; res = gmcrypto.INSTANCE.GMC_Decrypt(gm, inByte, inByte.length, outByte, total); byte[] temp=new byte[total.getValue()]; System.arraycopy(outByte, 0, temp, 0, temp.length); outByte=temp; System.out.println("解密結果:" + res + " 長度:" + outByte.length); System.out.println(new String(outByte)); System.out.println("最近錯誤:" + gmcrypto.INSTANCE.GMC_GetLastErrMsg()); gmcrypto.INSTANCE.GMC_Delete_CTX(gm); } }
重要說明:
1、gmcrypto就代表需要引用的dll/so文件的名稱,由於java是跨平台,所以不加后綴,文件名為gmcrypto.dll/libgmcrypto.so
2、java中可以用IntByReference對象代表指針
3、windows需要把dll文件放到C:\Windows\System32路徑。Linux需要把so文件放到/lib64。當然要根據情況區分32位和64位的情況