BadPaddingException AES 128 CBC между Python 2 и Java 1.6 - PullRequest
2 голосов
/ 19 марта 2011

У меня есть скрипт сервера Python и клиентское приложение Java, и цель состоит в том, чтобы зашифровать данные через сокет.Данные всегда будут строкой.(Я новичок в Java)

Что должно произойти:
Скрипт Python создает сокет и прослушивает соединения, принимает новые соединения, отправляет IV в виде простого текстадля AES CBC PKCS5Padding устанавливает шифры, затем получает зашифрованные данные из приложения Java (дешифрует, затем распечатывает в терминале), затем отправляет зашифрованные данные в приложение Java, а затем закрывает соединение.

Приложение Java подключается к сокету Python, получает IV, устанавливает те же шифры, шифрует простую заполненную строку, отправляет ее на сервер Python, затем ждет ответа, расшифровывает ответ и печатает егона экран.Затем соединение закрывается.

Что на самом деле происходит
Сервер устанавливает сокет, Java-клиент подключается и получает IV, оба устанавливают одинаковые шифры / дешифрование, затем Java-клиент отправляетзашифрованная дополненная строка.сервер Python успешно получает зашифрованный текст, расшифровывает строку и распаковывает ее, и строка отображается правильно.Затем сервер отправляет зашифрованную дополняемую строку клиенту java, клиент получает строку, а затем при дешифровании происходит сбой из-за BadPaddingException.

Я проверил, что заполнение PKCS5 на стороне Python является правильным в соответствии сRFC.Я пробовал другие методы заполнения (дополнение нулями и т. Д.), И ничего не работает.Я также пробовал несколько разных кодировок в строках, я также пробовал M2Crypto (теперь использую pycrypto) и т. Д. Я пробовал использовать CipherInputStream поверх сокета для клиента, и тот же результат.

Ничего не работает.Я все еще думаю, что может быть проблема кодирования между Python и Java, но я застрял в гадости только по поводу того, почему клиенту Java не удается расшифровать.

Python Server:

#!/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

Ответы [ 5 ]

2 голосов
/ 13 августа 2011

Я могу опоздать, но вот что я получил: Существует проблема в вашей подпрограмме дополнения Python. Если размер открытого текста кратен 16, заполнение не будет добавлено, в то время как Java ожидает 16 '\ x10'.

Это, конечно, может не решить проблему коррупции при транспортировке. Лучше всего было бы проверить, связана ли проблема с шифром или транспортом: как насчет записи закодированного текста в файл и чтения Java из него, просто чтобы быть уверенным?

0 голосов
/ 15 сентября 2015

У меня с вами похожая проблема, я не уверен, что следующий код даст вам представление.


код питона

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-код

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;
        }
    }
0 голосов
/ 22 февраля 2012

Вы упомянули о попытке nopadding, вы пытались переключить режим шифрования? CTR должен быть таким же безопасным, как CBC для вашего приложения и не требует заполнения.

0 голосов
/ 19 марта 2011

У меня возникало то же исключение, когда я использовал расширенные функции безопасности Java JDK (особенно - шифрование и дешифрование AES). Ключом к выполнению этой работы была установка расширенных файлов политики безопасности для моего производственного JDK. Вы можете попробовать это сделать.

Вы можете скачать их здесь http://www.oracle.com/technetwork/java/javase/downloads/index.html (поиск расширения Java для криптографии внизу страницы). После загрузки вам нужно будет скопировать оба файла * .jar в JDK_HOME / jre / lib / security и перезаписать предыдущие файлы.

Надеюсь, это поможет, привет.

0 голосов
/ 19 марта 2011

В программе Java вы используете 2 Cipher экземпляров для шифрования и дешифрования, оба инициализируются с одним и тем же IV; в коде Python вы используете один и тот же объект для дешифрования и шифрования. Я не знаком с библиотекой pycrypto (и не знаю свободно на python), но более вероятно, что объект шифрования содержит внутреннее состояние, особенно в режиме CBC. И если вы не сбросите шифр после первоначального дешифрования, я не уверен, что вы шифруете ответ ожидаемым IV, что может объяснить исключение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...