Merge "Add a CipherManager for en/decryption using AndroidCrypto" into udc-dev

This commit is contained in:
Guojing Yuan
2023-02-20 21:06:42 +00:00
committed by Android (Google) Code Review

View File

@@ -0,0 +1,143 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.companion.transport;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.util.Slog;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
/**
* This class can be used to encrypt and decrypt bytes using Android Cryptography
*/
public class CryptoManager {
private static final String TAG = "CDM_CryptoManager";
private static final String KEY_STORE_ALIAS = "cdm_secret";
private static final String ALGORITHM = KeyProperties.KEY_ALGORITHM_AES;
private static final String BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC;
private static final String PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7;
private static final String TRANSFORMATION = ALGORITHM + "/" + BLOCK_MODE + "/" + PADDING;
private final KeyStore mKeyStore;
public CryptoManager() {
// Initialize KeyStore
try {
mKeyStore = KeyStore.getInstance("AndroidKeyStore");
mKeyStore.load(null);
} catch (KeyStoreException | IOException | NoSuchAlgorithmException
| CertificateException e) {
throw new RuntimeException(e);
}
}
/**
* Output format: | iv length (int) | iv | encrypted bytes length (int) | encrypted bytes |
*/
public byte[] encrypt(byte[] input) {
try {
// Encrypt using Cipher
Cipher encryptCipher = Cipher.getInstance(TRANSFORMATION);
encryptCipher.init(Cipher.ENCRYPT_MODE, getKey());
byte[] encryptedBytes = encryptCipher.doFinal(input);
// Write to bytes
ByteBuffer buffer = ByteBuffer.allocate(
4 + encryptCipher.getIV().length + 4 + encryptedBytes.length)
.putInt(encryptCipher.getIV().length)
.put(encryptCipher.getIV())
.putInt(encryptedBytes.length)
.put(encryptedBytes);
return buffer.array();
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| IllegalBlockSizeException | BadPaddingException e) {
Slog.e(TAG, e.getMessage());
return null;
}
}
/**
* Input format: | iv length (int) | iv | encrypted bytes length (int) | encrypted bytes |
*/
public byte[] decrypt(byte[] input) {
ByteBuffer buffer = ByteBuffer.wrap(input);
byte[] iv = new byte[buffer.getInt()];
buffer.get(iv);
byte[] encryptedBytes = new byte[buffer.getInt()];
buffer.get(encryptedBytes);
try {
Cipher decryptCipher = Cipher.getInstance(TRANSFORMATION);
decryptCipher.init(Cipher.DECRYPT_MODE, getKey(), new IvParameterSpec(iv));
return decryptCipher.doFinal(encryptedBytes);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| InvalidAlgorithmParameterException | IllegalBlockSizeException
| BadPaddingException e) {
Slog.e(TAG, e.getMessage());
return null;
}
}
private SecretKey getKey() {
try {
KeyStore.Entry keyEntry = mKeyStore.getEntry(KEY_STORE_ALIAS, null);
if (keyEntry instanceof KeyStore.SecretKeyEntry
&& ((KeyStore.SecretKeyEntry) keyEntry).getSecretKey() != null) {
return ((KeyStore.SecretKeyEntry) keyEntry).getSecretKey();
} else {
return createKey();
}
} catch (NoSuchAlgorithmException | UnrecoverableEntryException | KeyStoreException e) {
throw new RuntimeException(e);
}
}
private SecretKey createKey() {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
keyGenerator.init(
new KeyGenParameterSpec.Builder(KEY_STORE_ALIAS,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(BLOCK_MODE)
.setEncryptionPaddings(PADDING)
.setUserAuthenticationRequired(false)
.setRandomizedEncryptionRequired(true)
.build());
return keyGenerator.generateKey();
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
}
}
}