我有一个Python服务器脚本和一个Java客户端应用程序,目标是通过套接字加密数据。数据始终是一个字符串。 (我是Java新手)
应该发生什么:
Python脚本创建一个套接字并侦听连接,接受新连接,以纯文本形式发送IV用于AES CBC PKCS5Padding,设置密码,然后从Java应用程序接收加密数据(解密,然后在终端打印出来) ,然后将加密数据发送到Java应用程序,然后关闭连接。
Java应用程序连接到python套接字,接收IV,设置相同的密码,加密一个简单的填充字符串,将其发送到python服务器,然后等待回复,解密回复并将其打印到屏幕。然后连接关闭。
实际发生的事情
服务器设置套接字,java客户端连接并接收IV,两者都设置相同的加密/解密密码,然后java客户端发送加密的填充字符串。 Python服务器成功接收密文,解密字符串和unpad,字符串正确显示。然后,服务器将加密的填充字符串发送到java客户端,客户端接收字符串,然后在解密时由于BadPaddingException而失败。
根据RFC,我已经验证了python端的PKCS5 Padding是正确的。我尝试过其他填充方法(零填充等),但没有任何工作。我还尝试了几种不同的字符串编码,我也尝试了M2Crypto(现在使用pycrypto)等等。我尝试在客户端的套接字上使用CipherInputStream,结果相同。
没有任何作用。我仍然认为Python和Java之间可能存在编码问题,但我只是为了解释为什么Java客户端在解密时失败了。
Python服务器:
#!/usr/bin/env python
import os, time, threading, json
from socket import *
import sys, base64
from Crypto.Cipher import AES
from socket import *
### Settings
serverHost = '' # localhost
serverPort = 5555 # non-reserved
masterkey = 'mysecretpassword'
BLOCK_SIZE = 16 # Block-size for cipher (16, 24 or 32 for AES)
#PADDING = '{' # block padding for AES
# PKCS5 Padding
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
unpad = lambda s : s[0:-ord(s[-1])]
# generate new IV's - why you ask? ... just look at WEP
def createCipher(key):
iv = os.urandom(16)
return (AES.new(key, AES.MODE_CBC, iv), iv)
class IRCTalkServer(threading.Thread):
def __init__(self, masterkey, host='', port=5555):
try:
self.sockobj = socket(AF_INET, SOCK_STREAM) # create TCP socket obj
self.sockobj.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # make port reusable
self.sockobj.bind((host, port)) # bind socket to port
self.sockobj.listen(5) # listen, allow 5 pending connects
if host == '': host='localhost'
print "Started server on %s:%d" % (host, port)
except:
print "Error starting server"
sys.exit(0)
threading.Thread.__init__(self)
self.die = False # loop killer
self.masterkey = masterkey
self.host = host
self.port = port
def run(self):
try:
while True and not self.die: # infinite loop unless called to quit
connection, address = self.sockobj.accept()
print 'Server connected by ', address
# generate cipher and get first IV - prevent same WEP hacks
#paddedKey = keypad(masterkey)
#print "master key:", masterkey, " - padded key:", paddedKey, " - diff key:", keypad('test')
self.cipher, self.iv = createCipher(masterkey)
print "IV:", self.iv.encode('hex'), "Sending IV: ", repr(self.iv)
connection.send("%s%s" % (self.iv.encode('hex'),'\n')) # send iv first
while True and not self.die: # read from client
print "waiting for client"
data = connection.recv(10485760)
print "recieved from client:", repr(data.rstrip())
if not data:
print "NO DATA"
break
dataCheck, JSON = self.decryptData(data.rstrip())
print 'Recieved from Client:', repr(JSON)
#print 'Recieved from Client:', repr(data)
#if dataCheck:
#senddata = self.encryptData('SUCCESS')
#print "Size of compresseddata:", len(senddata)
#connection.send(senddata)
#morestuff = 'abc123def456'*int(10000/9)
#senddata = self.encryptData(['test', morestuff, 'test1', {'key': 'value'}, 2223])
#print "Size of compresseddata:", len(senddata)
#connection.send(senddata)
print "Sending reply to client..."
senddata = self.encryptData('test')
print "reply data:", repr("%s%s" % (senddata, "\n"))
connection.send("%s%s" % (senddata, "\n"))
#connection.send("Hello back mr android!")
#successReply = connection.recv(256) # only for "END REQUEST"
break
print "Closing connection... \n\n"
connection.close()
except:
print "exception on try loop"
pass # an error occurred, just drop it, the client will try again later
def encryptData(self, plaintext):
# convert to json string, pad the string, then encrypt, then compress
#JSON = json.dumps(plaintext, separators=(',',':'))
JSON = plaintext
#print "Size of JSON:", len(JSON)
ciphertext = pad(unicode(JSON))
print "padded text:", repr(ciphertext)
ciphertext = self.cipher.encrypt(ciphertext)
print "ciphertext:", repr(ciphertext), "|", len(ciphertext), "|", ciphertext
ciphertext = ciphertext.encode('hex').upper()
print "hexified text:", repr(ciphertext)
#ciphertext = self.cipher.encrypt(pad(JSON)).encode('hex').upper()
print "Size of ciphertext:", len(ciphertext)
return ciphertext
def decryptData(self, ciphertext):
try:
# decompress data to ciphertext, decrypt, convert to json
print "length of ciphertext:", len(ciphertext)
ptext = ciphertext.decode('hex')
print "unhexifed:", repr(ptext)
ptext = self.cipher.decrypt(ptext)
print "decrypted:", repr(ptext)
ptext = unpad(ptext)
print "unpadded:", repr(ptext)
#ptext = unpad(self.cipher.decrypt(ciphertext.decode('hex')))
print "ptext: ", repr(ptext)
JSON = ptext
#plaintext = unpad(self.cipher.decrypt(ciphertext))
##JSON = json.loads(plaintext)
#JSON = plaintext
except:
print "Error on decryption"
JSON = None
return (True, JSON)
def addToQueue(self, data):
pass
def getFromQueue(self):
pass
testserver =IRCTalkServer(masterkey)
testserver.start()
testserver.join()
Java客户端:(此代码的部分内容仍需正确记入 - [我还没有完成])
import java.io.*;
import java.util.*;
import java.net.*;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
//import javax.crypto.CipherInputStream;
//import javax.crypto.CipherOutputStream;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.spec.SecretKeySpec;
class clientHandler {
/*
* This class sets up AES CBC encryption for the socket,
* new instance for every connection since the IV changes
*/
// Declare variables
private Cipher ecipher;
private Cipher dcipher;
Socket testSocket = null;
//DataOutputStream out = null;
//DataInputStream in = null;
PrintWriter out = null;
BufferedReader in = null;
String masterkey = "mysecretpassword";
static final String HEXES = "0123456789ABCDEF";
public static String byteToHex( byte [] raw ) {
if ( raw == null ) {
return null;
}
final StringBuilder hex = new StringBuilder( 2 * raw.length );
for ( final byte b : raw ) {
hex.append(HEXES.charAt((b & 0xF0) >> 4))
.append(HEXES.charAt((b & 0x0F)));
}
return hex.toString();
}
public static byte[] hexToByte(String hexString) {
int len = hexString.length();
byte[] ba = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
ba[i/2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i+1), 16));
}
return ba;
}
public String encrypt(String plaintext) {
// encrypt string
try {
byte[] cipherbyte = ecipher.doFinal(plaintext.getBytes("UTF-8"));
//String ciphertext = new String(ecipher.doFinal(plaintext.getBytes("UTF8")));
return byteToHex(cipherbyte);
} catch (Exception e){
e.printStackTrace();
return null;
}
}
public String decrypt(String ciphertext) {
// decrypt hex string
try {
System.out.println("decrypt byte length: " + hexToByte(ciphertext).length);
String tp = new String(hexToByte(ciphertext), "UTF-8");
System.out.println("toString(): " + tp);
String plaintext = new String(dcipher.doFinal(hexToByte(ciphertext.trim())), "UTF-8");
//String plaintext = new String(dcipher.doFinal(ciphertext.getBytes("UTF8")), "UTF-8");
return plaintext;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public void setupCrypto(byte[] iv, String key) {
// setup AES CBC encryption
// convert IV and key to byte array for crypto
try {
System.out.println("Setting up Crypto...");
//byte[] ivb = iv.getBytes("UTF8");
byte[] keyb = key.getBytes("UTF8");
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
SecretKeySpec skey = new SecretKeySpec(keyb, "AES");
ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
ecipher.init(Cipher.ENCRYPT_MODE, skey, paramSpec);
dcipher.init(Cipher.DECRYPT_MODE, skey, paramSpec);
} catch (Exception e) {
System.err.println("Error:" + e);
}
}
public void startServer() {
// starts server
try {
System.out.println("Connecting to Server");
testSocket = new Socket("localhost", 5555);
//out = new DataOutputStream(testSocket.getOutputStream());
//in = new DataInputStream(testSocket.getInputStream());
out = new PrintWriter(testSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(testSocket.getInputStream()));
} catch (UnknownHostException e) {
System.err.println("Uknown Host");
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection");
}
}
public void androidClientHandler() {
// actual communication
if (testSocket != null && out != null && in != null) {
try {
System.out.println("Waiting for IV..");
String iv;
iv = in.readLine();
byte [] ivb = hexToByte(iv);
System.out.println("Got IV..." + iv);
setupCrypto(ivb, masterkey);
String ciphertext;
String plaintext;
System.out.println("Sending \"test\" to server");
//out.writeBytes("test\n");
ciphertext = encrypt("test");
System.out.println("Sent: " + ciphertext);
out.println(ciphertext);
System.out.println("Waiting for Server to reply");
String responseLine;
responseLine = in.readLine().replaceAll("\\\\n", "");
System.out.println("Recieved from Server: " + responseLine + " - length: " + responseLine.length());
plaintext = decrypt(responseLine);
System.out.println("Recieved from Server: " + plaintext);
System.out.println("Closing Connection");
out.close();
in.close();
testSocket.close();
} catch (UnknownHostException e) {
System.err.println("Trying to connect to unknown host:" + e);
} catch (IOException e) {
System.err.println("IOException: " + e);
}
}
}
public void javaIsGay() {
startServer();
androidClientHandler();
}
public static void main(String[] args) {
System.setProperty("file.encoding", "UTF-8");
clientHandler c = new clientHandler();
c.javaIsGay();
}
}
Python服务器的输出:
Started server on localhost:5555
Server connected by ('127.0.0.1', 59683)
IV: c54aae0a5c43f547f0355ee7a0ee38c1 Sending IV: '\xc5J\xae\n\\C\xf5G\xf05^\xe7\xa0\xee8\xc1'
waiting for client
recieved from client: 'BBE7E09093625204CD3F7B755066419D'
length of ciphertext: 32
unhexifed: '\xbb\xe7\xe0\x90\x93bR\x04\xcd?{uPfA\x9d'
decrypted: 'test\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c'
unpadded: 'test'
ptext: 'test'
Recieved from Client: 'test'
Sending reply to client...
padded text: u'test\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c'
ciphertext: '\x9fT\xfc\xf0\xa8\xc2\xd3N*\x8f\x8e~\xc7\x8a\xbfR' | 16 | �T�����N*~NJ�R
hexified text: '9F54FCF0A8C2D34E2A8F8E7EC78ABF52'
Size of ciphertext: 32
reply data: '9F54FCF0A8C2D34E2A8F8E7EC78ABF52\n'
Closing connection...
Java客户端的输出:
Connecting to Server
Waiting for IV..
Got IV...c54aae0a5c43f547f0355ee7a0ee38c1
Setting up Crypto...
Sending "test" to server
Sent: BBE7E09093625204CD3F7B755066419D
Waiting for Server to reply
Recieved from Server: 9F54FCF0A8C2D34E2A8F8E7EC78ABF52 - length: 32
decrypt byte length: 16
toString(): �T����N*��~NJ�R
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:317)
at javax.crypto.Cipher.doFinal(Cipher.java:1813)
at clientHandler.decrypt(SocketClient.java:70)
at clientHandler.androidClientHandler(SocketClient.java:134)
at clientHandler.javaIsGay(SocketClient.java:151)
at clientHandler.main(SocketClient.java:157)
Recieved from Server: null
Closing Connection
答案 0 :(得分:2)
我可能为时已晚,但这就是我所得到的: 你的python填充例程有一个问题。如果明文的大小是16的倍数,则不会添加填充,而Java需要16'\ x10'。
这当然不能解决您在运输过程中的腐败问题。你最好的选择是检查问题是来自密码还是传输:如何将编码文本写入文件并从中读取Java,只是为了确定?
答案 1 :(得分:0)
在Java程序中,您使用2 Cipher
个实例进行加密和解密,并使用相同的IV初始化;在python代码中,您使用相同的对象进行解密和加密。我不熟悉 pycrypto 库(并且不熟悉python),但加密对象很可能包含内部状态,尤其是在CBC模式下。如果您在初始解密后没有重置密码,我不确定您是否使用预期的IV加密回复,这可以解释异常。
答案 2 :(得分:0)
当我使用Java JDK的扩展安全功能(特别是AES加密和解密)时,我抛出了同样的异常。实现这项工作的关键是为我的生产JDK安装扩展安全策略文件。您也可以尝试这样做。
您可以在此处下载http://www.oracle.com/technetwork/java/javase/downloads/index.html(在页面底部搜索Java Cryptography Extension)。下载后,您必须将* .jar文件复制到JDK_HOME / jre / lib / security并覆盖以前的文件。
希望这有帮助,问候。
答案 3 :(得分:0)
您提到尝试nopadding,您是否尝试切换密码模式?对于您的应用,CTR应该与CBC一样安全,并且不需要填充。
答案 4 :(得分:0)
我遇到了类似的问题,我不确定下面的代码是否会给你一个想法。
import base64
from Crypto.Cipher import AES
BS = AES.block_size
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[0:-ord(s[-1])]
class MyAESCipher:
def __init__( self, key ):
"""
Requires hex encoded param as a key
"""
self.key = key
def encrypt( self, raw ):
"""
Returns hex encoded encrypted value!
"""
raw = pad(raw)
cipher = AES.new(self.key, AES.MODE_ECB, self.key)
return base64.b64encode(cipher.encrypt(raw))
// java code
private static final String INSTANCE = "AES/CBC/PKCS5Padding";
public static String decrypt(String sSrc, String sKey) throws Exception {
try {
// 判断Key是否正确
if (sKey == null) {
// LOGGER.error("Key should not be null");
return null;
}
if (sKey.length() != 16) {
return null;
}
byte[] raw = sKey.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance(INSTANCE);
IvParameterSpec iv = new IvParameterSpec(sKey.getBytes());
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] encrypted1 = Base64.decodeBase64(sSrc.getBytes());
try {
byte[] original = cipher.doFinal(encrypted1);
return new String(original,"UTF-8");
} catch (Exception e) {
return null;
}
} catch (Exception ex) {
// LOGGER.error("decrypt fail", ex);
return null;
}
}