Loading core/java/android/app/admin/DevicePolicyManager.java +18 −3 Original line number Diff line number Diff line Loading @@ -64,6 +64,9 @@ import android.security.AttestedKeyPair; import android.security.Credentials; import android.security.KeyChain; import android.security.KeyChainException; import android.security.keymaster.KeymasterCertificateChain; import android.security.keystore.AttestationUtils; import android.security.keystore.KeyAttestationException; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.ParcelableKeyGenParameterSpec; import android.service.restrictions.RestrictionsReceiver; Loading Loading @@ -4005,15 +4008,27 @@ public class DevicePolicyManager { try { final ParcelableKeyGenParameterSpec parcelableSpec = new ParcelableKeyGenParameterSpec(keySpec); KeymasterCertificateChain attestationChain = new KeymasterCertificateChain(); final boolean success = mService.generateKeyPair( admin, mContext.getPackageName(), algorithm, parcelableSpec); admin, mContext.getPackageName(), algorithm, parcelableSpec, attestationChain); if (!success) { Log.e(TAG, "Error generating key via DevicePolicyManagerService."); return null; } final KeyPair keyPair = KeyChain.getKeyPair(mContext, keySpec.getKeystoreAlias()); return new AttestedKeyPair(keyPair, null); final String alias = keySpec.getKeystoreAlias(); final KeyPair keyPair = KeyChain.getKeyPair(mContext, alias); Certificate[] outputChain = null; try { if (AttestationUtils.isChainValid(attestationChain)) { outputChain = AttestationUtils.parseCertificateChain(attestationChain); } } catch (KeyAttestationException e) { Log.e(TAG, "Error parsing attestation chain for alias " + alias, e); mService.removeKeyPair(admin, mContext.getPackageName(), alias); return null; } return new AttestedKeyPair(keyPair, outputChain); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (KeyChainException e) { Loading core/java/android/app/admin/IDevicePolicyManager.aidl +4 −1 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.os.Bundle; import android.os.PersistableBundle; import android.os.RemoteCallback; import android.os.UserHandle; import android.security.keymaster.KeymasterCertificateChain; import android.security.keystore.ParcelableKeyGenParameterSpec; import java.util.List; Loading Loading @@ -166,7 +167,9 @@ interface IDevicePolicyManager { in byte[] certBuffer, in byte[] certChainBuffer, String alias, boolean requestAccess, boolean isUserSelectable); boolean removeKeyPair(in ComponentName who, in String callerPackage, String alias); boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm, in ParcelableKeyGenParameterSpec keySpec); boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm, in ParcelableKeyGenParameterSpec keySpec, out KeymasterCertificateChain attestationChain); void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback); void setDelegatedScopes(in ComponentName who, in String delegatePackage, in List<String> scopes); Loading keystore/java/android/security/IKeyChainService.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.security; import android.content.pm.StringParceledListSlice; import android.security.keymaster.KeymasterCertificateChain; import android.security.keystore.ParcelableKeyGenParameterSpec; /** Loading @@ -33,6 +34,7 @@ interface IKeyChainService { void setUserSelectable(String alias, boolean isUserSelectable); boolean generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec); boolean attestKey(in String alias, in byte[] challenge, out KeymasterCertificateChain chain); // APIs used by CertInstaller and DevicePolicyManager String installCaCertificate(in byte[] caCertificate); Loading keystore/java/android/security/keystore/AttestationUtils.java +38 −15 Original line number Diff line number Diff line Loading @@ -72,6 +72,33 @@ public abstract class AttestationUtils { */ public static final int ID_TYPE_MEID = 3; /** * Creates an array of X509Certificates from the provided KeymasterCertificateChain. * * @hide Only called by the DevicePolicyManager. */ @NonNull public static X509Certificate[] parseCertificateChain( final KeymasterCertificateChain kmChain) throws KeyAttestationException { // Extract certificate chain. final Collection<byte[]> rawChain = kmChain.getCertificates(); if (rawChain.size() < 2) { throw new KeyAttestationException("Attestation certificate chain contained " + rawChain.size() + " entries. At least two are required."); } final ByteArrayOutputStream concatenatedRawChain = new ByteArrayOutputStream(); try { for (final byte[] cert : rawChain) { concatenatedRawChain.write(cert); } return CertificateFactory.getInstance("X.509").generateCertificates( new ByteArrayInputStream(concatenatedRawChain.toByteArray())) .toArray(new X509Certificate[0]); } catch (Exception e) { throw new KeyAttestationException("Unable to construct certificate chain", e); } } /** * Performs attestation of the device's identifiers. This method returns a certificate chain * whose first element contains the requested device identifiers in an extension. The device's Loading Loading @@ -173,22 +200,18 @@ public abstract class AttestationUtils { KeyStore.getKeyStoreException(errorCode)); } // Extract certificate chain. final Collection<byte[]> rawChain = outChain.getCertificates(); if (rawChain.size() < 2) { throw new DeviceIdAttestationException("Attestation certificate chain contained " + rawChain.size() + " entries. At least two are required."); } final ByteArrayOutputStream concatenatedRawChain = new ByteArrayOutputStream(); try { for (final byte[] cert : rawChain) { concatenatedRawChain.write(cert); return parseCertificateChain(outChain); } catch (KeyAttestationException e) { throw new DeviceIdAttestationException(e.getMessage(), e); } return CertificateFactory.getInstance("X.509").generateCertificates( new ByteArrayInputStream(concatenatedRawChain.toByteArray())) .toArray(new X509Certificate[0]); } catch (Exception e) { throw new DeviceIdAttestationException("Unable to construct certificate chain", e); } /** * Returns true if the attestation chain provided is a valid key attestation chain. * @hide */ public static boolean isChainValid(KeymasterCertificateChain chain) { return chain != null && chain.getCertificates().size() >= 2; } } keystore/java/android/security/keystore/KeyAttestationException.java 0 → 100644 +46 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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 android.security.keystore; /** * Thrown when {@link AttestationUtils} is unable to attest the given key or handle * the resulting attestation record. * * @hide */ public class KeyAttestationException extends Exception { /** * Constructs a new {@code KeyAttestationException} with the current stack trace and the * specified detail message. * * @param detailMessage the detail message for this exception. */ public KeyAttestationException(String detailMessage) { super(detailMessage); } /** * Constructs a new {@code KeyAttestationException} with the current stack trace, the * specified detail message and the specified cause. * * @param message the detail message for this exception. * @param cause the cause of this exception, may be {@code null}. */ public KeyAttestationException(String message, Throwable cause) { super(message, cause); } } Loading
core/java/android/app/admin/DevicePolicyManager.java +18 −3 Original line number Diff line number Diff line Loading @@ -64,6 +64,9 @@ import android.security.AttestedKeyPair; import android.security.Credentials; import android.security.KeyChain; import android.security.KeyChainException; import android.security.keymaster.KeymasterCertificateChain; import android.security.keystore.AttestationUtils; import android.security.keystore.KeyAttestationException; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.ParcelableKeyGenParameterSpec; import android.service.restrictions.RestrictionsReceiver; Loading Loading @@ -4005,15 +4008,27 @@ public class DevicePolicyManager { try { final ParcelableKeyGenParameterSpec parcelableSpec = new ParcelableKeyGenParameterSpec(keySpec); KeymasterCertificateChain attestationChain = new KeymasterCertificateChain(); final boolean success = mService.generateKeyPair( admin, mContext.getPackageName(), algorithm, parcelableSpec); admin, mContext.getPackageName(), algorithm, parcelableSpec, attestationChain); if (!success) { Log.e(TAG, "Error generating key via DevicePolicyManagerService."); return null; } final KeyPair keyPair = KeyChain.getKeyPair(mContext, keySpec.getKeystoreAlias()); return new AttestedKeyPair(keyPair, null); final String alias = keySpec.getKeystoreAlias(); final KeyPair keyPair = KeyChain.getKeyPair(mContext, alias); Certificate[] outputChain = null; try { if (AttestationUtils.isChainValid(attestationChain)) { outputChain = AttestationUtils.parseCertificateChain(attestationChain); } } catch (KeyAttestationException e) { Log.e(TAG, "Error parsing attestation chain for alias " + alias, e); mService.removeKeyPair(admin, mContext.getPackageName(), alias); return null; } return new AttestedKeyPair(keyPair, outputChain); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (KeyChainException e) { Loading
core/java/android/app/admin/IDevicePolicyManager.aidl +4 −1 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.os.Bundle; import android.os.PersistableBundle; import android.os.RemoteCallback; import android.os.UserHandle; import android.security.keymaster.KeymasterCertificateChain; import android.security.keystore.ParcelableKeyGenParameterSpec; import java.util.List; Loading Loading @@ -166,7 +167,9 @@ interface IDevicePolicyManager { in byte[] certBuffer, in byte[] certChainBuffer, String alias, boolean requestAccess, boolean isUserSelectable); boolean removeKeyPair(in ComponentName who, in String callerPackage, String alias); boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm, in ParcelableKeyGenParameterSpec keySpec); boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm, in ParcelableKeyGenParameterSpec keySpec, out KeymasterCertificateChain attestationChain); void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback); void setDelegatedScopes(in ComponentName who, in String delegatePackage, in List<String> scopes); Loading
keystore/java/android/security/IKeyChainService.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.security; import android.content.pm.StringParceledListSlice; import android.security.keymaster.KeymasterCertificateChain; import android.security.keystore.ParcelableKeyGenParameterSpec; /** Loading @@ -33,6 +34,7 @@ interface IKeyChainService { void setUserSelectable(String alias, boolean isUserSelectable); boolean generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec); boolean attestKey(in String alias, in byte[] challenge, out KeymasterCertificateChain chain); // APIs used by CertInstaller and DevicePolicyManager String installCaCertificate(in byte[] caCertificate); Loading
keystore/java/android/security/keystore/AttestationUtils.java +38 −15 Original line number Diff line number Diff line Loading @@ -72,6 +72,33 @@ public abstract class AttestationUtils { */ public static final int ID_TYPE_MEID = 3; /** * Creates an array of X509Certificates from the provided KeymasterCertificateChain. * * @hide Only called by the DevicePolicyManager. */ @NonNull public static X509Certificate[] parseCertificateChain( final KeymasterCertificateChain kmChain) throws KeyAttestationException { // Extract certificate chain. final Collection<byte[]> rawChain = kmChain.getCertificates(); if (rawChain.size() < 2) { throw new KeyAttestationException("Attestation certificate chain contained " + rawChain.size() + " entries. At least two are required."); } final ByteArrayOutputStream concatenatedRawChain = new ByteArrayOutputStream(); try { for (final byte[] cert : rawChain) { concatenatedRawChain.write(cert); } return CertificateFactory.getInstance("X.509").generateCertificates( new ByteArrayInputStream(concatenatedRawChain.toByteArray())) .toArray(new X509Certificate[0]); } catch (Exception e) { throw new KeyAttestationException("Unable to construct certificate chain", e); } } /** * Performs attestation of the device's identifiers. This method returns a certificate chain * whose first element contains the requested device identifiers in an extension. The device's Loading Loading @@ -173,22 +200,18 @@ public abstract class AttestationUtils { KeyStore.getKeyStoreException(errorCode)); } // Extract certificate chain. final Collection<byte[]> rawChain = outChain.getCertificates(); if (rawChain.size() < 2) { throw new DeviceIdAttestationException("Attestation certificate chain contained " + rawChain.size() + " entries. At least two are required."); } final ByteArrayOutputStream concatenatedRawChain = new ByteArrayOutputStream(); try { for (final byte[] cert : rawChain) { concatenatedRawChain.write(cert); return parseCertificateChain(outChain); } catch (KeyAttestationException e) { throw new DeviceIdAttestationException(e.getMessage(), e); } return CertificateFactory.getInstance("X.509").generateCertificates( new ByteArrayInputStream(concatenatedRawChain.toByteArray())) .toArray(new X509Certificate[0]); } catch (Exception e) { throw new DeviceIdAttestationException("Unable to construct certificate chain", e); } /** * Returns true if the attestation chain provided is a valid key attestation chain. * @hide */ public static boolean isChainValid(KeymasterCertificateChain chain) { return chain != null && chain.getCertificates().size() >= 2; } }
keystore/java/android/security/keystore/KeyAttestationException.java 0 → 100644 +46 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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 android.security.keystore; /** * Thrown when {@link AttestationUtils} is unable to attest the given key or handle * the resulting attestation record. * * @hide */ public class KeyAttestationException extends Exception { /** * Constructs a new {@code KeyAttestationException} with the current stack trace and the * specified detail message. * * @param detailMessage the detail message for this exception. */ public KeyAttestationException(String detailMessage) { super(detailMessage); } /** * Constructs a new {@code KeyAttestationException} with the current stack trace, the * specified detail message and the specified cause. * * @param message the detail message for this exception. * @param cause the cause of this exception, may be {@code null}. */ public KeyAttestationException(String message, Throwable cause) { super(message, cause); } }