繁体   English   中英

JAVA AES256加密/解密java.lang.NullPointerException + javax.crypto.BadPaddingExceptions

[英]JAVA AES256 ENCRYPTION/DECRYPTION java.lang.NullPointerException + javax.crypto.BadPaddingExceptions

因此,我一直在设计自己的聊天程序时遇到一个非常烦人的问题。 客户端以AES256格式加密消息,将它们作为字符串发送到服务器,然后服务器以以下形式将其重新分发给客户端:

<encrypted username> encrypted message

加密和解密消息是客户端的工作。 从服务器返回的字符串与发送的字符串匹配。

使用本地定义的加密字符串时,ENCRYPTOR和DECRYPTOR可以正常工作 将未在同一实例中加密的加密消息放入DECRYPTOR中会产生空指针异常,BadPadding异常或其他奇怪的错误。

聊天系统工作正常,但是当需要解密消息时会出现问题

因此,我弄乱了代码,并尝试了许多变通办法以查看数据是否会再次解密。 我最终尝试了多种解密方法,以查看其他方法是否可行,但无济于事。 同样的问题。

 int port=1234;
                           String host="localhost";
                    try
                    {
                                socket=new Socket(host,port);
                                userip=new BufferedReader(new InputStreamReader(System.in));
                                output=new PrintStream(socket.getOutputStream());
                                input=new BufferedReader(new InputStreamReader(socket.getInputStream()));
                           //     AES256 encryptedmessage = new AES256();
                    }
                    catch(Exception e)
                    {
                                System.err.println("Couldn't connect. "+host);
                    }
                    if(socket!=null)
                    {
                                try
                                {
                                            //AES256 message = new AES256();
                                            String encrypted = "";
                                            new Thread(new Client()).start();
                                            while(!flag)
                                            {
                                            encrypted = Global.encryptor.encrypt(userip.readLine());
                                            //encrypted = message.encrypt(userip.readLine());
                                            output.println(encrypted);
                                            System.out.println(encrypted);
                                            //NOT NECESSARY ^^^
                                            }
                                output.close();
                                input.close();
                                socket.close();
                    }

将输入发送到服务器的部分是

output.println(encrypted);

其中,已加密是通过AES256.java中的加密方法放入的字符串(我尝试在其自己的文件中声明一个AES256对象,以避免出现空指针错误/其他重复发生的错误。它的工作原理完全相同并导致相同的错误。)

客户端正确接收用户的输入,对其进行加密,然后将其发送到服务器。

SERVER通过以下代码接收加密的用户名和用户消息:

            public void run()
        {
              //AES256 X = new AES256();
              String msg;
              String username;
              try
              {
                    input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    output = new PrintStream(socket.getOutputStream());
                    output.println("Enter your alias: ");
                    username = input.readLine();
                    //THIS IS IMPORTANT.
                    output.println("CONNECTION ESTABLISHED");
                    output.println(username + ": CONNECTED.");
                    output.println("To disconnect, enter $$");
                    output.println("END UNENC");
                    for (int i = 0; i <= 9; i++)
                          if (th[i] != null && th[i] != this)
                                th[i].output.println("------------A new user connected: " + username);
                          while (true)
                          {
                                msg = input.readLine();
                                if (msg.startsWith("$$"))
                                      break;
                                for (int i = 0; i <= 9; i++)
                                      if (th[i] != null){
                                            th[i].output.println("<" + username + "> " + msg);
                                      }
                                      }

这里的相关代码是

for (int i = 0; i <= 9; i++)
                                      if (th[i] != null){
                                            th[i].output.println("<" + username + "> " + msg);
                                      }
                                      }

上面的代码接收来自客户端的用户名消息 ,并将其发送给所有连接的用户。 用户名和消息仍然被加密。

此时,客户会收到以下性质的东西:

<nf8q3nfqlidcnalkm2> djiami2j389danfill23nlfuinvavv34

用户名和消息在字符方面与最初发送到服务器的完全相同。 但是,当我尝试解密这两件事时,就会出现错误(主要是空指针错误)。

此信息通过以下代码在客户端文件中返回给客户端:

public void run()
{
String msg;
String unencrypt = "";
String line = "";
String username = "";
String message = "";
int index1 = 0;
int index2 = 0;

try
{
while((msg=input.readLine())!=null)
{
//  d.SetMessage(msg);
//  String original = msg;
//  byte[] utf8Bytes = original.getBytes("UTF-8");
    if(!msg.contains("<")){
    System.out.println(msg);

    }
    else if(msg.contains("<"))
    {
        username = msg.substring(1, msg.indexOf(">"));
        System.out.println("USERNAME:"+username);
        message = msg.substring(msg.indexOf(">")+2,msg.length());
        System.out.println("MESSAGE:"+message);
        index1 = line.indexOf('<');
        index2 = line.indexOf('>');
        unencrypt = Global.encryptor.decrypt(username);
        //System.out.println("<" + d.decrypt(d.GetMessage() + ">"));
    }
}
flag=true;
}
catch(IOException e)
{
System.err.println("IOException" + e);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

您会注意到有很多未使用的,多余的或未完成的代码用于处理工作,但这仅仅是由于我尝试了千种不同的处理方法。 与所有其他实例一样,此特定实例中的错误在客户端尝试解密 从服务器返回的字符串时出现。

请记住,当字符串直接从加密器传回到同一实例中的解密器时,加密器和解密器可以完美地工作。

导致所有问题的主要原因是,试图以任何方式解密来自服务器的此字符串。 此方法只是以下方法之一:

unencrypt = Global.encryptor.decrypt(username);

每当使用服务器上的字符串运行此解密器时,我们来看一下它带来的错误:

javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at Messenger.AES256.decrypt(AES256.java:84)
at Messenger.Client.run(Client.java:106)
at java.lang.Thread.run(Unknown Source)
java.lang.NullPointerException
at java.lang.String.<init>(Unknown Source)
at Messenger.AES256.decrypt(AES256.java:91)
at Messenger.Client.run(Client.java:106)
at java.lang.Thread.run(Unknown Source)

我对如何解决这个问题一无所知。 这是即将发挥作用的AES256东西:

加密器( 发送到服务器的内容

  public String encrypt(String plainText) throws Exception {   

    //get salt
    salt = generateSalt();      
    byte[] saltBytes = salt.getBytes("UTF-8");

    // Derive the key
    SecretKeyFactory factory =        SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    PBEKeySpec spec = new PBEKeySpec(
            password.toCharArray(), 
            saltBytes, 
            pswdIterations, 
            keySize
            );

    SecretKey secretKey = factory.generateSecret(spec);
    SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");

    //encrypt the message
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, secret);
    AlgorithmParameters params = cipher.getParameters();
    ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
    byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
    return new Base64().encodeAsString(encryptedTextBytes);
  }

DECRYPTOR( 在发送给客户端的字符串上运行的内容

 @SuppressWarnings("static-access")
public String decrypt(String encryptedText) throws Exception {

    byte[] saltBytes = salt.getBytes("UTF-8");
    byte[] encryptedTextBytes = new Base64().decodeBase64(encryptedText);

    // Derive the key
    SecretKeyFactory factory =     SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    PBEKeySpec spec = new PBEKeySpec(
            password.toCharArray(), 
            saltBytes, 
            pswdIterations, 
            keySize
            );

    SecretKey secretKey = factory.generateSecret(spec);
    SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");

    // Decrypt the message
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));


    byte[] decryptedTextBytes = null;
    try {
        decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    } catch (BadPaddingException e) {
        e.printStackTrace();
    }

    return new String(decryptedTextBytes);
  }

我的信念是,当字符串发送到服务器并返回时,有关字符串的内容会发生变化。 或者,AES256类需要在对字符串进行加密后记住一些有关字符串的信息。 我不确定。

有人可以给我一些关于这件事的见解吗? 我感觉它已经接近工作了,但是一旦将这些字符串发送回客户端,我就无法解密。

编辑:

            final protected static char[] hexArray =     "0123456789ABCDEF".toCharArray();
        public static String bytesToHex(byte[] bytes) {
            char[] hexChars = new char[bytes.length * 2];
            for ( int j = 0; j < bytes.length; j++ ) {
                int v = bytes[j] & 0xFF;
                hexChars[j * 2] = hexArray[v >>> 4];
                hexChars[j * 2 + 1] = hexArray[v & 0x0F];
            }
            return new String(hexChars);}

我正在尝试将ivBytes和salt转换为十六进制,以便可以将它们发送到服务器并返回,然后再次获取需要解密的内容。

当我尝试打印此新的十六进制时,我得到:

HEXADECIMAL IV BYTES: ?T???/0??q"?H

编辑#2:

我已经成功将ivBytes []和saltBytes []转换为十六进制格式。 我正在以...的形式将输入发送到服务器...

HexIvBytes + " " + HexSaltBytes + " " + EncryptedMessage

这是输出示例:

testing
HEXADECIMAL IV BYTES: 70FDE9D7D1A0E2AAD92A95E113186067
IV BYTES: [B@2aafb23c
HEXADECIMAL SALT BYTES:     71C385C387C2ACC2BDC3B2CB9C1BC3926F63370749C3B4C2BD04E280B9C2A90E
SALT BYTES: [B@2b80d80f
ENCRYPTED MESSAGE:eYgv/GxKP3oG3SbbnflTyw==
IVBYTES:[B@2aafb23c
<6C5D1C71F8C775C363C914982C617B11 C386C3A5C3A4C3AE1942C5BE565268175C5A7354C39F6F15C5A1C2A7 NtDi9/P696DgTezN7T0ZVg==> 70FDE9D7D1A0E2AAD92A95E113186067 71C385C387C2ACC2BDC3B2CB9C1BC3926F63370749C3B4C2BD04E280B9C2A90E eYgv/GxKP3oG3SbbnflTyw==

因此,我现在尝试在客户端解密这些内容。 这就是我正在使用的:

if(msg.contains("<"))
    {
        username = msg.substring(1, msg.indexOf(">"));
        usernameivHex = username.substring(0, username.indexOf(" "));
        saltHex = username.substring(username.indexOf(" "), username.indexOf("|"));
        encryptedUsername = username.substring(username.indexOf("|"),username.indexOf(">"));

        d.setivBytes(hexStringToByteArray(usernameivHex));
        d.setSaltBytes(hexStringToByteArray(saltHex));
        System.out.println("DECRYPYTED USERNAME: " + d.decrypt(encryptedUsername));

    }
}

我在此行中遇到了很多错误:

            encryptedUsername = username.substring(username.indexOf("|"),username.indexOf(">"));

java.lang.StringIndexOutOfBoundsException: String index out of range: -100
at java.lang.String.substring(Unknown Source)
at Messenger.Client.run(Client.java:155)
at java.lang.Thread.run(Unknown Source)

编辑#3:

成功! 使用传输的IvBytes和SaltBytes,我能够在客户端成功解密字符串。

您需要使用相同的盐,而不仅仅是相同的密码。 密码和盐确定密钥种子的值-PBKDF2的输出。 这反过来又是密钥的输入材料(有时只是使用密钥种子中正确数量的字节)。

如果密钥无效,则很可能会出现填充错误。 如果您想确保输入错误的盐/密码会出错,则可能要包括一个身份验证标签(例如,使用HMAC生成)。

发送盐的最简单方法是编码之前简单地将其前缀为密文。 如果要分隔盐和密文,则可以将它们作为JSON string : value发送string : value

"salt"=<base64>
"ciphertext"=<base64>

IV也一样。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM