Форум: "Прочее";
Текущий архив: 2009.02.22;
Скачать: [xml.tar.bz2];
Вниз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;
Скачать: [xml.tar.bz2];
Память: 0.52 MB
Время: 0.009 c