AES将任何Java对象加密为base64字符串

问题描述 投票:1回答:3

我正在尝试使用Cipher类将任何java对象(在此示例中为Integer,但Date也可以使用)加密为base64字符串。基本上,我使用ByteArrayOutputStream将给定的对象转换为字节数组,并使用Cipher对该字节数组进行加密。见下文

for (Integer i = 0; i < 10; i++) {

    ByteArrayOutputStream bos = new ByteArrayOutputStream();

    ObjectOutput oos = new ObjectOutputStream(bos);
    oos.writeObject(i);
    oos.flush();

    byte[] data = bos.toByteArray();


    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec("&E(H+MbQeThWmZq4".getBytes("UTF-8"), "AES"));

    String base64output = Base64.getEncoder().encodeToString(cipher.doFinal(data));

    System.out.println(i + " - " + base64output);
}

输出

0 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa94LOaOdEXeZZm8qNoELOLdj
1 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa97aK6ELffW8n7vEkNAbC9RW
2 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa97mJ1m8lVtjwfGbHbMO2rxu
3 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa942rroZJbe2KN0/t8ukOkWd
4 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa97rbkvF4HLzuvGTm4JMJw+2
5 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa94zvSlIQe8RQI8t5/H74ShO
6 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa97tNLWZHmR0rNkDXZtVWA2Y
7 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa94lr84KZ6MnUsPOFyJIfDTB
8 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa97e6ihJ8SXmz9sy9XXwWeAz
9 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa97neBL2tLG2TXgCI/wDuyMo

由于相同的前缀BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa9,我觉得很奇怪对于每个加密对象。在此示例中,我为每个对象使用相同的键,但这不应该成为此问题的原因。

我还用字符串和日期(而不是整数)测试了此示例。将日期编码为字节数组并使用相同的方法对其进行加密也会导致这个问题对于所有Date对象都具有相同的前缀,而使用相同的方法对字符串进行编码似乎效果很好。每个编码和加密字符串导致另一个加密的base64字符串。见下文

加密日期的输出:(也具有相同的前缀)

0 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JRj1HrbSaioOqhbM2uZi2r0
1 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JQ0q0kophfAfiPxe0U+sb1R
2 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JTeTKnbYsLo6TjfuQF9PYIk
3 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JSrDPGtepg4HWUL6VeBtWg7
4 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JS7dlSsNjnY011F2BooNnKW
5 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JStO2xPQvT76/k+xMdaDBpQ
6 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JQqz4J3yO8G9taHi7b/Zefl
7 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JR8/fOAiuGM8tO8zMcju4Xk
8 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JSMDHi6UyD5QQY1jRXNCErc
9 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JRfKstfsC8dPYuPfd9f2B+B

加密字符串的输出:(按预期工作)

0 - TNpI3oLRzH5id6c/yRJlQQ==
1 - yMkm+ZuYWs4EnISo56Zljw==
2 - 03i1Lv01Nn2sGDGmtpRAIg==
3 - 5skvWbkcVXfT2TScaGxNfQ==
4 - 0p9qg5U+DqAnCBdyji+L9Q==
5 - gD5xPtAMy34xC90hKCQeWA==
6 - oQwKUhuxC5X/f6U9G9la8Q==
7 - 72cvCiLks3DDaTLAQvoVfw==
8 - wQu7Ug5RHg5egbNTI0YXQw==
9 - x1BQVwy3r6MP3SDLl/mktw==

任何想法?

编辑:即使当我使用CBC或其他加密方法(例如DES或Blowfish)时,也会发生相同的问题。我希望ByteArrayOutputStream中的每个字节数组都应加密为完全不同的base64字符串,即使它们具有相同的前缀且长度约为〜90%。]

我正在尝试使用Cipher类将任何java对象(在此示例中为Integer,但Date也可以使用)加密为base64字符串。基本上,我使用...

java arrays encryption aes encryption-symmetric
3个回答
0
投票

如Mark所指出的,ObjectOutputStream创建一个对象标头,所以通用前缀是因为and


-1
投票

在加密之前使用对象序列化不是一个好主意。您可以对数据进行加密以进行传输保护,在这种情况下,TLS更有意义。或者您正在加密以延长存储时间,在这种情况下,序列化很危险,因为序列化格式可能会更改。哎呀,您将来可能希望更改整个语言/运行时。


-1
投票

