Merge "Add attestation API to Android KeyStore." into nyc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
ea46bb2cbf
@@ -34045,6 +34045,7 @@ package android.security.keystore {
|
|||||||
|
|
||||||
public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
|
public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
|
||||||
method public java.security.spec.AlgorithmParameterSpec getAlgorithmParameterSpec();
|
method public java.security.spec.AlgorithmParameterSpec getAlgorithmParameterSpec();
|
||||||
|
method public byte[] getAttestationChallenge();
|
||||||
method public java.lang.String[] getBlockModes();
|
method public java.lang.String[] getBlockModes();
|
||||||
method public java.util.Date getCertificateNotAfter();
|
method public java.util.Date getCertificateNotAfter();
|
||||||
method public java.util.Date getCertificateNotBefore();
|
method public java.util.Date getCertificateNotBefore();
|
||||||
@@ -34069,6 +34070,7 @@ package android.security.keystore {
|
|||||||
ctor public KeyGenParameterSpec.Builder(java.lang.String, int);
|
ctor public KeyGenParameterSpec.Builder(java.lang.String, int);
|
||||||
method public android.security.keystore.KeyGenParameterSpec build();
|
method public android.security.keystore.KeyGenParameterSpec build();
|
||||||
method public android.security.keystore.KeyGenParameterSpec.Builder setAlgorithmParameterSpec(java.security.spec.AlgorithmParameterSpec);
|
method public android.security.keystore.KeyGenParameterSpec.Builder setAlgorithmParameterSpec(java.security.spec.AlgorithmParameterSpec);
|
||||||
|
method public android.security.keystore.KeyGenParameterSpec.Builder setAttestationChallenge(byte[]);
|
||||||
method public android.security.keystore.KeyGenParameterSpec.Builder setBlockModes(java.lang.String...);
|
method public android.security.keystore.KeyGenParameterSpec.Builder setBlockModes(java.lang.String...);
|
||||||
method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotAfter(java.util.Date);
|
method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotAfter(java.util.Date);
|
||||||
method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotBefore(java.util.Date);
|
method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotBefore(java.util.Date);
|
||||||
|
|||||||
@@ -36528,6 +36528,7 @@ package android.security.keystore {
|
|||||||
|
|
||||||
public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
|
public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
|
||||||
method public java.security.spec.AlgorithmParameterSpec getAlgorithmParameterSpec();
|
method public java.security.spec.AlgorithmParameterSpec getAlgorithmParameterSpec();
|
||||||
|
method public byte[] getAttestationChallenge();
|
||||||
method public java.lang.String[] getBlockModes();
|
method public java.lang.String[] getBlockModes();
|
||||||
method public java.util.Date getCertificateNotAfter();
|
method public java.util.Date getCertificateNotAfter();
|
||||||
method public java.util.Date getCertificateNotBefore();
|
method public java.util.Date getCertificateNotBefore();
|
||||||
@@ -36552,6 +36553,7 @@ package android.security.keystore {
|
|||||||
ctor public KeyGenParameterSpec.Builder(java.lang.String, int);
|
ctor public KeyGenParameterSpec.Builder(java.lang.String, int);
|
||||||
method public android.security.keystore.KeyGenParameterSpec build();
|
method public android.security.keystore.KeyGenParameterSpec build();
|
||||||
method public android.security.keystore.KeyGenParameterSpec.Builder setAlgorithmParameterSpec(java.security.spec.AlgorithmParameterSpec);
|
method public android.security.keystore.KeyGenParameterSpec.Builder setAlgorithmParameterSpec(java.security.spec.AlgorithmParameterSpec);
|
||||||
|
method public android.security.keystore.KeyGenParameterSpec.Builder setAttestationChallenge(byte[]);
|
||||||
method public android.security.keystore.KeyGenParameterSpec.Builder setBlockModes(java.lang.String...);
|
method public android.security.keystore.KeyGenParameterSpec.Builder setBlockModes(java.lang.String...);
|
||||||
method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotAfter(java.util.Date);
|
method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotAfter(java.util.Date);
|
||||||
method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotBefore(java.util.Date);
|
method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotBefore(java.util.Date);
|
||||||
|
|||||||
@@ -34060,6 +34060,7 @@ package android.security.keystore {
|
|||||||
|
|
||||||
public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
|
public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
|
||||||
method public java.security.spec.AlgorithmParameterSpec getAlgorithmParameterSpec();
|
method public java.security.spec.AlgorithmParameterSpec getAlgorithmParameterSpec();
|
||||||
|
method public byte[] getAttestationChallenge();
|
||||||
method public java.lang.String[] getBlockModes();
|
method public java.lang.String[] getBlockModes();
|
||||||
method public java.util.Date getCertificateNotAfter();
|
method public java.util.Date getCertificateNotAfter();
|
||||||
method public java.util.Date getCertificateNotBefore();
|
method public java.util.Date getCertificateNotBefore();
|
||||||
@@ -34084,6 +34085,7 @@ package android.security.keystore {
|
|||||||
ctor public KeyGenParameterSpec.Builder(java.lang.String, int);
|
ctor public KeyGenParameterSpec.Builder(java.lang.String, int);
|
||||||
method public android.security.keystore.KeyGenParameterSpec build();
|
method public android.security.keystore.KeyGenParameterSpec build();
|
||||||
method public android.security.keystore.KeyGenParameterSpec.Builder setAlgorithmParameterSpec(java.security.spec.AlgorithmParameterSpec);
|
method public android.security.keystore.KeyGenParameterSpec.Builder setAlgorithmParameterSpec(java.security.spec.AlgorithmParameterSpec);
|
||||||
|
method public android.security.keystore.KeyGenParameterSpec.Builder setAttestationChallenge(byte[]);
|
||||||
method public android.security.keystore.KeyGenParameterSpec.Builder setBlockModes(java.lang.String...);
|
method public android.security.keystore.KeyGenParameterSpec.Builder setBlockModes(java.lang.String...);
|
||||||
method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotAfter(java.util.Date);
|
method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotAfter(java.util.Date);
|
||||||
method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotBefore(java.util.Date);
|
method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotBefore(java.util.Date);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package android.security;
|
|||||||
import android.security.keymaster.ExportResult;
|
import android.security.keymaster.ExportResult;
|
||||||
import android.security.keymaster.KeyCharacteristics;
|
import android.security.keymaster.KeyCharacteristics;
|
||||||
import android.security.keymaster.KeymasterArguments;
|
import android.security.keymaster.KeymasterArguments;
|
||||||
|
import android.security.keymaster.KeymasterCertificateChain;
|
||||||
import android.security.keymaster.KeymasterBlob;
|
import android.security.keymaster.KeymasterBlob;
|
||||||
import android.security.keymaster.OperationResult;
|
import android.security.keymaster.OperationResult;
|
||||||
import android.security.KeystoreArguments;
|
import android.security.KeystoreArguments;
|
||||||
@@ -74,4 +75,5 @@ interface IKeystoreService {
|
|||||||
int addAuthToken(in byte[] authToken);
|
int addAuthToken(in byte[] authToken);
|
||||||
int onUserAdded(int userId, int parentId);
|
int onUserAdded(int userId, int parentId);
|
||||||
int onUserRemoved(int userId);
|
int onUserRemoved(int userId);
|
||||||
|
int attestKey(String alias, in KeymasterArguments params, out KeymasterCertificateChain chain);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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.keymaster;
|
||||||
|
|
||||||
|
/* @hide */
|
||||||
|
parcelable KeymasterCertificateChain;
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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.keymaster;
|
||||||
|
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for the Java side of keystore-generated certificate chains.
|
||||||
|
*
|
||||||
|
* Serialization code for this must be kept in sync with system/security/keystore
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public class KeymasterCertificateChain implements Parcelable {
|
||||||
|
|
||||||
|
private List<byte[]> mCertificates;
|
||||||
|
|
||||||
|
public static final Parcelable.Creator<KeymasterCertificateChain> CREATOR = new
|
||||||
|
Parcelable.Creator<KeymasterCertificateChain>() {
|
||||||
|
public KeymasterCertificateChain createFromParcel(Parcel in) {
|
||||||
|
return new KeymasterCertificateChain(in);
|
||||||
|
}
|
||||||
|
public KeymasterCertificateChain[] newArray(int size) {
|
||||||
|
return new KeymasterCertificateChain[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public KeymasterCertificateChain() {
|
||||||
|
mCertificates = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeymasterCertificateChain(List<byte[]> mCertificates) {
|
||||||
|
this.mCertificates = mCertificates;
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeymasterCertificateChain(Parcel in) {
|
||||||
|
readFromParcel(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<byte[]> getCertificates() {
|
||||||
|
return mCertificates;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel out, int flags) {
|
||||||
|
if (mCertificates == null) {
|
||||||
|
out.writeInt(0);
|
||||||
|
} else {
|
||||||
|
out.writeInt(mCertificates.size());
|
||||||
|
for (byte[] arg : mCertificates) {
|
||||||
|
out.writeByteArray(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readFromParcel(Parcel in) {
|
||||||
|
int length = in.readInt();
|
||||||
|
mCertificates = new ArrayList<byte[]>(length);
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
mCertificates.add(in.createByteArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -58,6 +58,8 @@ public final class KeymasterDefs {
|
|||||||
public static final int KM_TAG_BLOB_USAGE_REQUIREMENTS = KM_ENUM | 705;
|
public static final int KM_TAG_BLOB_USAGE_REQUIREMENTS = KM_ENUM | 705;
|
||||||
|
|
||||||
public static final int KM_TAG_RSA_PUBLIC_EXPONENT = KM_ULONG | 200;
|
public static final int KM_TAG_RSA_PUBLIC_EXPONENT = KM_ULONG | 200;
|
||||||
|
public static final int KM_TAG_INCLUDE_UNIQUE_ID = KM_BOOL | 202;
|
||||||
|
|
||||||
public static final int KM_TAG_ACTIVE_DATETIME = KM_DATE | 400;
|
public static final int KM_TAG_ACTIVE_DATETIME = KM_DATE | 400;
|
||||||
public static final int KM_TAG_ORIGINATION_EXPIRE_DATETIME = KM_DATE | 401;
|
public static final int KM_TAG_ORIGINATION_EXPIRE_DATETIME = KM_DATE | 401;
|
||||||
public static final int KM_TAG_USAGE_EXPIRE_DATETIME = KM_DATE | 402;
|
public static final int KM_TAG_USAGE_EXPIRE_DATETIME = KM_DATE | 402;
|
||||||
@@ -74,11 +76,12 @@ public final class KeymasterDefs {
|
|||||||
public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
|
public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
|
||||||
public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
|
public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
|
||||||
|
|
||||||
public static final int KM_TAG_APPLICATION_DATA = KM_BYTES | 700;
|
|
||||||
public static final int KM_TAG_CREATION_DATETIME = KM_DATE | 701;
|
public static final int KM_TAG_CREATION_DATETIME = KM_DATE | 701;
|
||||||
public static final int KM_TAG_ORIGIN = KM_ENUM | 702;
|
public static final int KM_TAG_ORIGIN = KM_ENUM | 702;
|
||||||
public static final int KM_TAG_ROLLBACK_RESISTANT = KM_BOOL | 703;
|
public static final int KM_TAG_ROLLBACK_RESISTANT = KM_BOOL | 703;
|
||||||
public static final int KM_TAG_ROOT_OF_TRUST = KM_BYTES | 704;
|
public static final int KM_TAG_ROOT_OF_TRUST = KM_BYTES | 704;
|
||||||
|
public static final int KM_TAG_UNIQUE_ID = KM_BYTES | 707;
|
||||||
|
public static final int KM_TAG_ATTESTATION_CHALLENGE = KM_BYTES | 708;
|
||||||
|
|
||||||
public static final int KM_TAG_ASSOCIATED_DATA = KM_BYTES | 1000;
|
public static final int KM_TAG_ASSOCIATED_DATA = KM_BYTES | 1000;
|
||||||
public static final int KM_TAG_NONCE = KM_BYTES | 1001;
|
public static final int KM_TAG_NONCE = KM_BYTES | 1001;
|
||||||
|
|||||||
@@ -20,9 +20,11 @@ import android.content.ActivityNotFoundException;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.org.bouncycastle.util.io.pem.PemObject;
|
import com.android.org.bouncycastle.util.io.pem.PemObject;
|
||||||
import com.android.org.bouncycastle.util.io.pem.PemReader;
|
import com.android.org.bouncycastle.util.io.pem.PemReader;
|
||||||
import com.android.org.bouncycastle.util.io.pem.PemWriter;
|
import com.android.org.bouncycastle.util.io.pem.PemWriter;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -147,20 +149,23 @@ public class Credentials {
|
|||||||
Reader reader = new InputStreamReader(bai, StandardCharsets.US_ASCII);
|
Reader reader = new InputStreamReader(bai, StandardCharsets.US_ASCII);
|
||||||
PemReader pr = new PemReader(reader);
|
PemReader pr = new PemReader(reader);
|
||||||
|
|
||||||
CertificateFactory cf = CertificateFactory.getInstance("X509");
|
try {
|
||||||
|
CertificateFactory cf = CertificateFactory.getInstance("X509");
|
||||||
|
|
||||||
List<X509Certificate> result = new ArrayList<X509Certificate>();
|
List<X509Certificate> result = new ArrayList<X509Certificate>();
|
||||||
PemObject o;
|
PemObject o;
|
||||||
while ((o = pr.readPemObject()) != null) {
|
while ((o = pr.readPemObject()) != null) {
|
||||||
if (o.getType().equals("CERTIFICATE")) {
|
if (o.getType().equals("CERTIFICATE")) {
|
||||||
Certificate c = cf.generateCertificate(new ByteArrayInputStream(o.getContent()));
|
Certificate c = cf.generateCertificate(new ByteArrayInputStream(o.getContent()));
|
||||||
result.add((X509Certificate) c);
|
result.add((X509Certificate) c);
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Unknown type " + o.getType());
|
throw new IllegalArgumentException("Unknown type " + o.getType());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
|
} finally {
|
||||||
|
pr.close();
|
||||||
}
|
}
|
||||||
pr.close();
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Credentials singleton;
|
private static Credentials singleton;
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package android.security;
|
|||||||
import android.app.ActivityThread;
|
import android.app.ActivityThread;
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.app.KeyguardManager;
|
import android.app.KeyguardManager;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.hardware.fingerprint.FingerprintManager;
|
import android.hardware.fingerprint.FingerprintManager;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
@@ -32,6 +31,7 @@ import android.security.keymaster.ExportResult;
|
|||||||
import android.security.keymaster.KeyCharacteristics;
|
import android.security.keymaster.KeyCharacteristics;
|
||||||
import android.security.keymaster.KeymasterArguments;
|
import android.security.keymaster.KeymasterArguments;
|
||||||
import android.security.keymaster.KeymasterBlob;
|
import android.security.keymaster.KeymasterBlob;
|
||||||
|
import android.security.keymaster.KeymasterCertificateChain;
|
||||||
import android.security.keymaster.KeymasterDefs;
|
import android.security.keymaster.KeymasterDefs;
|
||||||
import android.security.keymaster.OperationResult;
|
import android.security.keymaster.OperationResult;
|
||||||
import android.security.keystore.KeyExpiredException;
|
import android.security.keystore.KeyExpiredException;
|
||||||
@@ -615,6 +615,17 @@ public class KeyStore {
|
|||||||
return onUserPasswordChanged(UserHandle.getUserId(Process.myUid()), newPassword);
|
return onUserPasswordChanged(UserHandle.getUserId(Process.myUid()), newPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int attestKey(
|
||||||
|
String alias, KeymasterArguments params, KeymasterCertificateChain outChain) {
|
||||||
|
try {
|
||||||
|
return mBinder.attestKey(alias, params, outChain);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.w(TAG, "Cannot connect to keystore", e);
|
||||||
|
return SYSTEM_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link KeyStoreException} corresponding to the provided keystore/keymaster error
|
* Returns a {@link KeyStoreException} corresponding to the provided keystore/keymaster error
|
||||||
* code.
|
* code.
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import android.security.KeyPairGeneratorSpec;
|
|||||||
import android.security.KeyStore;
|
import android.security.KeyStore;
|
||||||
import android.security.keymaster.KeyCharacteristics;
|
import android.security.keymaster.KeyCharacteristics;
|
||||||
import android.security.keymaster.KeymasterArguments;
|
import android.security.keymaster.KeymasterArguments;
|
||||||
|
import android.security.keymaster.KeymasterCertificateChain;
|
||||||
import android.security.keymaster.KeymasterDefs;
|
import android.security.keymaster.KeymasterDefs;
|
||||||
|
|
||||||
import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
|
import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
|
||||||
@@ -46,6 +47,8 @@ import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
|
|||||||
|
|
||||||
import libcore.util.EmptyArray;
|
import libcore.util.EmptyArray;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
@@ -57,14 +60,17 @@ import java.security.PublicKey;
|
|||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.UnrecoverableKeyException;
|
import java.security.UnrecoverableKeyException;
|
||||||
import java.security.cert.CertificateEncodingException;
|
import java.security.cert.CertificateEncodingException;
|
||||||
|
import java.security.cert.CertificateParsingException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.security.spec.AlgorithmParameterSpec;
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
import java.security.spec.ECGenParameterSpec;
|
import java.security.spec.ECGenParameterSpec;
|
||||||
import java.security.spec.RSAKeyGenParameterSpec;
|
import java.security.spec.RSAKeyGenParameterSpec;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -166,6 +172,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
|
|||||||
mOriginalKeymasterAlgorithm = keymasterAlgorithm;
|
mOriginalKeymasterAlgorithm = keymasterAlgorithm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
public void initialize(int keysize, SecureRandom random) {
|
public void initialize(int keysize, SecureRandom random) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
@@ -173,6 +180,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
|
|||||||
+ " required to initialize this KeyPairGenerator");
|
+ " required to initialize this KeyPairGenerator");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
public void initialize(AlgorithmParameterSpec params, SecureRandom random)
|
public void initialize(AlgorithmParameterSpec params, SecureRandom random)
|
||||||
throws InvalidAlgorithmParameterException {
|
throws InvalidAlgorithmParameterException {
|
||||||
@@ -447,6 +455,69 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
|
|||||||
+ ", but the user has not yet entered the credential");
|
+ ", but the user has not yet entered the credential");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
byte[] additionalEntropy =
|
||||||
|
KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
|
||||||
|
mRng, (mKeySizeBits + 7) / 8);
|
||||||
|
|
||||||
|
Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid);
|
||||||
|
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + mEntryAlias;
|
||||||
|
boolean success = false;
|
||||||
|
try {
|
||||||
|
generateKeystoreKeyPair(
|
||||||
|
privateKeyAlias, constructKeyGenerationArguments(), additionalEntropy, flags);
|
||||||
|
KeyPair keyPair = loadKeystoreKeyPair(privateKeyAlias);
|
||||||
|
|
||||||
|
storeCertificateChain(flags, createCertificateChain(privateKeyAlias, keyPair));
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
return keyPair;
|
||||||
|
} finally {
|
||||||
|
if (!success) {
|
||||||
|
Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Iterable<byte[]> createCertificateChain(final String privateKeyAlias, KeyPair keyPair)
|
||||||
|
throws ProviderException {
|
||||||
|
byte[] challenge = mSpec.getAttestationChallenge();
|
||||||
|
if (challenge != null) {
|
||||||
|
KeymasterArguments args = new KeymasterArguments();
|
||||||
|
args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_CHALLENGE, challenge);
|
||||||
|
return getAttestationChain(privateKeyAlias, keyPair, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Very short certificate chain in the non-attestation case.
|
||||||
|
return Collections.singleton(generateSelfSignedCertificateBytes(keyPair));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateKeystoreKeyPair(final String privateKeyAlias, KeymasterArguments args,
|
||||||
|
byte[] additionalEntropy, final int flags) throws ProviderException {
|
||||||
|
KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
|
||||||
|
int errorCode = mKeyStore.generateKey(privateKeyAlias, args, additionalEntropy,
|
||||||
|
mEntryUid, flags, resultingKeyCharacteristics);
|
||||||
|
if (errorCode != KeyStore.NO_ERROR) {
|
||||||
|
throw new ProviderException(
|
||||||
|
"Failed to generate key pair", KeyStore.getKeyStoreException(errorCode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyPair loadKeystoreKeyPair(final String privateKeyAlias) throws ProviderException {
|
||||||
|
try {
|
||||||
|
KeyPair result = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(
|
||||||
|
mKeyStore, privateKeyAlias, mEntryUid);
|
||||||
|
if (!mJcaKeyAlgorithm.equalsIgnoreCase(result.getPrivate().getAlgorithm())) {
|
||||||
|
throw new ProviderException(
|
||||||
|
"Generated key pair algorithm does not match requested algorithm: "
|
||||||
|
+ result.getPrivate().getAlgorithm() + " vs " + mJcaKeyAlgorithm);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (UnrecoverableKeyException e) {
|
||||||
|
throw new ProviderException("Failed to load generated key pair from keystore", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeymasterArguments constructKeyGenerationArguments() {
|
||||||
KeymasterArguments args = new KeymasterArguments();
|
KeymasterArguments args = new KeymasterArguments();
|
||||||
args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
|
args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
|
||||||
args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
|
args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
|
||||||
@@ -466,73 +537,72 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
|
|||||||
mSpec.getKeyValidityForConsumptionEnd());
|
mSpec.getKeyValidityForConsumptionEnd());
|
||||||
addAlgorithmSpecificParameters(args);
|
addAlgorithmSpecificParameters(args);
|
||||||
|
|
||||||
byte[] additionalEntropy =
|
if (mSpec.isUniqueIdIncluded())
|
||||||
KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
|
args.addBoolean(KeymasterDefs.KM_TAG_INCLUDE_UNIQUE_ID);
|
||||||
mRng, (mKeySizeBits + 7) / 8);
|
|
||||||
|
|
||||||
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + mEntryAlias;
|
return args;
|
||||||
boolean success = false;
|
}
|
||||||
try {
|
|
||||||
Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid);
|
|
||||||
KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
|
|
||||||
int errorCode = mKeyStore.generateKey(
|
|
||||||
privateKeyAlias,
|
|
||||||
args,
|
|
||||||
additionalEntropy,
|
|
||||||
mEntryUid,
|
|
||||||
flags,
|
|
||||||
resultingKeyCharacteristics);
|
|
||||||
if (errorCode != KeyStore.NO_ERROR) {
|
|
||||||
throw new ProviderException(
|
|
||||||
"Failed to generate key pair", KeyStore.getKeyStoreException(errorCode));
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyPair result;
|
private void storeCertificateChain(final int flags, Iterable<byte[]> iterable)
|
||||||
try {
|
throws ProviderException {
|
||||||
result = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(
|
Iterator<byte[]> iter = iterable.iterator();
|
||||||
mKeyStore, privateKeyAlias, mEntryUid);
|
storeCertificate(
|
||||||
} catch (UnrecoverableKeyException e) {
|
Credentials.USER_CERTIFICATE, iter.next(), flags, "Failed to store certificate");
|
||||||
throw new ProviderException("Failed to load generated key pair from keystore", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mJcaKeyAlgorithm.equalsIgnoreCase(result.getPrivate().getAlgorithm())) {
|
if (!iter.hasNext()) {
|
||||||
throw new ProviderException(
|
return;
|
||||||
"Generated key pair algorithm does not match requested algorithm: "
|
|
||||||
+ result.getPrivate().getAlgorithm() + " vs " + mJcaKeyAlgorithm);
|
|
||||||
}
|
|
||||||
|
|
||||||
final X509Certificate cert;
|
|
||||||
try {
|
|
||||||
cert = generateSelfSignedCertificate(result.getPrivate(), result.getPublic());
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new ProviderException("Failed to generate self-signed certificate", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] certBytes;
|
|
||||||
try {
|
|
||||||
certBytes = cert.getEncoded();
|
|
||||||
} catch (CertificateEncodingException e) {
|
|
||||||
throw new ProviderException(
|
|
||||||
"Failed to obtain encoded form of self-signed certificate", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
int insertErrorCode = mKeyStore.insert(
|
|
||||||
Credentials.USER_CERTIFICATE + mEntryAlias,
|
|
||||||
certBytes,
|
|
||||||
mEntryUid,
|
|
||||||
flags);
|
|
||||||
if (insertErrorCode != KeyStore.NO_ERROR) {
|
|
||||||
throw new ProviderException("Failed to store self-signed certificate",
|
|
||||||
KeyStore.getKeyStoreException(insertErrorCode));
|
|
||||||
}
|
|
||||||
|
|
||||||
success = true;
|
|
||||||
return result;
|
|
||||||
} finally {
|
|
||||||
if (!success) {
|
|
||||||
Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ByteArrayOutputStream certificateConcatenationStream = new ByteArrayOutputStream();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
byte[] data = iter.next();
|
||||||
|
certificateConcatenationStream.write(data, 0, data.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
storeCertificate(Credentials.CA_CERTIFICATE, certificateConcatenationStream.toByteArray(),
|
||||||
|
flags, "Failed to store attestation CA certificate");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void storeCertificate(String prefix, byte[] certificateBytes, final int flags,
|
||||||
|
String failureMessage) throws ProviderException {
|
||||||
|
int insertErrorCode = mKeyStore.insert(
|
||||||
|
prefix + mEntryAlias,
|
||||||
|
certificateBytes,
|
||||||
|
mEntryUid,
|
||||||
|
flags);
|
||||||
|
if (insertErrorCode != KeyStore.NO_ERROR) {
|
||||||
|
throw new ProviderException(failureMessage,
|
||||||
|
KeyStore.getKeyStoreException(insertErrorCode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] generateSelfSignedCertificateBytes(KeyPair keyPair) throws ProviderException {
|
||||||
|
try {
|
||||||
|
return generateSelfSignedCertificate(keyPair.getPrivate(), keyPair.getPublic())
|
||||||
|
.getEncoded();
|
||||||
|
} catch (IOException | CertificateParsingException e) {
|
||||||
|
throw new ProviderException("Failed to generate self-signed certificate", e);
|
||||||
|
} catch (CertificateEncodingException e) {
|
||||||
|
throw new ProviderException(
|
||||||
|
"Failed to obtain encoded form of self-signed certificate", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Iterable<byte[]> getAttestationChain(String privateKeyAlias,
|
||||||
|
KeyPair keyPair, KeymasterArguments args)
|
||||||
|
throws ProviderException {
|
||||||
|
KeymasterCertificateChain outChain = new KeymasterCertificateChain();
|
||||||
|
int errorCode = mKeyStore.attestKey(privateKeyAlias, args, outChain);
|
||||||
|
if (errorCode != KeyStore.NO_ERROR) {
|
||||||
|
throw new ProviderException("Failed to generate attestation certificate chain",
|
||||||
|
KeyStore.getKeyStoreException(errorCode));
|
||||||
|
}
|
||||||
|
Collection<byte[]> chain = outChain.getCertificates();
|
||||||
|
if (chain.size() < 2) {
|
||||||
|
throw new ProviderException("Attestation certificate chain contained "
|
||||||
|
+ chain.size() + " entries. At least two are required.");
|
||||||
|
}
|
||||||
|
return chain;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addAlgorithmSpecificParameters(KeymasterArguments keymasterArgs) {
|
private void addAlgorithmSpecificParameters(KeymasterArguments keymasterArgs) {
|
||||||
@@ -548,8 +618,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private X509Certificate generateSelfSignedCertificate(
|
private X509Certificate generateSelfSignedCertificate(PrivateKey privateKey,
|
||||||
PrivateKey privateKey, PublicKey publicKey) throws Exception {
|
PublicKey publicKey) throws CertificateParsingException, IOException {
|
||||||
String signatureAlgorithm =
|
String signatureAlgorithm =
|
||||||
getCertificateSignatureAlgorithm(mKeymasterAlgorithm, mKeySizeBits, mSpec);
|
getCertificateSignatureAlgorithm(mKeymasterAlgorithm, mKeySizeBits, mSpec);
|
||||||
if (signatureAlgorithm == null) {
|
if (signatureAlgorithm == null) {
|
||||||
@@ -587,7 +657,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
|
|||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private X509Certificate generateSelfSignedCertificateWithFakeSignature(
|
private X509Certificate generateSelfSignedCertificateWithFakeSignature(
|
||||||
PublicKey publicKey) throws Exception {
|
PublicKey publicKey) throws IOException, CertificateParsingException {
|
||||||
V3TBSCertificateGenerator tbsGenerator = new V3TBSCertificateGenerator();
|
V3TBSCertificateGenerator tbsGenerator = new V3TBSCertificateGenerator();
|
||||||
ASN1ObjectIdentifier sigAlgOid;
|
ASN1ObjectIdentifier sigAlgOid;
|
||||||
AlgorithmIdentifier sigAlgId;
|
AlgorithmIdentifier sigAlgId;
|
||||||
|
|||||||
@@ -250,6 +250,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
|||||||
private final boolean mRandomizedEncryptionRequired;
|
private final boolean mRandomizedEncryptionRequired;
|
||||||
private final boolean mUserAuthenticationRequired;
|
private final boolean mUserAuthenticationRequired;
|
||||||
private final int mUserAuthenticationValidityDurationSeconds;
|
private final int mUserAuthenticationValidityDurationSeconds;
|
||||||
|
private final byte[] mAttestationChallenge;
|
||||||
|
private final boolean mUniqueIdIncluded;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hide should be built with Builder
|
* @hide should be built with Builder
|
||||||
@@ -273,7 +275,9 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
|||||||
@KeyProperties.BlockModeEnum String[] blockModes,
|
@KeyProperties.BlockModeEnum String[] blockModes,
|
||||||
boolean randomizedEncryptionRequired,
|
boolean randomizedEncryptionRequired,
|
||||||
boolean userAuthenticationRequired,
|
boolean userAuthenticationRequired,
|
||||||
int userAuthenticationValidityDurationSeconds) {
|
int userAuthenticationValidityDurationSeconds,
|
||||||
|
byte[] attestationChallenge,
|
||||||
|
boolean uniqueIdIncluded) {
|
||||||
if (TextUtils.isEmpty(keyStoreAlias)) {
|
if (TextUtils.isEmpty(keyStoreAlias)) {
|
||||||
throw new IllegalArgumentException("keyStoreAlias must not be empty");
|
throw new IllegalArgumentException("keyStoreAlias must not be empty");
|
||||||
}
|
}
|
||||||
@@ -315,6 +319,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
|||||||
mRandomizedEncryptionRequired = randomizedEncryptionRequired;
|
mRandomizedEncryptionRequired = randomizedEncryptionRequired;
|
||||||
mUserAuthenticationRequired = userAuthenticationRequired;
|
mUserAuthenticationRequired = userAuthenticationRequired;
|
||||||
mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
|
mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
|
||||||
|
mAttestationChallenge = Utils.cloneIfNotNull(attestationChallenge);
|
||||||
|
mUniqueIdIncluded = uniqueIdIncluded;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -538,6 +544,48 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
|||||||
return mUserAuthenticationValidityDurationSeconds;
|
return mUserAuthenticationValidityDurationSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the attestation challenge value that will be placed in attestation certificate for
|
||||||
|
* this key pair.
|
||||||
|
*
|
||||||
|
* <p>If this method returns non-{@code null}, the public key certificate for this key pair will
|
||||||
|
* contain an extension that describes the details of the key's configuration and
|
||||||
|
* authorizations, including the content of the attestation challenge value. If the key is in
|
||||||
|
* secure hardware, and if the secure hardware supports attestation, the certificate will be
|
||||||
|
* signed by a chain of certificates rooted at a trustworthy CA key. Otherwise the chain will
|
||||||
|
* be rooted at an untrusted certificate.
|
||||||
|
*
|
||||||
|
* <p>If this method returns {@code null}, and the spec is used to generate an asymmetric (RSA
|
||||||
|
* or EC) key pair, the public key will have a self-signed certificate if it has purpose {@link
|
||||||
|
* KeyProperties#PURPOSE_SIGN} (see {@link #KeyGenParameterSpec(String, int)). If does not have
|
||||||
|
* purpose {@link KeyProperties#PURPOSE_SIGN}, it will have a fake certificate.
|
||||||
|
*
|
||||||
|
* <p>Symmetric keys, such as AES and HMAC keys, do not have public key certificates. If a
|
||||||
|
* {@link KeyGenParameterSpec} with {@link #hasAttestationCertificate()} returning
|
||||||
|
* non-{@code null} is used to generate a symmetric (AES or HMAC) key,
|
||||||
|
* {@link KeyGenerator#generateKey())} will throw
|
||||||
|
* {@link java.security.InvalidAlgorithmParameterException}.
|
||||||
|
*
|
||||||
|
* @see Builder#setAttestationChallenge(byte[])
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* TODO(swillden): Update this documentation to describe the hardware and software root keys,
|
||||||
|
* including information about CRL/OCSP services for discovering revocations, and to link to
|
||||||
|
* documentation of the extension format and content.
|
||||||
|
*/
|
||||||
|
public byte[] getAttestationChallenge() {
|
||||||
|
return Utils.cloneIfNotNull(mAttestationChallenge);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide This is a system-only API
|
||||||
|
*
|
||||||
|
* Returns {@code true} if the attestation certificate will contain a unique ID field.
|
||||||
|
*/
|
||||||
|
public boolean isUniqueIdIncluded() {
|
||||||
|
return mUniqueIdIncluded;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder of {@link KeyGenParameterSpec} instances.
|
* Builder of {@link KeyGenParameterSpec} instances.
|
||||||
*/
|
*/
|
||||||
@@ -562,6 +610,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
|||||||
private boolean mRandomizedEncryptionRequired = true;
|
private boolean mRandomizedEncryptionRequired = true;
|
||||||
private boolean mUserAuthenticationRequired;
|
private boolean mUserAuthenticationRequired;
|
||||||
private int mUserAuthenticationValidityDurationSeconds = -1;
|
private int mUserAuthenticationValidityDurationSeconds = -1;
|
||||||
|
private byte[] mAttestationChallenge = null;
|
||||||
|
private boolean mUniqueIdIncluded = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of the {@code Builder}.
|
* Creates a new instance of the {@code Builder}.
|
||||||
@@ -957,6 +1007,59 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO(swillden): Update this documentation to describe the hardware and software root
|
||||||
|
* keys, including information about CRL/OCSP services for discovering revocations, and to
|
||||||
|
* link to documentation of the extension format and content.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Sets whether an attestation certificate will be generated for this key pair, and what
|
||||||
|
* challenge value will be placed in the certificate. The attestation certificate chain
|
||||||
|
* can be retrieved with with {@link java.security.KeyStore#getCertificateChain(String)}.
|
||||||
|
*
|
||||||
|
* <p>If {@code attestationChallenge} is not {@code null}, the public key certificate for
|
||||||
|
* this key pair will contain an extension that describes the details of the key's
|
||||||
|
* configuration and authorizations, including the {@code attestationChallenge} value. If
|
||||||
|
* the key is in secure hardware, and if the secure hardware supports attestation, the
|
||||||
|
* certificate will be signed by a chain of certificates rooted at a trustworthy CA key.
|
||||||
|
* Otherwise the chain will be rooted at an untrusted certificate.
|
||||||
|
*
|
||||||
|
* <p>The purpose of the challenge value is to enable relying parties to verify that the key
|
||||||
|
* was created in response to a specific request. If attestation is desired but no
|
||||||
|
* challenged is needed, any non-{@code null} value may be used, including an empty byte
|
||||||
|
* array.
|
||||||
|
*
|
||||||
|
* <p>If {@code attestationChallenge} is {@code null}, and this spec is used to generate an
|
||||||
|
* asymmetric (RSA or EC) key pair, the public key certificate will be self-signed if the
|
||||||
|
* key has purpose {@link KeyProperties#PURPOSE_SIGN} (see
|
||||||
|
* {@link #KeyGenParameterSpec(String, int)). If the key does not have purpose
|
||||||
|
* {@link KeyProperties#PURPOSE_SIGN}, it is not possible to use the key to sign a
|
||||||
|
* certificate, so the public key certificate will contain a dummy signature.
|
||||||
|
*
|
||||||
|
* <p>Symmetric keys, such as AES and HMAC keys, do not have public key certificates. If a
|
||||||
|
* {@code getAttestationChallenge} returns non-{@code null} and the spec is used to
|
||||||
|
* generate a symmetric (AES or HMAC) key, {@link KeyGenerator#generateKey()} will throw
|
||||||
|
* {@link java.security.InvalidAlgorithmParameterException}.
|
||||||
|
*
|
||||||
|
* @see Builder#setAttestationChallenge(String attestationChallenge)
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Builder setAttestationChallenge(byte[] attestationChallenge) {
|
||||||
|
mAttestationChallenge = attestationChallenge;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide Only system apps can use this method.
|
||||||
|
*
|
||||||
|
* Sets whether to include a temporary unique ID field in the attestation certificate.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Builder setUniqueIdIncluded(boolean uniqueIdIncluded) {
|
||||||
|
mUniqueIdIncluded = uniqueIdIncluded;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds an instance of {@code KeyGenParameterSpec}.
|
* Builds an instance of {@code KeyGenParameterSpec}.
|
||||||
*/
|
*/
|
||||||
@@ -981,7 +1084,9 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
|||||||
mBlockModes,
|
mBlockModes,
|
||||||
mRandomizedEncryptionRequired,
|
mRandomizedEncryptionRequired,
|
||||||
mUserAuthenticationRequired,
|
mUserAuthenticationRequired,
|
||||||
mUserAuthenticationValidityDurationSeconds);
|
mUserAuthenticationValidityDurationSeconds,
|
||||||
|
mAttestationChallenge,
|
||||||
|
mUniqueIdIncluded);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,4 +29,8 @@ abstract class Utils {
|
|||||||
static Date cloneIfNotNull(Date value) {
|
static Date cloneIfNotNull(Date value) {
|
||||||
return (value != null) ? (Date) value.clone() : null;
|
return (value != null) ? (Date) value.clone() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static byte[] cloneIfNotNull(byte[] value) {
|
||||||
|
return (value != null) ? value.clone() : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user