Показаны сообщения с ярлыком шифрование. Показать все сообщения
Показаны сообщения с ярлыком шифрование. Показать все сообщения

среда, 27 мая 2015 г.

Https в Android: делаем правильно

To что я опишу ниже - не открытие и не тайна. Это есть даже в официальной документации. Проблема в том, что большинство программистов под Android (и я в том числе) пришли из других, более старых платформ, где проблемы с ssl решались иначе или не возникали вообще.
Например, если сертификат вашего сервера почему-то не в порядке (например он самоподписной), то решение отключить проверку сертификата напрашивается само собой. Такое себе "быстрое решение". В результате вроде бы https и все круто, но на публичном wifi ваш клиент рискует пообщаться по "защищенному" каналу с мошенником. Или, к примеру, вы подключаетесь по ip к своему серверу, а в сертификате прописан домен. Отключаем проверку домена? Давайте не будем спешить. Есть решение которое в "особых" случаях не только не снижает безопасность соединения вашего приложения с сервером но и повышает её до воистину параноидального уровня.

понедельник, 15 апреля 2013 г.

Шифрование с открытым ключом в java

Давным-давно я как-то описывал несколько примеров использования различных алгоритмов шифрования в java. Решил вот продолжить полезную тему и описать простой пример реализации шифрования алгоритмом RSA.
В отличие от описанных ранее, этот алгоритм асимметричный: т.е. данные шифруются одним ключом (публичным) а расшифровываются другим (приватным).
Есть много преимуществ такого алгоритма перед симметричными, и одно из главных - нем не нужен защищённый канал для передачи секретного ключа. Публичный ключ из своей пары мы можем разместить где угодно ни о чём не беспокоясь: этим ключом расшифровать адресованные нам сообщения в принципе невозможно. И наоборот, наш "собеседник" также свободно распространяет свой публичный ключ и мы можем передавать ему свои сообщения не опасаясь, что их прочитает кто-нибудь посторонний.
Ключи для обмена данными обычно генерируются один раз. После генерации их можно сохранить в файлы. При установке соединения с кем-то мы сохраняем его публичный ключ и передаём ему свой. Если получено шифрованное сообщение, мы восстанавливаем свой приватный ключ и с его помощью выполняем расшифровку.

вторник, 4 октября 2011 г.

JSch: Работаем с удалённой машиной по ssh из java-приложения

Хочу рассказать об одной интересной находке: библиотеке JSch. Это реализация ssh соединения на java.  Не уникальная, само собой, но из полудесятка пересмотренных мной, именно она понравилась больше всего.
Возможности весьма впечатляют: аворизация по ключам, порт-форвардинг, соединение через прокси и ещё много чего. Также обрадовала простота работы с библиотекой. Вот, к примеру, реализация удалённой консоли в три десятка строк:

import com.jcraft.jsch.*;
import javax.swing.*;

public class Shell {

  public static void main(String[] arg) {
    try {
      JSch jsch = new JSch();
      String connstr = null;
      if (arg.length > 0) {
        connstr = arg[0];
      } else {
        connstr = JOptionPane.showInputDialog("Enter username:password@hostname", System.getProperty("user.name")+":<pass>"+"@localhost");
      }
      String user = connstr.substring(0, connstr.indexOf(':'));
      String pass = connstr.substring(connstr.indexOf(':')+1, connstr.indexOf('@'));
      String host = connstr.substring(connstr.indexOf('@') + 1);
      Session session = jsch.getSession(user, host, 22);
      session.setPassword(pass);
      session.setConfig("StrictHostKeyChecking", "no");
      session.connect();
      Channel channel = session.openChannel("shell");
      channel.setInputStream(System.in);
      channel.setOutputStream(System.out);
      channel.connect();
    } catch (Exception e) {
      System.out.println(e);
    }
  }
}

Тут мы делаем следующее:
  • Получаем от пользователя настройки подключения (как параметр вызова или диалоговым окном)
  • Создаём сессию и устанавливаем соединение
  • Создаём Channel (в данном случае для интерактивного исполнения команд, хотя есть и другие варианты)
  • "Заворачиваем" системные потоки ввода и вывода в полученный канал
  • наслаждаемся :)

четверг, 1 июля 2010 г.

Освоение криптографии - 3: SHA-1

