/** * FDroid's index.jar is signed using a particular format and does not allow lots of * signing setups that would be valid for a regular jar. This validates those * restrictions. */ X509Certificate getSigningCertFromJar(JarEntry jarEntry) throws SigningException { final CodeSigner[] codeSigners = jarEntry.getCodeSigners(); if (codeSigners == null || codeSigners.length == 0) { throw new SigningException(repo, "No signature found in index"); } /* we could in theory support more than 1, but as of now we do not */ if (codeSigners.length > 1) { throw new SigningException(repo, "index.jar must be signed by a single code signer!"); } List<? extends Certificate> certs = codeSigners[0].getSignerCertPath().getCertificates(); if (certs.size() != 1) { throw new SigningException(repo, "index.jar code signers must only have a single certificate!"); } return (X509Certificate) certs.get(0); }
/** * Create the named SignatureFileVerifier. * * @param name the name of the signature block file (.DSA/.RSA/.EC) * * @param rawBytes the raw bytes of the signature block file */ public SignatureFileVerifier(ArrayList<CodeSigner[]> signerCache, ManifestDigester md, String name, byte rawBytes[]) throws IOException, CertificateException { // new PKCS7() calls CertificateFactory.getInstance() // need to use local providers here, see Providers class Object obj = null; try { obj = Providers.startJarVerification(); block = new PKCS7(rawBytes); sfBytes = block.getContentInfo().getData(); certificateFactory = CertificateFactory.getInstance("X509"); } finally { Providers.stopJarVerification(obj); } this.name = name.substring(0, name.lastIndexOf(".")) .toUpperCase(Locale.ENGLISH); this.md = md; this.signerCache = signerCache; }
/** * process the signature block file. Goes through the .SF file * and adds code signers for each section where the .SF section * hash was verified against the Manifest section. * * */ public void process(Hashtable<String, CodeSigner[]> signers, List<Object> manifestDigests) throws IOException, SignatureException, NoSuchAlgorithmException, JarException, CertificateException { // calls Signature.getInstance() and MessageDigest.getInstance() // need to use local providers here, see Providers class Object obj = null; try { obj = Providers.startJarVerification(); processImpl(signers, manifestDigests); } finally { Providers.stopJarVerification(obj); } }
/** * Create the named SignatureFileVerifier. * * @param name the name of the signature block file (.DSA/.RSA/.EC) * * @param rawBytes the raw bytes of the signature block file */ public SignatureFileVerifier(ArrayList<CodeSigner[]> signerCache, ManifestDigester md, String name, byte[] rawBytes) throws IOException, CertificateException { // new PKCS7() calls CertificateFactory.getInstance() // need to use local providers here, see Providers class Object obj = null; try { obj = Providers.startJarVerification(); block = new PKCS7(rawBytes); sfBytes = block.getContentInfo().getData(); certificateFactory = CertificateFactory.getInstance("X509"); } finally { Providers.stopJarVerification(obj); } this.name = name.substring(0, name.lastIndexOf('.')) .toUpperCase(Locale.ENGLISH); this.md = md; this.signerCache = signerCache; }
static CodeSource findSource(ClassLoader loader, String name) { String fileName = name.replace('.', '/') + ".class"; URL url = loader.getResource(fileName); if (url == null) return null; CodeSigner[] signers = null; if (name.lastIndexOf('.') > -1) { try { URLConnection connection = url.openConnection(); if (connection instanceof JarURLConnection) { JarURLConnection jarConnection = (JarURLConnection) connection; url = jarConnection.getJarFileURL(); JarFile jarFile = jarConnection.getJarFile(); if (jarFile != null && jarFile.getManifest() != null) { signers = jarFile.getJarEntry(fileName).getCodeSigners(); } } } catch (IOException e) { return null; } } return new CodeSource(url, signers); }
/** * FDroid's index.jar is signed using a particular format and does not allow lots of * signing setups that would be valid for a regular jar. This validates those * restrictions. */ private X509Certificate getSigningCertFromJar(JarEntry jarEntry) throws SigningException { final CodeSigner[] codeSigners = jarEntry.getCodeSigners(); if (codeSigners == null || codeSigners.length == 0) { throw new SigningException(repo, "No signature found in index"); } /* we could in theory support more than 1, but as of now we do not */ if (codeSigners.length > 1) { throw new SigningException(repo, "index.jar must be signed by a single code signer!"); } List<? extends Certificate> certs = codeSigners[0].getSignerCertPath().getCertificates(); if (certs.size() != 1) { throw new SigningException(repo, "index.jar code signers must only have a single certificate!"); } return (X509Certificate) certs.get(0); }
@Test public void testManyEntriesSingleValidSigner() throws Exception { JarCertVerifier jcv = new JarCertVerifier(null); CodeSigner[] signers = { alphaSigner }; Vector<JarEntry> entries = new Vector<JarEntry>(); entries.add(new JarCertVerifierEntry("firstSignedByOne", signers)); entries.add(new JarCertVerifierEntry("secondSignedByOne", signers)); entries.add(new JarCertVerifierEntry("thirdSignedByOne", signers)); VerifyResult result = jcv.verifyJarEntryCerts("", true, entries); Assert.assertEquals("Three entries signed by one signer should be considered signed and okay.", VerifyResult.SIGNED_OK, result); Assert.assertEquals("Three entries signed by one signer means one signer in the verifier.", 1, jcv.getCertsList().size()); Assert.assertTrue("Three entries signed by one signer means one signer in the verifier.", jcv.getCertsList().contains(alphaSigner.getSignerCertPath())); }
@Test public void testSingleEntryMultipleValidSigners() throws Exception { JarCertVerifier jcv = new JarCertVerifier(null); CodeSigner[] signers = { alphaSigner, betaSigner, charlieSigner }; Vector<JarEntry> entries = new Vector<JarEntry>(); entries.add(new JarCertVerifierEntry("firstSignedByThree", signers)); VerifyResult result = jcv.verifyJarEntryCerts("", true, entries); Assert.assertEquals("One entry signed by three signers should be considered signed and okay.", VerifyResult.SIGNED_OK, result); Assert.assertEquals("One entry signed by three means three signers in the verifier.", 3, jcv.getCertsList().size()); Assert.assertTrue("One entry signed by three means three signers in the verifier.", jcv.getCertsList().contains(alphaSigner.getSignerCertPath()) && jcv.getCertsList().contains(betaSigner.getSignerCertPath()) && jcv.getCertsList().contains(charlieSigner.getSignerCertPath())); }
@Test public void testManyEntriesMultipleValidSigners() throws Exception { JarCertVerifier jcv = new JarCertVerifier(null); CodeSigner[] signers = { alphaSigner, betaSigner, charlieSigner }; Vector<JarEntry> entries = new Vector<JarEntry>(); entries.add(new JarCertVerifierEntry("firstSignedByThree", signers)); entries.add(new JarCertVerifierEntry("secondSignedByThree", signers)); entries.add(new JarCertVerifierEntry("thirdSignedByThree", signers)); VerifyResult result = jcv.verifyJarEntryCerts("", true, entries); Assert.assertEquals("Three entries signed by three signers should be considered signed and okay.", VerifyResult.SIGNED_OK, result); Assert.assertEquals("Three entries signed by three means three signers in the verifier.", 3, jcv.getCertsList().size()); Assert.assertTrue("Three entries signed by three means three signers in the verifier.", jcv.getCertsList().contains(alphaSigner.getSignerCertPath()) && jcv.getCertsList().contains(betaSigner.getSignerCertPath()) && jcv.getCertsList().contains(charlieSigner.getSignerCertPath())); }
@Test public void testOneCommonSigner() throws Exception { JarCertVerifier jcv = new JarCertVerifier(null); CodeSigner[] alphaSigners = { alphaSigner }; CodeSigner[] betaSigners = { alphaSigner, betaSigner }; CodeSigner[] charlieSigners = { alphaSigner, charlieSigner }; Vector<JarEntry> entries = new Vector<JarEntry>(); entries.add(new JarCertVerifierEntry("firstSignedByOne", alphaSigners)); entries.add(new JarCertVerifierEntry("secondSignedByTwo", betaSigners)); entries.add(new JarCertVerifierEntry("thirdSignedByTwo", charlieSigners)); VerifyResult result = jcv.verifyJarEntryCerts("", true, entries); Assert.assertEquals("Three entries signed by at least one common signer should be considered signed and okay.", VerifyResult.SIGNED_OK, result); Assert.assertEquals("Three entries signed completely by only one signer means one signer in the verifier.", 1, jcv.getCertsList().size()); Assert.assertTrue("Three entries signed completely by only one signer means one signer in the verifier.", jcv.getCertsList().contains(alphaSigner.getSignerCertPath())); }
@Test public void testNoCommonSigner() throws Exception { JarCertVerifier jcv = new JarCertVerifier(null); CodeSigner[] alphaSigners = { alphaSigner }; CodeSigner[] betaSigners = { betaSigner }; CodeSigner[] charlieSigners = { charlieSigner }; Vector<JarEntry> entries = new Vector<JarEntry>(); entries.add(new JarCertVerifierEntry("firstSignedByAlpha", alphaSigners)); entries.add(new JarCertVerifierEntry("secondSignedByBeta", betaSigners)); entries.add(new JarCertVerifierEntry("thirdSignedByCharlie", charlieSigners)); VerifyResult result = jcv.verifyJarEntryCerts("", true, entries); Assert.assertEquals("Three entries signed by no common signers should be considered unsigned.", VerifyResult.UNSIGNED, result); Assert.assertEquals("Three entries signed by no common signers means no signers in the verifier.", 0, jcv.getCertsList().size()); }
@Test public void testFewButNotAllCommonSigners() throws Exception { JarCertVerifier jcv = new JarCertVerifier(null); CodeSigner[] alphaSigners = { alphaSigner }; CodeSigner[] betaSigners = { betaSigner }; Vector<JarEntry> entries = new Vector<JarEntry>(); entries.add(new JarCertVerifierEntry("firstSignedByAlpha", alphaSigners)); entries.add(new JarCertVerifierEntry("secondSignedByAlpha", alphaSigners)); entries.add(new JarCertVerifierEntry("thirdSignedByBeta", betaSigners)); VerifyResult result = jcv.verifyJarEntryCerts("", true, entries); Assert.assertEquals("First two entries signed by alpha signer, third entry signed by beta signer should be considered unisgned.", VerifyResult.UNSIGNED, result); Assert.assertEquals("Three entries signed by some common signers but not all means no signers in the verifier.", 0, jcv.getCertsList().size()); }
@Test public void testManyEntriesExpiredSigner() throws Exception { JarCertVerifier jcv = new JarCertVerifier(null); CodeSigner[] expiredSigners = { expiredSigner }; Vector<JarEntry> entries = new Vector<JarEntry>(); entries.add(new JarCertVerifierEntry("firstSignedByExpired", expiredSigners)); entries.add(new JarCertVerifierEntry("secondSignedBExpired", expiredSigners)); entries.add(new JarCertVerifierEntry("thirdSignedByExpired", expiredSigners)); VerifyResult result = jcv.verifyJarEntryCerts("", true, entries); Assert.assertEquals("Three entries signed by expired cert, should be considered signed but not okay.", VerifyResult.SIGNED_NOT_OK, result); Assert.assertEquals("Three entries signed by expired cert means one signer in the verifier.", 1, jcv.getCertsList().size()); Assert.assertTrue("Three entries signed by expired cert means one signer in the verifier.", jcv.getCertsList().contains(expiredSigner.getSignerCertPath())); }
@Test public void testManyEntriesExpiringSigner() throws Exception { JarCertVerifier jcv = new JarCertVerifier(null); CodeSigner[] expiringSigners = { expiringSigner }; Vector<JarEntry> entries = new Vector<JarEntry>(); entries.add(new JarCertVerifierEntry("firstSignedByExpiring", expiringSigners)); entries.add(new JarCertVerifierEntry("secondSignedBExpiring", expiringSigners)); entries.add(new JarCertVerifierEntry("thirdSignedByExpiring", expiringSigners)); VerifyResult result = jcv.verifyJarEntryCerts("", true, entries); Assert.assertEquals("Three entries signed by expiring cert, should be considered signed and okay.", VerifyResult.SIGNED_OK, result); Assert.assertEquals("Three entries signed by expiring cert means one signer in the verifier.", 1, jcv.getCertsList().size()); Assert.assertTrue("Three entries signed by expiring cert means one signer in the verifier.", jcv.getCertsList().contains(expiringSigner.getSignerCertPath())); }
@Test public void testManyEntriesNotYetValidSigner() throws Exception { JarCertVerifier jcv = new JarCertVerifier(null); CodeSigner[] notYetValidSigners = { notYetValidSigner }; Vector<JarEntry> entries = new Vector<JarEntry>(); entries.add(new JarCertVerifierEntry("firstSignedByNotYetValid", notYetValidSigners)); entries.add(new JarCertVerifierEntry("secondSignedByNotYetValid", notYetValidSigners)); entries.add(new JarCertVerifierEntry("thirdSignedByNotYetValid", notYetValidSigners)); VerifyResult result = jcv.verifyJarEntryCerts("", true, entries); Assert.assertEquals("Three entries signed by cert that is not yet valid, should be considered signed but not okay.", VerifyResult.SIGNED_NOT_OK, result); Assert.assertEquals("Three entries signed by cert that is not yet valid means one signer in the verifier.", 1, jcv.getCertsList().size()); Assert.assertTrue("Three entries signed by cert that is not yet valid means one signer in the verifier.", jcv.getCertsList().contains(notYetValidSigner.getSignerCertPath())); }
@Test public void testManyEntryExpiringAndNotYetValidSigner() throws Exception { JarCertVerifier jcv = new JarCertVerifier(null); CodeSigner[] expiringAndNotYetValidSigners = { expiringAndNotYetValidSigner }; Vector<JarEntry> entries = new Vector<JarEntry>(); entries.add(new JarCertVerifierEntry("firstSignedByExpiringNotYetValid", expiringAndNotYetValidSigners)); entries.add(new JarCertVerifierEntry("secondSignedByExpiringNotYetValid", expiringAndNotYetValidSigners)); entries.add(new JarCertVerifierEntry("thirdSignedByExpiringNotYetValid", expiringAndNotYetValidSigners)); VerifyResult result = jcv.verifyJarEntryCerts("", true, entries); Assert.assertEquals("Three entries signed by cert that is not yet valid but also expiring, should be considered signed but not okay.", VerifyResult.SIGNED_NOT_OK, result); Assert.assertEquals("Three entries signed by cert that is not yet valid but also expiring means one signer in the verifier.", 1, jcv.getCertsList().size()); Assert.assertTrue("Three entries signed by cert that is not yet valid but also expiring means one signer in the verifier.", jcv.getCertsList().contains(expiringAndNotYetValidSigner.getSignerCertPath())); Assert.assertTrue("Three entries signed by cert that is not yet valid but also expiring means expiring issue should be in details list.", jcv.getDetails(expiringAndNotYetValidSigner.getSignerCertPath()).contains(R("SHasExpiringCert"))); }
@Test public void testSingleEntryOneExpiredOneValidSigner() throws Exception { JarCertVerifier jcv = new JarCertVerifier(null); CodeSigner[] oneExpiredOneValidSigner = { expiredSigner, alphaSigner }; Vector<JarEntry> entries = new Vector<JarEntry>(); entries.add(new JarCertVerifierEntry("firstSignedByTwo", oneExpiredOneValidSigner)); VerifyResult result = jcv.verifyJarEntryCerts("", true, entries); Assert.assertEquals("One entry signed by one expired cert and another valid cert, should be considered signed and okay.", VerifyResult.SIGNED_OK, result); Assert.assertEquals("One entry signed by one expired cert and another valid cert means two signers in the verifier.", 2, jcv.getCertsList().size()); Assert.assertTrue("One entry signed by one expired cert and another valid cert means two signers in the verifier.", jcv.getCertsList().contains(expiredSigner.getSignerCertPath()) && jcv.getCertsList().contains(alphaSigner.getSignerCertPath())); }
@Test public void testManyEntriesOneExpiredOneValidSigner() throws Exception { JarCertVerifier jcv = new JarCertVerifier(null); CodeSigner[] oneExpiredOneValidSigner = { expiredSigner, alphaSigner }; Vector<JarEntry> entries = new Vector<JarEntry>(); entries.add(new JarCertVerifierEntry("firstSignedByTwo", oneExpiredOneValidSigner)); entries.add(new JarCertVerifierEntry("secondSignedByTwo", oneExpiredOneValidSigner)); entries.add(new JarCertVerifierEntry("thirdSignedByTwo", oneExpiredOneValidSigner)); VerifyResult result = jcv.verifyJarEntryCerts("", true, entries); Assert.assertEquals("Three entries signed by one expired cert and another valid cert, should be considered signed and okay.", VerifyResult.SIGNED_OK, result); Assert.assertEquals("Three entries signed by one expired cert and another valid cert means two signers in the verifier.", 2, jcv.getCertsList().size()); Assert.assertTrue("Three entries signed by one expired cert and another valid cert means two signers in the verifier.", jcv.getCertsList().contains(expiredSigner.getSignerCertPath()) && jcv.getCertsList().contains(alphaSigner.getSignerCertPath())); }
@Test public void testSomeExpiredEntries() throws Exception { JarCertVerifier jcv = new JarCertVerifier(null); CodeSigner[] oneExpiredOneValidSigners = { expiredSigner, alphaSigner }; CodeSigner[] expiredSigners = { expiredSigner }; Vector<JarEntry> entries = new Vector<JarEntry>(); entries.add(new JarCertVerifierEntry("firstSignedByTwo", oneExpiredOneValidSigners)); entries.add(new JarCertVerifierEntry("secondSignedByTwo", oneExpiredOneValidSigners)); entries.add(new JarCertVerifierEntry("thirdSignedByExpired", expiredSigners)); VerifyResult result = jcv.verifyJarEntryCerts("", true, entries); Assert.assertEquals("Two entries signed by one expired and one valid cert, third signed by just expired cert, should be considered signed but not okay.", VerifyResult.SIGNED_NOT_OK, result); Assert.assertEquals("Two entries signed by one expired and one valid cert, third signed by just expired cert means one signer in the verifier.", 1, jcv.getCertsList().size()); Assert.assertTrue("Two entries signed by one expired and one valid cert, third signed by just expired cert means one signer in the verifier.", jcv.getCertsList().contains(expiredSigner.getSignerCertPath())); }
@Test public void testManyInvalidOneValidStillSignedOkay() throws Exception { JarCertVerifier jcv = new JarCertVerifier(null); CodeSigner[] oneExpiredOneValidSigners = { alphaSigner, expiredSigner }; CodeSigner[] oneNotYetValidOneValidSigners = { alphaSigner, notYetValidSigner }; CodeSigner[] oneExpiringSigners = { alphaSigner, expiringSigner }; Vector<JarEntry> entries = new Vector<JarEntry>(); entries.add(new JarCertVerifierEntry("META-INF/MANIFEST.MF")); entries.add(new JarCertVerifierEntry("firstSigned", oneExpiredOneValidSigners)); entries.add(new JarCertVerifierEntry("secondSigned", oneNotYetValidOneValidSigners)); entries.add(new JarCertVerifierEntry("thirdSigned", oneExpiringSigners)); entries.add(new JarCertVerifierEntry("oneDir/")); entries.add(new JarCertVerifierEntry("oneDir/fourthSigned", oneExpiredOneValidSigners)); VerifyResult result = jcv.verifyJarEntryCerts("", true, entries); Assert.assertEquals("Three entries sharing valid cert and others with issues, should be considered signed and okay.", VerifyResult.SIGNED_OK, result); Assert.assertEquals("Three entries sharing valid cert and others with issues means one signer in the verifier.", 1, jcv.getCertsList().size()); Assert.assertTrue("Three entries sharing valid cert and others with issues means one signer in the verifier.", jcv.getCertsList().contains(alphaSigner.getSignerCertPath())); }
/** * Gets all JAR file entry certificates. * Method scans entry for signers and than collects all their certificates. * * @param entry JAR file entry. * @return Array of certificates which corresponds to the entry. */ private static Certificate[] getCertificates(JarEntry entry) { Certificate[] certs = null; CodeSigner[] signers = entry.getCodeSigners(); // Extract the certificates in each code signer's cert chain. if (signers != null) { List<Certificate> certChains = new ArrayList<>(); for (CodeSigner signer : signers) { certChains.addAll(signer.getSignerCertPath().getCertificates()); } // Convert into a Certificate[] return certChains.toArray(new Certificate[certChains.size()]); } return certs; }
private CodeSource normalizeCodeSource(CodeSource codeSource) { URL codeSourceURL = PolicyUtils.normalizeURL(codeSource.getLocation()); CodeSource result = codeSource; if (codeSourceURL != codeSource.getLocation()) { // URL was normalized - recreate codeSource with new URL CodeSigner[] signers = codeSource.getCodeSigners(); if (signers == null) { result = new CodeSource(codeSourceURL, codeSource .getCertificates()); } else { result = new CodeSource(codeSourceURL, signers); } } return result; }
/** * Tests whether the getCertificates() returns certificates obtained from * the signers. */ @TestTargetNew( level = TestLevel.PARTIAL_COMPLETE, notes = "", method = "getCertificates", args = {} ) public void testGetCertificates_01() { if (!has_15_features()) { return; } CertPath cpath = TestCertUtils.getCertPath(); Certificate[] certs = (Certificate[]) cpath.getCertificates().toArray(); CodeSigner[] signers = { new CodeSigner(cpath, null) }; CodeSource cs = new CodeSource(null, signers); Certificate[] got = cs.getCertificates(); // The set of certificates must be exactly the same, // but the order is not specified assertTrue(presented(certs, got)); assertTrue(presented(got, certs)); }
protected Object[] getData() { URL url; CodeSigner[] signers = null; CertPath cpath = TestCertUtils.getCertPath(); Date now = new Date(); Timestamp ts = new Timestamp(now, cpath); try { url = new URL("http://localhost"); signers = new CodeSigner[] { new CodeSigner(cpath, ts) }; } catch (Exception ex) { throw new Error(ex); } Certificate[] x509chain = new Certificate[] { TestCertUtils.rootCA }; Object[] data = new Object[] { new CodeSource(url, (Certificate[])null), new CodeSource(url, new Certificate[0]), new CodeSource(url, signers), new CodeSource(null, x509chain), }; return data; }
/** * getCodeSigners(). Make sure, that CertException is handled properly */ public void testGetCodeSigners_04() { try { TestCertUtils.install_test_x509_factory(); X500Principal[] ps = TestCertUtils.UniGen.genX500s(1); // 2-certs chain X509Certificate rootCA = TestCertUtils.rootCA; X509Certificate c0 = new TestCertUtils.TestInvalidX509Certificate( ps[0], rootCA.getIssuerX500Principal()); java.security.cert.Certificate [] certs = new java.security.cert.Certificate[] { c0, rootCA }; CodeSource cs = new CodeSource(null, certs); CodeSigner[] signers = cs.getCodeSigners(); assertNull(signers); // Must force a check for 'factory==null' cs.getCodeSigners(); } finally { TestCertUtils.uninstall_test_x509_factory(); } }
private Class<?> defineClass(final String name, final URL url, final Manifest man, final byte[] b, final CodeSigner[] signers) { final int i = name.lastIndexOf('.'); if (i != -1) { final String pkgname = name.substring(0, i); if (getAndVerifyPackage(pkgname, man, url) == null) try { if (man != null) definePackage(pkgname, man, url); else definePackage(pkgname, null, null, null, null, null, null, null); } catch (final IllegalArgumentException iae) { if (getAndVerifyPackage(pkgname, man, url) == null) throw new AssertionError("Cannot find package " + pkgname); } } final CodeSource cs = new CodeSource(url, signers); return defineClass(name, b, 0, b.length, cs); }
/** * @tests java.util.jar.JarEntry#getCodeSigners() */ public void test_getCodeSigners() throws IOException { String jarFileName = "TestCodeSigners.jar"; Support_Resources.copyFile(resources, null, jarFileName); File file = new File(resources, jarFileName); JarFile jarFile = new JarFile(file); JarEntry jarEntry = jarFile.getJarEntry("Test.class"); InputStream in = jarFile.getInputStream(jarEntry); byte[] buffer = new byte[1024]; while (in.available() > 0) { in.read(buffer); } CodeSigner[] codeSigners = jarEntry.getCodeSigners(); assertEquals(2, codeSigners.length); List<?> certs_bob = codeSigners[0].getSignerCertPath().getCertificates(); List<?> certs_alice = codeSigners[1].getSignerCertPath().getCertificates(); if (1 == certs_bob.size()) { List<?> temp = certs_bob; certs_bob = certs_alice; certs_alice = temp; } assertEquals(2, certs_bob.size()); assertEquals(1, certs_alice.size()); assertNull("getCodeSigners() of a primitive JarEntry should return null", new JarEntry( "aaa").getCodeSigners()); }