Андроид и .Net шифрование

Андроид и .Net шифрование

Это статья кратко объясняет как используется шифрование/дешифрование в операционной системе Android и на платформе .Net, с концентрацией на передаче зашифрованных данных из андроида в .Net.

Содержание

Предисловие

Конфиденциальные данные, которые могут увидеть посторонние люди, должны быть зашифрована. Какие данные из вашей переписки могут оказаться уязвимыми? Это могут быть любая персональная информация, картинки, которые вы отправляете по сети, ваши GPS координаты и т.д.
Конфиденциальные данные уязвимы, когда становятся видными за предела вашей программы при отправке через интернет или сохранении в локальных файлах компании.
Эта статья не о безопасности, а о простой реализации шифрования/ дешифрования.

Что такое шифрование (кодирование)?

Шифрование –это трансформация открытой информации в зашифрованную.
Расшифровка обратно – это преобразование зашифрованного текста в обычный текст, в удобный для чтения формате.
Алгоритмы шифрования могут быть разделены на 2 группы: симметричные и асимметричные. Оба способа имеют свои преимущества и недостатки.

Симметричное Шифрование

Шифрование и дешифрование выполняется при помощи одиночного открытого ключа. Этот ключ открыт и для отправителя и для получателя.
Для того, чтобы зашифровать сообщение отправитель использует открытый ключ. Получатель должен использовать тот же ключ для дешифрования сообщения.
Симметричный алгоритм очень быстрый, но требует обе стороны иметь уникальный одинаковый ключ, который может стать проблемой в больших системах, так как возникает необходимость в управление огромным количеством ключей.

Ассиметричное шифрование ( криптография с открытым ключом)

Отправить и получатель используют различные ключи для шифрования и дешифрования сообщения. У каждого их них есть пара ключей – открытый и закрытый. Этот подход медленнее, но управление ключом намного проще для больших систем.

Симметричное шифромание в Андроид

В этом примере мы будем использовать класс Gipher из Android’s SDK, для наших целей шифрования / дешифрования. Класс Gipher обеспечивает доступ к реализации криптографических шифров для шифрования и дешифрования. Подробнее об этом можно прочитать по ссылки: http://developer.android.com/reference/javax/crypto/Cipher.html

CryptoHandler class

Наш Cipher зависит от двух ключей, первый из них передается в качестве строки в конструктор, и второй определен как секретный ключ, который имеет тип 16-byte long.

Давайте рассмотрим наш класс CryptoHandler class:

//CryptoHandler constructor
public CryptoHandler(String passphrase) {
	//decodes passd phrase to encrypted byte[]
	byte[] passwordKey = encodeDigest(passphrase);

	try {
		//_aesCipher instantiation passing transformation parameter
		_aesCipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
	} catch (NoSuchAlgorithmException e) {
		//Invalid algorithm in passed transformation parameter
		Log.e(TAG, "No such algorithm " + CIPHER_ALGORITHM, e);
	} catch (NoSuchPaddingException e) {
		//Invalid padding in passed transformation parameter
		Log.e(TAG, "No such padding PKCS5", e);
	}

	//Encodes the passed password phrase to a byte[]
	//that will be stored in class private member of SecretKey type
	_secretKey = new SecretKeySpec(passwordKey, CIPHER_ALGORITHM);

	// Creates a new IvParameterSpec instance with the bytes
	//from the specified buffer iv used as initialization vector.

	_ivParameterSpec = new IvParameterSpec(rawSecretKey);
}

В нашем классе CryptoHandler мы будем использовать AES (Расширенный стандарт шифрования) шифрование, о котором вы можете прочитать здесь:

http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
Внедрение AES происходит следующим образом:

private static String CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding";
_aesCipher = Cipher.getInstance(CIPHER_TRANSFORMATION);

Мы вызываем в Cipher статический метод, который возвращает экземпляр класса  (если не были вызваны исключительные ситуации).

