1 /* 2 * Copyright (C) 2022 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 17 package com.android.rkpdapp.unittest; 18 19 import static com.android.rkpdapp.unittest.Utils.generateEcdsaKeyPair; 20 import static com.android.rkpdapp.unittest.Utils.getP256PubKeyFromBytes; 21 import static com.android.rkpdapp.unittest.Utils.signPublicKey; 22 23 import static com.google.common.truth.Truth.assertThat; 24 import static com.google.common.truth.Truth.assertWithMessage; 25 26 import android.util.Base64; 27 28 import androidx.test.ext.junit.runners.AndroidJUnit4; 29 30 import com.android.rkpdapp.utils.X509Utils; 31 32 import org.junit.Test; 33 import org.junit.runner.RunWith; 34 35 import java.io.ByteArrayInputStream; 36 import java.io.ByteArrayOutputStream; 37 import java.io.InputStream; 38 import java.security.KeyPair; 39 import java.security.cert.CertificateException; 40 import java.security.cert.CertificateFactory; 41 import java.security.cert.X509Certificate; 42 43 @RunWith(AndroidJUnit4.class) 44 public class X509UtilsTest { 45 46 @Test testFormatX509Certs()47 public void testFormatX509Certs() throws Exception { 48 KeyPair root = generateEcdsaKeyPair(); 49 KeyPair intermediate = generateEcdsaKeyPair(); 50 KeyPair leaf = generateEcdsaKeyPair(); 51 X509Certificate[] certs = new X509Certificate[3]; 52 certs[2] = signPublicKey(root, root.getPublic()); 53 certs[1] = signPublicKey(root, intermediate.getPublic()); 54 certs[0] = signPublicKey(intermediate, leaf.getPublic()); 55 ByteArrayOutputStream os = new ByteArrayOutputStream(); 56 for (X509Certificate cert : certs) { 57 os.write(cert.getEncoded()); 58 } 59 X509Certificate[] roundTrip = X509Utils.formatX509Certs(os.toByteArray()); 60 assertThat(certs.length).isEqualTo(roundTrip.length); 61 for (int i = 0; i < certs.length; i++) { 62 assertWithMessage("Failed on index " + i) 63 .that(certs[i].getEncoded()) 64 .isEqualTo(roundTrip[i].getEncoded()); 65 } 66 } 67 68 @Test testGetAndFormatRawPublicKey()69 public void testGetAndFormatRawPublicKey() throws Exception { 70 KeyPair testKey = generateEcdsaKeyPair(); 71 X509Certificate testCert = signPublicKey(testKey, testKey.getPublic()); 72 byte[] formattedKey = X509Utils.getAndFormatRawPublicKey(testCert); 73 byte[] xPoint = new byte[32]; 74 byte[] yPoint = new byte[32]; 75 System.arraycopy(formattedKey, 0 /* offset */, xPoint, 0 /* offset */, 32 /* length */); 76 System.arraycopy(formattedKey, 32 /* offset */, yPoint, 0 /* offset */, 32 /* length */); 77 assertThat(testKey.getPublic()).isEqualTo(getP256PubKeyFromBytes(xPoint, yPoint)); 78 } 79 80 @Test testCertificateChains()81 public void testCertificateChains() throws Exception { 82 String encodedTestCert = "MIIBvTCCAWOgAwIBAgIRAKrDc87UaGSeFTRzF4vz0IcwCgYIKoZIzj0EAwIwIDEN" 83 + "MAsGA1UECgwERmFrZTEPMA0GA1UEAwwGSXNzdWVyMCAXDTIzMDIwMTE1MzExMVoYDzIxMjMwMTA4MTU" 84 + "zMTExWjA5MQwwCgYDVQQKDANURUUxKTAnBgNVBAMMIGFhYzM3M2NlZDQ2ODY0OWUxNTM0NzMxNzhiZj" 85 + "NkMDg3MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcC8SjTKkEqpPGQMXiZMC1/Dk3Fo/PsCZBI0E8" 86 + "N4zXhBHJJZdT4LnYUNQXhSndDhrPO/x0MSySnz+hDZiRlRdzKNjMGEwHQYDVR0OBBYEFMcyyg91rTsG" 87 + "QxM2hY2dfrmcYNIoMB8GA1UdIwQYMBaAFN2wvxbmHbqJicPAK1Ce+692JkfcMA8GA1UdEwEB/wQFMAM" 88 + "BAf8wDgYDVR0PAQH/BAQDAgIEMAoGCCqGSM49BAMCA0gAMEUCIQD/ZJAabKvYlyuL6Ehc7bZMZFn9e7" 89 + "Gu8f+QTA2fPjN/EQIgUeJPlHjNhoiu0QPpAoRbd4idOLyf5pqNEiXt7n8VDe0="; 90 String encodedRootCert = "MIIBpDCCAUmgAwIBAgIQf7TE7zQ0iDLyiZIIpqKCvjAKBggqhkjOPQQDAjAgMQ0w" 91 + "CwYDVQQKDARGYWtlMQ8wDQYDVQQDDAZJc3N1ZXIwIBcNMjMwMjAxMTUxMDM0WhgPMjEyMzAxMDgxNTE" 92 + "wMzRaMCAxDTALBgNVBAoMBEZha2UxDzANBgNVBAMMBklzc3VlcjBZMBMGByqGSM49AgEGCCqGSM49Aw" 93 + "EHA0IABNh7P0mPpgFdSw9pC+aDMDRWnZa6g7H+jdy/a4V+erKJ+lDqdsV4Ao+2+vt2WelEP0DIZl51U" 94 + "CaS8CKqZtRGLB6jYzBhMB0GA1UdDgQWBBTdsL8W5h26iYnDwCtQnvuvdiZH3DAfBgNVHSMEGDAWgBTd" 95 + "sL8W5h26iYnDwCtQnvuvdiZH3DAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwICBDAKBggqhkj" 96 + "OPQQDAgNJADBGAiEAm9Y2YGYe/2RqI6xMGq2IFJzeJ0qjfQzBLg6KjRLiJ10CIQCxpJCHRN4Gj17/ON" 97 + "JGL2npbIsQVpSn1M5xPsY+9/qB1g=="; 98 99 X509Certificate rootCert = generateCertificateFromEncodedBytes(encodedRootCert); 100 X509Certificate testCert = generateCertificateFromEncodedBytes(encodedTestCert); 101 X509Certificate[] validCertChain = new X509Certificate[]{testCert, rootCert}; 102 X509Certificate[] invalidCertChain = new X509Certificate[]{rootCert, testCert}; 103 104 assertThat(X509Utils.isCertChainValid(validCertChain)).isTrue(); 105 assertThat(X509Utils.isCertChainValid(invalidCertChain)).isFalse(); 106 } 107 108 @Test testCertChainSwapOAndCN()109 public void testCertChainSwapOAndCN() throws Exception { 110 String encodedTestCert = "MIIBvTCCAWOgAwIBAgIRAKrDc87UaGSeFTRzF4vz0IcwCgYIKoZIzj0EAwIwIDEP" 111 + "MA0GA1UEAwwGSXNzdWVyMQ0wCwYDVQQKDARGYWtlMCAXDTIzMDIwMTE1MzExMVoYDzIxMjMwMTA4MTU" 112 + "zMTExWjA5MQwwCgYDVQQKDANURUUxKTAnBgNVBAMMIGFhYzM3M2NlZDQ2ODY0OWUxNTM0NzMxNzhiZj" 113 + "NkMDg3MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcC8SjTKkEqpPGQMXiZMC1/Dk3Fo/PsCZBI0E8" 114 + "N4zXhBHJJZdT4LnYUNQXhSndDhrPO/x0MSySnz+hDZiRlRdzKNjMGEwHQYDVR0OBBYEFMcyyg91rTsG" 115 + "QxM2hY2dfrmcYNIoMB8GA1UdIwQYMBaAFN2wvxbmHbqJicPAK1Ce+692JkfcMA8GA1UdEwEB/wQFMAM" 116 + "BAf8wDgYDVR0PAQH/BAQDAgIEMAoGCCqGSM49BAMCA0gAMEUCIQD/ZJAabKvYlyuL6Ehc7bZMZFn9e7" 117 + "Gu8f+QTA2fPjN/EQIgUeJPlHjNhoiu0QPpAoRbd4idOLyf5pqNEiXt7n8VDe0="; 118 String encodedRootCert = "MIIBpDCCAUmgAwIBAgIQf7TE7zQ0iDLyiZIIpqKCvjAKBggqhkjOPQQDAjAgMQ0w" 119 + "CwYDVQQKDARGYWtlMQ8wDQYDVQQDDAZJc3N1ZXIwIBcNMjMwMjAxMTUxMDM0WhgPMjEyMzAxMDgxNTE" 120 + "wMzRaMCAxDTALBgNVBAoMBEZha2UxDzANBgNVBAMMBklzc3VlcjBZMBMGByqGSM49AgEGCCqGSM49Aw" 121 + "EHA0IABNh7P0mPpgFdSw9pC+aDMDRWnZa6g7H+jdy/a4V+erKJ+lDqdsV4Ao+2+vt2WelEP0DIZl51U" 122 + "CaS8CKqZtRGLB6jYzBhMB0GA1UdDgQWBBTdsL8W5h26iYnDwCtQnvuvdiZH3DAfBgNVHSMEGDAWgBTd" 123 + "sL8W5h26iYnDwCtQnvuvdiZH3DAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwICBDAKBggqhkj" 124 + "OPQQDAgNJADBGAiEAm9Y2YGYe/2RqI6xMGq2IFJzeJ0qjfQzBLg6KjRLiJ10CIQCxpJCHRN4Gj17/ON" 125 + "JGL2npbIsQVpSn1M5xPsY+9/qB1g=="; 126 127 X509Certificate rootCert = generateCertificateFromEncodedBytes(encodedRootCert); 128 X509Certificate testCert = generateCertificateFromEncodedBytes(encodedTestCert); 129 X509Certificate[] certChain = new X509Certificate[]{testCert, rootCert}; 130 131 assertThat(X509Utils.isSelfSignedCertificate(rootCert)).isTrue(); 132 assertThat(X509Utils.isSelfSignedCertificate(testCert)).isFalse(); 133 assertThat(X509Utils.isCertChainValid(certChain)).isFalse(); 134 } 135 generateCertificateFromEncodedBytes(String encodedCert)136 private X509Certificate generateCertificateFromEncodedBytes(String encodedCert) 137 throws CertificateException { 138 CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 139 InputStream in = new ByteArrayInputStream(Base64.decode(encodedCert, Base64.DEFAULT)); 140 return (X509Certificate) certFactory.generateCertificate(in); 141 } 142 } 143