Use Jan 1 2009 as timestamp in APKs and OTA update ZIPs.
Previously, the timestamp was one hour ahead of NotBefore of the signer's certificate, adjusted for the current timezone. With this change the MS-DOS timestamp in output APK/ZIP files is Jan 1 2009 00:00:00. Bug: 26864066 Change-Id: Id6263c38ac7042489ab695454f8e0fb2d85a3958
This commit is contained in:
@@ -74,6 +74,7 @@ import java.util.Iterator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.TimeZone;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.jar.Attributes;
|
import java.util.jar.Attributes;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
@@ -680,18 +681,20 @@ class SignApk {
|
|||||||
private final File publicKeyFile;
|
private final File publicKeyFile;
|
||||||
private final X509Certificate publicKey;
|
private final X509Certificate publicKey;
|
||||||
private final PrivateKey privateKey;
|
private final PrivateKey privateKey;
|
||||||
|
private final long timestamp;
|
||||||
private final int minSdkVersion;
|
private final int minSdkVersion;
|
||||||
private final OutputStream outputStream;
|
private final OutputStream outputStream;
|
||||||
private final ASN1ObjectIdentifier type;
|
private final ASN1ObjectIdentifier type;
|
||||||
private WholeFileSignerOutputStream signer;
|
private WholeFileSignerOutputStream signer;
|
||||||
|
|
||||||
public CMSSigner(JarFile inputJar, File publicKeyFile,
|
public CMSSigner(JarFile inputJar, File publicKeyFile,
|
||||||
X509Certificate publicKey, PrivateKey privateKey, int minSdkVersion,
|
X509Certificate publicKey, PrivateKey privateKey, long timestamp,
|
||||||
OutputStream outputStream) {
|
int minSdkVersion, OutputStream outputStream) {
|
||||||
this.inputJar = inputJar;
|
this.inputJar = inputJar;
|
||||||
this.publicKeyFile = publicKeyFile;
|
this.publicKeyFile = publicKeyFile;
|
||||||
this.publicKey = publicKey;
|
this.publicKey = publicKey;
|
||||||
this.privateKey = privateKey;
|
this.privateKey = privateKey;
|
||||||
|
this.timestamp = timestamp;
|
||||||
this.minSdkVersion = minSdkVersion;
|
this.minSdkVersion = minSdkVersion;
|
||||||
this.outputStream = outputStream;
|
this.outputStream = outputStream;
|
||||||
this.type = new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId());
|
this.type = new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId());
|
||||||
@@ -729,6 +732,7 @@ class SignApk {
|
|||||||
signFile(manifest,
|
signFile(manifest,
|
||||||
new X509Certificate[]{ publicKey },
|
new X509Certificate[]{ publicKey },
|
||||||
new PrivateKey[]{ privateKey },
|
new PrivateKey[]{ privateKey },
|
||||||
|
timestamp,
|
||||||
minSdkVersion,
|
minSdkVersion,
|
||||||
false, // Don't sign using APK Signature Scheme v2
|
false, // Don't sign using APK Signature Scheme v2
|
||||||
outputJar);
|
outputJar);
|
||||||
@@ -757,10 +761,10 @@ class SignApk {
|
|||||||
|
|
||||||
private static void signWholeFile(JarFile inputJar, File publicKeyFile,
|
private static void signWholeFile(JarFile inputJar, File publicKeyFile,
|
||||||
X509Certificate publicKey, PrivateKey privateKey,
|
X509Certificate publicKey, PrivateKey privateKey,
|
||||||
int minSdkVersion,
|
long timestamp, int minSdkVersion,
|
||||||
OutputStream outputStream) throws Exception {
|
OutputStream outputStream) throws Exception {
|
||||||
CMSSigner cmsOut = new CMSSigner(inputJar, publicKeyFile,
|
CMSSigner cmsOut = new CMSSigner(inputJar, publicKeyFile,
|
||||||
publicKey, privateKey, minSdkVersion, outputStream);
|
publicKey, privateKey, timestamp, minSdkVersion, outputStream);
|
||||||
|
|
||||||
ByteArrayOutputStream temp = new ByteArrayOutputStream();
|
ByteArrayOutputStream temp = new ByteArrayOutputStream();
|
||||||
|
|
||||||
@@ -826,12 +830,11 @@ class SignApk {
|
|||||||
|
|
||||||
private static void signFile(Manifest manifest,
|
private static void signFile(Manifest manifest,
|
||||||
X509Certificate[] publicKey, PrivateKey[] privateKey,
|
X509Certificate[] publicKey, PrivateKey[] privateKey,
|
||||||
|
long timestamp,
|
||||||
int minSdkVersion,
|
int minSdkVersion,
|
||||||
boolean additionallySignedUsingAnApkSignatureScheme,
|
boolean additionallySignedUsingAnApkSignatureScheme,
|
||||||
JarOutputStream outputJar)
|
JarOutputStream outputJar)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
// Assume the certificate is valid for at least an hour.
|
|
||||||
long timestamp = publicKey[0].getNotBefore().getTime() + 3600L * 1000;
|
|
||||||
|
|
||||||
// MANIFEST.MF
|
// MANIFEST.MF
|
||||||
JarEntry je = new JarEntry(JarFile.MANIFEST_NAME);
|
JarEntry je = new JarEntry(JarFile.MANIFEST_NAME);
|
||||||
@@ -1087,10 +1090,12 @@ class SignApk {
|
|||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the ZIP file timestamp to the starting valid time
|
// Set all ZIP file timestamps to Jan 1 2009 00:00:00.
|
||||||
// of the 0th certificate plus one hour (to match what
|
long timestamp = 1230768000000L;
|
||||||
// we've historically done).
|
// The Java ZipEntry API we're using converts milliseconds since epoch into MS-DOS
|
||||||
long timestamp = publicKey[0].getNotBefore().getTime() + 3600L * 1000;
|
// timestamp using the current timezone. We thus adjust the milliseconds since epoch
|
||||||
|
// value to end up with MS-DOS timestamp of Jan 1 2009 00:00:00.
|
||||||
|
timestamp -= TimeZone.getDefault().getOffset(timestamp);
|
||||||
|
|
||||||
PrivateKey[] privateKey = new PrivateKey[numKeys];
|
PrivateKey[] privateKey = new PrivateKey[numKeys];
|
||||||
for (int i = 0; i < numKeys; ++i) {
|
for (int i = 0; i < numKeys; ++i) {
|
||||||
@@ -1105,7 +1110,9 @@ class SignApk {
|
|||||||
// compression level for OTA update files and maximum compession level for APKs).
|
// compression level for OTA update files and maximum compession level for APKs).
|
||||||
if (signWholeFile) {
|
if (signWholeFile) {
|
||||||
SignApk.signWholeFile(inputJar, firstPublicKeyFile,
|
SignApk.signWholeFile(inputJar, firstPublicKeyFile,
|
||||||
publicKey[0], privateKey[0], minSdkVersion, outputFile);
|
publicKey[0], privateKey[0],
|
||||||
|
timestamp, minSdkVersion,
|
||||||
|
outputFile);
|
||||||
} else {
|
} else {
|
||||||
// Generate, in memory, an APK signed using standard JAR Signature Scheme.
|
// Generate, in memory, an APK signed using standard JAR Signature Scheme.
|
||||||
ByteArrayOutputStream v1SignedApkBuf = new ByteArrayOutputStream();
|
ByteArrayOutputStream v1SignedApkBuf = new ByteArrayOutputStream();
|
||||||
@@ -1117,7 +1124,8 @@ class SignApk {
|
|||||||
copyFiles(manifest, inputJar, outputJar, timestamp, alignment);
|
copyFiles(manifest, inputJar, outputJar, timestamp, alignment);
|
||||||
signFile(
|
signFile(
|
||||||
manifest,
|
manifest,
|
||||||
publicKey, privateKey, minSdkVersion, signUsingApkSignatureSchemeV2,
|
publicKey, privateKey,
|
||||||
|
timestamp, minSdkVersion, signUsingApkSignatureSchemeV2,
|
||||||
outputJar);
|
outputJar);
|
||||||
outputJar.close();
|
outputJar.close();
|
||||||
ByteBuffer v1SignedApk = ByteBuffer.wrap(v1SignedApkBuf.toByteArray());
|
ByteBuffer v1SignedApk = ByteBuffer.wrap(v1SignedApkBuf.toByteArray());
|
||||||
|
Reference in New Issue
Block a user