“AES/CBC/PKCS5Padding “(передается строковой параметр для преобразования)

AES – Алгоритм шифрования (Расширенный стандарт шифрования).

CBC – Название в режиме обратной связи (в нашем случае Cipher Block Chaining).

PKCS5Padding – Имя дополнительной схемы.

Методы класса CryptoHandler

В добавок конструктор у нас так же реализует 3 метода в нашем классе CryptoHandler :

public byte[] Encrypt(byte[] clearData);

Метод шифрования (Encrypt) устанавливает режим шифрования _aesCipher для обработки константы Cipher.ENCRYPT_MODE в методе init().

public byte[] Decrypt(byte[] data);

Метод расшифровки (Decrypt) делает тоже самое, но вместо Cipher.ENCRYPT_MODE он передает Cipher.DECRYPT_MODE.

Оба метода шифрования (Decrypt) и дешифрования (Encrypt) вызывают метод DoWork(), единственная разница между ними — режим работы шифровальщика, устанавливаемый константой.

public byte[] DoWork(byte[] data);

Вызываем метод _aesCipher.doFinal(data) и перехватываем исключения.

private byte[] encodeDigest(String text);

Используем одни раз в конструкторе как пароль шифрования.

Простой ключ сохраняем в битовый массив в классе:

private static byte[] _rawSecretKey = {
 0x12, 0x00, 0x22, 0x55, 0x33, 0x78, 0x25, 0x11, 0x33, 0x45, 0x00, 0x00, 0x34, 0x00, 0x23, 0x28 
};

Этот ключ должен быть открыт между отправителем и получателем, в нашем случае мы участвуем с обоих сторон, так что мы используем тот же экземпляр ключа для шифрования и дешифрования.

Реализация для Android

На MainActivity layout вы можете увидеть две цветные области, синяя область для ввода текста, который нужна зашифровать, зеленая только для отображения результатов шифрования.

При нажатии на кнопку шифрования, зашифрованные данные будут записываться во внутреннюю файловую систему (новый файл можно найти  в списке файлов папки) и показываться тарабарщина в зеленой области (это зашифрованный текст оригинального текста).

*Сохраняемые данные перезаписывают существующие, а не добавляются к имеющимся в файле.

/**
 * Encrypts the String value that entered in the _tvMessage EditText View
 * and saves is to file on local file system
 **/
private void EncryptMessage() {
	String message = _tvMessage.getText().toString();
	if (message != null && message.length() > 0) {
		//performs text encryption
		byte[] bytes = _crypto.Encrypt(message.getBytes());
		//sets view value
		SetTextViewEncryptresult(bytes);
		//saves encrypted text to internal file
		_streamHandler.SaveTextFile(FileStreamHandler.ENCRYPTED_TXT_FILENAME, bytes);
	}
}

encrypt_view

Информацию о том, как получить доступ к внутренним данным на устройстве под управлением Android можно получить в статье

http://www.codeproject.com/Articles/825304/Accessing-internal-data-on-Android-device.

Все операции с файлами выполняются через класс FileStreamHandler из пакета handlers. Существует несколько методов для работы с внутренними файлами, их чтения и записи.

Когда вы нажмете на кнопку Decrypt на MainActivity, сохраненное сообщение будет прочитано из файла и отображено во всплывающем окне.

/** 
 * Decrypt the message from saved local file content.
 * calls ShowToast() method
 **/
private void DecryptMessage() {
	byte[] fileContent = _streamHandler.ReadFromLocalFile(FileStreamHandler.ENCRYPTED_TXT_FILENAME);
	if (fileContent != null && fileContent.length > 0) {
		//preforms decryption of the fuile content              
		byte[] decrypted = _crypto.Decrypt(fileContent);
		//Creates new String instance of passed byte[] as UTF-8              
		String readableData = StringHandler.GetString(decrypted);
		String encrypted = StringHandler.GetString(fileContent);
		if (readableData != null & amp; & amp; encrypted != null) {
			//showing toast                     
			ShowToast(getString(R.string.msg_decrypted) + readableData, getString(R.string.msg_encrypted) + encrypted);
		}
	} else {
		//if file not exist or file content is empty              
		ShowToast(":(", "!");
	}
}

