У меня есть клиент (приложение Android с Java) и сервер Node.js. Я хочу сгенерировать общий секрет, используя открытые ключи Диффи-Хеллмана клиента и сервера.
Сервер и клиент отправляют свои открытые ключи Диффи-Хеллмана друг другу через TCP (протокол управления передачей), чтобы они могли сгенерировать общий секрет. Когда сервер отправляет свой открытый ключ клиенту в виде строки base64, я могу успешно преобразовать эту строку в байты на стороне клиента.
Проблема в том, что при преобразовании байтов в экземпляр PublicKey
в отмеченной строке 9 я получаю ошибку:
java.security.spec.InvalidKeySpecException: спецификация закодированного ключа не распознана: не удалось создать последовательность из byte[]: длина DER более 4 байтов: 33
Вот мой Java-код:
public boolean generateSharedSecret() throws Exception {
byte[] serverDiffieHellmanPublicKey = receiveDiffieHellmanPublicKey();
Log.e("String Length", String.valueOf(Base64.encodeToString(serverDiffieHellmanPublicKey, Base64.DEFAULT).length())); // Outputs 90.
Log.e("Bytes Length", String.valueOf(serverDiffieHellmanPublicKey.length)); // Outputs 64.
KeyFactory keyFactory = KeyFactory.getInstance("DH");
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(serverDiffieHellmanPublicKey);
PublicKey dhPublicKeyFromServer = keyFactory.generatePublic(x509EncodedKeySpec); // Line 9
// ...
}
public byte[] receiveDiffieHellmanPublicKey() {
try {
Socket socket = new Socket("185.255.95.248", 1211);
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[1024];
int bytesRead = inputStream.read(buffer);
String keyString = new String(buffer, 0, bytesRead);
return Base64.decode(keyString, Base64.DEFAULT);
} catch (Exception ignore) {}
}
generateSharedSecret();
Вот мой код Node.js:
const net = require("net");
const crypto = require("crypto");
const dh = crypto.createDiffieHellman(512);
const serverDiffieHellman = crypto.generateDiffieHellman(dh.getPrime());
net.createServer((socket) => {
socket.on("connect", () => socket.write(serverDiffieHellman.getPublicKey().toString("base64"));
}).listen(1211, "185.255.95.248", () => console.info("TCP server is running..."));
Любая помощь будет приятно оценена. С уважением...
@Роберт, спасибо. Я смог решить проблему. Вы можете посмотреть мой ответ...
В конечном итоге мне удалось полностью решить проблему. Проблема вызвана разницей в формате открытых ключей Диффи-Хеллмана Node.js и Java. Открытый ключ в Node.js является необработанным, но в Java он не является необработанным, поскольку к открытому ключу Java добавляются некоторые дополнительные метаданные. Итак, я решил точную проблему, создав открытый ключ, используя класс BigInteger с шестнадцатеричной строкой простого числа, генератора и открытого ключа, поступающего с моего TCP-сервера.
Вот мой Java-код для решения проблемы:
Socket socket = new Socket("185.255.95.248", 1211);
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[16384];
int bytesRead = inputStream.read(buffer);
String serverResponse = new String(buffer, 0, bytesRead);
JSONObject jsonObject = new JSONObject(serverResponse);
BigInteger publicKey = new BigInteger(jsonObject.getString("public_key"), 16);
BigInteger prime = new BigInteger(jsonObject.getString("prime"), 16);
BigInteger generator = new BigInteger(jsonObject.getString("generator"), 16);
DHParameterSpec spec = new DHParameterSpec(publicKey, prime, generator);
KeyFactory keyFactory = KeyFactory.getInstance("DH");
PublicKey key = keyFactory.generatePublic(spec);
Log.d("Public Key", Base64.encodeToString(key.getEncoded(), Base64.DEFAULT));
Вот мой код Node.js для решения этой проблемы:
const net = require("net");
const crypto = require("crypto");
const dh = crypto.createDiffieHellman(512);
const serverDiffieHellman = crypto.createDiffieHellman(dh.getPrime());
net.createServer((socket) => {
socket.on("connect", () => socket.write(JSON.stringify({
public_key: serverDiffieHellman.getPublicKey("hex"),
prime: serverDiffieHellman.getPrime("hex"),
generator: serverDiffieHellman.getGenerator("hex")
})));
}).listen(1211, "185.255.95.248", () => console.info("TCP server is running..."));
Первая ошибка — использовать
inputStream.read(buffer);
и ожидать, что он прочитает все данные. Если вы хотите передать данные, сначала отправьте длину данных, чтобы получатель знал, сколько байтов нужно прочитать и использовать. Что касается формата ключа, я бы рекомендовал посмотреть его структуру ASN.1 в этом средстве просмотра: lapo.it/asn1js Затем сравните его с форматом, указанным в docs.oracle.com/javase/8/docs/api. /java/security/spec/…