[英]How to handle BadPaddingException During AES256 encryption in C# and decryption in Java
[英]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.