[英]Why is my Java socket receiving an array size larger than what was sent?
我正在創建一個客戶端/服務器程序來使用 256 位 AES 執行加密。 我從 ECDH 派生我的密鑰。 我發送我用來表示我的各種鍵和字符串的字節 arrays 的大小。 我遇到的問題是,當我嘗試將我的加密字符串的大小從我的客戶端發送到我的服務器時,我的服務器說我發送的大小比我實際發送的要大得多。 發送大小適用於需要發送的所有其他字節 arrays。 客戶端發送的加密字符串大小為 16 字節。 服務器收到一個大小為 276032497 字節的 integer。 我檢查過我實際上是從客戶端發送 16 個字節。
知道問題可能是什么嗎?
服務器代碼:
//generate public key for server
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
kpg.initialize(256);
KeyPair kp = kpg.generateKeyPair();
byte[] ourPk = kp.getPublic().getEncoded();
String format = kp.getPublic().getFormat();
int ourPkLength = ourPk.length;
int arrSize;
//send client our pk
out.writeInt(ourPkLength);
out.write(ourPk);
System.out.println("sent PK!");
//receive pk from client
arrSize = fromClient.readInt();
byte[] otherPk = new byte[arrSize];
fromClient.read(otherPk);
System.out.println("recived client PK!");
KeyFactory kf = KeyFactory.getInstance("EC");
X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(otherPk);
PublicKey otherPublicKey = kf.generatePublic(pkSpec);
//Perform key agreement
KeyAgreement ka = KeyAgreement.getInstance("ECDH");
ka.init(kp.getPrivate());
ka.doPhase(otherPublicKey, true);
// Send shared secret
byte[] sharedSecret = ka.generateSecret();
// Derive a key from the shared secret and both public keys
MessageDigest hash = MessageDigest.getInstance("SHA-256");
hash.update(sharedSecret);
// Simple deterministic ordering
List<ByteBuffer> keys = Arrays.asList(ByteBuffer.wrap(ourPk), ByteBuffer.wrap(otherPk));
Collections.sort(keys);
hash.update(keys.get(0));
hash.update(keys.get(1));
byte[] derivedKey = hash.digest();
System.out.println("derived key: " + derivedKey + " length: " + derivedKey.length);
//Convert byte [] to secret key
//Define cipher
SecretKeySpec symmetricKey = new SecretKeySpec(derivedKey, 0, 32, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, symmetricKey, new IvParameterSpec(new byte[16]));
//receive encrypted message from client and try to decrypt.
arrSize = fromClient.readInt();
System.out.println("array size sent: " + arrSize);
byte[] decryptArr = new byte[arrSize];
fromClient.read(decryptArr);
System.out.println("Recieved encrypted string: " + decryptArr + " length: " + decryptArr.length);
String decryptStr = Base64.getEncoder().encodeToString(cipher.doFinal(decryptArr));
System.out.println("Decrypted String: " + decryptStr);
客戶端代碼:
//generate public key for client
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
kpg.initialize(256);
KeyPair kp = kpg.generateKeyPair();
byte[] ourPk = kp.getPublic().getEncoded();
//String format = kp.getPublic().getFormat();
int ourPkLength = ourPk.length;
int arrSize;
//Receive generated public key from the Server
arrSize = fromServ.readInt();
byte[] otherPk = new byte[arrSize];
fromServ.read(otherPk);
System.out.println("recived server PK!");
//Send the server our public key
out.writeInt(ourPkLength);
out.write(ourPk);
System.out.println("sent PK!");
KeyFactory kf = KeyFactory.getInstance("EC");
X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(otherPk);
PublicKey otherPublicKey = kf.generatePublic(pkSpec);
//Perform key agreement
KeyAgreement ka = KeyAgreement.getInstance("ECDH");
ka.init(kp.getPrivate());
ka.doPhase(otherPublicKey, true);
// Generate a shared secret
byte[] sharedSecret = ka.generateSecret();
// Derive a key from the shared secret and both public keys
MessageDigest hash = MessageDigest.getInstance("SHA-256");
hash.update(sharedSecret);
// Simple deterministic ordering
List<ByteBuffer> keys = Arrays.asList(ByteBuffer.wrap(ourPk), ByteBuffer.wrap(otherPk));
Collections.sort(keys);
hash.update(keys.get(0));
hash.update(keys.get(1));
byte[] derivedKey = hash.digest();
System.out.println("derived key: " + derivedKey + " length: " + derivedKey.length);
//Convert the derivedkey from a byte array to a Secret key Spec of type AES
SecretKeySpec secretKey = new SecretKeySpec(derivedKey, 0, 32, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(new byte[16]));
String plainText = "Testing!";
byte[] cipherText = cipher.doFinal(plainText.getBytes());
System.out.println("Encrypted str: " + cipherText + " length: "+ cipherText.length);
//Send encrypted string to Server
int len = cipherText.length;
System.out.println("length: " + len);
out.write(len);
out.write(cipherText);
System.out.println("Sent encrypted string!");
在您的客戶中,您寫道: out.write(len)
。 您的代碼片段沒有解釋out
是什么,但我猜.write(someIntValue)
是來自java.io.OutputStream
的傳遞,它具有該方法( .write(int)
)。 問題是,這會寫入一個 byte ,刪除 int 中的所有位,除了底部的 8 位。
服務器代碼中的匹配調用是:
arrSize = fromClient.readInt();
這不是InputStream
的調用(我猜你有一些 class 擴展 InputStream 並添加這些),大概是readInt
所做的,讀取4個字節,並通過假設 Big Endian 排序將它們重組為單個 java int
。
因此,您從客戶端發送 1 個字節,然后是字符串,但服務器將讀取 4 個字節的長度:該 1 個字節(客戶端發送的實際長度)加上字符串的前 3 個,並嘗試解釋作為一個長度,導致數字大相徑庭。
276032497 是一個數字這一事實,如果通過大端順序放入字節,則以一個值為 16 的字節開頭,這正是您發送的長度,強烈暗示這是您的問題。
修復似乎相當微不足道。 把out.write(len)
變成out.writeInt(len)
。 而且如果你要做這樣的字節級協議,你需要一個比¯\_(ツ)_/¯ 更好的測試和調試計划,我想我會問 StackOverflow。 這可能就是為什么大多數人使用不同的解決方案來處理原始字節協議(所以,調查一下 ProtoBuf 和朋友)。 至少,讓實際的 pipe 成為一個可插拔的概念,這樣您就可以插入一個不加密任何東西的虛擬 pipe,這樣您就可以通過網絡傳輸的字節來查找此類問題; 這不太可能是您最后一次不匹配服務器和客戶端的代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.