5

我正在制作一个加密一些文件的应用程序。我想使用 gnu 的 cryptix 库。它说它自 2005 年以来不再开发,但我想它拥有我需要的一切......我应该使用其他东西吗?

我有一个关于加密单个文件的问题。现在我用这样的循环来做:

for(int i=0; i+block_size < bdata.length; i += block_size)
    cipher.encryptBlock(bdata, i, cdata, i);

所以我的问题是如何加密可能与block_size大小不同的最后一个块。我在想也许应该在最后一个块中添加一些额外的数据,但我不知道如何解密......

4

4 回答 4

7

我强烈建议使用 AES 加密,它也带有 JAVA SDK。看看:Using AES with Java Technology,它会给你一些很好的例子。要了解有关 AES 的更多信息,请参阅:高级加密标准 - 维基百科

切勿使用您自己的加密方案或旧形式的加密方案。AES 已经由比我们在该领域有更丰富知识的人进行了尝试和测试,因此您知道它会起作用。与您自己的或旧的加密方案一样,我们可能会错过一个致命的漏洞,这将使我们的数据容易受到攻击。

在此处查看此问题以查看加密方案的区别:DES、Triple DES、AES、河豚加密数据的比较

附录:

Java 中的 AES 可以完美运行 192 位和 256 位密钥,但您必须安装较新的 JCE 策略文件。见这里这里。您还应该将文件放在 JDK 中,否则从 IDE 执行时它将无法工作。

注意:确保下载正确的 JCE 策略文件,具体取决于您的 Java 版本,即 1.4、1.5、1.6 或 7。

但是,如果您使用 128 位密钥,则无需安装较新的 JCE 文件。

这是 java 中一些安全 AES 使用的模板,它使用CBC/AES/PKCS5Padding和使用RandomSecure.

请注意,您需要密钥和 IV 来解密:

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * This program generates a AES key, retrieves its raw bytes, and then
 * reinstantiates a AES key from the key bytes. The reinstantiated key is used
 * to initialize a AES cipher for encryption and decryption.
 */
public class AES {

    /**
     * Encrypt a sample message using AES in CBC mode with a random IV genrated
     * using SecyreRandom.
     *
     */
    public static void main(String[] args) {
        try {
            String message = "This string contains a secret message.";
            System.out.println("Plaintext: " + message + "\n");

            // generate a key
            KeyGenerator keygen = KeyGenerator.getInstance("AES");
            keygen.init(128);  // To use 256 bit keys, you need the "unlimited strength" encryption policy files from Sun.
            byte[] key = keygen.generateKey().getEncoded();
            SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");

            // build the initialization vector (randomly).
            SecureRandom random = new SecureRandom();
            byte iv[] = new byte[16];//generate random 16 byte IV AES is always 16bytes
            random.nextBytes(iv);
            IvParameterSpec ivspec = new IvParameterSpec(iv);

            // initialize the cipher for encrypt mode
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivspec);

            System.out.println("Key: " + new String(key, "utf-8") + " This is important when decrypting");
            System.out.println("IV: " + new String(iv, "utf-8") + " This is important when decrypting");
            System.out.println();

            // encrypt the message
            byte[] encrypted = cipher.doFinal(message.getBytes());
            System.out.println("Ciphertext: " + asHex(encrypted) + "\n");

            // reinitialize the cipher for decryption
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivspec);

            // decrypt the message
            byte[] decrypted = cipher.doFinal(encrypted);
            System.out.println("Plaintext: " + new String(decrypted) + "\n");
        } catch (IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException | InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException | NoSuchAlgorithmException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Turns array of bytes into string
     *
     * @param buf   Array of bytes to convert to hex string
     * @return  Generated hex string
     */
    public static String asHex(byte buf[]) {
        StringBuilder strbuf = new StringBuilder(buf.length * 2);
        int i;
        for (i = 0; i < buf.length; i++) {
            if (((int) buf[i] & 0xff) < 0x10) {
                strbuf.append("0");
            }
            strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
        }
        return strbuf.toString();
    }
}
于 2012-07-29T10:36:03.273 回答
5

我总是使用BouncyCastle

