Шифрование и генерация случайных чисел в Android приложениях. Тестовые примеры
В этой статье мы приведем тестовые фрагменты кода, реализующего две основополагающие с точки зрения безопасности функции в Android приложениях: генерацию случайных чисел и шифрование данных. Рекомендуем попробовать все приведенные варианты, а по прочтении текста — скомпилировать тестовое приложение, скачав его по ссылке.Шифрование данных Шифрование имеет важное значение, поскольку позволяет скрыть от посторонних глаз то, что им не следует видеть. Мобильные устройства хранят все больше и больше значимой информации, и защитить ее — прямая обязанность каждого разработчика.Существует два варианта шифрования данных под Android: с использованием Java Crypto API и OpenSSL API (нативный код). Мы рассмотрим оба.
Java Crypto API Использовать Java Crypto API под Android очень просто. Сначала вам необходимо сгенерировать ключ шифрования. За это отвечает класс KeyGenerator в пэкедже javax.crypto. mKey = null; try { kgen = KeyGenerator.getInstance («AES»); mKey = kgen.generateKey ();
} catch (NoSuchAlgorithmException e) { e.printStackTrace (); } Теперь вы можете использовать сгенерированный ключ для шифрования файлов с данными. Для этого зашифруем блоки байтов по алгоритму AES с помощью javax.crypto. // open stream to read origFilepath. We are going to save encrypted contents to outfile InputStream fis = new FileInputStream (origFilepath); File outfile = new File (encFilepath); int read = 0; if (! outfile.exists ()) outfile.createNewFile (); FileOutputStream encfos = new FileOutputStream (outfile); // Create Cipher using «AES» provider Cipher encipher = Cipher.getInstance («AES»); encipher.init (Cipher.ENCRYPT_MODE, mKey); CipherOutputStream cos = new CipherOutputStream (encfos, encipher); // capture time it takes to encrypt file start = System.nanoTime (); Log.d (TAG, String.valueOf (start)); byte[] block = new byte[mBlocksize]; while ((read = fis.read (block,0, mBlocksize)) != -1) { cos.write (block,0, read); } cos.close (); stop = System.nanoTime (); Log.d (TAG, String.valueOf (stop)); seconds = (stop — start) / 1000000;// for milliseconds Log.d (TAG, String.valueOf (seconds)); fis.close (); OpenSSL API Шифрование данных через OpenSSL под Android требует написания нативного кода С, который доступен в Java через вызовы JNI. Это отнимает больше времени, зато быстродействие в результате будет выше.Для начала сгенерируем ключ и iv. unsigned char cKeyBuffer[KEYSIZE/sizeof (unsigned char)]; unsigned char iv[] = »01234567890123456»; int opensslIsSeeded = 0; if (! opensslIsSeeded) { if (! RAND_load_file (»/dev/urandom», seedbytes)) { return -1; } opensslIsSeeded = 1; }
if (! RAND_bytes ((unsigned char *)cKeyBuffer, KEYSIZE)) { } Теперь мы можем использовать сгенерированный ключ (cKeyBuffer) для шифрования файла. Инициализируем EVP с помощью вашего ключа и iv. Теперь подаем блоки байтов на вход функции EVP_EncryptUpdate. Последняя порция байтов из вашего файла должна быть скормлена функции EVP_EncryptFinal_ex.
if (!(EVP_EncryptInit_ex (e_ctx, EVP_aes_256_cbc (), NULL, cKeyBuffer, iv))) { ret = -1; printf («ERROR: EVP_ENCRYPTINIT_EXn»); } // go through file, and encrypt if (orig_file!= NULL) { origData = new unsigned char[aes_blocksize]; encData = new unsigned char[aes_blocksize+EVP_CIPHER_CTX_block_size (e_ctx)]; // potential for encryption to be 16 bytes longer than original
printf («Encoding file: %sn», filename);
bytesread = fread (origData, 1, aes_blocksize, orig_file); // read bytes from file, then send to cipher while (bytesread) {
if (!(EVP_EncryptUpdate (e_ctx, encData, &len, origData, bytesread))) { ret = -1; printf («ERROR: EVP_ENCRYPTUPDATEn»); } encData_len = len;
fwrite (encData, 1, encData_len, enc_file); // read more bytes bytesread = fread (origData, 1, aes_blocksize, orig_file); } // last step encryption if (!(EVP_EncryptFinal_ex (e_ctx, encData, &len))) { ret = -1; printf («ERROR: EVP_ENCRYPTFINAL_EXn»); } encData_len = len;
fwrite (encData, 1, encData_len, enc_file);
// free cipher EVP_CIPHER_CTX_free (e_ctx); Оригинал статьи на сайте Intel IDZИсходники тестового приложенияГенерация случайных чисел Генератор случайных чисел (RNG) — это программа или устройство для производства случайной последовательности чисел на определенном промежутке. RNG является жизненно важным для безопасности приложения. В реальности криптографический протокол может быть очень надежным, но при этом подверженным разнообразным атакам из-за того, что в своей основе использует слабые методы генерации ключа. Для усиления ключа и повышения надежности всей системы в целом может использоваться аппаратная поддержка RNG.
Существует целых 4 способа сгенерировать случайные числа в Android:
java.util.random java.security.SecureRandom /dev/urandom OpenSSL API Однако, если вы используете RNG для генерации ключа, защищающего ваши данные, использовать обычный класс Random не рекомендуется, так его легче всего взломать. Остальные 3 метода обеспечивают более надежную защиту.java.util.random Использовать Java Random Number API очень просто. Вызов Random.nextInt () возвратит 4-байтное случайное значение (общее количество возможных значений — 232). Это API вполне годится для случаев, когда не требуется полагаться на действительно случайные числа. for (int i = 0; i < lastVal; i += 2) { dataRandomPoints[i] = (rand.nextInt() % widget_width); dataRandomPoints[i+1] = (rand.nextInt() % widget_height);
} java.security.SecureRandom SecureRandom похож на java.util.Random в том смысле, что также возвращает 4-байтовое значение. SecureRandom криптографически более надежен, однако разработчики должны ознакомиться с недавней рекомендацией генерировать затравочную величину с помощью /dev/urandom для SecureRandom перед генерацией случайных чисел. В примере ниже /dev/urandom не используется. SecureRandom srand = new SecureRandom (); shouldDraw = (srand.nextInt () % randomMod); /dev/urandom Во всех операционных системах семейства Linux, включая Android, имеется специальный файл, созданный ядром, с помощью которого можно предоставить случайные числа приложениям. Среди всех 4 способов этот самый медленный, он генерирует криптографически безопасные значения с высокой энтропией путем объединения шумовых величин из различных частей операционной системы (например, драйверов устройств) для RNG. Мы можем получить случайное число непосредственно из ядра, прочитав файл /dev/urandom. /dev/urandom имеет доступ к аппаратному RNG, если таковой имеется. unsigned int cKeyBuffer[keysize]; memset (cKeyBuffer, 0, sizeof (unsigned int) * keysize);
FILE *fin; strcpy (filein,»/dev/urandom»); fin = fopen (filein, «rb»);
if (fin!= NULL) { fread (cKeyBuffer, sizeof (int), keysize, fin); fclose (fin); } OpenSSL API Мы также можем использовать OpenSSL API для получения случайных чисел в нативном коде С. В OpenSSL возможно использование затравочных байт из /dev/urandom для генерации криптографически безопасных случайных чисел. OpenSSL API обратится к аппаратному RNG, если таковой имеется. int seedbytes = 1024; unsigned int cKeyBuffer[keysize]; memset (cKeyBuffer, 0, sizeof (unsigned int) * keysize);
if (! opensslIsSeeded) {
if (! RAND_load_file (»/dev/urandom», seedbytes)) { __android_log_print (ANDROID_LOG_ERROR, TAG, «Failed to seed OpenSSL RNG»); return jKeyBuffer; }
opensslIsSeeded = 1; }
if (! RAND_bytes ((unsigned char *)cKeyBuffer, keysize * sizeof (int))) { __android_log_print (ANDROID_LOG_ERROR, TAG, «Faled to create OpenSSSL random integers: %ul», ERR_get_error); } Оригинал статьи на сайте Intel IDZИсходники тестового приложения