Android RSA加密解密

需求

在Android上实现RSA 2048 PKCS#1加密,秘钥不能放在Java层(其实公钥被别人看了也没啥吧?)。

几个思路:

  1. 所有加密的程序都在native层。
  2. 秘钥在Native层,加密在Java实现。
  3. 所有都在Java实现。(不满足公钥不能放在Java层的要求)

RSA加密

RSA是一种非对称加密算法。

通常的使用流程是A生成一对秘钥,公钥私钥,而这对秘钥有这样的特性:

使用公钥加密的数据,利用私钥进行解密,使用私钥加密的数据,利用公钥进行解密

A把公钥交给B,B使用公钥加密后把数据给回A,A使用私钥进行解密。

2048是RSA密钥长度bit数,数字越大安全性越大。

PKCS#1是公钥加密标准(Public Key Cryptography Standards, PKCS),是秘钥的一种格式。

在这里可以体验一下。

Android在native中使用OpenSSL

项目地址:https://github.com/etet2007/RSAencrypt

在Android中添加native代码的步骤在官网中说得很清楚了。

总的来说有几个Point。

  1. cpp文件夹下用于解密的cpp文件。Java层调用的native方法与其关联。
//Java

public static native byte[] encodeByRSAPubKey(byte[] src);
//CPP

extern "C" JNIEXPORT jbyteArray JNICALL
Java_lau_stephen_rsaencrypt_EncryptUtils_encodeByRSAPubKey(JNIEnv *env, jclass type,
                                                           jbyteArray src_) {
    std::string keys = "-----BEGIN PUBLIC KEY-----\n"
                       "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwm/vZlgdvA/s3o+Epq2B\n"
                       "TF9Pk1goY4+wji1fjkmePasZkt12Rx0A0qXuzBfe8K4Y1uf/sKD1XHeXtvPol5TF\n"
                       "ZKq6dEQpd3PsweFMFGYfZbA5IdwEQWXFJqJSpru/jXENCanUARVV5Au0fjaMw71x\n"
                       "dGbHQ7gNdxln9xeoPkyCLBuWor5B3U47NFGEz8ZMELCib0+9bPtzIVuBYA5BsT9A\n"
                       "WgHZpuRZRgQ2r3a0ehe7gO1H+SKrLStVzUZ7EUW4PBM4IhIrR+BfORHi4PgD+4rZ\n"
                       "IuMzg99Y20ytaHIm6tw6+dvt3gSY2q2VWITCVE2dtH167R/AR+mJDFhp89Ss1sGE\n"
                       "wQIDAQAB\n"
                       "-----END PUBLIC KEY-----";

    jbyte *src = env->GetByteArrayElements(src_, NULL);

    jsize src_Len = env->GetArrayLength(src_);

    int encryptedValueSize = 0, src_flen = 0, cipherText_offset = 0, desText_len = 0, src_offset = 0;

    //BIO_new_mem_buf() creates a memory BIO using len bytes of data at buf,
    // if len is -1 then the buf is assumed to be nul terminated and its length is determined by strlen.
    BIO *keyBio = BIO_new_mem_buf((void *) keys.c_str(), -1);
    //The RSA structure consists of several BIGNUM components.
    // It can contain public as well as private RSA keys:
    RSA *publicKey = PEM_read_bio_RSA_PUBKEY(keyBio, NULL, NULL, NULL);
    //释放BIO
    BIO_free_all(keyBio);

    //RSA_size returns the RSA modulus size in bytes.
    // It can be used to determine how much memory must be allocated for an RSA encrypted value.
    int flen = RSA_size(publicKey);

    //复制src到srcOrigin
    unsigned char *srcOrigin = (unsigned char *) malloc(src_Len);
    memset(srcOrigin, 0, src_Len);
    memcpy(srcOrigin, src, src_Len);
    //每次加密后的长度
    unsigned char *encryptedValue = (unsigned char *) malloc(flen);

    desText_len = flen * (src_Len / (flen - 11) + 1);
    unsigned char *desText = (unsigned char *) malloc(desText_len);
    memset(desText, 0, desText_len);

    //对数据进行公钥加密运算
    //对于1024bit,2048应该为256
    //RSA_PKCS1_PADDING 最大加密长度 为 128 -11
    //RSA_NO_PADDING 最大加密长度为  128
    //rsa_size = rsa_size - RSA_PKCS1_PADDING_SIZE;
    for (int i = 0; i <= src_Len / (flen - 11); i++) {
        src_flen = (i == src_Len / (flen - 11)) ? src_Len % (flen - 11) : flen - 11;
        if (src_flen == 0) {
            break;
        }
        //重置encryptedValue
        memset(encryptedValue, 0, flen);
        //encrypt srcOrigin + src_offset到encryptedValue
        //returns the size of the encrypted data
        encryptedValueSize = RSA_public_encrypt(src_flen, srcOrigin + src_offset, encryptedValue,
                                                publicKey, RSA_PKCS1_PADDING);
        if (encryptedValueSize == -1) {
            RSA_free(publicKey);
            CRYPTO_cleanup_all_ex_data();
            env->ReleaseByteArrayElements(src_, src, 0);
            free(srcOrigin);
            free(encryptedValue);
            free(desText);

            return NULL;
        }
        //复制encryptedValue到desText + cipherText_offset
        memcpy(desText + cipherText_offset, encryptedValue, encryptedValueSize);

        cipherText_offset += encryptedValueSize;
        src_offset += src_flen;
    }

    RSA_free(publicKey);
    //清除管理CRYPTO_EX_DATA的全局hash表中的数据,避免内存泄漏
    CRYPTO_cleanup_all_ex_data();

    //从jni释放数据指针
    env->ReleaseByteArrayElements(src_, src, 0);

    jbyteArray cipher = env->NewByteArray(cipherText_offset);

    //在堆中分配ByteArray数组对象成功,将拷贝数据到数组中
    env->SetByteArrayRegion(cipher, 0, cipherText_offset, (jbyte *) desText);
    //释放内存
    free(srcOrigin);
    free(encryptedValue);
    free(desText);

    return cipher;
}
  1. 放置OpenSSL的库于项目中。这里的库是.a的静态库或者.so的动态库,可以自行编译或下载别人的来用。
    动态库,静态库,编译。

在Gradle中设置jniLibs的地址,把带有so库目录地址告诉Gradle,在打包的时候才会把so库打进APK。

    sourceSets.main {
        jniLibs.srcDirs = ['libs/lib']
    }
  1. 提供CMake文件

Gradle提供CMakeList.txt文件的路径,我这里是放在app下。记住路径都是相对路径来的。

    externalNativeBuild {
        cmake {
            path file('CMakeLists.txt')
        }
    }

CMakeList.txt文件,告诉系统用encrypt.cpp编译动态库,其中引用到OpenSSL的crypto、ssl动态库。

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

include_directories(libs/include)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
        encrypt
        # Sets the library as a shared library.
        SHARED
        # Provides a relative path to your source file(s).
        src/main/cpp/encrypt.cpp)

add_library(crypto SHARED IMPORTED)
add_library(ssl SHARED IMPORTED)

set_target_properties(crypto PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/lib/${ANDROID_ABI}/libcrypto.so)
set_target_properties(ssl PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/lib/${ANDROID_ABI}/libssl.so)

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

target_link_libraries( # Specifies the target library.
        encrypt
        crypto
        ssl
        # Links the target library to the log library
        # included in the NDK.
        ${log-lib} )

加密在Java实现

Android 密钥库系统

你可能感兴趣的:(Android RSA加密解密)