1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.security.keystore.recovery; 18 19import android.annotation.NonNull; 20import android.os.Parcel; 21import android.os.Parcelable; 22 23import com.android.internal.util.Preconditions; 24 25import java.io.ByteArrayInputStream; 26import java.security.cert.CertPath; 27import java.security.cert.CertificateEncodingException; 28import java.security.cert.CertificateException; 29import java.security.cert.CertificateFactory; 30 31/** 32 * The certificate path of the recovery service. 33 * 34 * @hide 35 */ 36public final class RecoveryCertPath implements Parcelable { 37 38 private static final String CERT_PATH_ENCODING = "PkiPath"; 39 40 private final byte[] mEncodedCertPath; 41 42 /** 43 * Wraps a {@code CertPath} to create a {@code Parcelable} for Binder calls. 44 * 45 * @param certPath The certificate path to be wrapped. 46 * @throws CertificateException if the given certificate path cannot be encoded properly. 47 */ 48 public static @NonNull RecoveryCertPath createRecoveryCertPath(@NonNull CertPath certPath) 49 throws CertificateException { 50 // Perform the encoding here to avoid throwing exceptions in writeToParcel 51 try { 52 return new RecoveryCertPath(encodeCertPath(certPath)); 53 } catch (CertificateEncodingException e) { 54 throw new CertificateException("Failed to encode the given CertPath", e); 55 } 56 } 57 58 /** 59 * Obtains the {@code CertPath} wrapped in the Parcelable. 60 * 61 * @return the wrapped certificate path. 62 * @throws CertificateException if the wrapped certificate path cannot be decoded properly. 63 */ 64 public @NonNull CertPath getCertPath() throws CertificateException { 65 // Perform the decoding here to avoid throwing exceptions in createFromParcel 66 return decodeCertPath(mEncodedCertPath); 67 } 68 69 private RecoveryCertPath(@NonNull byte[] encodedCertPath) { 70 mEncodedCertPath = Preconditions.checkNotNull(encodedCertPath); 71 } 72 73 private RecoveryCertPath(Parcel in) { 74 mEncodedCertPath = in.createByteArray(); 75 } 76 77 public static final Parcelable.Creator<RecoveryCertPath> CREATOR = 78 new Parcelable.Creator<RecoveryCertPath>() { 79 public RecoveryCertPath createFromParcel(Parcel in) { 80 return new RecoveryCertPath(in); 81 } 82 83 public RecoveryCertPath[] newArray(int length) { 84 return new RecoveryCertPath[length]; 85 } 86 }; 87 88 @Override 89 public void writeToParcel(Parcel out, int flags) { 90 out.writeByteArray(mEncodedCertPath); 91 } 92 93 @Override 94 public int describeContents() { 95 return 0; 96 } 97 98 @NonNull 99 private static byte[] encodeCertPath(@NonNull CertPath certPath) 100 throws CertificateEncodingException { 101 Preconditions.checkNotNull(certPath); 102 return certPath.getEncoded(CERT_PATH_ENCODING); 103 } 104 105 @NonNull 106 private static CertPath decodeCertPath(@NonNull byte[] bytes) throws CertificateException { 107 Preconditions.checkNotNull(bytes); 108 CertificateFactory certFactory; 109 try { 110 certFactory = CertificateFactory.getInstance("X.509"); 111 } catch (CertificateException e) { 112 // Should not happen, as X.509 is mandatory for all providers. 113 throw new RuntimeException(e); 114 } 115 return certFactory.generateCertPath(new ByteArrayInputStream(bytes), CERT_PATH_ENCODING); 116 } 117} 118