我是加密世界的新手。如果这是基本的,请原谅。在Android方面,我尝试使用以下代码片段进行加密:
// Get the salt
SecureRandom random = new SecureRandom();
byte[] salt = new byte[saltLength];
random.nextBytes(salt);
// Secret key
SecretKey secretKey = getSecretKey(seed, salt);
// Get Cipher instance for AES with Padding algorithm PKCS5
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Initialization vector, as CBC requires IV
byte[] iv = new byte[cipher.getBlockSize()];
random.nextBytes(iv);
// Algorithm spec for IV
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParams);
// Encrypt the text
byte[] encryptedTextInBytes = cipher.doFinal(textToBeEncrypted
.getBytes("UTF-8"));
return Base64Encoder.encode(encryptedTextInBytes);
我的问题是如何在服务器端解密,因为salt是完全随机的。初始化矢量(IV)也出现了同样的问题。有任何想法吗?这可以通过为IV和盐提供硬编码来解决,但这会破坏安全性的目的。
答案 0 :(得分:0)
盐或IV都不需要保密,所以您只需将密文发送到另一侧即可。通常,它们以密文为前缀,但可以使用任何编码。
下面是一个包含以下内容的示例:
ByteArrayOutputStream blob = new ByteArrayOutputStream();
DataOutputStream dataBlob = new DataOutputStream(blob);
// Get the salt
SecureRandom random = new SecureRandom();
byte[] salt = new byte[saltLength];
random.nextBytes(salt);
dataBlob.writeShort(saltLength);
dataBlob.write(salt);
// Secret key
SecretKey secretKey = getSecretKey(seed, salt);
// Get Cipher instance for AES with Padding algorithm PKCS5
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Initialization vector, as CBC requires IV
byte[] iv = new byte[cipher.getBlockSize()];
random.nextBytes(iv);
dataBlob.write(iv.length);
dataBlob.write(iv);
// Algorithm spec for IV
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParams);
// Encrypt the text
byte[] encryptedTextInBytes = cipher.doFinal(textToBeEncrypted
.getBytes(StandardCharsets.UTF_8));
dataBlob.writeInt(encryptedTextInBytes.length);
dataBlob.write(encryptedTextInBytes);
// out of scope: add HMAC protection over current contents of blob here
// (or while writing it to dataBlob, also update a HMAC)
// Base64Encoder encode;
return Base64Encoder.encode(blob.toByteArray());
为了安全起见,您需要添加完整性和身份验证保护。这意味着在盐,IV和密文的(编码)的字节上应用HMAC。如果完整性不受保护,由于明文/填充oracle攻击,这种方案可能甚至不利于机密性。
例如:
public static String encrypt(int saltLength, byte[] seed, String textToBeEncrypted) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
Mac hmac = Mac.getInstance("HmacSHA256");
// Get the salt
SecureRandom random = new SecureRandom();
byte[] salt = new byte[saltLength];
random.nextBytes(salt);
// Secret key
SecretKey secretKey = getSecretKey(seed, salt);
// Initialization vector, as CBC requires IV
byte[] iv = new byte[cipher.getBlockSize()];
random.nextBytes(iv);
// Algorithm spec for IV
IvParameterSpec ivParams = new IvParameterSpec(iv);
// Get Cipher instance for AES with Padding algorithm PKCS5
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParams);
hmac.init(secretKey);
ByteArrayOutputStream blob = new ByteArrayOutputStream();
MacOutputStream macStream = new MacOutputStream(blob, hmac);
DataOutputStream dataBlob = new DataOutputStream(macStream);
dataBlob.writeShort(saltLength);
dataBlob.write(salt);
dataBlob.write(iv.length);
dataBlob.write(iv);
// Encrypt the text
byte[] encryptedTextInBytes = cipher.doFinal(textToBeEncrypted
.getBytes(StandardCharsets.UTF_8));
dataBlob.writeInt(encryptedTextInBytes.length);
dataBlob.write(encryptedTextInBytes);
dataBlob.writeShort(hmac.getMacLength());
dataBlob.write(macStream.getMac());
return Base64Encoder encode(blob.toByteArray());
你也需要流媒体课程:
public class MacOutputStream extends FilterOutputStream {
private final Mac mac;
public MacOutputStream(OutputStream out, Mac mac) {
super(out);
this.mac = mac;
}
@Override
public void write(byte[] b) throws IOException {
mac.update(b);
out.write(b);
}
@Override
public void write(int b) throws IOException {
mac.update((byte) b);
out.write(b);
}
public byte[] getMac() {
return mac.doFinal();
}
}
请注意,使用单独的密钥更好,并且在验证MAC时不要忘记使用时间静态比较。我对流处理并不十分满意,因此尽可能优化。