diff options
author | Paulo Casanova <pasc@google.com> | 2016-11-14 16:02:08 +0000 |
---|---|---|
committer | Paulo Casanova <pasc@google.com> | 2016-11-16 05:38:00 +0000 |
commit | 5a1ccbecca5fc359bf488e717db310d307c0c9fc (patch) | |
tree | b414a0e3428c16f5cb24b9bff8c89b9a792d8211 /src/test/java/com/android/apkzlib/sign | |
parent | a5c71db7e08ae4e72804a64e470c6d4e816e2ee7 (diff) | |
download | apkzlib-5a1ccbecca5fc359bf488e717db310d307c0c9fc.tar.gz |
Renamed apkzlib packages.
Test: Included
Change-Id: I9cce74b77719003875deaa5a0056e35f2930429e
Diffstat (limited to 'src/test/java/com/android/apkzlib/sign')
4 files changed, 793 insertions, 0 deletions
diff --git a/src/test/java/com/android/apkzlib/sign/FullApkSignTest.java b/src/test/java/com/android/apkzlib/sign/FullApkSignTest.java new file mode 100644 index 0000000..d41978a --- /dev/null +++ b/src/test/java/com/android/apkzlib/sign/FullApkSignTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2016 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.apkzlib.sign; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertNotNull; + +import com.android.apkzlib.utils.ApkZLibPair; +import com.android.apkzlib.zip.AlignmentRule; +import com.android.apkzlib.zip.AlignmentRules; +import com.android.apkzlib.zip.StoredEntry; +import com.android.apkzlib.zip.ZFile; +import com.android.apkzlib.zip.ZFileOptions; +import com.android.apkzlib.zip.ZFileTestConstants; +import com.android.apkzlib.utils.ApkZFileTestUtils; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +/** + * Tests that verify {@link FullApkSignExtension}. + */ +public class FullApkSignTest { + + /** + * Folder used for tests. + */ + @Rule + public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); + + @Test + public void testSignature() throws Exception { + File out = new File(mTemporaryFolder.getRoot(), "apk"); + + ApkZLibPair<PrivateKey, X509Certificate> signData = + SignatureTestUtils.generateSignaturePre18(); + + // The byte arrays below are larger when compressed, so we end up storing them uncompressed, + // which would normally cause them to be 4-aligned. Disable that, to make calculations + // easier. + ZFileOptions options = new ZFileOptions(); + options.setAlignmentRule(AlignmentRules.constant(AlignmentRule.NO_ALIGNMENT)); + + /* + * Generate a signed zip. + */ + ZFile zf = new ZFile(out, options); + FullApkSignExtension signExtension = + new FullApkSignExtension(zf, 13, signData.v2, signData.v1); + signExtension.register(); + String f1Name = "abc"; + byte[] f1Data = new byte[] { 1, 1, 1, 1 }; + zf.add(f1Name, new ByteArrayInputStream(f1Data)); + String f2Name = "defg"; + byte[] f2Data = new byte[] { 2, 2, 2, 2, 3, 3, 3, 3}; + zf.add(f2Name, new ByteArrayInputStream(f2Data)); + zf.close(); + + /* + * We should see the data in place. + */ + int f1DataStart = ZFileTestConstants.LOCAL_HEADER_SIZE + f1Name.length(); + int f1DataEnd = f1DataStart + f1Data.length; + int f2DataStart = f1DataEnd + ZFileTestConstants.LOCAL_HEADER_SIZE + f2Name.length(); + int f2DataEnd = f2DataStart + f2Data.length; + + byte[] read1 = ApkZFileTestUtils.readSegment(out, f1DataStart, f1Data.length); + assertArrayEquals(f1Data, read1); + byte[] read2 = ApkZFileTestUtils.readSegment(out, f2DataStart, f2Data.length); + assertArrayEquals(f2Data, read2); + + /* + * Read the signed zip. + */ + ZFile zf2 = new ZFile(out); + + StoredEntry se1 = zf2.get(f1Name); + assertNotNull(se1); + assertArrayEquals(f1Data, se1.read()); + + StoredEntry se2 = zf2.get(f2Name); + assertNotNull(se2); + assertArrayEquals(f2Data, se2.read()); + + zf2.close(); + } +} diff --git a/src/test/java/com/android/apkzlib/sign/JarSigningTest.java b/src/test/java/com/android/apkzlib/sign/JarSigningTest.java new file mode 100644 index 0000000..191bf53 --- /dev/null +++ b/src/test/java/com/android/apkzlib/sign/JarSigningTest.java @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2016 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.apkzlib.sign; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; + +import com.android.apkzlib.zip.StoredEntry; +import com.android.apkzlib.zip.ZFile; +import com.android.apkzlib.utils.ApkZFileTestUtils; +import com.android.apkzlib.utils.ApkZLibPair; +import com.google.common.base.Charsets; +import com.google.common.hash.Hashing; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.Base64; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class JarSigningTest { + + @Rule + public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); + + @Test + public void signEmptyJar() throws Exception { + File zipFile = new File(mTemporaryFolder.getRoot(), "a.zip"); + + try (ZFile zf = new ZFile(zipFile)) { + ManifestGenerationExtension manifestExtension = + new ManifestGenerationExtension("Me", "Me"); + manifestExtension.register(zf); + + ApkZLibPair<PrivateKey, X509Certificate> p = + SignatureTestUtils.generateSignaturePre18(); + + SignatureExtension signatureExtension = + new SignatureExtension(manifestExtension, 12, p.v2, p.v1, null); + signatureExtension.register(); + } + + try (ZFile verifyZFile = new ZFile(zipFile)) { + StoredEntry manifestEntry = verifyZFile.get("META-INF/MANIFEST.MF"); + assertNotNull(manifestEntry); + + Manifest manifest = new Manifest(new ByteArrayInputStream(manifestEntry.read())); + assertEquals(3, manifest.getMainAttributes().size()); + assertEquals("1.0", manifest.getMainAttributes().getValue("Manifest-Version")); + assertEquals("Me", manifest.getMainAttributes().getValue("Created-By")); + assertEquals("Me", manifest.getMainAttributes().getValue("Built-By")); + } + } + + @Test + public void signJarWithPrexistingSimpleTextFilePre18() throws Exception { + File zipFile = new File(mTemporaryFolder.getRoot(), "a.zip"); + ApkZLibPair<PrivateKey, X509Certificate> p = SignatureTestUtils.generateSignaturePre18(); + + try (ZFile zf1 = new ZFile(zipFile)) { + zf1.add("directory/file", + new ByteArrayInputStream("useless text".getBytes(Charsets.US_ASCII))); + } + + try (ZFile zf2 = new ZFile(zipFile)) { + ManifestGenerationExtension me = new ManifestGenerationExtension("Merry", "Christmas"); + me.register(zf2); + new SignatureExtension(me, 10, p.v2, p.v1, null).register(); + } + + try (ZFile zf3 = new ZFile(zipFile)) { + StoredEntry manifestEntry = zf3.get("META-INF/MANIFEST.MF"); + assertNotNull(manifestEntry); + + Manifest manifest = new Manifest(new ByteArrayInputStream(manifestEntry.read())); + assertEquals(3, manifest.getMainAttributes().size()); + assertEquals("1.0", manifest.getMainAttributes().getValue("Manifest-Version")); + assertEquals("Merry", manifest.getMainAttributes().getValue("Built-By")); + assertEquals("Christmas", manifest.getMainAttributes().getValue("Created-By")); + + Attributes attrs = manifest.getAttributes("directory/file"); + assertNotNull(attrs); + assertEquals(1, attrs.size()); + assertEquals("OOQgIEXBissIvva3ydRoaXk29Rk=", attrs.getValue("SHA1-Digest")); + + StoredEntry signatureEntry = zf3.get("META-INF/CERT.SF"); + assertNotNull(signatureEntry); + + Manifest signature = new Manifest(new ByteArrayInputStream(signatureEntry.read())); + assertEquals(3, signature.getMainAttributes().size()); + assertEquals("1.0", signature.getMainAttributes().getValue("Signature-Version")); + assertEquals("1.0 (Android)", signature.getMainAttributes().getValue("Created-By")); + + byte[] manifestTextBytes = manifestEntry.read(); + byte[] manifestSha1Bytes = Hashing.sha1().hashBytes(manifestTextBytes).asBytes(); + String manifestSha1 = Base64.getEncoder().encodeToString(manifestSha1Bytes); + + assertEquals(manifestSha1, + signature.getMainAttributes().getValue("SHA1-Digest-Manifest")); + + Attributes signAttrs = signature.getAttributes("directory/file"); + assertNotNull(signAttrs); + assertEquals(1, signAttrs.size()); + assertEquals("OOQgIEXBissIvva3ydRoaXk29Rk=", signAttrs.getValue("SHA1-Digest")); + + StoredEntry rsaEntry = zf3.get("META-INF/CERT.RSA"); + assertNotNull(rsaEntry); + } + } + + @Test + public void signJarWithPrexistingSimpleTextFilePos18() throws Exception { + File zipFile = new File(mTemporaryFolder.getRoot(), "a.zip"); + try (ZFile zf1 = new ZFile(zipFile)) { + zf1.add("directory/file", new ByteArrayInputStream("useless text".getBytes( + Charsets.US_ASCII))); + } + + ApkZLibPair<PrivateKey, X509Certificate> p = SignatureTestUtils.generateSignaturePos18(); + + try (ZFile zf2 = new ZFile(zipFile)) { + ManifestGenerationExtension me = new ManifestGenerationExtension("Merry", "Christmas"); + me.register(zf2); + new SignatureExtension(me, 21, p.v2, p.v1, null).register(); + } + + try (ZFile zf3 = new ZFile(zipFile)) { + StoredEntry manifestEntry = zf3.get("META-INF/MANIFEST.MF"); + assertNotNull(manifestEntry); + + Manifest manifest = new Manifest(new ByteArrayInputStream(manifestEntry.read())); + assertEquals(3, manifest.getMainAttributes().size()); + assertEquals("1.0", manifest.getMainAttributes().getValue("Manifest-Version")); + assertEquals("Merry", manifest.getMainAttributes().getValue("Built-By")); + assertEquals("Christmas", manifest.getMainAttributes().getValue("Created-By")); + + Attributes attrs = manifest.getAttributes("directory/file"); + assertNotNull(attrs); + assertEquals(1, attrs.size()); + assertEquals("QjupZsopQM/01O6+sWHqH64ilMmoBEtljg9VEqN6aI4=", + attrs.getValue("SHA-256-Digest")); + + StoredEntry signatureEntry = zf3.get("META-INF/CERT.SF"); + assertNotNull(signatureEntry); + + Manifest signature = new Manifest(new ByteArrayInputStream(signatureEntry.read())); + assertEquals(3, signature.getMainAttributes().size()); + assertEquals("1.0", signature.getMainAttributes().getValue("Signature-Version")); + assertEquals("1.0 (Android)", signature.getMainAttributes().getValue("Created-By")); + + byte[] manifestTextBytes = manifestEntry.read(); + byte[] manifestSha256Bytes = Hashing.sha256().hashBytes(manifestTextBytes).asBytes(); + String manifestSha256 = Base64.getEncoder().encodeToString(manifestSha256Bytes); + + assertEquals(manifestSha256, signature.getMainAttributes().getValue( + "SHA-256-Digest-Manifest")); + + Attributes signAttrs = signature.getAttributes("directory/file"); + assertNotNull(signAttrs); + assertEquals(1, signAttrs.size()); + assertEquals("QjupZsopQM/01O6+sWHqH64ilMmoBEtljg9VEqN6aI4=", + signAttrs.getValue("SHA-256-Digest")); + + StoredEntry ecdsaEntry = zf3.get("META-INF/CERT.EC"); + assertNotNull(ecdsaEntry); + } + } + + @Test + public void v2SignAddsApkSigningBlock() throws Exception { + File zipFile = new File(mTemporaryFolder.getRoot(), "a.zip"); + try (ZFile zf = new ZFile(zipFile)) { + ManifestGenerationExtension manifestExtension = + new ManifestGenerationExtension("Me", "Me"); + manifestExtension.register(zf); + + ApkZLibPair<PrivateKey, X509Certificate> p = SignatureTestUtils.generateSignaturePre18(); + + FullApkSignExtension signatureExtension = + new FullApkSignExtension(zf, 12, p.v2, p.v1); + signatureExtension.register(); + } + + try (ZFile verifyZFile = new ZFile(zipFile)) { + long centralDirOffset = verifyZFile.getCentralDirectoryOffset(); + byte[] apkSigningBlockMagic = new byte[16]; + verifyZFile.directFullyRead( + centralDirOffset - apkSigningBlockMagic.length, apkSigningBlockMagic); + assertEquals("APK Sig Block 42", new String(apkSigningBlockMagic, "US-ASCII")); + } + } + + @Test + public void v1ReSignOnFileChange() throws Exception { + File zipFile = new File(mTemporaryFolder.getRoot(), "a.zip"); + ApkZLibPair<PrivateKey, X509Certificate> p = SignatureTestUtils.generateSignaturePos18(); + + byte[] file1Contents = "I am a test file".getBytes(Charsets.US_ASCII); + String file1Name = "path/to/file1"; + byte[] file1Sha = Hashing.sha256().hashBytes(file1Contents).asBytes(); + String file1ShaTxt = Base64.getEncoder().encodeToString(file1Sha); + + String builtBy = "Santa Claus"; + String createdBy = "Uses Android"; + + try (ZFile zf1 = new ZFile(zipFile)) { + zf1.add(file1Name, new ByteArrayInputStream(file1Contents)); + ManifestGenerationExtension me = new ManifestGenerationExtension(builtBy, createdBy); + me.register(zf1); + new SignatureExtension(me, 21, p.v2, p.v1, null).register(); + + zf1.update(); + + StoredEntry manifestEntry = zf1.get("META-INF/MANIFEST.MF"); + assertNotNull(manifestEntry); + + try (InputStream manifestIs = manifestEntry.open()) { + Manifest manifest = new Manifest(manifestIs); + + assertEquals(1, manifest.getEntries().size()); + + Attributes file1Attrs = manifest.getEntries().get(file1Name); + assertNotNull(file1Attrs); + assertEquals(file1ShaTxt, file1Attrs.getValue("SHA-256-Digest")); + } + + /* + * Change the file without closing the zip. + */ + file1Contents = "I am a modified test file".getBytes(Charsets.US_ASCII); + file1Sha = Hashing.sha256().hashBytes(file1Contents).asBytes(); + file1ShaTxt = Base64.getEncoder().encodeToString(file1Sha); + + zf1.add(file1Name, new ByteArrayInputStream(file1Contents)); + + zf1.update(); + + manifestEntry = zf1.get("META-INF/MANIFEST.MF"); + assertNotNull(manifestEntry); + + try (InputStream manifestIs = manifestEntry.open()) { + Manifest manifest = new Manifest(manifestIs); + + assertEquals(1, manifest.getEntries().size()); + + Attributes file1Attrs = manifest.getEntries().get(file1Name); + assertNotNull(file1Attrs); + assertEquals(file1ShaTxt, file1Attrs.getValue("SHA-256-Digest")); + } + } + + /* + * Change the file closing the zip. + */ + file1Contents = "I have changed again!".getBytes(Charsets.US_ASCII); + file1Sha = Hashing.sha256().hashBytes(file1Contents).asBytes(); + file1ShaTxt = Base64.getEncoder().encodeToString(file1Sha); + + try (ZFile zf2 = new ZFile(zipFile)) { + ManifestGenerationExtension me = new ManifestGenerationExtension(builtBy, createdBy); + me.register(zf2); + new SignatureExtension(me, 21, p.v2, p.v1, null).register(); + + zf2.add(file1Name, new ByteArrayInputStream(file1Contents)); + + zf2.update(); + + StoredEntry manifestEntry = zf2.get("META-INF/MANIFEST.MF"); + assertNotNull(manifestEntry); + + try (InputStream manifestIs = manifestEntry.open()) { + Manifest manifest = new Manifest(manifestIs); + + assertEquals(1, manifest.getEntries().size()); + + Attributes file1Attrs = manifest.getEntries().get(file1Name); + assertNotNull(file1Attrs); + assertEquals(file1ShaTxt, file1Attrs.getValue("SHA-256-Digest")); + } + } + } + + @Test + public void openSignedJarDoesNotForcesWriteifSignatureIsNotCorrect() throws Exception { + File zipFile = new File(mTemporaryFolder.getRoot(), "a.zip"); + + ApkZLibPair<PrivateKey, X509Certificate> p = SignatureTestUtils.generateSignaturePos18(); + + String fileName = "file"; + byte[] fileContents = "Very interesting contents".getBytes(Charsets.US_ASCII); + + try (ZFile zf = new ZFile(zipFile)) { + ManifestGenerationExtension me = new ManifestGenerationExtension("I", "Android"); + me.register(zf); + new SignatureExtension(me, 21, p.v2, p.v1, null).register(); + + zf.add(fileName, new ByteArrayInputStream(fileContents)); + } + + long fileTimestamp = zipFile.lastModified(); + + ApkZFileTestUtils.waitForFileSystemTick(fileTimestamp); + + /* + * Open the zip file, but don't touch it. + */ + try (ZFile zf = new ZFile(zipFile)) { + ManifestGenerationExtension me = new ManifestGenerationExtension("I", "Android"); + me.register(zf); + new SignatureExtension(me, 21, p.v2, p.v1, null).register(); + } + + /* + * Check the file wasn't touched. + */ + assertEquals(fileTimestamp, zipFile.lastModified()); + + /* + * Change the file contents ignoring any signing. + */ + fileContents = "Not so interesting contents".getBytes(Charsets.US_ASCII); + try (ZFile zf = new ZFile(zipFile)) { + zf.add(fileName, new ByteArrayInputStream(fileContents)); + } + + fileTimestamp = zipFile.lastModified(); + + /* + * Wait to make sure the timestamp can increase. + */ + while (true) { + File notUsed = mTemporaryFolder.newFile(); + long notTimestamp = notUsed.lastModified(); + notUsed.delete(); + if (notTimestamp > fileTimestamp) { + break; + } + } + + /* + * Open the zip file, but do any changes. The need to updating the signature should force + * a file update. + */ + try (ZFile zf = new ZFile(zipFile)) { + ManifestGenerationExtension me = new ManifestGenerationExtension("I", "Android"); + me.register(zf); + new SignatureExtension(me, 21, p.v2, p.v1, null).register(); + } + + /* + * Check the file was touched. + */ + assertNotEquals(fileTimestamp, zipFile.lastModified()); + } +} diff --git a/src/test/java/com/android/apkzlib/sign/ManifestGenerationTest.java b/src/test/java/com/android/apkzlib/sign/ManifestGenerationTest.java new file mode 100644 index 0000000..746617b --- /dev/null +++ b/src/test/java/com/android/apkzlib/sign/ManifestGenerationTest.java @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2016 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.apkzlib.sign; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import com.android.apkzlib.zip.StoredEntry; +import com.android.apkzlib.zip.ZFile; +import com.android.apkzlib.utils.ApkZFileTestUtils; +import com.google.common.base.Charsets; +import com.google.common.io.Closer; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.util.Set; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.mockito.internal.util.collections.Sets; + +public class ManifestGenerationTest { + + private static final String WIKI_PATH = "/testData/packaging/text-files/wikipedia.html"; + + @Rule + public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); + + @Test + public void elementaryManifestGeneration() throws Exception { + File zip = new File(mTemporaryFolder.getRoot(), "f.zip"); + + try (ZFile zf = new ZFile(zip)) { + zf.add("abc", new ByteArrayInputStream(new byte[]{1})); + zf.add("x/", new ByteArrayInputStream(new byte[0])); + zf.add("x/abc", new ByteArrayInputStream(new byte[]{2})); + + ManifestGenerationExtension extension = + new ManifestGenerationExtension("Me, of course", "Myself"); + extension.register(zf); + + zf.update(); + + StoredEntry se = zf.get("META-INF/MANIFEST.MF"); + assertNotNull(se); + + String text = new String(se.read(), Charsets.US_ASCII); + text = text.trim(); + String lines[] = text.split(System.getProperty("line.separator")); + assertEquals(3, lines.length); + + assertEquals("Manifest-Version: 1.0", lines[0].trim()); + + Set<String> linesSet = Sets.newSet(); + for (String l : lines) { + linesSet.add(l.trim()); + } + + assertTrue(linesSet.contains("Built-By: Me, of course")); + assertTrue(linesSet.contains("Created-By: Myself")); + } + } + + @Test + public void manifestGenerationOnHalfWrittenFile() throws Exception { + File zip = new File(mTemporaryFolder.getRoot(), "f.zip"); + try (Closer closer = Closer.create()) { + ZFile zf = closer.register(new ZFile(zip)); + + try (InputStream wiki = getClass().getResourceAsStream(WIKI_PATH)) { + zf.add("wiki", wiki); + } + + ManifestGenerationExtension extension = + new ManifestGenerationExtension("Me, of course", "Myself"); + extension.register(zf); + + zf.close(); + + StoredEntry se = zf.get("META-INF/MANIFEST.MF"); + assertNotNull(se); + + String text = new String(se.read(), Charsets.US_ASCII); + text = text.trim(); + String lines[] = text.split(System.getProperty("line.separator")); + assertEquals(3, lines.length); + + assertEquals("Manifest-Version: 1.0", lines[0].trim()); + + Set<String> linesSet = Sets.newSet(); + for (String l : lines) { + linesSet.add(l.trim()); + } + + assertTrue(linesSet.contains("Built-By: Me, of course")); + assertTrue(linesSet.contains("Created-By: Myself")); + } + } + + @Test + public void manifestGenerationOnExistingFile() throws Exception { + File zip = new File(mTemporaryFolder.getRoot(), "f.zip"); + try (Closer closer = Closer.create()) { + ZFile zf = closer.register(new ZFile(zip)); + + try (InputStream wiki = getClass().getResourceAsStream(WIKI_PATH)) { + zf.add("wiki", wiki); + } + + zf.close(); + + ManifestGenerationExtension extension = + new ManifestGenerationExtension("Me, of course", "Myself"); + extension.register(zf); + + zf.close(); + + StoredEntry se = zf.get("META-INF/MANIFEST.MF"); + assertNotNull(se); + + String text = new String(se.read(), Charsets.US_ASCII); + text = text.trim(); + String lines[] = text.split(System.getProperty("line.separator")); + assertEquals(3, lines.length); + + assertEquals("Manifest-Version: 1.0", lines[0].trim()); + + Set<String> linesSet = Sets.newSet(); + for (String l : lines) { + linesSet.add(l.trim()); + } + + assertTrue(linesSet.contains("Built-By: Me, of course")); + assertTrue(linesSet.contains("Created-By: Myself")); + } + } + + @Test + public void manifestGenerationOnIncrementalNoChanges() throws Exception { + File zip = new File(mTemporaryFolder.getRoot(), "f.zip"); + try (Closer closer = Closer.create()) { + ZFile zf = closer.register(new ZFile(zip)); + + ManifestGenerationExtension extension = + new ManifestGenerationExtension("Me, of course", "Myself"); + extension.register(zf); + + try (InputStream wiki = getClass().getResourceAsStream(WIKI_PATH)) { + zf.add("wiki", wiki); + } + + zf.close(); + + long timeOfWriting = zip.lastModified(); + + ApkZFileTestUtils.waitForFileSystemTick(timeOfWriting); + + zf = closer.register(new ZFile(zip)); + zf.close(); + + long secondTimeOfWriting = zip.lastModified(); + assertEquals(timeOfWriting, secondTimeOfWriting); + } + } +} diff --git a/src/test/java/com/android/apkzlib/sign/SignatureTestUtils.java b/src/test/java/com/android/apkzlib/sign/SignatureTestUtils.java new file mode 100644 index 0000000..71610ef --- /dev/null +++ b/src/test/java/com/android/apkzlib/sign/SignatureTestUtils.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2016 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.apkzlib.sign; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import com.android.annotations.NonNull; +import com.android.apkzlib.utils.ApkZLibPair; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPublicKey; +import java.util.Date; +import javax.security.auth.x500.X500Principal; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.X509v1CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.crypto.params.RSAKeyParameters; +import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.junit.Assume; + +/** + * Utilities to use signatures in tests. + */ +public class SignatureTestUtils { + + /** + * Generates a private key / certificate for pre-18 systems. + * + * @return the pair with the private key and certificate + * @throws Exception failed to generate the signature data + */ + @NonNull + public static ApkZLibPair<PrivateKey, X509Certificate> generateSignaturePre18() + throws Exception { + return generateSignature("RSA", "SHA1withRSA"); + } + + /** + * Generates a private key / certificate for post-18 systems. + * + * @return the pair with the private key and certificate + * @throws Exception failed to generate the signature data + */ + @NonNull + public static ApkZLibPair<PrivateKey, X509Certificate> generateSignaturePos18() + throws Exception { + return generateSignature("EC", "SHA256withECDSA"); + } + + /** + * Generates a private key / certificate. + * + * @param sign the asymmetric cypher, <em>e.g.</em>, {@code RSA} + * @param full the full signature algorithm name, <em>e.g.</em>, {@code SHA1withRSA} + * @return the pair with the private key and certificate + * @throws Exception failed to generate the signature data + */ + @NonNull + public static ApkZLibPair<PrivateKey, X509Certificate> generateSignature( + @NonNull String sign, + @NonNull String full) + throws Exception { + // http://stackoverflow.com/questions/28538785/ + // easy-way-to-generate-a-self-signed-certificate-for-java-security-keystore-using + + KeyPairGenerator generator = null; + try { + generator = KeyPairGenerator.getInstance(sign); + } catch (NoSuchAlgorithmException e) { + Assume.assumeNoException("Algorithm " + sign + " not supported.", e); + } + + assertNotNull(generator); + KeyPair keyPair = generator.generateKeyPair(); + + Date notBefore = new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000); + Date notAfter = new Date(System.currentTimeMillis() + 365L * 24 * 60 * 60 * 1000); + + X500Name issuer = new X500Name(new X500Principal("cn=Myself").getName()); + + SubjectPublicKeyInfo publicKeyInfo; + + if (keyPair.getPublic() instanceof RSAPublicKey) { + RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); + publicKeyInfo = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo( + new RSAKeyParameters(false, rsaPublicKey.getModulus(), + rsaPublicKey.getPublicExponent())); + } else if (keyPair.getPublic() instanceof ECPublicKey) { + publicKeyInfo = SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()); + } else { + fail(); + publicKeyInfo = null; + } + + X509v1CertificateBuilder builder = new X509v1CertificateBuilder(issuer, BigInteger.ONE, + notBefore, notAfter, issuer, publicKeyInfo); + + ContentSigner signer = new JcaContentSignerBuilder(full).setProvider( + new BouncyCastleProvider()).build(keyPair.getPrivate()); + X509CertificateHolder holder = builder.build(signer); + + JcaX509CertificateConverter converter = new JcaX509CertificateConverter() + .setProvider(new BouncyCastleProvider()); + + return new ApkZLibPair(keyPair.getPrivate(), converter.getCertificate(holder)); + } + +} |