Обмен симметричными ключами между клиентом и сервером с использованием открытого ключа

Я хочу обмениваться симметричными ключами между клиентской и серверной программой, работающей через Java RMI.

Мой сервер делает открытый ключ:

KeyPairGenerator keyGen = null;
try {
    keyGen = KeyPairGenerator.getInstance("DSA", "SUN");
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
} catch (NoSuchProviderException e) {
    e.printStackTrace();
}
KeyPair pair = keyGen.generateKeyPair();
this.priv = pair.getPrivate();
this.pub = pair.getPublic();

Я отправляю клиенту открытый ключ. Клиент создаст шифр, используя открытый ключ сервера. Я хочу использовать этот шифр для шифрования объекта SealedObject, инкапсулирующего симметричный ключ, созданного клиентом, и отправки его на сервер.

//create cipher using server's public key
Cipher cipher = null;
try {
    cipher = Cipher.getInstance(serverKey.getAlgorithm(), "SUN");
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
} catch (NoSuchPaddingException e) {
    e.printStackTrace();
} catch (NoSuchProviderException e) {
    e.printStackTrace();
}
try {
    cipher.init(Cipher.ENCRYPT_MODE, serverKey);
} catch (InvalidKeyException e) {
    e.printStackTrace();
}

Но когда я запускаю программу, у меня возникает исключение при инициализации шифра:

java.security.NoSuchAlgorithmException: No such algorithm: DSA
at javax.crypto.Cipher.getInstance(Cipher.java:646)
at javax.crypto.Cipher.getInstance(Cipher.java:568)
at Client.main(Client.java:91)

Я не понимаю, почему я получаю это исключение NoSuchAlgorithm. Я не пойму этого, если сделаю открытый ключ с помощью RSA, а не DSA, но RSA дает мне:

javax.crypto.IllegalBlockSizeException: 
Data must not be longer than 117 bytes

Итак, что я должен использовать для безопасной отправки запечатанного объекта, содержащего мой симметричный ключ?


person Drok    schedule 24.11.2015    source источник
comment
Разве вы не можете использовать вместо него стандартный SSLServerSocket? Примерно это делает TLS за вас   -  person zapl    schedule 24.11.2015
comment
Я не могу. Это упражнение, которое мне нужно реализовать с использованием симметричных ключей, но сначала я должен безопасно поделиться ключом с сервером.   -  person Drok    schedule 24.11.2015
comment
Такого алгоритма нет: здесь объясняется DSA: crypto.stackexchange.com/questions/2585/, и причина ошибки ограничения размера блока заключается в том, что RSA также не является обычным блочным шифром: stackoverflow.com/questions/685470/ или security.stackexchange.com/questions/1878/ и поэтому может использоваться только в определенных пределах, например в en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange   -  person zapl    schedule 25.11.2015
comment
Обратите внимание, что все это бессмысленно, если у клиента нет способа проверить, действительно ли открытый ключ сервера принадлежит серверу.   -  person Kevin Krumwiede    schedule 25.11.2015


Ответы (1)


Как отмечает @zapl, DSA не является алгоритмом шифрования, и RSA может шифровать данные только с размером меньше размера ключа за вычетом небольших в основном фиксированных накладных расходов (11 байтов для PKCS1, который используется по умолчанию, и, следовательно, вы использовали; немного больше для OAEP).

Хотя не в вашем опубликованном коде, я уверен, что проблема в том, что вы пытаетесь запечатать объект SecretKey. Шифрование работает только с байтовыми (или битовыми) последовательностями, представленными в Java байтовыми массивами или иногда частями массивов, поэтому SealedObject фактически сериализует объект, который вы ему передаете, в байты и шифрует эти байты, и, наоборот, дешифрует эти байты и десериализует их для повторной обработки. сформировать объект. Сериализация Java имеет некоторые накладные расходы, и различные типы Key, в частности, организованы в иерархию, которая усугубляет это. Например, сериализация SecretKey из KeyGenerator.getInstance("TDES") составляет 282 байта, что слишком велико для RSA-1024, который, как указано в вашем исключении, может составлять только 117 байтов.

Вместо этого запечатайте только байты значения ключа, полученного SecretKey.getEncoded(). На приемнике (сервере) поместите байты обратно в SecretKeySpec (с правильным алгоритмом; отправьте это также, если не зафиксировано заранее), и вы можете фактически использовать его как Key для симметричного, даже не прогоняя его через фабрику (в отличие от асимметричных ключей, которые иметь больше структуры). Даже простой byte[] имеет некоторые накладные расходы на сериализацию, но этого недостаточно, чтобы вызвать здесь проблему.

person dave_thompson_085    schedule 25.11.2015