Java 解密创建附加符号
Java Decryption creating additional symbols
我写的加解密代码如下
import java.io.*;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.BadPaddingException;
import java.nio.file.Files;
import java.util.Scanner;
public class EncryptFile
{
public static void main(String args[]) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
//Encrypt Mode
FileOutputStream outputStream = new FileOutputStream(new File("D:\encryptedNewStringFile.txt"));
Key secretKey = new SecretKeySpec("encKey".getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] getFileBytes = "writing a file using encryption ".getBytes();
byte[] outputBytes = cipher.doFinal(getFileBytes);
outputStream.write(outputBytes);
getFileBytes = "\n".getBytes();
outputBytes = cipher.doFinal(getFileBytes);
outputStream.write(outputBytes);
getFileBytes = "This is New Line 2 \nThis is NewLine 3".getBytes();
outputBytes = cipher.doFinal(getFileBytes);
outputStream.write(outputBytes);
outputStream.close();
//Decrypt Mode
File curFile = new File("D:\encryptedNewStringFile.txt");
secretKey = new SecretKeySpec("encKey".getBytes(), "Blowfish");
cipher = Cipher.getInstance("Blowfish/ECB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
getFileBytes = Files.readAllBytes(curFile.toPath());
outputBytes = cipher.doFinal(getFileBytes);
InputStream bai = new ByteArrayInputStream(outputBytes);
BufferedReader bfReader = new BufferedReader(new InputStreamReader(bai));
Scanner scan = new Scanner(bfReader);
while(scan.hasNextLine())
{
System.out.println(scan.nextLine());
}
}
}
这里我在输出中遇到问题,打印输出中有一些额外的符号(即问号和方框符号)。
我收到的输出是
提前致谢,任何建议都会很有帮助
Cipher cipher = Cipher.getInstance("Blowfish");
相当于
Cipher cipher = Cipher.getInstance("Blowfish/ECB/PKCS5Padding");
这意味着每次调用 cipher.doFinal
都会产生额外的填充。
为了写入没有间歇填充的文件,您应该使用
outputBytes = cipher.update(getFileBytes);
并仅在最后一次写入文件时使用cipher.doFinal
。然后,您将能够在解密过程中使用 PKCS5Padding 而不是 NoPadding,以便自动删除末尾的有效填充。
安全注意事项:
- ECB 模式不好,不应使用。只有极少数的用例才有意义。至少使用带有随机生成的 IV 的 CBC 模式。 IV 不需要保密,只是不可预测。我们通常将它添加到密文之前,并在解密之前将其切掉。由于它始终具有预定义的长度,因此很容易做到。
- 使用像 GCM 这样的经过身份验证的操作模式,或者使用像 HMAC-SHA256 这样的消息身份验证代码来检测密文的(恶意)操纵并做出反应。
- 今天不应该使用河豚。虽然它没有直接的漏洞,但它的小块大小可能会让你面临不同的基于协议的漏洞。建议使用块大小为 128 位的块密码。想到 AES。
每次将字符串转换为字节数组时,都会使用 VM 属性中的默认文件编码,而不是 UTF-8。
因此,要解决此问题,您有两个选择:在 java 系统属性中定义默认编码:
System.setProperty("file.encoding", StandardCharsets.UTF_8.name());
或通过每次将字符串转换为字节来添加字符集编码:
"writing a file using encryption ".getBytes(StandardCharsets.UTF_8);
结合@Artjom B. 和@The 5th column mouse 的答案,您将得到一个文件加密程序,它将在 CBC 模式 中使用 Blowfish 加密文件。加密和解密以块的形式完成,因此大文件(最多几 GB)可以在没有“内存不足错误”的情况下进行加密和解密。
密钥是随机生成的,您应该牢记——不知道密钥就无法解密文件。
输出:
file encryption with Blowfish CBC mode
used key (Base64): jsErS04so1NCC7Jmds6Grr+0tPkNoaj0hx/izLaW5H8=
result encryption: true
result decryption: true
安全警告:代码没有异常处理,没有正确的文件处理(例如覆盖,恕不另行通知),仅用于教育目的:
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
public class BlowfishCbcFileEncryption {
public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException, IOException,
InvalidKeyException, InvalidAlgorithmParameterException {
System.out.println("file encryption with Blowfish CBC mode");
String uncryptedFilename = "uncrypted.txt";
String encryptedFilename = "encrypted.enc";
String decryptedFilename = "decrypted.txt";
// random blowfish 256 key
byte[] key = new byte[32];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(key);
System.out.println("used key (Base64): " + base64Encoding(key));
// random iv
byte[] iv = new byte[8]; // blowfish iv is 8 bytes long
secureRandom.nextBytes(iv);
boolean result;
result = encryptCbcFileBufferedCipherOutputStream(uncryptedFilename, encryptedFilename, key, iv);
System.out.println("result encryption: " + result);
result = decryptCbcFileBufferedCipherInputStream(encryptedFilename, decryptedFilename, key);
System.out.println("result decryption: " + result);
}
public static boolean encryptCbcFileBufferedCipherOutputStream(String inputFilename, String outputFilename, byte[] key, byte[] iv)
throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException {
Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
try (FileInputStream in = new FileInputStream(inputFilename);
FileOutputStream out = new FileOutputStream(outputFilename);
CipherOutputStream encryptedOutputStream = new CipherOutputStream(out, cipher);) {
out.write(iv);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "Blowfish");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] buffer = new byte[8096];
int nread;
while ((nread = in.read(buffer)) > 0) {
encryptedOutputStream.write(buffer, 0, nread);
}
encryptedOutputStream.flush();
}
if (new File(outputFilename).exists()) {
return true;
} else {
return false;
}
}
public static boolean decryptCbcFileBufferedCipherInputStream(String inputFilename, String outputFilename, byte[] key) throws
IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException {
byte[] iv = new byte[8]; // blowfish iv is 8 bytes long
Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
try (FileInputStream in = new FileInputStream(inputFilename); // i don't care about the path as all is local
CipherInputStream cipherInputStream = new CipherInputStream(in, cipher);
FileOutputStream out = new FileOutputStream(outputFilename)) // i don't care about the path as all is local
{
byte[] buffer = new byte[8192];
in.read(iv);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "Blowfish");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
int nread;
while ((nread = cipherInputStream.read(buffer)) > 0) {
out.write(buffer, 0, nread);
}
out.flush();
}
if (new File(outputFilename).exists()) {
return true;
} else {
return false;
}
}
private static String base64Encoding(byte[] input) {
return Base64.getEncoder().encodeToString(input);
}
}
我写的加解密代码如下
import java.io.*;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.BadPaddingException;
import java.nio.file.Files;
import java.util.Scanner;
public class EncryptFile
{
public static void main(String args[]) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
//Encrypt Mode
FileOutputStream outputStream = new FileOutputStream(new File("D:\encryptedNewStringFile.txt"));
Key secretKey = new SecretKeySpec("encKey".getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] getFileBytes = "writing a file using encryption ".getBytes();
byte[] outputBytes = cipher.doFinal(getFileBytes);
outputStream.write(outputBytes);
getFileBytes = "\n".getBytes();
outputBytes = cipher.doFinal(getFileBytes);
outputStream.write(outputBytes);
getFileBytes = "This is New Line 2 \nThis is NewLine 3".getBytes();
outputBytes = cipher.doFinal(getFileBytes);
outputStream.write(outputBytes);
outputStream.close();
//Decrypt Mode
File curFile = new File("D:\encryptedNewStringFile.txt");
secretKey = new SecretKeySpec("encKey".getBytes(), "Blowfish");
cipher = Cipher.getInstance("Blowfish/ECB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
getFileBytes = Files.readAllBytes(curFile.toPath());
outputBytes = cipher.doFinal(getFileBytes);
InputStream bai = new ByteArrayInputStream(outputBytes);
BufferedReader bfReader = new BufferedReader(new InputStreamReader(bai));
Scanner scan = new Scanner(bfReader);
while(scan.hasNextLine())
{
System.out.println(scan.nextLine());
}
}
}
这里我在输出中遇到问题,打印输出中有一些额外的符号(即问号和方框符号)。
我收到的输出是
提前致谢,任何建议都会很有帮助
Cipher cipher = Cipher.getInstance("Blowfish");
相当于
Cipher cipher = Cipher.getInstance("Blowfish/ECB/PKCS5Padding");
这意味着每次调用 cipher.doFinal
都会产生额外的填充。
为了写入没有间歇填充的文件,您应该使用
outputBytes = cipher.update(getFileBytes);
并仅在最后一次写入文件时使用cipher.doFinal
。然后,您将能够在解密过程中使用 PKCS5Padding 而不是 NoPadding,以便自动删除末尾的有效填充。
安全注意事项:
- ECB 模式不好,不应使用。只有极少数的用例才有意义。至少使用带有随机生成的 IV 的 CBC 模式。 IV 不需要保密,只是不可预测。我们通常将它添加到密文之前,并在解密之前将其切掉。由于它始终具有预定义的长度,因此很容易做到。
- 使用像 GCM 这样的经过身份验证的操作模式,或者使用像 HMAC-SHA256 这样的消息身份验证代码来检测密文的(恶意)操纵并做出反应。
- 今天不应该使用河豚。虽然它没有直接的漏洞,但它的小块大小可能会让你面临不同的基于协议的漏洞。建议使用块大小为 128 位的块密码。想到 AES。
每次将字符串转换为字节数组时,都会使用 VM 属性中的默认文件编码,而不是 UTF-8。
因此,要解决此问题,您有两个选择:在 java 系统属性中定义默认编码:
System.setProperty("file.encoding", StandardCharsets.UTF_8.name());
或通过每次将字符串转换为字节来添加字符集编码:
"writing a file using encryption ".getBytes(StandardCharsets.UTF_8);
结合@Artjom B. 和@The 5th column mouse 的答案,您将得到一个文件加密程序,它将在 CBC 模式 中使用 Blowfish 加密文件。加密和解密以块的形式完成,因此大文件(最多几 GB)可以在没有“内存不足错误”的情况下进行加密和解密。
密钥是随机生成的,您应该牢记——不知道密钥就无法解密文件。
输出:
file encryption with Blowfish CBC mode
used key (Base64): jsErS04so1NCC7Jmds6Grr+0tPkNoaj0hx/izLaW5H8=
result encryption: true
result decryption: true
安全警告:代码没有异常处理,没有正确的文件处理(例如覆盖,恕不另行通知),仅用于教育目的:
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
public class BlowfishCbcFileEncryption {
public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException, IOException,
InvalidKeyException, InvalidAlgorithmParameterException {
System.out.println("file encryption with Blowfish CBC mode");
String uncryptedFilename = "uncrypted.txt";
String encryptedFilename = "encrypted.enc";
String decryptedFilename = "decrypted.txt";
// random blowfish 256 key
byte[] key = new byte[32];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(key);
System.out.println("used key (Base64): " + base64Encoding(key));
// random iv
byte[] iv = new byte[8]; // blowfish iv is 8 bytes long
secureRandom.nextBytes(iv);
boolean result;
result = encryptCbcFileBufferedCipherOutputStream(uncryptedFilename, encryptedFilename, key, iv);
System.out.println("result encryption: " + result);
result = decryptCbcFileBufferedCipherInputStream(encryptedFilename, decryptedFilename, key);
System.out.println("result decryption: " + result);
}
public static boolean encryptCbcFileBufferedCipherOutputStream(String inputFilename, String outputFilename, byte[] key, byte[] iv)
throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException {
Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
try (FileInputStream in = new FileInputStream(inputFilename);
FileOutputStream out = new FileOutputStream(outputFilename);
CipherOutputStream encryptedOutputStream = new CipherOutputStream(out, cipher);) {
out.write(iv);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "Blowfish");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] buffer = new byte[8096];
int nread;
while ((nread = in.read(buffer)) > 0) {
encryptedOutputStream.write(buffer, 0, nread);
}
encryptedOutputStream.flush();
}
if (new File(outputFilename).exists()) {
return true;
} else {
return false;
}
}
public static boolean decryptCbcFileBufferedCipherInputStream(String inputFilename, String outputFilename, byte[] key) throws
IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException {
byte[] iv = new byte[8]; // blowfish iv is 8 bytes long
Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
try (FileInputStream in = new FileInputStream(inputFilename); // i don't care about the path as all is local
CipherInputStream cipherInputStream = new CipherInputStream(in, cipher);
FileOutputStream out = new FileOutputStream(outputFilename)) // i don't care about the path as all is local
{
byte[] buffer = new byte[8192];
in.read(iv);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "Blowfish");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
int nread;
while ((nread = cipherInputStream.read(buffer)) > 0) {
out.write(buffer, 0, nread);
}
out.flush();
}
if (new File(outputFilename).exists()) {
return true;
} else {
return false;
}
}
private static String base64Encoding(byte[] input) {
return Base64.getEncoder().encodeToString(input);
}
}