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