Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2009.02.22;
Скачать: CL | DM;

Вниз

Java & MS CryptoAPI   Найти похожие ветки 

 
Eraser ©   (2008-12-21 03:01) [0]

История такая. Имеется сервер и клиенты, все написано на Делфи. Канал связи между сервером и клиентом защищен с помощью RSA с использованием MS CryptoAPI.
Необходимо написать клиента к серверу на Java. Соответственно должно устанавливаться и RSA соединения, а потом и предача данных, зашифрованных симметричным алгоритмом.

Есть ли какие-либо готовые решения, для обеспечения совместимости ключей шифрования CryptoAPI <==> Java?


 
iZEN   (2008-12-21 17:47) [1]

JAAS


 
Eraser ©   (2008-12-22 03:08) [2]

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


 
Eraser ©   (2008-12-22 03:14) [3]

+ оказалось что Java не поддерживает 256 битные AES ключи. Точнее ключ генерирует, но когда доходит дело до шифрования им
      KeyGenerator kgen = KeyGenerator.getInstance("AES");
      kgen.init(256); // 192 and 256 bits may not be available (пример с сайта sun)

получаем
java.security.InvalidKeyException: Illegal key size or default parameters

но это известная проблема, посоветовали http://www.bouncycastle.org/java.html сейчас пробую как раз.


 
Eraser ©   (2008-12-22 18:17) [4]

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

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

сервер (MS CryptoAPI) генерирует открытый и закрытый RSA ключи. отсылает клиенту (Java) открытый ключ.
клиент принимает ключ. на самом деле это не ключ в чистом виде, а Public Key BLOB, структуру которого можно посмотреть тут http://msdn.microsoft.com/en-us/library/aa387459(VS.85).aspx . В приниципе там все понятно. Выделяю из этого BLOB"а 4 байтную экспоненту и 256 байтный (2048bit) modulus. Реверсирую у обоих порядок байт, чтобы привеси их к виду BigIndian.

