I'm trying to send/receive encrypted messages from and to the server. The server is using Python, while Java for the Android. I'm doing the following:
- Sending RSA Public key to the server
- Generating (key + IV) and Encrypting basic data with AES
- Encrypting the (key + IV) (as JSON) with the received RSA Public key
- Sending the RSA encrypted AES pair over the socket to the client
- Sending the AES encrypted data
- When receiving the AES pair, decrypting it with the RSA private key, the received data matches
- When trying to decrypt the AES data with the received AES key it is failing.
On both platforms when trying to encrypt/decrypt with both it is working flawlessly. I believe there is something with the way I get the AES (key + IV) from the JSON, but that is what I found multiple times.
The failure:
I/System.out: MESSAGE: {"key": "bgOQ0c9xJVI60BrLUzSGK8IDyM9sVrdd", "iv": "2FbQMXqMrd4fDR4V"}
W/System.err: javax.crypto.BadPaddingException: pad block corrupted
at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$BufferedGenericBlockCipher.doFinal(BaseBlockCipher.java:1337)
W/System.err: at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(BaseBlockCipher.java:1170)
at javax.crypto.Cipher.doFinal(Cipher.java:2055)
at com.example.tests.encrypt.AES.decrypt(AES.java:62)
at com.example.tests.encrypt.AES.decrypt(AES.java:50)
at com.example.tests.server.Server$ReceivingThread.run(Server.java:66)
at java.lang.Thread.run(Thread.java:764)
Here is some code.
JAVA:
Receiving the data:
byte[] in = Base64.decode(fromServerStream.readLine(), Base64.DEFAULT);
byte[] in2 = Base64.decode(fromServerStream.readLine(), Base64.DEFAULT);
if (in != null && in2 != null) {
String keys = RSA.decrypt(in);
System.out.println("MESSAGE: " +keys);
String msg2 = AES.decrypt(in2, keys);
System.out.println(msg2);
}
Generating Keys:
public static void generateKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance(Consts.RSA);
kpg.initialize(Consts.RSASize);
KeyPair kp = kpg.genKeyPair();
publicKey = kp.getPublic();
privateKey = kp.getPrivate();
}
Getting PubKey as String (using case "PEM" only at moment):
public static String getPublicKey(String option) {
switch (option) {
case "PEM":
String pkcs1pem = "-----BEGIN RSA PUBLIC KEY-----\n";
pkcs1pem += Base64.encodeToString(publicKey.getEncoded(), Base64.DEFAULT);
pkcs1pem += "-----END RSA PUBLIC KEY-----";
return pkcs1pem;
case "pkcs8-pem":
String pkcs8pem = "-----BEGIN PUBLIC KEY-----\n";
pkcs8pem += Base64.encodeToString(publicKey.getEncoded(), Base64.DEFAULT);
pkcs8pem += "-----END PUBLIC KEY-----";
return pkcs8pem;
default:
return Base64.encodeToString(publicKey.getEncoded(), Base64.DEFAULT).replace("\n", "\\n");
}
}
RSA Decrypting:
public static String decrypt(byte[] result)
throws NoSuchAlgorithmException,
NoSuchPaddingException,
InvalidKeyException,
IllegalBlockSizeException,
BadPaddingException {
Cipher cipher1 = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
cipher1.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher1.doFinal(result), Consts.charsetEncoding);
}
AES Decrypting:
public static String decrypt(byte[] plaintext, String JSONKey) throws Exception
{
JSONObject jsonObject = new JSONObject(JSONKey);
String key = jsonObject.getString("key");
String iv = jsonObject.getString("iv");
return AES.decrypt(
plaintext,
new SecretKeySpec(key.getBytes(Consts.charsetEncoding), Consts.AES),
iv.getBytes(Consts.charsetEncoding));
}
public static String decrypt(byte[] cipherText, SecretKey key, byte[] IV)
{
try {
Cipher cipher = Cipher.getInstance(Consts.AES);
IvParameterSpec ivSpec = new IvParameterSpec(IV);
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
return new String(cipher.doFinal(cipherText), Consts.charsetEncoding);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Consts:
// ENCODING
public static final String encoding = "ASCII";
public static final Charset charsetEncoding = StandardCharsets.US_ASCII;
// AES
public static final String AES = "AES";
public static final int AESSize = 256;
public static final int IVSize = 16;
===============================================================
Python / ServerSide:
Firstly the output:
'{"type": "keyExchange", "pubKey": "-----BEGIN RSA PUBLIC KEY-----\\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjfqo/3Cqw28s8AuXq1/keHgPzcrthSvy\\npepeuu3RANiORN2MIjQjF+KEQPsdNphtBvHA/v4l/9MmrcRFZlLf+1KXnjMWSkr+0lnpVpu7x7Mf\\nnP0RBSS5I5wpxq7FmaTaIGIdDk5G7SZVLChdXDXtpoU95UJ/VLyJe7X17N3nYPIAPxsOMqzKZFN1\\n1EbgmyeSWkU6y5OA3Txggd6csjKq+BGDaVsex50PdS7bP43j2r/+wRb5zDhXI9nHsG6Sb49D1TE4\\ncJksRzkKc1NuBzyY4qmq/UQDdDSowDavEF2hf6zU9xo4o5rvIgGAF+ZHUUMS7qixmx4Vln88WnbW\\nDWCD8J7MRprJmb6zN8YgePQPzbf1527oBW9jCi9OFI7+FPKZOodvMTR6hZSon8LykVY4K8LifsXK\\n7UWy1DljBDakzWJZGHZv9dyXrCeZNxmYTrIWeh19rMWKMoX/X0mWPVPHzfAxqVnryrWsBBu+ImQP\\nAZvnY3lwvOii11T2sv0pXwk+hUKNiA1DmDF8dPQxTeX0I95Sum1VvDFrNNeEsS4NAfeOvnjIwl4Z\\npR0inRbbw7lZmQHerk4kN/x7NM27eOmi8/y6D3G9/Rf4yqLDewA7Qoe+El5VNW7Zd1Iw4sr8JGmb\\nm75mqcZlYtCaweJxfjmHoL4jvJnq5yjxGFyjI/tslxcCAwEAAQ==\\n-----END RSA PUBLIC KEY-----"}'
KEY: bgOQ0c9xJVI60BrLUzSGK8IDyM9sVrdd
IV: 2FbQMXqMrd4fDR4V
b'ASJzulQs9qX3i3k8zMIvTlaNdwrsy4K0GgD/W3L+seH56uTtJ7FO8Mev0j+hwHuGkUj4UZ56MTolYutPpwgnlYcWlzIFjPgKbAG/MwsYdp+JoTS8bSo9YS8x0+ARhXmLa/mQefGUq/0l1YKkVB/SzXp27ni802c4ApPSkP98xp/IhZZgNQswB0c6Mm29o8MTw1/bv3lV8bhkHLtWVDClz91RKz53jTH6XpAFsLtk90ZgX7hRZSO+jAwFlCkRNLKxqwjVANtRuaWm1Agmf10HwKYBVSzgXLpC8+hT90Eicj/8TOj1HSnE/IUweSz21aQbm101H+VP76mbVofq9ID4Mmc+6a+VP2/DHNHUQO0sJuoNUecoe31UaXjHShLaPY4kFHMloOKcuZUsQdnBoTaak3W68pJ5AmHICcZ689FO2e/6QaUF9YzyP1eB3nQpxP48eR2KlK+I288dycMTpapfNWThWHOa+bEjGobLL26+pk3lixyw56RRd+YZ4Fm+omnnpHdtW3Qn388HzmU9Hz0ho/r88ofpVcdxBICLNBjwRgGt5ArfGo4wNvuS7VceFohqk3foVXgkZmHsakjewiqosSk2gqP0yeux0DRweL5vOMo08kMbGlz/Zr8HWcM7UkDbFcGLALY+sf8yu/3nnv/gw0Z45o7TopRiLW8m0cvKYOk='
b'3JRcYYCjwt8/RSuis6TjdJOpxOV4OzIJODkLRGXJb6d89r+cwgZ9s7kcgR0uUOvR'
Receive + Send (testing):
rec = self.connection.recv(Server.Consts.MESSAGE_SIZE).decode()
if not rec:
break
rec = rec.replace('\n', '\\n')
print(repr(rec))
js = json.loads(rec)
key = MRSA.getKeyFromString(js['pubKey'])
dict = {"type": "keyExchange", "pubKey": "ECHO"}
aeskey, aesiv, aesData = MAES.encrypt(json.dumps(dict))
print("KEY: {}".format(base64.b64decode(aeskey).decode()))
print("IV: {}".format(base64.b64decode(aesiv).decode()))
d = {"key": base64.b64decode(aeskey).decode(), "iv": base64.b64decode(aesiv).decode()}
x = MRSA.encrypt(json.dumps(d), key.publickey())
self.send(x)
self.send(aesData)
Whole AES:
@staticmethod
def generateKey():
return \
(''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for x in range(Consts.AESSize))).encode(Consts.encoding), \
(''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for x in range(Consts.IVSize))).encode(Consts.encoding)
@staticmethod
def padding(data):
if len(data) % 16 != 0:
return '{}{}'.format(data, ' ' * (16 - len(data) % 16))
return data
@staticmethod
def encrypt(data):
key, iv = MAES.generateKey()
data = MAES.padding(data)
return base64.b64encode(key), \
base64.b64encode(iv), \
base64.b64encode(AES.new(key, AES.MODE_CBC, iv).encrypt(data.encode(Consts.encoding)))
@staticmethod
def decrypt(data, key, iv):
return AES.new(base64.b64decode(key), AES.MODE_CBC, base64.b64decode(iv)).decrypt(base64.b64decode(data)).strip().decode()
Whole RSA:
@staticmethod
def generateKey():
return RSA.generate(Consts.RSASize)
@staticmethod
def getKeyFromString(str):
return RSA.import_key(bytes(str, encoding=Consts.encoding))
@staticmethod
def encrypt(msg, public):
return base64.b64encode(PKCS1_OAEP.new(public).encrypt(msg.encode(Consts.encoding)))
@staticmethod
def decrypt(data, keyPair):
return PKCS1_OAEP.new(keyPair).decrypt(base64.b64decode(data)).decode(Consts.encoding)
And finally the Consts:
encoding = "ASCII"
AES = "AES"
AESSize = 32
IVSize = 16
RSA = "RSA"
RSASize = 4096
EDIT: If you are having any trouble, please read the comments of the answer from Maarten Bodewes.