【问题标题】:Decrypting in Java with Blowfish使用 Blowfish 在 Java 中解密
【发布时间】:2021-07-29 17:30:56
【问题描述】:

你好,

我正在使用 Blowfish 在 Java 中进行加密和解密。

加密工作正常,但解密失败。

这是我用于解密的 Java 代码:

String encryptedString = … ;
String decryptedString = null;
SecretKeySpec key = new SecretKeySpec(myKey.getBytes(), "Blowfish");
Cipher cipher;
try {
    cipher = Cipher.getInstance("Blowfish");
    cipher.init(Cipher.DECRYPT_MODE, key);
    byte[] decrypted = cipher.doFinal(encryptedString.getBytes());
    decryptedString = new String(decrypted, Charset.forName("UTF-8"));
} [ catch Exceptions … ]

我得到一个例外:

Exception. javax.crypto.IllegalBlockSizeException: Input length must be multiple of 8 when decrypting with padded cipher

您能告诉我如何简单地完成这项工作吗?谢谢。

我提供的输入来自我的加密 Java 代码,+ Base64 中的编码,我在将其提供给此解密操作之前从 Base64 对其进行解码。

【问题讨论】:

  • 编码数据必须以 8 位块的形式出现。如果您说 22 位,则在加密时使用额外的 2 填充。这就是分组密码的全部想法。见en.wikipedia.org/wiki/Blowfish_%28cipher%29
  • @John — 这对我帮助不大。我已将帖子编辑得更准确。

标签: java cryptography encryption blowfish


【解决方案1】:

将字节转换为十六进制并返回是很棘手的。这应该可以解决您的问题。 (您需要修复您的 encryptedString 的字符串表示形式)

输出:

StackOverflow 537461636B4F766572666C6F77 [83, 116, 97, 99, 107, 79, 118, 101, 114, 102, 108, 111, 119]
J~3¹ÙÂÖ"¢ª„¨u 194A7E33B9060CD9C2D622A2AA84A875 [25, 74, 126, 51, -71, 6, 12, -39, -62, -42, 34, -94, -86, -124, -88, 117]
StackOverflow 537461636B4F766572666C6F77 [83, 116, 97, 99, 107, 79, 118, 101, 114, 102, 108, 111, 119]

代码:

import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

public class Main {

  public static void main(String[] args) throws Exception {

    KeyGenerator keygenerator = KeyGenerator.getInstance("Blowfish");
    SecretKey secretkey = keygenerator.generateKey();

    String plaintextString = "StackOverflow";
    System.out.println(plaintextString + " " + bytesToHex(plaintextString.getBytes()) + " " + Arrays.toString(plaintextString.getBytes()));

    SecretKeySpec key = new SecretKeySpec(secretkey.getEncoded(), "Blowfish");
    Cipher cipher = Cipher.getInstance("Blowfish");

    cipher.init(Cipher.ENCRYPT_MODE, key);
    byte[] encrypted = cipher.doFinal(plaintextString.getBytes());
    String encryptedString = bytesToHex(encrypted);
    System.out.println(new String(encrypted) + " " + encryptedString + " " + Arrays.toString(encrypted));

    cipher.init(Cipher.DECRYPT_MODE, key);
    byte[] decrypted = cipher.doFinal(hexToBytes(encryptedString));
    String decryptedString = bytesToHex(decrypted);
    System.out.println(new String(decrypted) + " " + decryptedString + " " + Arrays.toString(decrypted));

  }

  public static byte[] hexToBytes(String str) {
    if (str == null) {
      return null;
    } else if (str.length() < 2) {
      return null;
    } else {
      int len = str.length() / 2;
      byte[] buffer = new byte[len];
      for (int i = 0; i < len; i++) {
        buffer[i] = (byte) Integer.parseInt(str.substring(i * 2, i * 2 + 2), 16);
      }
      return buffer;
    }

  }

  public static String bytesToHex(byte[] data) {
    if (data == null) {
      return null;
    } else {
      int len = data.length;
      String str = "";
      for (int i = 0; i < len; i++) {
        if ((data[i] & 0xFF) < 16)
          str = str + "0" + java.lang.Integer.toHexString(data[i] & 0xFF);
        else
          str = str + java.lang.Integer.toHexString(data[i] & 0xFF);
      }
      return str.toUpperCase();
    }
  }
}

【讨论】:

  • 谢谢@Sanchit,但这太复杂了。看看我即将到来的答案。
【解决方案2】:

您的 myKey 变量长度必须是 8 的倍数

【讨论】:

  • 我的密钥是一个 16 个字符的字符串,所以这就是一个 128 位的密钥。
  • 一个字符不一定是8位。加密密钥是二进制数据,如果您尝试将其存储在字符串中而不使用 base64 等合适的编码,您一定会遇到问题。
【解决方案3】:
String encryptedString = … ;  
String decryptedString = null;
SecretKeySpec key = new SecretKeySpec(myKey.getBytes(), "Blowfish");
private static byte[] linebreak = {}; // Remove Base64 encoder default linebreak
private static Base64 coder;
Cipher cipher;
try {
    coder = new Base64(32, linebreak, true);
    cipher = Cipher.getInstance("Blowfish");
    cipher.init(Cipher.DECRYPT_MODE, key);
    byte[] decrypted = cipher.doFinal(encryptedString.getBytes());
    decryptedString = new String(coder.encode(decrypted));
} [ catch Exceptions … ]

