Android设备和.Net服务器上的AES加密

时间:2015-01-10 18:34:56

标签: android .net encryption cross-platform aes

我是加密世界的新手。如果这是基本的,请原谅。在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和盐提供硬编码来解决,但这会破坏安全性的目的。

1 个答案:

答案 0 :(得分:0)

盐或IV都不需要保密,所以您只需将密文发送到另一侧即可。通常,它们以密文为前缀,但可以使用任何编码。

代码

下面是一个包含以下内容的示例:

  • 2字节盐大小
  • 2字节IV大小
  • IV
  • 4字节密文大小
  • 密文

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时不要忘记使用时间静态比较。我对流处理并不十分满意,因此尽可能优化。