Если нажать на кнопку Decrypt, то можно увидеть что-то наподобие этого:

decrypt_toast

Такое сообщение означает, что цикл шифрования-расшифровки полностью пройден: вы ввели простой читаемый текст, который был зашифрован в не читаемый и на первый взгляд бессмысленный, сохранен в файл на локальном устройстве, после чего из файла был прочитан этот зашифрованный текст и расшифрован снова в читаемый.

В данном примере мы жестко указали ключевую фразу в виде константы в нашем классе, но это, конечно, далеко не оптимальное решение, в первую очередь потому, что добраться до значения константы можно очень легко, проведя декомпиляцию приложения. Поэтому стоит хранить пароли только в своей голове, или другом, не менее защищенном месте.

Расшифровка сообщения с помощью .NET-приложения

Давайте посмотрим, как расшифровать сообщение, зашифрованное в предыдущем примере на устройстве под управлением Android, с помощью других средств. Поскольку мы использовали стандартизованный алгоритм шифрования, мы можем расшифровать наше сообщение с помощью любого инструмента, поддерживающего этот стандарт. Попробуем написать программу под платформу .NET на языке C# для расшифровки текста. Мы будем забирать созданный ранее файл с устройства на компьютер, и загружать его в простое консольное приложение, созданное в среде Visual Studio.

Итак, давайте создадим файл и заберем его на компьютер через adb.

1. Запустим приложение, введем текст и нажмем Encrypt. Сообщение зашифровано, и сохранено на устройстве.

hello_from_android_msg

2. Теперь нам нужно выполнить несколько команд для получения доступа к файлу.

*Кроме того, если вы не поняли, посмотрите пожалуйста статью о механизме adb и о расширениях внутренних файлах андроида:

http://www.codeproject.com/Articles/825304/Accessing-internal-data-on-Android-device

adb_permissions_and_pull

Поскольку все мои adb команды были выполнены успешно, я могу просмотреть мой файл в каталоге загрузок:

downlload_pulled_txt

Теперь мы собираемся импортировать его в наше .Net консольного приложения.

*Не пытайтесь копировать текст, здесь мы имеем дело с двоичными данными и копирование ее в качестве строки может изменить его значение! Просто скопируйте файл как есть.

В нашем консольном приложение на C# присутствует CryptoHandler класс, который в основном такой же как на Android. Обратите внимание, что на .Net мы использовали два экземпляра ICryptoTransform как механизм дешифрования и шифрования. Примерно так же, как мы использовали в Android константы для указания нужного действия, так здесь мы использовали фабричный метод класса RijndaelManaged.

Если все реализовано правильно, то при запуске приложения вы увидите то же самое сообщение, которое видели при запуске приложения на устройстве под управлением операционной системы Android:

hello_from_android_console_application

Резюме

Мы зашифровали и расшифровали наши простые сообщения на Android и .Net приложения. Такое шифрование может использоваться для организации клиент-серверного взаимодействия (в случае если клиентом выступает Android-приложение, а серверная логика реализована на .Net — если дополнить её функционалом сетевой коммуникации приложений и другими необходимыми функциями.

Следует помнить, что нельзя зашифровывать все, что вы имеете на руках, особенно при работе с большими бинарными файлами. Это может снизить эффективность применения алгоритмов шифрования, особенно если вы защищаете данные, которыми никто не интересуется.

Лицензия

Эта статья, вместе со всеми связанными с ним файлами и исходным кодом, под лицензией код проекта открытой лицензии (CPOL).

Оригинальный текст статьи.