Loading services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java +3 −1 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static android.companion.CompanionDeviceManager.COMPANION_DEVICE_DISCOVER import static android.content.ComponentName.createRelative; import static com.android.server.companion.Utils.prepareForIpc; import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_PERMISSION_RESTORE; import android.annotation.NonNull; import android.annotation.Nullable; Loading Loading @@ -91,7 +92,8 @@ public class SystemDataTransferProcessor { mAssociationStore = associationStore; mSystemDataTransferRequestStore = systemDataTransferRequestStore; mTransportManager = transportManager; mTransportManager.setListener(this::onReceivePermissionRestore); mTransportManager.addListener(MESSAGE_REQUEST_PERMISSION_RESTORE, this::onReceivePermissionRestore); mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class); mExecutor = Executors.newSingleThreadExecutor(); } Loading services/companion/java/com/android/server/companion/securechannel/AttestationVerifier.java +1 −1 Original line number Diff line number Diff line Loading @@ -35,7 +35,7 @@ import java.util.function.BiConsumer; /** * Helper class to perform attestation verification synchronously. */ class AttestationVerifier { public class AttestationVerifier { private static final long ATTESTATION_VERIFICATION_TIMEOUT_SECONDS = 10; // 10 seconds private static final String PARAM_OWNED_BY_SYSTEM = "android.key_owned_by_system"; Loading services/companion/java/com/android/server/companion/securechannel/SecureChannel.java +4 −3 Original line number Diff line number Diff line Loading @@ -110,7 +110,7 @@ public class SecureChannel { this(in, out, callback, null, new AttestationVerifier(context)); } private SecureChannel( public SecureChannel( final InputStream in, final OutputStream out, Callback callback, Loading Loading @@ -381,9 +381,10 @@ public class SecureChannel { private void exchangeAuthentication() throws IOException, GeneralSecurityException, BadHandleException, CryptoException { if (mVerifier == null) { if (mPreSharedKey != null) { exchangePreSharedKey(); } else { } if (mVerifier != null) { exchangeAttestation(); } } Loading services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java +96 −21 Original line number Diff line number Diff line Loading @@ -19,9 +19,9 @@ package com.android.server.companion.transport; import static android.Manifest.permission.DELIVER_COMPANION_MESSAGES; import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_PERMISSION_RESTORE; import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_PLATFORM_INFO; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.ActivityManagerInternal; import android.content.Context; Loading @@ -30,12 +30,17 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.os.Binder; import android.os.Build; import android.os.ParcelFileDescriptor; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; import com.android.server.companion.transport.Transport.Listener; import java.io.IOException; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; Loading @@ -44,6 +49,8 @@ public class CompanionTransportManager { private static final String TAG = "CDM_CompanionTransportManager"; private static final boolean DEBUG = false; private static final int NON_ANDROID = -1; private boolean mSecureTransportEnabled = true; private static boolean isRequest(int message) { Loading @@ -54,24 +61,29 @@ public class CompanionTransportManager { return (message & 0xFF000000) == 0x33000000; } public interface Listener { void onRequestPermissionRestore(byte[] data); } private final Context mContext; @GuardedBy("mTransports") private final SparseArray<Transport> mTransports = new SparseArray<>(); @Nullable private Listener mListener; @NonNull private final Map<Integer, Listener> mListeners = new HashMap<>(); private Transport mTempTransport; public CompanionTransportManager(Context context) { mContext = context; } public void setListener(@NonNull Listener listener) { mListener = listener; /** * Add a message listener when a message is received for the message type */ @GuardedBy("mTransports") public void addListener(int message, @NonNull Listener listener) { mListeners.put(message, listener); for (int i = 0; i < mTransports.size(); i++) { mTransports.valueAt(i).addListener(message, listener); } } /** Loading Loading @@ -105,15 +117,7 @@ public class CompanionTransportManager { detachSystemDataTransport(packageName, userId, associationId); } final Transport transport; if (isSecureTransportEnabled(associationId)) { transport = new SecureTransport(associationId, fd, mContext, mListener); } else { transport = new RawTransport(associationId, fd, mContext, mListener); } transport.start(); mTransports.put(associationId, transport); initializeTransport(associationId, fd); } } Loading @@ -128,13 +132,85 @@ public class CompanionTransportManager { } } @GuardedBy("mTransports") private void initializeTransport(int associationId, ParcelFileDescriptor fd) { if (!isSecureTransportEnabled()) { Transport transport = new RawTransport(associationId, fd, mContext); for (Map.Entry<Integer, Listener> entry : mListeners.entrySet()) { transport.addListener(entry.getKey(), entry.getValue()); } transport.start(); mTransports.put(associationId, transport); Slog.i(TAG, "RawTransport is created"); return; } // Exchange platform info to decide which transport should be created mTempTransport = new RawTransport(associationId, fd, mContext); for (Map.Entry<Integer, Listener> entry : mListeners.entrySet()) { mTempTransport.addListener(entry.getKey(), entry.getValue()); } mTempTransport.addListener(MESSAGE_REQUEST_PLATFORM_INFO, this::onPlatformInfoReceived); mTempTransport.start(); int sdk = Build.VERSION.SDK_INT; String release = Build.VERSION.RELEASE; // data format: | SDK_INT (int) | release length (int) | release | final ByteBuffer data = ByteBuffer.allocate(4 + 4 + release.getBytes().length) .putInt(sdk) .putInt(release.getBytes().length) .put(release.getBytes()); // TODO: it should check if preSharedKey is given mTempTransport.requestForResponse(MESSAGE_REQUEST_PLATFORM_INFO, data.array()); } /** * Depending on the remote platform info to decide which transport should be created */ @GuardedBy("mTransports") private void onPlatformInfoReceived(byte[] data) { // TODO: it should check if preSharedKey is given ByteBuffer buffer = ByteBuffer.wrap(data); int remoteSdk = buffer.getInt(); byte[] remoteRelease = new byte[buffer.getInt()]; buffer.get(remoteRelease); Slog.i(TAG, "Remote device SDK: " + remoteSdk + ", release:" + new String(remoteRelease)); Transport transport = mTempTransport; mTempTransport = null; int sdk = Build.VERSION.SDK_INT; String release = Build.VERSION.RELEASE; if (remoteSdk == NON_ANDROID) { // TODO: pass in a real preSharedKey transport = new SecureTransport(transport.getAssociationId(), transport.getFd(), mContext, null, null); } else if (sdk < Build.VERSION_CODES.UPSIDE_DOWN_CAKE || remoteSdk < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { // TODO: depending on the release version, either // 1) using a RawTransport for old T versions // 2) or an Ukey2 handshaked transport for UKey2 backported T versions } else { Slog.i(TAG, "Creating a secure channel"); transport = new SecureTransport(transport.getAssociationId(), transport.getFd(), mContext); for (Map.Entry<Integer, Listener> entry : mListeners.entrySet()) { transport.addListener(entry.getKey(), entry.getValue()); } transport.start(); } mTransports.put(transport.getAssociationId(), transport); } public Future<?> requestPermissionRestore(int associationId, byte[] data) { synchronized (mTransports) { final Transport transport = mTransports.get(associationId); if (transport == null) { return CompletableFuture.failedFuture(new IOException("Missing transport")); } return transport.requestForResponse(MESSAGE_REQUEST_PERMISSION_RESTORE, data); } } Loading @@ -146,10 +222,9 @@ public class CompanionTransportManager { this.mSecureTransportEnabled = enabled; } private boolean isSecureTransportEnabled(int associationId) { private boolean isSecureTransportEnabled() { boolean enabled = !Build.IS_DEBUGGABLE || mSecureTransportEnabled; // TODO: version comparison logic return enabled; } } services/companion/java/com/android/server/companion/transport/CryptoManager.java +38 −65 Original line number Diff line number Diff line Loading @@ -16,51 +16,51 @@ 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 java.util.Arrays; 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; import javax.crypto.spec.SecretKeySpec; /** * This class can be used to encrypt and decrypt bytes using Android Cryptography * This class uses Java Cryptography to encrypt and decrypt messages */ public class CryptoManager { private static final String TAG = "CDM_CryptoManager"; private static final int SECRET_KEY_LENGTH = 32; private static final String ALGORITHM = "AES"; private static final String TRANSFORMATION = "AES/CBC/PKCS7Padding"; 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 byte[] mPreSharedKey; private Cipher mEncryptCipher; private Cipher mDecryptCipher; private final KeyStore mKeyStore; private SecretKey mSecretKey; public CryptoManager() { // Initialize KeyStore public CryptoManager(byte[] preSharedKey) { if (preSharedKey == null) { mPreSharedKey = Arrays.copyOf(new byte[0], SECRET_KEY_LENGTH); } else { mPreSharedKey = Arrays.copyOf(preSharedKey, SECRET_KEY_LENGTH); } mSecretKey = new SecretKeySpec(mPreSharedKey, ALGORITHM); try { mKeyStore = KeyStore.getInstance("AndroidKeyStore"); mKeyStore.load(null); } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException e) { throw new RuntimeException(e); mEncryptCipher = Cipher.getInstance(TRANSFORMATION); mEncryptCipher.init(Cipher.ENCRYPT_MODE, mSecretKey); mDecryptCipher = Cipher.getInstance(TRANSFORMATION); } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) { Slog.e(TAG, e.getMessage()); } } Loading @@ -69,21 +69,19 @@ public class CryptoManager { */ 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); if (mEncryptCipher == null) { return null; } // Write to bytes byte[] encryptedBytes = mEncryptCipher.doFinal(input); ByteBuffer buffer = ByteBuffer.allocate( 4 + encryptCipher.getIV().length + 4 + encryptedBytes.length) .putInt(encryptCipher.getIV().length) .put(encryptCipher.getIV()) 4 + mEncryptCipher.getIV().length + 4 + encryptedBytes.length) .putInt(mEncryptCipher.getIV().length) .put(mEncryptCipher.getIV()) .putInt(encryptedBytes.length) .put(encryptedBytes); return buffer.array(); } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) { } catch (IllegalBlockSizeException | BadPaddingException e) { Slog.e(TAG, e.getMessage()); return null; } Loading @@ -99,45 +97,20 @@ public class CryptoManager { 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) { mDecryptCipher.init(Cipher.DECRYPT_MODE, getKey(), new IvParameterSpec(iv)); return mDecryptCipher.doFinal(encryptedBytes); } catch (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); if (mSecretKey != null) { return mSecretKey; } mSecretKey = new SecretKeySpec(mPreSharedKey, ALGORITHM); return mSecretKey; } } Loading
services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java +3 −1 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static android.companion.CompanionDeviceManager.COMPANION_DEVICE_DISCOVER import static android.content.ComponentName.createRelative; import static com.android.server.companion.Utils.prepareForIpc; import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_PERMISSION_RESTORE; import android.annotation.NonNull; import android.annotation.Nullable; Loading Loading @@ -91,7 +92,8 @@ public class SystemDataTransferProcessor { mAssociationStore = associationStore; mSystemDataTransferRequestStore = systemDataTransferRequestStore; mTransportManager = transportManager; mTransportManager.setListener(this::onReceivePermissionRestore); mTransportManager.addListener(MESSAGE_REQUEST_PERMISSION_RESTORE, this::onReceivePermissionRestore); mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class); mExecutor = Executors.newSingleThreadExecutor(); } Loading
services/companion/java/com/android/server/companion/securechannel/AttestationVerifier.java +1 −1 Original line number Diff line number Diff line Loading @@ -35,7 +35,7 @@ import java.util.function.BiConsumer; /** * Helper class to perform attestation verification synchronously. */ class AttestationVerifier { public class AttestationVerifier { private static final long ATTESTATION_VERIFICATION_TIMEOUT_SECONDS = 10; // 10 seconds private static final String PARAM_OWNED_BY_SYSTEM = "android.key_owned_by_system"; Loading
services/companion/java/com/android/server/companion/securechannel/SecureChannel.java +4 −3 Original line number Diff line number Diff line Loading @@ -110,7 +110,7 @@ public class SecureChannel { this(in, out, callback, null, new AttestationVerifier(context)); } private SecureChannel( public SecureChannel( final InputStream in, final OutputStream out, Callback callback, Loading Loading @@ -381,9 +381,10 @@ public class SecureChannel { private void exchangeAuthentication() throws IOException, GeneralSecurityException, BadHandleException, CryptoException { if (mVerifier == null) { if (mPreSharedKey != null) { exchangePreSharedKey(); } else { } if (mVerifier != null) { exchangeAttestation(); } } Loading
services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java +96 −21 Original line number Diff line number Diff line Loading @@ -19,9 +19,9 @@ package com.android.server.companion.transport; import static android.Manifest.permission.DELIVER_COMPANION_MESSAGES; import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_PERMISSION_RESTORE; import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_PLATFORM_INFO; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.ActivityManagerInternal; import android.content.Context; Loading @@ -30,12 +30,17 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.os.Binder; import android.os.Build; import android.os.ParcelFileDescriptor; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; import com.android.server.companion.transport.Transport.Listener; import java.io.IOException; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; Loading @@ -44,6 +49,8 @@ public class CompanionTransportManager { private static final String TAG = "CDM_CompanionTransportManager"; private static final boolean DEBUG = false; private static final int NON_ANDROID = -1; private boolean mSecureTransportEnabled = true; private static boolean isRequest(int message) { Loading @@ -54,24 +61,29 @@ public class CompanionTransportManager { return (message & 0xFF000000) == 0x33000000; } public interface Listener { void onRequestPermissionRestore(byte[] data); } private final Context mContext; @GuardedBy("mTransports") private final SparseArray<Transport> mTransports = new SparseArray<>(); @Nullable private Listener mListener; @NonNull private final Map<Integer, Listener> mListeners = new HashMap<>(); private Transport mTempTransport; public CompanionTransportManager(Context context) { mContext = context; } public void setListener(@NonNull Listener listener) { mListener = listener; /** * Add a message listener when a message is received for the message type */ @GuardedBy("mTransports") public void addListener(int message, @NonNull Listener listener) { mListeners.put(message, listener); for (int i = 0; i < mTransports.size(); i++) { mTransports.valueAt(i).addListener(message, listener); } } /** Loading Loading @@ -105,15 +117,7 @@ public class CompanionTransportManager { detachSystemDataTransport(packageName, userId, associationId); } final Transport transport; if (isSecureTransportEnabled(associationId)) { transport = new SecureTransport(associationId, fd, mContext, mListener); } else { transport = new RawTransport(associationId, fd, mContext, mListener); } transport.start(); mTransports.put(associationId, transport); initializeTransport(associationId, fd); } } Loading @@ -128,13 +132,85 @@ public class CompanionTransportManager { } } @GuardedBy("mTransports") private void initializeTransport(int associationId, ParcelFileDescriptor fd) { if (!isSecureTransportEnabled()) { Transport transport = new RawTransport(associationId, fd, mContext); for (Map.Entry<Integer, Listener> entry : mListeners.entrySet()) { transport.addListener(entry.getKey(), entry.getValue()); } transport.start(); mTransports.put(associationId, transport); Slog.i(TAG, "RawTransport is created"); return; } // Exchange platform info to decide which transport should be created mTempTransport = new RawTransport(associationId, fd, mContext); for (Map.Entry<Integer, Listener> entry : mListeners.entrySet()) { mTempTransport.addListener(entry.getKey(), entry.getValue()); } mTempTransport.addListener(MESSAGE_REQUEST_PLATFORM_INFO, this::onPlatformInfoReceived); mTempTransport.start(); int sdk = Build.VERSION.SDK_INT; String release = Build.VERSION.RELEASE; // data format: | SDK_INT (int) | release length (int) | release | final ByteBuffer data = ByteBuffer.allocate(4 + 4 + release.getBytes().length) .putInt(sdk) .putInt(release.getBytes().length) .put(release.getBytes()); // TODO: it should check if preSharedKey is given mTempTransport.requestForResponse(MESSAGE_REQUEST_PLATFORM_INFO, data.array()); } /** * Depending on the remote platform info to decide which transport should be created */ @GuardedBy("mTransports") private void onPlatformInfoReceived(byte[] data) { // TODO: it should check if preSharedKey is given ByteBuffer buffer = ByteBuffer.wrap(data); int remoteSdk = buffer.getInt(); byte[] remoteRelease = new byte[buffer.getInt()]; buffer.get(remoteRelease); Slog.i(TAG, "Remote device SDK: " + remoteSdk + ", release:" + new String(remoteRelease)); Transport transport = mTempTransport; mTempTransport = null; int sdk = Build.VERSION.SDK_INT; String release = Build.VERSION.RELEASE; if (remoteSdk == NON_ANDROID) { // TODO: pass in a real preSharedKey transport = new SecureTransport(transport.getAssociationId(), transport.getFd(), mContext, null, null); } else if (sdk < Build.VERSION_CODES.UPSIDE_DOWN_CAKE || remoteSdk < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { // TODO: depending on the release version, either // 1) using a RawTransport for old T versions // 2) or an Ukey2 handshaked transport for UKey2 backported T versions } else { Slog.i(TAG, "Creating a secure channel"); transport = new SecureTransport(transport.getAssociationId(), transport.getFd(), mContext); for (Map.Entry<Integer, Listener> entry : mListeners.entrySet()) { transport.addListener(entry.getKey(), entry.getValue()); } transport.start(); } mTransports.put(transport.getAssociationId(), transport); } public Future<?> requestPermissionRestore(int associationId, byte[] data) { synchronized (mTransports) { final Transport transport = mTransports.get(associationId); if (transport == null) { return CompletableFuture.failedFuture(new IOException("Missing transport")); } return transport.requestForResponse(MESSAGE_REQUEST_PERMISSION_RESTORE, data); } } Loading @@ -146,10 +222,9 @@ public class CompanionTransportManager { this.mSecureTransportEnabled = enabled; } private boolean isSecureTransportEnabled(int associationId) { private boolean isSecureTransportEnabled() { boolean enabled = !Build.IS_DEBUGGABLE || mSecureTransportEnabled; // TODO: version comparison logic return enabled; } }
services/companion/java/com/android/server/companion/transport/CryptoManager.java +38 −65 Original line number Diff line number Diff line Loading @@ -16,51 +16,51 @@ 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 java.util.Arrays; 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; import javax.crypto.spec.SecretKeySpec; /** * This class can be used to encrypt and decrypt bytes using Android Cryptography * This class uses Java Cryptography to encrypt and decrypt messages */ public class CryptoManager { private static final String TAG = "CDM_CryptoManager"; private static final int SECRET_KEY_LENGTH = 32; private static final String ALGORITHM = "AES"; private static final String TRANSFORMATION = "AES/CBC/PKCS7Padding"; 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 byte[] mPreSharedKey; private Cipher mEncryptCipher; private Cipher mDecryptCipher; private final KeyStore mKeyStore; private SecretKey mSecretKey; public CryptoManager() { // Initialize KeyStore public CryptoManager(byte[] preSharedKey) { if (preSharedKey == null) { mPreSharedKey = Arrays.copyOf(new byte[0], SECRET_KEY_LENGTH); } else { mPreSharedKey = Arrays.copyOf(preSharedKey, SECRET_KEY_LENGTH); } mSecretKey = new SecretKeySpec(mPreSharedKey, ALGORITHM); try { mKeyStore = KeyStore.getInstance("AndroidKeyStore"); mKeyStore.load(null); } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException e) { throw new RuntimeException(e); mEncryptCipher = Cipher.getInstance(TRANSFORMATION); mEncryptCipher.init(Cipher.ENCRYPT_MODE, mSecretKey); mDecryptCipher = Cipher.getInstance(TRANSFORMATION); } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) { Slog.e(TAG, e.getMessage()); } } Loading @@ -69,21 +69,19 @@ public class CryptoManager { */ 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); if (mEncryptCipher == null) { return null; } // Write to bytes byte[] encryptedBytes = mEncryptCipher.doFinal(input); ByteBuffer buffer = ByteBuffer.allocate( 4 + encryptCipher.getIV().length + 4 + encryptedBytes.length) .putInt(encryptCipher.getIV().length) .put(encryptCipher.getIV()) 4 + mEncryptCipher.getIV().length + 4 + encryptedBytes.length) .putInt(mEncryptCipher.getIV().length) .put(mEncryptCipher.getIV()) .putInt(encryptedBytes.length) .put(encryptedBytes); return buffer.array(); } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) { } catch (IllegalBlockSizeException | BadPaddingException e) { Slog.e(TAG, e.getMessage()); return null; } Loading @@ -99,45 +97,20 @@ public class CryptoManager { 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) { mDecryptCipher.init(Cipher.DECRYPT_MODE, getKey(), new IvParameterSpec(iv)); return mDecryptCipher.doFinal(encryptedBytes); } catch (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); if (mSecretKey != null) { return mSecretKey; } mSecretKey = new SecretKeySpec(mPreSharedKey, ALGORITHM); return mSecretKey; } }