Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/329.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
AES将任何java对象加密为base64字符串_Java_Arrays_Encryption_Aes_Encryption Symmetric - Fatal编程技术网

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

AES将任何java对象加密为base64字符串,java,arrays,encryption,aes,encryption-symmetric,Java,Arrays,Encryption,Aes,Encryption Symmetric,我试图使用Cipher类将任何java对象(在本例中为整数,但Date也可以)加密为base64字符串。 基本上,我使用ByteArrayOutputStream将给定对象转换为字节数组,并使用密码对该字节数组进行加密。见下文 for (Integer i = 0; i < 10; i++) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutput oos = new ObjectOu

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

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);
}
对我来说似乎很奇怪,因为相同的前缀
BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/hfx9lgpdjc0ilxw8fyd1hfb54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa9
对于每个加密对象。在本例中,我对每个对象使用相同的键,但这不应该是出现此问题的原因

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

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-CPQXMKQW7MHCksxxSymmjTrpnfgujbjylivkehgm2JRJ1HRBSAIOQHBM2UZI2R0
1-cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JQ0q0kophfAfiPxe0U+sb1R
2-cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JTeTKnbYsLo6TjfuQF9PYIk
3-cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JSrDPGtepg4HWUL6VeBtWg7
4-cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JS7dlSsNjnY011F2BooNnKW
5-cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JStO2xPQvT76/k+xMdaDBpQ
6-CPQxMkQW7MHCksxxSymmJTRPNFGUJBJYLIVKEHGM2JQZ4J3YO8G9TAHI7B/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-OQWKUUXC5X/f6U9G9la8Q==
7-72cvCiLks3DDaTLAQvoVfw==
8-wQu7Ug5RHg5egbNTI0YXQw==
9-x1BQVwy3r6MP3SDLl/mktw==
有什么想法吗

编辑:
即使我使用CBC或其他加密方法,如DES或Blowfish,也会出现同样的问题。我希望ByteArrayOutputStream中的每个字节数组都应该加密为完全不同的base64字符串,即使它们的前缀相同,长度约为90%。

正如Mark指出的,
ObjectOutputStream
创建了一个对象头,因此,公共前缀是因为您没有使用salt您使用的是相同的加密密钥


这些弱点使得加密解决方案(即您的代码)容易受到攻击,即使算法本身非常好。您刚刚以一种不安全的方式实现了它。

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

我建议你制定自己的协议。例如,在这种情况下,您可以使用或将整数编码为4字节。这样,整数的最小值为4字节(作为无符号32位big-endian值)。对于非常复杂的方法,您甚至可以查看ASN.1结构和编码(在BouncyCastle和其他库中实现)

Java日期实际上只是内部的
,可以完美地存储在8个字节中。另一个选项是将其编码为(UTC)日期字符串,并使用US ASCII兼容编码(
StandardCharsets.US_ASCII
)存储该字符串


请注意,欧洲央行模式非常危险。例如,假设
0x00FFFFFF
以上的值不常见,并且您不想泄露这些值的存在。还可以设想,最重要的字节是块的最后一个字节,而不是头字节。在这种情况下,很容易区分具有例如
0x01
的块和具有
0x00
的块,这在这种情况下应该更常见。所以你立即泄露了你的明文信息

如果使用静态IV而不是随机(或至少完全不可预测)IV值,则在CBC模式下,此问题同样突出。您必须为每个CBC加密使用随机IV以确保安全。您可以将IV与密文一起存储。对于CBC来说,16字节IV通常只是在密文前加上前缀。但是,您最好使用具有12字节随机nonce的认证GCM模式

有点遗憾的是,Java完全允许重用密码实例——例如,它不允许
密码
在使用后销毁密钥材料。它默认为一种不安全的模式,在这种模式下,静脉注射是重复的,这是非常可耻的。你得自己处理静脉注射问题


使用GCM和ByteBuffer的示例:

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;
}

它由nonce、整数的密文、表示日期的长值和“hello world”字符串以及最终的身份验证标记组成,在Java中,它被认为是密文的一部分。

您看到的行为是使用无模式
ECB
模式与明文中的相似性相结合的结果。所有分组密码(AES、Blowfish、DES)都会有相同的问题

当使用
CBC
时,如果您根据需要提供IVs,所有这些都会消失:


顺便说一句您使用的是一个16字节的密钥,得到的是AES-128而不是AES-256。

序列化的整数看起来太长了。尝试检查序列化整数的字节数组(
data
)。我从来没做过,但我知道
0 - jt3Mk13pGjeaFf1oNq4LfmQ4z/31nRG4KtZ4H3RK6k/GA1anC3/lrzSXoLsQ6jMsVEpnxU13wAu6lkZJ3it1Ei4i4EsNFixc+YX4K6cIIv4ByY5Q246jd3H0m11C2FZJ
1 - Jqd0RB6lOITqifAaWluW6jx8F8gY4btZHx12CiXtZjfnehhtk64jva4eGTQd4EpvB/5Q/ORhZCNgF3ue0/Na1R9MCsK+mULAcyANdNcLyKbXo272G21z0LPCeweXdjhu
2 - xHdCG6rWNDyLTl8zruo8u+45V/RMXkrB7K+QU5r9lpc3FzvDwpl0wmy9Yj3FOyjMulmVT1zahH+wWVrmB9gNcXy7sGyCH/anJANC396OcDyQXqNIyvOPw9mpUmmRQcwR
3 - ygIDkLtQTupkbB35SzRflE3RAMmdYGSkdGZgRctFHdZCqGt+Arb3RbvhoAiiE9PwkyLmifyllQTTSutvV/ZtlGaGMX3v4bQUZDoaSyXQd9xn+pUSJk87NDVGi37xWw1O
4 - cJYSthCHHGeCqnuBJY8YdUbptKD3XNb2nt+pyIc94vRvjquYf7atu0+bDndFnePWvrlPzFIFXVB8CuANIsDhzRSNEOOU/wOkwcAN2AdavCqlZqN0Mtqdg4vqKGWx2oAE
5 - f7/gu8fJ8jkyhRAXJkLqdnJMLjCfFSjq8ovjhlNcuDPk8N/mYlA2845PGgi74Kb/zCG1WH8NtFK06xrpn15KyUxSANxoQ6C9QnzE9sc4aZj5rUatWeekvBfbqngq3JpG
6 - PitP2MuX4/Yysso8dCl1h2VK3MKoU2YpyzvLgZ3hZX/cBzSWp9O0Eafzj6GIMvAGVaL0x0V+K2Wv4eBOLIhDczhJXvHmKvTU7ZJnAwI37JXkOecN4HJdAXfFqg2WkT5f
7 - 1Mj8WnSqgLE08qfeYC1a3nZQ1jszxbT9J+ClUy8rCYusZHiArQcCgCwrNbWbI2yVfRjYOpsuTgyq31fnuHrkVfGu6RhiRhucR0a0Dign5fSU71STKksweHQ+oYQJibnQ
8 - TgGDGlOFWyfKO50xxPTPOmSpEsmpIVtWfnXkhhAoRsbZwo6z4oAuBJQs8EibsOr/r8KY5UHRbp+q3SlDhBE3mWszybMdOVRQKyJ1lZVXpmxmjXp/W2AqitsjCTKQaHi+
9 - 4xUnNjT8P0WiPtYg6ojrrQZnF0gU0wnndNQdLfPOMxoDvWjfe5OuEcY55yDRIosdpkeItTMVN1CRL4WecFgM8mBIVlnssE4Q1GM87PWNHipGZ91+MJwdsr0yUfCsJyRv