【轉載】java調用dll/so文件

大家都知道用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位的情況


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章