你可以使用 Base64 类来解决这个问题。

【讨论】:

  • 我已经有了 Base64 编码和解码。所以我不认为这是问题所在。看我即将到来的答案。还是谢谢你。
【解决方案4】:

现在我有了解决方案!

首先,Unicode 存在一些问题,所以我将 ISO-8859-1 放在了各处。 Including in the Base64 encoding and decoding.

然后,我已经处理了变体。

这是我用于 Blowfish 解密的 Java 代码:

String encryptedString = … ;
String decryptedString = null;
SecretKeySpec key = new SecretKeySpec(myKey.getBytes(CHARSET_ISO_8859_1), "Blowfish");
Cipher cipher;
try {
    cipher = Cipher.getInstance("Blowfish/ECB/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, key);
    byte[] decrypted = cipher.doFinal(encryptedString.getBytes(CHARSET_ISO_8859_1));
    decryptedString = new String(decrypted, CHARSET_ISO_8859_1);
} [ catch Exceptions … ]

请注意,我已将 "Blowfish" 替换为 "Blowfish/ECB/PKCS5Padding" 以获取 Cipher 实例,但是,如果您对密钥执行相同操作,则会失败。

密钥myKey 必须是 8 个字符的 Latin-1 字符串。这使得密钥为 128 位。 Blowfish 算法允许更大的密钥,但由于 JRE 中的美国出口限制,它们在 Java 中失败 - 美国允许加密,但不比 NSA 可以破解的更强。

CHARSET_ISO_8859_1 是一个常量,定义如下:

final Charset CHARSET_ISO_8859_1 = Charset.forName("ISO-8859-1");

Charsetjava.nio.charset.Charset

最后但同样重要的是,我已经相应地更改了我的加密 Java 代码。

【讨论】:

  • 不,这是错误的,是一个没有人应该效仿的糟糕例子。 ISO-8859-1 编码工作的事实只是运气。加密的输出不是任何字符集中的有效字符串,不应这样编码。如果必须这样做,请使用有效的二进制到文本编码,例如 base64 编码。
  • @Greg — 你错过了一集 — 我编辑了我的帖子。编码,UTF-8 或 ISO-8859-1,必须在那里。它不会取代 Base64。在此页面中,我将重点介绍 Blowfish 解密和加密。我在加密后进行 Base64 编码,在解密前进行 Base64 解码。但我在本页的 Java 代码中省略了这些步骤。
  • 您必须记住,您的答案不仅适用于您自己,也适用于其他有类似问题的人(可能是谷歌搜索)。所以,当我看到byte[] decrypted = cipher.doFinal(encryptedString.getBytes(CHARSET_ISO_8859_1)); 行时,我看到了一个糟糕的做法。该行实际上并不存在于您的程序中这一事实是无关紧要的。
  • @sana — 使用只有 8 个字符的密钥 myKey
【解决方案5】:

您绝对应该通过声明模式和填充来更明确地使用Cipher。这段代码是如何加密的? String encryptedString 实际上是什么?它是十六进制编码还是base64编码?如果它没有被编码,那肯定是一个麻烦的来源。

【讨论】:

    【解决方案6】:

    试试这个

    private byte[] encrypt(String key, String plainText) throws GeneralSecurityException {
    
        SecretKey secret_key = new SecretKeySpec(key.getBytes(), ALGORITM);
    
        Cipher cipher = Cipher.getInstance(ALGORITM);
        cipher.init(Cipher.ENCRYPT_MODE, secret_key);
    
        return cipher.doFinal(plainText.getBytes());
    }
    

    在这里你可以找到带有 enc/dec 的全班 --- http://dexxtr.com/post/57145943236/blowfish-encrypt-and-decrypt-in-java-android

    【讨论】:

    • 谢谢 Dexxtr,但我现在已经解决了这个问题。请参阅我的自我回答。
    • 我确信这行不通。 ALGORITM 将不存在。我对Cipher.ENCRYPT_MODE 的解密有很大的疑问。
    【解决方案7】:

    试试{ /** * 初始化用于解密的密码 / cipher.init(Cipher.DECRYPT_MODE, secretKey); /* * 初始化输入输出流 */ inStream = new FileInputStream(encryptedFile); outStream = new FileOutputStream(decryptedFile); 字节[]缓冲区=新字节[1024]; 国际化; 而 ((len = inStream.read(buffer)) > 0) { outStream.write(cipher.update(buffer, 0, len)); outStream.flush(); } outStream.write(cipher.doFinal()); inStream.close(); outStream.close(); }

    【讨论】:

      【解决方案8】:
          try {
              /**
               * Initialize the cipher for decryption
               */
           cipher.init(Cipher.DECRYPT_MODE, secretKey);
              /**
               * Initialize input and output streams
               */
              inStream = new FileInputStream(encryptedFile);
              outStream = new FileOutputStream(decryptedFile);
              byte[] buffer = new byte[1024];
              int len;
              while ((len = inStream.read(buffer)) > 0) {
                  outStream.write(cipher.update(buffer, 0, len));
                  outStream.flush();
              }
              outStream.write(cipher.doFinal());
              inStream.close();
              outStream.close();
          }
      

      ...

      1. 列表项

      【讨论】:

        猜你喜欢
        • 2018-12-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-25
        相关资源
        最近更新 更多