1. 程式人生 > >c語言實現rsa nopadding非對稱加密演算法(openssl api方式),匹配java後端 bouncycastle

c語言實現rsa nopadding非對稱加密演算法(openssl api方式),匹配java後端 bouncycastle

公私鑰:
—–BEGIN PUBLIC KEY—–
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC32V2IAfotG8bZhPF8/Bx8y65g
EDycAMyTWmvpPCfagEKORO/WvdkTYim7yhG/+pMs58wu86edP3LOK73VVlG1eeOg
shGVLsKRMYkFRFJ2a81VL2eOaZ8EyZazJ9bkGbNz+JRecx4LkPHdEZTRiSr7zQxN
f0UWR9Dxna1T7/C3twIDAQAB
—–END PUBLIC KEY—–

—–BEGIN RSA PRIVATE KEY—–
MIICXQIBAAKBgQC32V2IAfotG8bZhPF8/Bx8y65gEDycAMyTWmvpPCfagEKORO/W
vdkTYim7yhG/+pMs58wu86edP3LOK73VVlG1eeOgshGVLsKRMYkFRFJ2a81VL2eO
aZ8EyZazJ9bkGbNz+JRecx4LkPHdEZTRiSr7zQxNf0UWR9Dxna1T7/C3twIDAQAB
AoGBAIS0EDAhBTzUJOdTb6AAtmtZ9eb/MVCFvyTJisTSmA2+sMvSdLAzXuH/5BHZ
nJBPRTaPpjFpzF3Ts4GfzymTYev762lDaVNTId/md6mOcDHdkrBMpjLQcdJa8kv4
2gdxb9+1N+6X4wRh7DyIsow2gCKwrWE1rpf3M7xnqIBI3qSpAkEA3VzD/PAU9aDn
i3VJwEsML2eTJ52FdVAUtlvfaSnbUIgnTHNldCjVQUyT99bLs5FsRILnxM1+iFmH
dVUvZgGN6wJBANSd6NdtTcijOqy9wb/cqTaInCLNObGmLOHSrJcngpWOXtrrk5ZV
9JpKiNFycvM0j6+WS0aVERWjb3xo9msarmUCQAk1IJnWyqhk5vywBj9PX9Rg9IgD
T1fP2uyy6ZoQOaUh5LCTZDbus+AhdU4ryxFPDTfUUhuniInXX4EL6ArQKb0CQCY9
XGJag1Hhoazt2An02u429bYAqS69T3cyF957ua3CVGcVVY8FWJ/jZeYdqnT5cBpJ
crJ4HwGASo3apu8udk0CQQCzJ28e0WVCIwxgYK81FZgTxusaSjtkv4Dbz0BXvZLH
NX6zYO1fqM1Cm/FMqGtPvI8/O3AVJrcNeo4lgKvyDQVv
—–END RSA PRIVATE KEY—–

