【发布时间】:2015-11-19 04:56:40
【问题描述】:
所以我一直在设计一个聊天程序时遇到一个非常烦人的问题。客户端以 AES256 格式加密消息,将它们作为字符串发送到服务器,然后服务器以以下形式将它们重新分发给客户端:
<encrypted username> encrypted message
客户端的工作是对消息进行加密和解密。从服务器返回的字符串与发送的内容匹配。
ENCRYPTOR 和 DECRYPTOR 在使用 LOCALLY 定义的加密字符串时正常工作。 将同一实例中未加密的加密消息放入 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);
其中 encrypted 是经过加密方法的字符串 在 AES256.java 中 (我尝试在其自己的文件中声明一个 AES256 对象,以试图避免空指针错误/其他反复出现的错误。它的工作原理完全相同并导致相同的错误。)
客户端正确地接收用户的输入,加密,然后发送到服务器。
服务器接收用户通过此代码加密的用户名和消息:
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();
}
}
您会注意到很多未使用、冗余或未完成的代码用于执行操作,但这仅仅是因为我已经重试了上千种不同的方法。当客户端尝试解密它返回从服务器返回的字符串时,这个特定实例中的错误就出现了。
请记住,当字符串直接来自加密器并在同一实例中返回到解密器时,加密器和解密器可以正常工作。
ALL 问题的主要罪魁祸首是当这个来自服务器的字符串试图以任何方式解密时。这种方法只是其中一种方法:
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:
ENCRYPTOR(发送到服务器的内容)
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,我能够在客户端成功解密字符串。
【问题讨论】:
-
你是怎么记住盐的?为什么要标记问题c#?
标签: java encryption cryptography