blob: 9b6dccd63831b9731fc1899d0521e618d2edca12 [file] [log] [blame]
/*
* Copyright (C) 2019 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 com.android.signflinger;
import com.android.annotations.NonNull;
import com.android.apksig.SigningCertificateLineage;
import com.android.apksig.util.RunnablesExecutor;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
/**
* Options for SignedApk.
*
* <p>Private key size has an impact on performance. While 1024-bit keys are now obsolete,
* 2048-bit/4096-bit seem to provide the best performance. 16000-bit signing is 10x slower than
* 2048-bit. As the time of the writing of this lib, 2048-bit key are recommended.
*
* <p>Providing an executor speeds up considerably v2 signing. If not provided, apksig is
* single-threaded
*/
public class SignedApkOptions {
@NonNull final PrivateKey privateKey;
@NonNull final List<X509Certificate> certificates;
final RunnablesExecutor executor;
final boolean v1Enabled;
final boolean v2Enabled;
final boolean v3Enabled;
final SigningCertificateLineage v3SigningCertificateLineage;
final boolean v4Enabled;
final File v4Output;
final String v1CreatedBy;
final String v1BuiltBy;
final boolean v1TrustManifest;
final int minSdkVersion;
final byte[] sdkDependencies;
private SignedApkOptions(
PrivateKey privateKey,
List<X509Certificate> certificates,
RunnablesExecutor executor,
byte[] sdkDependencies,
boolean v1Enabled,
boolean v2Enabled,
boolean v3Enabled,
SigningCertificateLineage v3SigningCertificateLineage,
boolean v4Enabled,
File v4Output,
String v1CreatedBy,
String v1BuiltBy,
boolean v1TrustManifest,
int minSdkVersion) {
this.privateKey = privateKey;
this.certificates = certificates;
this.executor = executor;
this.sdkDependencies = sdkDependencies;
this.v1Enabled = v1Enabled;
this.v2Enabled = v2Enabled;
this.v3Enabled = v3Enabled;
this.v3SigningCertificateLineage = v3SigningCertificateLineage;
this.v4Enabled = v4Enabled;
this.v4Output = v4Output;
this.v1CreatedBy = v1CreatedBy;
this.v1BuiltBy = v1BuiltBy;
this.v1TrustManifest = v1TrustManifest;
this.minSdkVersion = minSdkVersion;
}
public static PrivateKey bytesToPrivateKey(String keyAlgorithm, byte[] bytes)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
// Keep overly strictly linter happy by limiting what JCA KeyFactory algorithms are used
// here
KeyFactory keyFactory;
switch (keyAlgorithm.toUpperCase(Locale.US)) {
case "RSA":
keyFactory = KeyFactory.getInstance("rsa");
break;
case "DSA":
keyFactory = KeyFactory.getInstance("dsa");
break;
case "EC":
keyFactory = KeyFactory.getInstance("ec");
break;
default:
throw new IllegalStateException("Unsupported key algorithm: " + keyAlgorithm);
}
return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(bytes));
}
public static List<X509Certificate> bytesToCertificateChain(byte[] bytes)
throws CertificateException {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Collection<? extends Certificate> certs =
certificateFactory.generateCertificates(new ByteArrayInputStream(bytes));
List<X509Certificate> result = new ArrayList<>(certs.size());
for (Certificate cert : certs) {
result.add((X509Certificate) cert);
}
return result;
}
public static class Builder {
PrivateKey privateKey;
List<X509Certificate> certificates;
RunnablesExecutor executor;
byte[] sdkDependencies;
boolean v1Enabled = false;
boolean v2Enabled = true;
boolean v3Enabled = false;
SigningCertificateLineage v3SigningCertificateLineage;
boolean v4Enabled = false;
File v4Output = null;
String v1CreatedBy = "Signflinger";
String v1BuiltBy = "Signflinger";
boolean v1TrustManifest;
int minSdkVersion;
public Builder setPrivateKey(@NonNull PrivateKey privateKey) {
this.privateKey = privateKey;
return this;
}
public Builder setCertificates(@NonNull List<X509Certificate> certificates) {
this.certificates = certificates;
return this;
}
public Builder setExecutor(@NonNull RunnablesExecutor executor) {
this.executor = executor;
return this;
}
public Builder setSdkDependencies(byte[] sdkDependencies) {
this.sdkDependencies = sdkDependencies;
return this;
}
public Builder setV1Enabled(boolean enabled) {
this.v1Enabled = enabled;
return this;
}
public Builder setV2Enabled(boolean enabled) {
this.v2Enabled = enabled;
return this;
}
public Builder setV3Enabled(boolean enabled) {
this.v3Enabled = enabled;
return this;
}
public Builder setV3SigningCertificateLineage(
SigningCertificateLineage v3SigningCertificateLineage) {
this.v3SigningCertificateLineage = v3SigningCertificateLineage;
return this;
}
public Builder setV4Enabled(boolean enabled) {
this.v4Enabled = enabled;
return this;
}
public Builder setV4Output(File output) {
this.v4Output = output;
return this;
}
public Builder setV1CreatedBy(@NonNull String creator) {
v1CreatedBy = creator;
return this;
}
public Builder setV1BuiltBy(@NonNull String builder) {
v1BuiltBy = builder;
return this;
}
public Builder setV1TrustManifest(boolean trust) {
v1TrustManifest = trust;
return this;
}
public Builder setMinSdkVersion(int minSdkVersion) {
this.minSdkVersion = minSdkVersion;
return this;
}
@NonNull
public SignedApkOptions build() {
return new SignedApkOptions(
privateKey,
certificates,
executor,
sdkDependencies,
v1Enabled,
v2Enabled,
v3Enabled,
v3SigningCertificateLineage,
v4Enabled,
v4Output,
v1CreatedBy,
v1BuiltBy,
v1TrustManifest,
minSdkVersion);
}
}
}