java中的公私鑰 (只是去掉了頭尾和換行,這樣才能在java中使用):
static String prikey1024 = “MIICXQIBAAKBgQC32V2IAfotG8bZhPF8/Bx8y65gEDycAMyTWmvpPCfagEKORO/WvdkTYim7yhG/+pMs58wu86edP3LOK73VVlG1eeOgshGVLsKRMYkFRFJ2a81VL2eOaZ8EyZazJ9bkGbNz+JRecx4LkPHdEZTRiSr7zQxNf0UWR9Dxna1T7/C3twIDAQABAoGBAIS0EDAhBTzUJOdTb6AAtmtZ9eb/MVCFvyTJisTSmA2+sMvSdLAzXuH/5BHZnJBPRTaPpjFpzF3Ts4GfzymTYev762lDaVNTId/md6mOcDHdkrBMpjLQcdJa8kv42gdxb9+1N+6X4wRh7DyIsow2gCKwrWE1rpf3M7xnqIBI3qSpAkEA3VzD/PAU9aDni3VJwEsML2eTJ52FdVAUtlvfaSnbUIgnTHNldCjVQUyT99bLs5FsRILnxM1+iFmHdVUvZgGN6wJBANSd6NdtTcijOqy9wb/cqTaInCLNObGmLOHSrJcngpWOXtrrk5ZV9JpKiNFycvM0j6+WS0aVERWjb3xo9msarmUCQAk1IJnWyqhk5vywBj9PX9Rg9IgDT1fP2uyy6ZoQOaUh5LCTZDbus+AhdU4ryxFPDTfUUhuniInXX4EL6ArQKb0CQCY9XGJag1Hhoazt2An02u429bYAqS69T3cyF957ua3CVGcVVY8FWJ/jZeYdqnT5cBpJcrJ4HwGASo3apu8udk0CQQCzJ28e0WVCIwxgYK81FZgTxusaSjtkv4Dbz0BXvZLHNX6zYO1fqM1Cm/FMqGtPvI8/O3AVJrcNeo4lgKvyDQVv”;
static String pubkey1024 = “MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC32V2IAfotG8bZhPF8/Bx8y65gEDycAMyTWmvpPCfagEKORO/WvdkTYim7yhG/+pMs58wu86edP3LOK73VVlG1eeOgshGVLsKRMYkFRFJ2a81VL2eOaZ8EyZazJ9bkGbNz+JRecx4LkPHdEZTRiSr7zQxNf0UWR9Dxna1T7/C3twIDAQAB”;

 Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
 public static String encrypt(String publicKey, String plainTextData) throws Exception {
//        if (TextUtils.isEmpty(publicKey)) {
//            throw new Exception("加密公鑰為空, 請設定");
//        }
        X509EncodedKeySpec bobPubKeySpec = new
X509EncodedKeySpec(Base64.decode(publicKey,Base64.DEFAULT)); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey pubKey = keyFactory.generatePublic(bobPubKeySpec); Cipher cipher; try { cipher = Cipher.getInstance("RSA/NONE/nopadding"); cipher.init(Cipher.ENCRYPT_MODE, pubKey); byte[] output = cipher.doFinal(plainTextData.getBytes("UTF-8")); return Base64.encodeToString(output, Base64.NO_WRAP); } catch (Exception e) { e.printStackTrace(); throw new Exception(); } } public static String decrypt(String privateKey, String cipherData) throws Exception { if (privateKey == null) { throw new Exception("解密私鑰為空, 請設定"); } PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey,Base64.DEFAULT)); KeyFactory keyf = KeyFactory.getInstance("RSA"); PrivateKey myprikey = keyf.generatePrivate(priPKCS8); Cipher cipher = null; try { cipher = Cipher.getInstance("RSA/NONE/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, myprikey); byte[] output = cipher.doFinal(Base64.decode(cipherData,Base64.DEFAULT)); return new String(output); } catch (Exception e) { e.printStackTrace(); throw new Exception(); } }

c語言通過編譯openssl並呼叫api實現java中的RSA/NONE/NoPadding非對稱加解密演算法,程式碼(注意加密後要進行base64編碼,解密前要進行base64解碼):

string decryptRSA(const std::string& data) {


    int ret, flen;
    BIO *bio = NULL;
    RSA *r = NULL;

    if ((bio = BIO_new_mem_buf((void *)prikey1024, -1)) == NULL)       //從字串讀取RSA私鑰
    {
        //LOGE("BIO_new_mem_buf failed!\n");
    }

    //    r = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
    r = RSA_new();
    PEM_read_bio_RSAPrivateKey(bio, &r, 0, 0);//讀取私鑰


    flen = RSA_size(r);



    static std::string gkbn;
    gkbn.clear();



    const char *gkbnP = gkbn.c_str();

    char de[512] = {0};
    memset(de, 0, (size_t)(512));
    const char *dataP = data.c_str();

    //RSA_private_encrypt  rsa_ossl_private_decrypt
    ret = RSA_private_encrypt(flen, (const unsigned char *)dataP, (unsigned char *)de, r,
        RSA_NO_PADDING);//RSA_NO_PADDING //RSA_PKCS1_PADDING

    RSA_free(r);
    BIO_free(bio);
    CRYPTO_cleanup_all_ex_data(); //清除管理CRYPTO_EX_DATA的全域性hash表中的資料,避免記憶體洩漏

    char *result_c;
    int len;
    for (size_t i = 0; i < flen; i++)
    {
        if (de[i] != '\0') {
            result_c = &de[i];
            break;
        }
    }
    len = strlen(result_c);
    printf("\ndecrypto:%s\n", result_c);
    gkbn.assign(result_c);
    //    LOGD("解密明文:%s ret:%d",gkbn.c_str(), ret);
    return  gkbn;

string encryptRSA(const std::string& data, int *lenreturn) {


    int ret, flen,bio_len;
    BIO *bio = NULL;
    RSA *r = NULL;

    if ((bio = BIO_new_mem_buf((void *)pubkey1024, strlen(pubkey1024))) == NULL)       //從字串讀取RSA公鑰
    {
        //LOGE("BIO_new_mem_buf failed!\n");
    }
    PEM_read_bio_RSA_PUBKEY(bio, &r, 0, 0);

    flen = RSA_size(r);
    lenreturn = &flen;


    static std::string gkbn;
    gkbn.clear();

    unsigned char *src = NULL;
    unsigned char *dst = NULL;


    src = (unsigned char *)OPENSSL_malloc(flen * 2);//填充0 到 128 長度
    dst = (unsigned char *)OPENSSL_malloc(flen);

    memset(src, 0, flen * 2);
    memcpy(src, data.c_str(), data.size());
    memset(dst, 0, flen + 1);



    //  RSA_PKCS1_PADDING 最大加密長度 為 128 -11
    //RSA_NO_PADDING 最大加密長度為  128
    ret = RSA_public_encrypt(flen, src, dst, r, RSA_NO_PADDING);//RSA_NO_PADDING //RSA_PKCS1_PADDING


    gkbn.assign((char *)dst,ret);

    RSA_free(r);

    //OPENSSL_free(src);
    //OPENSSL_free(dst);
    BIO_free(bio);
    CRYPTO_cleanup_all_ex_data(); //清除管理CRYPTO_EX_DATA的全域性hash表中的資料,避免記憶體洩漏

    return  gkbn;

}
}

測試:
c語言加密字串”ceshi123”,通過呼叫encryptRSA函式加密

char origin_text[117] = "ceshi123";
int retlen;
string crypto = encryptRSA(origin_text,&retlen);
crypto = TDBASE64::base64_encodestring(crypto);
printf("%s", crypto.c_str());

得到結果:PC5At5PvCitwSio5TQK1gj/vsp3ffQMnu6LpXTJyhwZbCOS3t+PFZoYvUtUyZNvstp7KZMie8CoAVDbhfJP2H+AZDcnVQljKAQTpeb+VjjZJGQzFV42cxzKgNo2Oq3M/7EoAvJYWp7XFE7Y1/lOJSFdcfFjDD3zFX26I4c+TzDI=,然後用java解密,java解密後的結果為 :
這裡寫圖片描述
發現末尾有亂碼,暫時不管,後文再討論
現在使用java加密”ceshi123”得到結果B3I3nI+DnI+Y8W8rzNOs9cRhWjfFu0CW+ac3Cf/b/vgR3fm/vQkM1oekT1hhm9BaYSc1usby3il0X1dv41s6d6mtFImUjs1p0ILWChHG0Nu6cAilE34bFhkBdp1eQJJXJ8gJ/6FxsH7yfNOsgtF9H+bjYDJNkofl+MiHfsVQAU8=,發現和c語言加密的結果不一樣,不管,用c語言解密,解密程式碼

string testText = "B3I3nI+DnI+Y8W8rzNOs9cRhWjfFu0CW+ac3Cf/b/vgR3fm/vQkM1oekT1hhm9BaYSc1usby3il0X1dv41s6d6mtFImUjs1p0ILWChHG0Nu6cAilE34bFhkBdp1eQJJXJ8gJ/6FxsH7yfNOsgtF9H+bjYDJNkofl+MiHfsVQAU8=";
        testText = TDBASE64::base64_decodestring(testText);
        decryptRSA(testText);     

觀察結果發現結果有點怪異,雖然解密成功了,解密後的記憶體區域佔用128個位元組,但解密後的結果”ceshi123”並不在陣列開始區域,而是在陣列末尾,這裡寫圖片描述,後來通過翻看openssl原始碼發現,openssl在rsa nopadding下會在解密前和加密後進行填充,可以看到原始碼中的rsa_none.c原始檔中的程式碼:

int RSA_padding_add_none(unsigned char *to, int tlen,
                         const unsigned char *from, int flen)
{
    if (flen > tlen) {
        RSAerr(RSA_F_RSA_PADDING_ADD_NONE, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
        return (0);
    }

    if (flen < tlen) {
        RSAerr(RSA_F_RSA_PADDING_ADD_NONE, RSA_R_DATA_TOO_SMALL_FOR_KEY_SIZE);
        return (0);
    }

    memcpy(to, from, (unsigned int)flen);
    return (1);
}

int RSA_padding_check_none(unsigned char *to, int tlen,
                           const unsigned char *from, int flen, int num)
{

    if (flen > tlen) {
        RSAerr(RSA_F_RSA_PADDING_CHECK_NONE, RSA_R_DATA_TOO_LARGE);
        return (-1);
    }

    memset(to, 0, tlen - flen);
    memcpy(to + tlen - flen, from, flen);
    return (tlen);
}

函式 RSA_padding_add_none 會在加密前呼叫,而 RSA_padding_check_none會在解密後呼叫,
針對明文”ceshi123”解密測試中發現 ,openssl內部解密呼叫 RSA_padding_check_none 時,tlen=128,flen=8,num=128,(num在這裡好像沒看到呼叫) ,呼叫memset(to, 0, tlen - flen);
memcpy(to + tlen - flen, from, flen) 後就導致了陣列中前面全是0,明文在末尾的情況,
結論,openssl的rsa nopadding並不能與 java bouncycastle 的 rsa nopadding完全匹配,當然手動處理出現0的情況也是可以的,比如去掉結果中出現0的字元,但可能某些情況會出現bug,推薦使用rsa pkcs1padding方式,這種方式openssl 可以完美匹配java bouncycastle的演算法,可以互相加解密而不會出現上面有0的情況 ,下一篇直接上java 和c語言呼叫openssl的RSA/NONE/PCKS1PADDING程式碼