далее нужно сформировать симметричный ключ, а на основе его, т.к. Simple Key BLOB ( http://msdn.microsoft.com/en-us/library/aa387765(VS.85).aspx ). Генерирую симметричный ключ 256 битный AES ключ.
           // Генерируем AES ключ.
           int skLength = 256;
           KeyGenerator kgen = KeyGenerator.getInstance("AES");
           kgen.init(skLength);

           // Generate the secret key specs.
           SecretKey skey = kgen.generateKey();
           byte[] secretKeyData = skey.getEncoded();

далее компоную Simple Key BLOB, по точно такому алгоритму, как привел на ссылке.

но при отсылке этого ключа не сервере в функции CryptImportKey (которая импортирует симметричный ключ, расшифровывая его закрытым ключем) возникает ошибка NTE_BAD_FLAGS = HRESULT($80090009);

           // Формируем BLOB совместимый с MS CryptoAPI.
           // По материалам с http://msdn.microsoft.com/en-us/library/aa387765(VS.85).aspx

           /* Компонуем заголовок. */
           byte[] blobHeader = new byte[12];
           blobHeader[0] = 1; // The key is a session key.
           blobHeader[1] = 2; // Версия. Далее 2 байта не используются.
           // Идентификатор алгоритма (4 байта в перевернутом виде) CALG_AES_256 0x00006610.
           blobHeader[4] = (byte) 0x10;
           blobHeader[5] = (byte) 0x66;
           blobHeader[6] = 0;
           blobHeader[7] = 0;
           // Идентификатор алгоритма открытого RSA ключа, котором зашифрован данный ключ.
           blobHeader[8] = 0;
           blobHeader[9] = (byte) 0xA4;
           blobHeader[10] = 0;
           blobHeader[11] = 0;

           // Компонуем PKCS #1 блок, раный по размеру длине открытого ключа.
           int pubKeySize = 256;
           byte[] blobPKCS = new byte[pubKeySize];
           blobPKCS[0] = 0; // Ноль.
           blobPKCS[1] = 0x02; // The PKCS block type (0x02).
           // Вычисляем длину заполнителя.
           int paddingSize = pubKeySize - 2 - (skLength / 8) - 1;
           int paddingStart = 3;
           int j = 0;
           // Заполняем случайными данными.
           for (int i = 0; i < paddingSize; i++) {
               // Реальный индекс элемента.
               j = paddingStart + i;

               // Заполняем случайным значением.
               //blobPKCS[j] = 0;
               blobPKCS[j] = (byte) ROMRandom.randomRange(1, 255);
           }
           // Ноль.
           blobPKCS[paddingStart + paddingSize] = 0;
           // Начало ключа.
           int keyStart = paddingStart + paddingSize;

           //secretKeyData[secretKeyData.length - 2] = 77;
           //secretKeyData = reverseArray(secretKeyData);

           // Копируем данные ключа.
           for (int i = 0; i < secretKeyData.length; i++) {
               // Индекс в PKCS блоке.
               j = keyStart + i;
               blobPKCS[j] = secretKeyData[i];
           }
           writeKeyBytesToFile(blobPKCS, "d:\\java_raw_PKCS.dat");
           // Реверсируем PKCS.
           //blobPKCS = reverseArray(blobPKCS); // !!!

           // Зашифруем PKCS открытым ключем.
           BigInteger data = new BigInteger(
                   1,
                   blobPKCS);

           // Perform RSA encryption:
           // ciphertext = plaintext^exponent % modulus.
           BigInteger cipherText = data.modPow(
                   exponent,
                   modulus);

           // Reverse the generated ciphertext.
           byte[] encryptedPKCS = cipherText.toByteArray();
           encryptedPKCS = reverseArray(encryptedPKCS); // !!!
           writeKeyBytesToFile(encryptedPKCS, "d:\\java_enc_PKCS.dat");

           //encryptedPKCS[encryptedPKCS.length - 5] = 77;

           // Объединим заголовок и зашифрованный PKCS.
           byte[] fullSecretKeyData = new byte[blobHeader.length + encryptedPKCS.length];

           for (int i = 0; i < blobHeader.length; i++) {
               fullSecretKeyData[i] = blobHeader[i];
           }

           for (int i = 0; i < encryptedPKCS.length; i++) {
               fullSecretKeyData[blobHeader.length + i] = encryptedPKCS[i];
           }

           writeKeyBytesToFile(fullSecretKeyData, "d:\\java_secret_simpleblob.dat");
// Далее идет код отсылки на сервер

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


 
Eraser ©   (2008-12-22 20:51) [5]

проблема решена, немного не правильно формировал Simple Key BLOB.
Вот корректный кусок кода для связки (RSA 2048 + AES 256):
           // Обмен ключами.
           // Размер открытого ключа.
           int iSize = input.readInt();
           byte[] publicKeyData = new byte[iSize];

           input.read(publicKeyData, 0, iSize);

           // Экспонента ключа.
           int exponentStart = 8 + 4 + 4;
           byte[] exponentBytes = Arrays.copyOfRange(publicKeyData,
                   exponentStart, exponentStart + 4);

           //writeKeyBytesToFile(exponentBytes, "d:\\java_pkey_exponent.dat");
           exponentBytes = reverseArray(exponentBytes);
           BigInteger exponent = new BigInteger(1, exponentBytes);
           //System.out.println("exponent: " + exponent);

           // Modulus ключа.
           int modulusStart = 8 + 12;
           byte[] modulusBytes = Arrays.copyOfRange(publicKeyData,
                   modulusStart, publicKeyData.length);

           //writeKeyBytesToFile(modulusBytes, "d:\\java_pkey_modulus.dat");
           modulusBytes = reverseArray(modulusBytes);
           BigInteger modulus = new BigInteger(1, modulusBytes);

           //System.out.println("modulus length: " + modulusBytes.length);
           //writeKeyBytesToFile(publicKeyData, "d:\\java_pkey.dat");

           // Генерируем AES ключ.
           int skLength = 256;
           KeyGenerator kgen = KeyGenerator.getInstance("AES");
           kgen.init(skLength);

           // Generate the secret key specs.
           SecretKey skey = kgen.generateKey();
           byte[] secretKeyData = skey.getEncoded();
           //writeKeyBytesToFile(secretKeyData, "d:\\java_secret_aes256.dat");

           // Формируем BLOB совместимый с MS CryptoAPI.
           // По материалам с http://msdn.microsoft.com/en-us/library/aa387765(VS.85).aspx

           // Компонуем заголовок.
           byte[] blobHeader = new byte[12];
           blobHeader[0] = 1; // The key is a session key.
           blobHeader[1] = 2; // Версия. Далее 2 байта не используются.
           // Идентификатор алгоритма (4 байта в перевернутом виде) CALG_AES_256 0x00006610.
           blobHeader[4] = (byte) 0x10;
           blobHeader[5] = (byte) 0x66;
           blobHeader[6] = 0;
           blobHeader[7] = 0;
           // Идентификатор алгоритма открытого RSA ключа, котором зашифрован данный ключ.
           blobHeader[8] = 0;
           blobHeader[9] = (byte) 0xA4;
           blobHeader[10] = 0;
           blobHeader[11] = 0;

           // Компонуем PKCS #1 блок, раный по размеру длине открытого ключа.
           int pubKeySize = 256;
           byte[] blobPKCS = new byte[pubKeySize];
           blobPKCS[0] = 0; // Ноль.
           blobPKCS[1] = 0x02; // The PKCS block type (0x02).
     
           // Вычисляем длину заполнителя.
           int paddingSize = pubKeySize - 2 - (skLength / 8) - 1;
           int paddingStart = 2;
           int j = 0;
           // Заполняем случайными данными.
           for (int i = 0; i < paddingSize; i++) {
               // Реальный индекс элемента.
               j = paddingStart + i;

               // Заполняем случайным значением.
               blobPKCS[j] = (byte) ROMRandom.randomRange(1, 255);
           }
           // Ноль.
           blobPKCS[paddingStart + paddingSize] = 0;
           // Начало ключа.
           int keyStart = paddingStart + paddingSize + 1;

           // Копируем данные ключа.
           for (int i = 0; i < secretKeyData.length; i++) {
               // Индекс в PKCS блоке.
               j = keyStart + i;
               blobPKCS[j] = secretKeyData[i];
           }

           blobPKCS = reverseArray(blobPKCS);
           //writeKeyBytesToFile(blobPKCS, "d:\\java_raw_PKCS.dat");
           
           // Реверсируем PKCS.
           blobPKCS = reverseArray(blobPKCS); // !!!

           // Зашифруем PKCS открытым ключем.
           // http://msdn.microsoft.com/en-us/library/cc240810(PROT.10).aspx
           BigInteger data = new BigInteger(
                   1,
                   blobPKCS);

           // Perform RSA encryption:
           // ciphertext = plaintext^exponent % modulus.
           BigInteger cipherText = data.modPow(
                   exponent,
                   modulus);

           // Reverse the generated ciphertext.
           byte[] encryptedPKCS = cipherText.toByteArray();
           encryptedPKCS = reverseArray(encryptedPKCS); // !!!
           //writeKeyBytesToFile(encryptedPKCS, "d:\\java_enc_PKCS.dat");

           // Объединим заголовок и зашифрованный PKCS.
           byte[] fullSecretKeyData = new byte[blobHeader.length + encryptedPKCS.length];

           for (int i = 0; i < blobHeader.length; i++) {
               fullSecretKeyData[i] = blobHeader[i];
           }

           for (int i = 0; i < encryptedPKCS.length; i++) {
               fullSecretKeyData[blobHeader.length + i] = encryptedPKCS[i];
           }

           //writeKeyBytesToFile(fullSecretKeyData, "d:\\java_secret_simpleblob.dat");

           // Отсылаем симметричный ключ.
           output.writeInt(fullSecretKeyData.length);
           output.write(fullSecretKeyData);

           // Считываем тип авторизации.
           int iAuthKind = input.readInt();
           if (iAuthKind != 1) {
               // Поддерживается только авторизация паролем.
               return;
           }

           // Зашифруем пароль симметричным ключем.
           byte[] passwordData = fConnection.getPassword().getBytes();
           SecretKeySpec skeySpec = new SecretKeySpec(secretKeyData, "AES");

           Cipher cipher = Cipher.getInstance("AES");
           cipher.init(Cipher.ENCRYPT_MODE, skeySpec);

           byte[] encrypted =
                   cipher.doFinal(passwordData);
           
           // Отсылаем зашированный пароль.
           output.writeInt(encrypted.length);
           output.write(encrypted);
           
           // Правильный или нет пароль.
           boolean passwordCorrect = input.readInt() == 1;

           System.out.println("password state: " + passwordCorrect);
           System.out.println("finished!");



Страницы: 1 вся ветка

Текущий архив: 2009.02.22;
Скачать: CL | DM;

Наверх




Память: 0.53 MB
Время: 0.014 c
2-1231835371
Кирил
2009-01-13 11:29
2009.02.22
Как узнать - сколько дней в месяце?


2-1231786870
JustForQuestion
2009-01-12 22:01
2009.02.22
GRID + ProgressBar


4-1203884779
art36
2008-02-24 23:26
2009.02.22
устройство веб камеры + видео поток + delphi


4-1205755686
rosl
2008-03-17 15:08
2009.02.22
закрыть файл


2-1231570514
RUBEY
2009-01-10 09:55
2009.02.22
Прием параметров через командную строку