Симметричное шифрование, которое я "освоил" в двух предыдущих постах иногда является излишним. Например, между приложениями есть https-канал и перехват данных нам не грозит. Но нам грозит другое: данные могут быть подделаны. Обычная схема защиты в таком случае сгенерировать сигнатуру и послать её вместе с данными. Сигнатура при этом получается как результат необратимого шифрования строки, составленной из передаваемых данных и пароля, известного получателю и отправителю. Получатель не расшифровывает сигнатуру (это невозможно), а создаёт её заново: запрос он получил, а пароль он знает. Совпадение сигнатуры с полученной подтверждает что данные не изменились при передаче, а отправитель знает верный пароль.
Ключевой элемент этой схемы - необратимое шифрование. Одним из самых популярных алгоритмов тут является SHA-1.
Итак, задача прежняя: на серверной стороне java. Код тут такой:
  1.     /**
  2.      * Хэширование sha1
  3.      * @param Param строка для хеширования
  4.      * @param Encode
  5.      * @return hex-представление хэш-строки
  6.      * @throws NoSuchAlgorithmException
  7.      * @throws UnsupportedEncodingException
  8.      */
  9.     public static String sha1(String Param) throws NoSuchAlgorithmException, UnsupportedEncodingException {
  10.         MessageDigest SHA = MessageDigest.getInstance("SHA-1");
  11.         SHA.reset();
  12.         SHA.update(Param.getBytes("UTF-8"), 0, Param.length());
  13.         byte[] sha1hash = SHA.digest();
  14.         return bytesToHexStr(sha1hash);
  15.     }
Тут полученный хэш мы сразу представляем в виде hex-строки. Для этого используем метод:
  1.     /**
  2.      * Преобразование байтового массива в hex-строку
  3.      * @param raw
  4.      * @return String
  5.      */
  6.     public static String bytesToHexStr(byte[] raw) {
  7.         char[] kDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
  8.         int length = raw.length;
  9.         char[] hex = new char[length * 2];
  10.         for (int i = 0; i < length; i++) {
  11.             int value = (raw[i] + 256) % 256;
  12.             int highIndex = value >> 4;
  13.             int lowIndex = value & 0x0f;
  14.             hex[i * 2 + 0] = kDigits[highIndex];
  15.             hex[i * 2 + 1] = kDigits[lowIndex];
  16.         }
  17.         return new String(hex);
  18.     }

Теперь аналогичное действие делаем во flex:
  1. var h:IHash = Crypto.getHash("sha1");
  2. var sidn:String = Hex.fromArray(h.hash(Hex.toArray(Hex.fromString("your_secret_string"))));
Тут, как видите, всё совсем просто. Конечно, если использовать правильные библиотеки :)
Я, как и в предыдущих случаях использовал библиотеку as3crypto

вторник, 29 июня 2010 г.

Освоение криптографии - 2: алгоритм Blowfish

Использование DES-шифрования, описанного в предыдущем посте весьма удобно, но оправдано далеко не всегда. Например, если передаваемые данные нужно защитить "с гарантией" или ключ меняется достаточно редко, использовать алгоритм такой стойкости уже нежелательно. Тем более, что смена алгоритма при нынешнем уровне развития платформ не требует больших усилий. Альтернативой устаревшему уже в начале века DES выбираем Blowfish. Он достаточно быстрый и, также являясь симметричным алгоритмом, не требует принципиальной престройки взаимодействия клиента с сервером.
Для реализации шифрования используем те же библиотеки, что и раньше. В случае flex-части вообще изменится только одна строчка кода:
var cipher:ICipher = Crypto.getCipher("blowfish-ecb", key);
В случае java всё немного сложнее...
Во-первых
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("Blowfish");
уже не сработает. SecretKeyFactory не может порождать ключи такого типа. Это решается достаточно просто, ключи делаем так:
key = new SecretKeySpec(keystr.getBytes(), "Blowfish");
Тут keystr - наш секретный пароль. Остальной код оставляем как в прошлом посте. Компилируем, запускаем, и... получаем Exception: Illegal key size. Очевидный путь наращивания длины ключа тут не поможет. Не в моём случае, по крайней мере. Помогло другое: Скачиваем отсюда так называемые "Unlimited Strength Jurisdiction Policy Files". Это 2 jar-ника, которыми можно заменить аналогичные файлы в java-машине, "ослабив" при этом политику безопасности до нужного нам уровня. Файлы, которые следует заменить в моём случае лежат тут: /usr/lib/jvm/java-6-sun/jre/lib/security. Старые, после окончания экспериментов лучше вернуть на место ;)

