 The Android platform includes the Bouncycastle framework, which is designed to perform crypto operations, such as encryption or digital signature verification. A distinctive feature of this framework is that it is entirely written in Java, without the use of native code. This increases its portability, but significantly reduces the speed. In the first approximation, the implementation of crypto functions using native code can give a significant performance boost. This can significantly increase the speed of an application using cryptography. Let's see if this assumption is confirmed.
 The Android platform includes the Bouncycastle framework, which is designed to perform crypto operations, such as encryption or digital signature verification. A distinctive feature of this framework is that it is entirely written in Java, without the use of native code. This increases its portability, but significantly reduces the speed. In the first approximation, the implementation of crypto functions using native code can give a significant performance boost. This can significantly increase the speed of an application using cryptography. Let's see if this assumption is confirmed.public class AES { public static int KEY_SIZE = 16; public static int IV_SIZE = 16; static public class CBC { private byte[] __key; private byte[] __iv; public CBC(byte[] key, byte[] iv) { __key = key; __iv = iv; } public CBC() { __key = GenerateKey(); __iv = GenerateIV(); } public native byte[] Encrypt(byte[] data); public native byte[] Decrypt(byte[] data); public byte[] GetKey() { return __key; } public byte[] GetIV() { return __iv; } } public static native byte[] GenerateIV(); public static native byte[] GenerateKey(); static { try { System.loadLibrary("nativecryptowrapper"); } catch (Exception e) { e.printStackTrace(); } } } native keyword.System.loadLibrary surrounded by a try-catch . It should be noted that the argument of the call to the loadLibrary method is the name of the library WITHOUT the lib prefix and the extension!%PRJ%/bin/%NativePrjName%%PRJ%/jni/nativecryptowrapper folder in the console and from there start the javah –classpath ../../bin/classes com.cryptodroid.AES . The -classpath parameter specifies where to look for compiled class files. Inside the %PRJ%/bin/classes class files are arranged according to the packages in which they are located, so it’s not necessary to specify a specific path to the class file. The second parameter is the full name of the class for which the header file * .h will be created. JNIEXPORT jbyteArray JNICALL Java_com_cryptodroid_crypto_AES_00024CBC_Encrypt(JNIEnv* env, jobject obj, jbyteArray source) { try { std::vector<jbyte> key = to_vector(env, get_field_value<jbyteArray>(env, obj, "__key")); std::vector<jbyte> iv = to_vector(env, get_field_value<jbyteArray>(env, obj, "__iv" )); CryptoPP::CBC_Mode< CryptoPP::AES >::Encryption e; e.SetKeyWithIV( reinterpret_cast<byte*>(&key.front()), KEY_SIZE, reinterpret_cast<byte*>(&iv.front()) ); CryptoPP::StreamTransformationFilter filter (e); jbytearray_holder data_holder(source, env); filter.Put(reinterpret_cast<byte*>(&*data_holder.begin()), data_holder.size()); filter.MessageEnd(); jbyteArray result = env->NewByteArray(filter.MaxRetrievable()); if (!result) throw std::runtime_error("No memory!"); jbytearray_holder result_holder(result, env); filter.Get(reinterpret_cast<byte*>(&*result_holder.begin()), result_holder.size()); return result; } catch (std::exception& e) { throw_jni_exception(env, e); } return NULL; } JNIEXPORT jbyteArray JNICALL Java_com_cryptodroid_crypto_AES_00024CBC_Decrypt(JNIEnv* env, jobject obj, jbyteArray source) { try { std::vector<jbyte> key = to_vector(env, get_field_value<jbyteArray>(env, obj, "__key")); std::vector<jbyte> iv = to_vector(env, get_field_value<jbyteArray>(env, obj, "__iv" )); CryptoPP::CBC_Mode< CryptoPP::AES >::Decryption d; d.SetKeyWithIV( reinterpret_cast<byte*>(&key.front()), KEY_SIZE, reinterpret_cast<byte*>(&iv.front()) ); CryptoPP::StreamTransformationFilter filter (d); jbytearray_holder data_holder(source, env); filter.Put(reinterpret_cast<byte*>(&*data_holder.begin()), data_holder.size()); filter.MessageEnd(); jbyteArray result = env->NewByteArray(filter.MaxRetrievable()); if (!result) throw std::runtime_error("No memory!"); jbytearray_holder result_holder(result, env); filter.Get(reinterpret_cast<byte*>(&*result_holder.begin()), result_holder.size()); return result; } catch (std::exception& e) { throw_jni_exception(env, e); } return NULL; } to_vector is a function that allows you to convert jbyteArray (jni-array) to a regular std::vector<jbyte>jbytearray_holder - a class that encapsulates, in accordance with the RAII paradigm, memory management of a managed arraythrow_jni_exception - throws an exception for managed codeget_field_value is a template function. While there is only a specialization for getting fields of the byte[] tapa, later, if necessary, other specializations will be added. const size_t KEY_SIZE = 16; const size_t IV_SIZE = 16; std::vector<jbyte> to_vector(JNIEnv* env, jbyteArray data) { size_t data_len = env->GetArrayLength(data); std::vector<jbyte> result(data_len); if (data_len) { env->GetByteArrayRegion(data, 0, data_len, &*result.begin()); } return result; } class jbytearray_holder { public: typedef jbyte* iterator; jbytearray_holder(jbyteArray& ar, JNIEnv* env): m_env(env), m_ar(ar) { jboolean is_copy; m_data = m_env->GetByteArrayElements(m_ar, &is_copy); } template<typename T> T get_as() { return reinterpret_cast<T>(m_data); } iterator begin() { return reinterpret_cast<iterator>(m_data); } iterator end() { return begin() + size(); } size_t size() { return m_env->GetArrayLength(m_ar); } ~jbytearray_holder() { m_env->ReleaseByteArrayElements(m_ar, m_data, 0); } private: JNIEnv* m_env; jbyte* m_data; jbyteArray& m_ar; jbytearray_holder(jbytearray_holder&); jbytearray_holder& operator= (jbytearray_holder&); }; void throw_jni_exception(JNIEnv* env, const std::exception& e) { jclass excClass = env->FindClass("java/lang/IllegalArgumentException"); if (excClass) { std::string message = "Exception from native code: "; message += e.what(); env->ThrowNew(excClass, message.c_str()); } } template<typename T> T get_field_value(JNIEnv* env, jobject obj, const std::string& field_name); template<> jbyteArray get_field_value<jbyteArray>(JNIEnv* env, jobject obj, const std::string& field_name) { jclass clazz = env->GetObjectClass(obj); if (!clazz) throw std::runtime_error("No class!"); jfieldID fld = env->GetFieldID(clazz, field_name.c_str(), "[B"); jbyteArray result = static_cast<jbyteArray>(env->GetObjectField(obj, fld)); return result; } JNIEXPORT jbyteArray JNICALL Java_com_cryptodroid_crypto_AES_00024CBC_Encrypt(JNIEnv* env, jobject obj, jbyteArray source) { try { std::vector<jbyte> key = to_vector(env, get_field_value<jbyteArray>(env, obj, "__key")); std::vector<jbyte> iv = to_vector(env, get_field_value<jbyteArray>(env, obj, "__iv" )); CryptoPP::CBC_Mode< CryptoPP::AES >::Encryption e; e.SetKeyWithIV( reinterpret_cast<byte*>(&key.front()), KEY_SIZE, reinterpret_cast<byte*>(&iv.front()) ); CryptoPP::StreamTransformationFilter filter (e); jbytearray_holder data_holder(source, env); filter.Put(reinterpret_cast<byte*>(&*data_holder.begin()), data_holder.size()); filter.MessageEnd(); jbyteArray result = env->NewByteArray(filter.MaxRetrievable()); if (!result) throw std::runtime_error("No memory!"); jbytearray_holder result_holder(result, env); filter.Get(reinterpret_cast<byte*>(&*result_holder.begin()), result_holder.size()); return result; } catch (std::exception& e) { throw_jni_exception(env, e); } return NULL; }  Cipher _e = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec skeySpec = new SecretKeySpec(key, "AES"); IvParameterSpec ivspec = new IvParameterSpec(iv); _e.init(Cipher.ENCRYPT_MODE, skeySpec, ivspec); byte[] encrypted = _e.doFinal(data); Cipher _d = Cipher.getInstance("AES/CBC/PKCS5Padding"); _d.init(Cipher.DECRYPT_MODE, skeySpec, ivspec); byte[] decrypted = _d.doFinal(encrypted);  AES.CBC e = new AES.CBC(iv, key); byte[] encrypted = e.Encrypt(data); byte[] decrypted = e.Decrypt(encrypted); LOCAL_SRC_FILES - list of source filesLOCAL_MODULE - module nameLOCAL_STATIC_LIBRARIES - a list of static libraries to be used when building (optional)LOCAL_CFLAGS - additional compiler flags (if needed)CLEAR_VARS macroBUILD_xxx one of the BUILD_xxx macros, for example BUILD_STATIC_LIBRARY or BUILD_SHARED_LIBRARYAPP_STL - STL Usage FlagAPP_ABI - list of target architecturesAPP_OPTIM - build type (debug / release)APP_PLATFORM - target platform indication LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := nativecryptowrapper LOCAL_CFLAGS := -fexceptions -frtti LOCAL_SRC_FILES := nativecryptowrapper/aes_base.cpp LOCAL_STATIC_LIBRARIES := cryptopp include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := cryptopp LOCAL_CFLAGS := -fexceptions -frtti LOCAL_SRC_FILES := \ cryptopp/3way.cpp \ .... cryptopp/zdeflate.cpp \ cryptopp/zinflate.cpp \ cryptopp/zlib.cpp include $(BUILD_STATIC_LIBRARY)  APP_STL := gnustl_static APP_ABI := armeabi armeabi-v7a x86 APP_OPTIM := release APP_PLATFORM=android-9 -fexceptions and -frtti to the -frtti variable of the LOCAL_CFLAGS file (since the Crypto ++ library uses exceptions and castings dynamic_cast)System. nanoTime() method was used System. nanoTime() System. nanoTime() from the standard Java class library on Android. Measurements were taken on: Megafon Mint, Pocketbook A10. The results are as follows:| Megafon Mint, ms | Pocketbook A10, ms | |
|---|---|---|
| System Crypt Provider | 998 | 1835 | 
| Crypto ++ library | 231 | 970 | 
| Acceleration | 4.3x | 1.9x | 
Source: https://habr.com/ru/post/170517/
All Articles