我还使用流框架而不是您描述的 for 循环:它处理提出的问题。大多数情况下我使用它,因为当涉及到密码学(和线程)时,我很少相信我自己的代码,我相信活着的人吃和呼吸它。这是我想要“gash”加密时使用的代码。即我没有特定的威胁模型,只是想要一些“有点安全”的东西。

密钥的十六进制编码使它们更容易操作/存储等等。我使用“makeKey”来......好吧......制作一个密钥,然后我可以在加密和解密方法中使用该密钥。您显然可以返回使用byte[]而不是使用十六进制字符串作为键。

    private static boolean initialised;
    private static void init() {
      if (initialised)
        return;
      Security.addProvider(new BouncyCastleProvider());
      initialised = true;
    }
    public static String makeKey() {
        init();
        KeyGenerator generator = KeyGenerator.getInstance(algorithm, provider);
        generator.init(keySize);
        Key key = generator.generateKey();
        byte[] encoded = key.getEncoded();
        return Strings.toHex(encoded);
}

public static String aesDecrypt(String hexKey, String hexCoded) {
        init();
        SecretKeySpec key = new SecretKeySpec(Strings.fromHex(hexKey), algorithm);
        Cipher cipher = Cipher.getInstance(algorithm + "/ECB/PKCS5Padding", provider);
        cipher.init(Cipher.DECRYPT_MODE, key);
        byte[] codedBytes = Strings.fromHex(hexCoded);
        CipherInputStream inputStream = new CipherInputStream(new ByteArrayInputStream(codedBytes), cipher);
        byte[] bytes = getBytes(inputStream, 256);
        String result = new String(bytes, "UTF-8");
        return result;
}

public static String aesEncrypt(String hexKey, String input) {
        init();
        SecretKeySpec key = new SecretKeySpec(Strings.fromHex(hexKey), algorithm);

        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "BC");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(input.length());
        CipherOutputStream outputStream = new CipherOutputStream(byteArrayOutputStream, cipher);
        setText(outputStream, input);
        byte[] outputBytes = byteArrayOutputStream.toByteArray();
        String output = new String(Strings.toHex(outputBytes));
        return output;
}
public static void setText(OutputStream outputStream, String text, String encoding) {
    try {
        outputStream.write(text.getBytes(encoding));
        outputStream.flush();
    } finally {
            outputStream.close();
    }
}
public static byte[] getBytes(InputStream inputStream, int bufferSize) {
    try {
        List<ByteArrayAndLength> list = Lists.newList();
        while (true) {
            byte[] buffer = new byte[bufferSize];
            int count = inputStream.read(buffer);
            if (count == -1) {
                byte[] result = new byte[ByteArrayAndLength.length(list)];
                int index = 0;
                for (ByteArrayAndLength byteArrayAndLength : list) {
                    System.arraycopy(byteArrayAndLength.bytes, 0, result, index, byteArrayAndLength.length);
                    index += byteArrayAndLength.length;
                }
                assert index == result.length;
                return result;
            }
            list.add(new ByteArrayAndLength(buffer, count));
        }
    } finally {
            inputStream.close();
    }
}
    static class ByteArrayAndLength {
    byte[] bytes;
    int length;

    public ByteArrayAndLength(byte[] bytes, int length) {
        super();
        this.bytes = bytes;
        this.length = length;
    }

    static int length(List<ByteArrayAndLength> list) {
        int result = 0;
        for (ByteArrayAndLength byteArrayAndLength : list) {
            result += byteArrayAndLength.length;
        }
        return result;
    }
}

我已经删除了一些异常捕获以减少代码的大小,并将Strings.fromHex字符串转换回byte[]

于 2012-07-29T09:43:48.830 回答
2

也许你应该考虑使用一个javax.crypto包。以下是如何使用密码的示例:

DES加密

希望这可以帮助

于 2012-07-29T09:13:00.713 回答
0

在走这条路之前,我会认真三思。该软件的开发已停止,因为存在标准替代品,请查看邮件列表,自 2009 年以来没有任何重大活动。在我的书中,这意味着该软件已被放弃,而被放弃的软件意味着您或多或少靠自己。

看看这里,有几个问题和答案可以帮助你喜欢这个jasypt乍一看很有趣,它可以为您简化事情(但仍使用标准的 JCE 基础架构)

于 2012-07-29T09:17:47.803 回答