Освоение криптографии - 1: алгоритм DES

До сих пор как-то не приходилось серьёзно знакомиться с шифрованием данных. Https защищал мои приложения не только от хакеров, но и от необходимости о них думать :). Но вот возникла задача обеспечить защиту трафика "вручную". На сервере Java, клиент - flex. Первый алгоритм с которым я познакомился (практически случайно) - DES. Алгоритм старый, имеет хорошие реализации практически на всех языках. Криптостойкость не абсолютная, как говорит wikipedia, ломается прямым перебором за 2^55 итераций. Правда для этого нужна пара открытой и закриптованной строк. Впрочем, в нашем случае его криптостойкость достаточна. 
Для серверной части используем классы javax.crypto.*. Код примерно такой:


  1. public class DesCrypter {
  2.  
  3.     Cipher ecipher;
  4.     Cipher dcipher;
  5.  
  6.     /**
  7.      * Конструктор
  8.      * @param key секретный ключ алгоритма DES
  9.      * @throws NoSuchAlgorithmException
  10.      * @throws NoSuchPaddingException
  11.      * @throws InvalidKeyException
  12.      */
  13.     public DesCrypter(String keystr) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidKeySpecException {
  14.         DESKeySpec desKeySpec = new DESKeySpec(keystr.getBytes());
  15.         SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
  16.         SecretKey key = keyFactory.generateSecret(desKeySpec);
  17.         ecipher = Cipher.getInstance("DES");
  18.         dcipher = Cipher.getInstance("DES");
  19.         ecipher.init(Cipher.ENCRYPT_MODE, key);
  20.         dcipher.init(Cipher.DECRYPT_MODE, key);
  21.     }
  22.  
  23.     /**
  24.      * Функция шифровнаия
  25.      * @param str строка открытого текста
  26.      * @return зашифрованная строка в формате Base64
  27.      */
  28.     public String encrypt(String str) throws UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException {
  29.         byte[] utf8 = str.getBytes("UTF8");
  30.         byte[] enc = ecipher.doFinal(utf8);
  31.         return new sun.misc.BASE64Encoder().encode(enc);
  32.     }
  33.  
  34.     /**
  35.      * Функция расшифрования
  36.      * @param str зашифрованная строка в формате Base64
  37.      * @return расшифрованная строка
  38.      */
  39.     public String decrypt(String str) throws IOException, IllegalBlockSizeException, BadPaddingException {
  40.         byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str);
  41.         byte[] utf8 = dcipher.doFinal(dec);
  42.         return new String(utf8, "UTF8");
  43.     }
  44.  
  45. }

На клиентской стороне используем библиотеку as3crypto. С её помощью делаем реализацию шифрования на ActionScript 3.5:


  1. /**
  2. * @param string Cтрока для зашифровки
  3. * @param passString Пароль
  4. */
  5. public static function encryptString(string:String, passString:String):String {
  6.     var key:ByteArray        = Hex.toArray(Hex.fromString(passString+postfix));
  7.     var plainText:ByteArray    = Hex.toArray(Hex.fromString(string));
  8.     var cipher:ICipher = Crypto.getCipher("simple-des-ecb", key);
  9.     cipher.encrypt(plainText);
  10.     return Base64.encodeByteArray(plainText);
  11. }
  12.  
  13. /**
  14. * @param string Cтрока для расшифровки
  15. * @param passString Пароль
  16. */
  17. public static function decryptString(string:String, passString:String):String {
  18.     var key:ByteArray        = Hex.toArray(Hex.fromString(passString+postfix));
  19.     var plainText:ByteArray    = Hex.toArray(string);
  20.     var cipher:ICipher = Crypto.getCipher("simple-des-ecb", key);
  21.     cipher.decrypt(plainText);
  22.     return(Hex.toString(Hex.fromArray(plainText)));
  23. }

Никаких неожиданностей не было, всё заработало сразу. Единственная особенность: функции шифрования работают с битовыми массивами, а передаём мы строки. При создании строки играет роль (часто губительную) кодировка. Чтобы избежать неприятностей с ней, везде используем Base64-кодирование результата.