您看到的行为是您将无模式public static void main(String[] args) throws Exception { // input, a date and message Date date = new Date(); String message = "hello world"; // AES-128 key (replace by a real 256 bit key in your case) SecretKey aesKey = new SecretKeySpec(new byte[128 / Byte.SIZE], "AES"); // default nonce sizes for GCM, using a constant should be preferred int nonceSize = 96; int tagSize = 128; String cts; try (StringWriter stringWriter = new StringWriter(); PrintWriter out = new PrintWriter(stringWriter)) { for (Integer i = 0; i < 10; i++) { byte[] randomNonce = createRandomIV(nonceSize); GCMParameterSpec gcmSpec = new GCMParameterSpec(128, randomNonce); byte[] encodedMessage = message.getBytes(StandardCharsets.UTF_8); ByteBuffer encodedNumberDateAndMessage = ByteBuffer.allocate(Integer.BYTES + Long.BYTES + encodedMessage.length); encodedNumberDateAndMessage.putInt(i); encodedNumberDateAndMessage.putLong(date.getTime()); encodedNumberDateAndMessage.put(encodedMessage); // for reading we need to flip the buffer encodedNumberDateAndMessage.flip(); ByteBuffer encryptedNumberDateAndMessage = ByteBuffer.allocate(nonceSize / Byte.SIZE + encodedNumberDateAndMessage.limit() + tagSize / Byte.SIZE); encryptedNumberDateAndMessage.put(randomNonce); Cipher gcm = Cipher.getInstance("AES/GCM/NoPadding"); gcm.init(Cipher.ENCRYPT_MODE, aesKey, gcmSpec); gcm.doFinal(encodedNumberDateAndMessage, encryptedNumberDateAndMessage); // not required, we'll be using array() method // encryptedNumberDateAndMessage.flip(); // we can use the full array as there String base64Ciphertext = Base64.getEncoder().encodeToString(encryptedNumberDateAndMessage.array()); if (i != 0) { out.write('\n'); } out.write(base64Ciphertext); } cts = stringWriter.toString(); } System.out.println(cts); // TODO decrypt ciphertexts in cts // hint use BufferedReader to read lines and don't forget to strip off the IV/Nonce first } private static byte[] createRandomIV(int sizeInBits) { if (sizeInBits % Byte.SIZE != 0) { throw new IllegalArgumentException("Invalid IV size, must be a multiple of 8 bits"); } byte[] randomNonce = new byte[sizeInBits / Byte.SIZE]; SecureRandom rbg = new SecureRandom(); rbg.nextBytes(randomNonce); return randomNonce; } 模式与明文相似性结合使用的结果。所有分组密码(AES,河豚,DES)都会遇到相同的问题。

使用LHMsZPgZOz7nEcN5adB03+twTG2/ITfPnUUy4DxdgFEBAxm3HNDg8eXVnuvo80i4WMjY eRJuw1ynrD3GeMmFTYiQc6VxelJuz8wHZtbl+7cepteKdtzcsdIDcDHBqvfjyzZp6WXd MOkTLt4pk+sFm6I+CH4c90lxrRmwFKmS1wbX5eRSZYy6xqEjSz6iGC1vBXkPbl3k1C5r cB5hKbpiAeNmbZYy1vdK5vissWYlkL6h6XJEYEFZaK7M097LkVAB01nu5GtCBUjPMjrK LHzr/iudU3BPYmrimAIugjSckzXrzm03Ucgyb8laKktbh/Um4K2nyAGE2+T1aLH6JaYX dg9SmcPl+dolHSIQPyvMUEPyu3VLSNPbN7ErPY93sjfKVyZsaGgft/cP4kUzNWEyRgAo PiLHu4TKZMfBlFXst1867hEywST3RBbSSQ1g9D4DOkqh3oPkvsXP5INIEANZr2BHta38 4pJITAvij26NphYf9/ry5yGm+qPAaNG0Hqrk5ruVa60+V7k0jqDozjsST8OygyvkLrgY HI6I3UHgzBNjskSJeo9fS3Cw3oKY8tneFbChtLz35DbcASOjpi7U9LKTL39lBTOBaZkG jRycn4uSfT6JlDk3jn64wTL07I7bHvTSPSbWVG7XdKeSgOibW7FiCtTXojDPi8iywD58 时,一切都会消失如果您根据需要提供IV:

© www.soinside.com 2019 - 2024. All rights reserved.