1 /* 2 * Copyright (C) 2012 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.apksig.internal.util; 18 19 import com.android.apksig.ApkSignerTest; 20 import com.android.apksig.SigningCertificateLineage; 21 import com.android.apksig.util.DataSource; 22 23 import java.io.IOException; 24 import java.io.InputStream; 25 import java.nio.ByteBuffer; 26 import java.security.KeyFactory; 27 import java.security.NoSuchAlgorithmException; 28 import java.security.PrivateKey; 29 import java.security.cert.Certificate; 30 import java.security.cert.CertificateException; 31 import java.security.cert.X509Certificate; 32 import java.security.spec.InvalidKeySpecException; 33 import java.security.spec.PKCS8EncodedKeySpec; 34 import java.util.ArrayList; 35 import java.util.Collection; 36 import java.util.List; 37 import java.util.Locale; 38 39 /** 40 * Assorted methods to obtaining test input from resources. 41 */ 42 public final class Resources { Resources()43 private Resources() {} 44 toByteArray(Class<?> cls, String resourceName)45 public static byte[] toByteArray(Class<?> cls, String resourceName) throws IOException { 46 try (InputStream in = cls.getResourceAsStream(resourceName)) { 47 if (in == null) { 48 throw new IllegalArgumentException("Resource not found: " + resourceName); 49 } 50 return ByteStreams.toByteArray(in); 51 } 52 } 53 toInputStream(Class<?> cls, String resourceName)54 public static InputStream toInputStream(Class<?> cls, String resourceName) throws IOException { 55 InputStream in = cls.getResourceAsStream(resourceName); 56 if (in == null) { 57 throw new IllegalArgumentException("Resource not found: " + resourceName); 58 } 59 return in; 60 } 61 toCertificate( Class <?> cls, String resourceName)62 public static X509Certificate toCertificate( 63 Class <?> cls, String resourceName) throws IOException, CertificateException { 64 try (InputStream in = cls.getResourceAsStream(resourceName)) { 65 if (in == null) { 66 throw new IllegalArgumentException("Resource not found: " + resourceName); 67 } 68 return X509CertificateUtils.generateCertificate(in); 69 } 70 } 71 toCertificateChain( Class <?> cls, String resourceName)72 public static List<X509Certificate> toCertificateChain( 73 Class <?> cls, String resourceName) throws IOException, CertificateException { 74 Collection<? extends Certificate> certs; 75 try (InputStream in = cls.getResourceAsStream(resourceName)) { 76 if (in == null) { 77 throw new IllegalArgumentException("Resource not found: " + resourceName); 78 } 79 certs = X509CertificateUtils.generateCertificates(in); 80 } 81 List<X509Certificate> result = new ArrayList<>(certs.size()); 82 for (Certificate cert : certs) { 83 result.add((X509Certificate) cert); 84 } 85 return result; 86 } 87 toPrivateKey(Class <?> cls, String resourceName)88 public static PrivateKey toPrivateKey(Class <?> cls, String resourceName) 89 throws IOException, InvalidKeySpecException, NoSuchAlgorithmException { 90 int delimiterIndex = resourceName.indexOf('-'); 91 if (delimiterIndex == -1) { 92 throw new IllegalArgumentException( 93 "Failed to autodetect key algorithm from resource name: " + resourceName); 94 } 95 String keyAlgorithm = resourceName.substring(0, delimiterIndex).toUpperCase(Locale.US); 96 return toPrivateKey(cls, resourceName, keyAlgorithm); 97 } 98 toPrivateKey( Class <?> cls, String resourceName, String keyAlgorithm)99 public static PrivateKey toPrivateKey( 100 Class <?> cls, String resourceName, String keyAlgorithm) 101 throws IOException, InvalidKeySpecException, NoSuchAlgorithmException { 102 byte[] encoded = toByteArray(cls, resourceName); 103 104 // Keep overly strictly linter happy by limiting what JCA KeyFactory algorithms are used 105 // here 106 KeyFactory keyFactory; 107 switch (keyAlgorithm.toUpperCase(Locale.US)) { 108 case "RSA": 109 keyFactory = KeyFactory.getInstance("rsa"); 110 break; 111 case "DSA": 112 keyFactory = KeyFactory.getInstance("dsa"); 113 break; 114 case "EC": 115 keyFactory = KeyFactory.getInstance("ec"); 116 break; 117 default: 118 throw new InvalidKeySpecException("Unsupported key algorithm: " + keyAlgorithm); 119 } 120 121 return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded)); 122 } 123 toLineageSignerConfig(Class<?> cls, String resourcePrefix)124 public static SigningCertificateLineage.SignerConfig toLineageSignerConfig(Class<?> cls, 125 String resourcePrefix) throws Exception { 126 PrivateKey privateKey = toPrivateKey(cls, resourcePrefix + ".pk8"); 127 X509Certificate cert = Resources.toCertificate(cls, 128 resourcePrefix + ".x509.pem"); 129 return new SigningCertificateLineage.SignerConfig.Builder(privateKey, cert).build(); 130 } 131 toDataSource(Class<?> cls, String dataSourceResourceName)132 public static DataSource toDataSource(Class<?> cls, String dataSourceResourceName) 133 throws IOException { 134 return new ByteBufferDataSource(ByteBuffer.wrap(Resources 135 .toByteArray(ApkSignerTest.class, dataSourceResourceName))); 136 } 137 toSigningCertificateLineage(Class<?> cls, String fileResourceName)138 public static SigningCertificateLineage toSigningCertificateLineage(Class<?> cls, 139 String fileResourceName) throws IOException { 140 DataSource lineageDataSource = toDataSource(cls, fileResourceName); 141 return SigningCertificateLineage.readFromDataSource(lineageDataSource); 142 } 143 } 144