aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog70
-rw-r--r--NEWS1
-rw-r--r--netx/net/sourceforge/jnlp/resources/Messages.properties3
-rw-r--r--netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java135
-rw-r--r--netx/net/sourceforge/jnlp/security/AppVerifier.java91
-rw-r--r--netx/net/sourceforge/jnlp/security/CertVerifier.java30
-rw-r--r--netx/net/sourceforge/jnlp/security/CertWarningPane.java4
-rw-r--r--netx/net/sourceforge/jnlp/security/CertsInfoPane.java2
-rw-r--r--netx/net/sourceforge/jnlp/security/HttpsCertVerifier.java28
-rw-r--r--netx/net/sourceforge/jnlp/security/JNLPAppVerifier.java143
-rw-r--r--netx/net/sourceforge/jnlp/security/MoreInfoPane.java4
-rw-r--r--netx/net/sourceforge/jnlp/security/PluginAppVerifier.java224
-rw-r--r--netx/net/sourceforge/jnlp/tools/CertInformation.java292
-rw-r--r--netx/net/sourceforge/jnlp/tools/JarCertVerifier.java650
-rw-r--r--tests/netx/unit/net/sourceforge/jnlp/tools/JarCertVerifierTest.java470
-rw-r--r--tests/test-extensions/net/sourceforge/jnlp/tools/CodeSignerCreator.java145
16 files changed, 1882 insertions, 410 deletions
diff --git a/ChangeLog b/ChangeLog
index 321c393..6429cb4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -28,6 +28,76 @@
* tests/reproducers/custom/AppletFolderInArchiveTag/srcs/Makefile: and
* tests/reproducers/custom/UnsignedContentInMETAINF/srcs/Makefile: following above renaming
+2012-10-19 Adam Domurad <[email protected]>
+
+ * netx/net/sourceforge/jnlp/security/AppVerifier.java: Use interface
+ types for declared types where applicable.
+ * netx/net/sourceforge/jnlp/security/PluginAppVerifier.java: Same.
+ * netx/net/sourceforge/jnlp/tools/JarCertVerifier.java: Same.
+
+2012-10-19 Danesh Dadachanji <[email protected]>
+
+ Rework JarCertVerifier certificate management to handle multiple
+ certificates and use different algorithms to verify JNLPs and Applets.
+ * netx/net/sourceforge/jnlp/resources/Messages.properties:
+ Removed SHasUnsignedEntry.
+ * netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java:
+ Set JCV instance to final but uninitialized.
+ (JNLPClassLoader): Initialized JCV with runtime dependent verifier.
+ (addNewJar), (initializeResources), (verifySignedJNLP):
+ Replaced use of local JarCertVerifier variable with the instance variable.
+ Added calls to isFullySigned wherever signer verification is done.
+ (activateJars): No longer verifies nested jars. These receive the same
+ security permissions as their parent jar, regardless of the nested
+ jar's signing.
+ (checkTrustWithUser): Removed JCV param, reimplemented to wrap around
+ JCV's checkTrustWithUser method.
+ (verifyJars): Removed.
+ * netx/net/sourceforge/jnlp/security/AppVerifier.java:
+ New strategy pattern interface that specifies verification methods
+ required regardless of the runtime.
+ * netx/net/sourceforge/jnlp/security/JNLPAppVerifier.java:
+ * netx/net/sourceforge/jnlp/security/PluginAppVerifier.java:
+ New strategy pattern classes used to determine which algorithms to use
+ depending on the runtime.
+ * netx/net/sourceforge/jnlp/security/CertVerifier.java:
+ Added CertPath param to all the methods.
+ (noSigningIssues): Removed.
+ * netx/net/sourceforge/jnlp/security/CertWarningPane.java:
+ * netx/net/sourceforge/jnlp/security/CertsInfoPane.java:
+ * netx/net/sourceforge/jnlp/security/MoreInfoPane.java:
+ Updated calls to the verifier's methods with the new CertPath param. All
+ are set to null so far.
+ * netx/net/sourceforge/jnlp/security/HttpsCertVerifier.java:
+ Added CertPath param to all the methods. It's mostly ignored though.
+ * netx/net/sourceforge/jnlp/tools/CertInformation.java:
+ New class to represent all the information about a signer with
+ with respect to all of the entries it has signed for the app.
+ * netx/net/sourceforge/jnlp/tools/JarCertVerifier.java:
+ Completely reworked to use CertInformation and AppVerifier functionality.
+ (getCertPath), (getCertInformation), (checkTrustWithUser),
+ (getJarSignableEntries), (getTotalJarEntries): New method.
+ (noSigningIssues), (anyJarsSigned): Removed.
+ (verifyResult): Renamed enum to VerifyResult
+ (JarCertVerifier): New constructor used to set AppVerifier instance.
+ (getAlreadyTrustPublisher), (getRootInCacerts): Now uses strategy pattern.
+ (hasSigningIssues), (getDetails), (checkTrustedCerts), (checkCertUsage):
+ Now uses cert info class.
+ (getCerts): Renamed to getCertsList.
+ (isFullySignedByASingleCert): renamed to isFullySigned and to use
+ the strategy pattern.
+ (add): New public method that resets some instance vars and
+ calls verifyJars.
+ (verifyJars): Modifier changed to private, above method should be used.
+ Also skips jars that have been verified before.
+ (verifyJar): Removed actual verification code, only reads jars into the JVM.
+ (verifyJarEntryCerts): New method. Does actual verification of jars.
+ (getPublisher), (getRoot): Use hacky currentlyUsed variable as the signer.
+ * tests/netx/unit/net/sourceforge/jnlp/tools/JarCertVerifierTest.java:
+ Unit test JCV's verifyJarEntryCerts method.
+ * tests/test-extensions/net/sourceforge/jnlp/tools/CodeSignerCreator.java:
+ Unit test helper that creates CodeSigner instances.
+
2012-10-16 Adam Domurad <[email protected]>
* tests/reproducers/simple/AppletTakesLastParam/srcs/AppletTakesLastParam.java:
diff --git a/NEWS b/NEWS
index de0c4c5..bdf2882 100644
--- a/NEWS
+++ b/NEWS
@@ -19,6 +19,7 @@ New in release 1.4 (2012-XX-XX):
- PR955: regression: SweetHome3D fails to run
- PR1145: IcedTea-Web can cause ClassCircularityError
- PR1161: X509VariableTrustManager does not work correctly with OpenJDK7
+ - PR822: Applets fail to load if jars have different signers
New in release 1.3 (2012-XX-XX):
* NetX
diff --git a/netx/net/sourceforge/jnlp/resources/Messages.properties b/netx/net/sourceforge/jnlp/resources/Messages.properties
index 1f3a1e5..4010837 100644
--- a/netx/net/sourceforge/jnlp/resources/Messages.properties
+++ b/netx/net/sourceforge/jnlp/resources/Messages.properties
@@ -82,7 +82,7 @@ LSignedAppJarUsingUnsignedJar=Signed application using unsigned jars.
LSignedAppJarUsingUnsignedJarInfo=The main application jar is signed, but some of the jars it is using aren't.
LSignedJNLPFileDidNotMatch=The signed JNLP file did not match the launching JNLP file.
LNoSecInstance=Error: No security instance for {0}. The application may have trouble continuing
-LCertFoundIn={0} found in cacerts ({1})
+LCertFoundIn={0} found in cacerts ({1})
LSingleInstanceExists=Another instance of this applet already exists and only one may be run at the same time.
JNotApplet=File is not an applet.
@@ -227,7 +227,6 @@ SJNLPFileIsNotSigned=This application contains a digital signature in which the
SBadKeyUsage=Resources contain entries whose signer certificate's KeyUsage extension doesn't allow code signing.
SBadExtendedKeyUsage=Resources contain entries whose signer certificate's ExtendedKeyUsage extension doesn't allow code signing.
SBadNetscapeCertType=Resources contain entries whose signer certificate's NetscapeCertType extension doesn't allow code signing.
-SHasUnsignedEntry=Resources contain unsigned entries which have not been integrity-checked.
SHasExpiredCert=The digital signature has expired.
SHasExpiringCert=Resources contain entries whose signer certificate will expire within six months.
SNotYetValidCert=Resources contain entries whose signer certificate is not yet valid.
diff --git a/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java b/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java
index 523d9bd..94d93a4 100644
--- a/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java
+++ b/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java
@@ -79,8 +79,10 @@ import net.sourceforge.jnlp.cache.CacheUtil;
import net.sourceforge.jnlp.cache.IllegalResourceDescriptorException;
import net.sourceforge.jnlp.cache.ResourceTracker;
import net.sourceforge.jnlp.cache.UpdatePolicy;
+import net.sourceforge.jnlp.security.AppVerifier;
+import net.sourceforge.jnlp.security.JNLPAppVerifier;
+import net.sourceforge.jnlp.security.PluginAppVerifier;
import net.sourceforge.jnlp.security.SecurityDialogs;
-import net.sourceforge.jnlp.security.SecurityDialogs.AccessType;
import net.sourceforge.jnlp.tools.JarCertVerifier;
import net.sourceforge.jnlp.util.FileUtils;
import sun.misc.JarIndex;
@@ -153,14 +155,8 @@ public class JNLPClassLoader extends URLClassLoader {
/** all jars not yet part of classloader or active */
private List<JARDesc> available = new ArrayList<JARDesc>();
- /** all of the jar files that were verified */
- private ArrayList<String> verifiedJars = null;
-
- /** all of the jar files that were not verified */
- private ArrayList<String> unverifiedJars = null;
-
/** the jar cert verifier tool to verify our jars */
- private JarCertVerifier jcv = null;
+ private final JarCertVerifier jcv;
private boolean signing = false;
@@ -223,6 +219,16 @@ public class JNLPClassLoader extends URLClassLoader {
this.mainClass = mainName;
+ AppVerifier verifier;
+
+ if (file instanceof PluginBridge && !((PluginBridge)file).useJNLPHref()) {
+ verifier = new PluginAppVerifier();
+ } else {
+ verifier = new JNLPAppVerifier();
+ }
+
+ jcv = new JarCertVerifier(verifier);
+
// initialize extensions
initializeExtensions();
@@ -604,10 +610,8 @@ public class JNLPClassLoader extends URLClassLoader {
if (JNLPRuntime.isVerifying()) {
- JarCertVerifier jcv;
-
try {
- jcv = verifyJars(initialJars);
+ jcv.add(initialJars, tracker);
} catch (Exception e) {
//we caught an Exception from the JarCertVerifier class.
//Note: one of these exceptions could be from not being able
@@ -618,7 +622,7 @@ public class JNLPClassLoader extends URLClassLoader {
}
//Case when at least one jar has some signing
- if (jcv.anyJarsSigned() && jcv.isFullySignedByASingleCert()) {
+ if (jcv.isFullySigned()) {
signing = true;
if (!jcv.allJarsSigned() &&
@@ -650,10 +654,10 @@ public class JNLPClassLoader extends URLClassLoader {
// If main jar was found, but a signed JNLP file was not located
if (!isSignedJNLP && foundMainJar)
file.setSignedJNLPAsMissing();
-
+
//user does not trust this publisher
- if (!jcv.getAlreadyTrustPublisher() && !jcv.isTriviallySigned()) {
- checkTrustWithUser(jcv);
+ if (!jcv.isTriviallySigned()) {
+ checkTrustWithUser();
} else {
/**
* If the user trusts this publisher (i.e. the publisher's certificate
@@ -864,7 +868,6 @@ public class JNLPClassLoader extends URLClassLoader {
private void verifySignedJNLP(JARDesc jarDesc, JarFile jarFile)
throws LaunchException {
- JarCertVerifier signer = new JarCertVerifier();
List<JARDesc> desc = new ArrayList<JARDesc>();
desc.add(jarDesc);
@@ -875,9 +878,9 @@ public class JNLPClassLoader extends URLClassLoader {
InputStreamReader jnlpReader = null;
try {
- signer.verifyJars(desc, tracker);
-
- if (signer.allJarsSigned()) { // If the jar is signed
+ // NOTE: verification should have happened by now. In other words,
+ // calling jcv.verifyJars(desc, tracker) here should have no affect.
+ if (jcv.isFullySigned()) {
Enumeration<JarEntry> entries = jarFile.entries();
JarEntry je;
@@ -961,7 +964,7 @@ public class JNLPClassLoader extends URLClassLoader {
/*
* After this exception is caught, it is escaped. If an exception is
* thrown while handling the jar file, (mainly for
- * JarCertVerifier.verifyJars) it assumes the jar file is unsigned and
+ * JarCertVerifier.add) it assumes the jar file is unsigned and
* skip the check for a signed JNLP file
*/
@@ -991,28 +994,18 @@ public class JNLPClassLoader extends URLClassLoader {
e.printStackTrace(System.err);
}
}
-
- private void checkTrustWithUser(JarCertVerifier jcv) throws LaunchException {
+
+ /**
+ * Prompt the user for trust on all the signers that require approval.
+ * @throws LaunchException if the user does not approve every dialog prompt.
+ */
+ private void checkTrustWithUser() throws LaunchException {
if (JNLPRuntime.isTrustAll()){
return;
}
- if (!jcv.getRootInCacerts()) { //root cert is not in cacerts
- boolean b = SecurityDialogs.showCertWarningDialog(
- AccessType.UNVERIFIED, file, jcv);
- if (!b)
- throw new LaunchException(null, null, R("LSFatal"),
- R("LCLaunching"), R("LNotVerified"), "");
- } else if (jcv.getRootInCacerts()) { //root cert is in cacerts
- boolean b = false;
- if (jcv.noSigningIssues())
- b = SecurityDialogs.showCertWarningDialog(
- AccessType.VERIFIED, file, jcv);
- else if (!jcv.noSigningIssues())
- b = SecurityDialogs.showCertWarningDialog(
- AccessType.SIGNING_ERROR, file, jcv);
- if (!b)
- throw new LaunchException(null, null, R("LSFatal"),
- R("LCLaunching"), R("LCancelOnUserRequest"), "");
+
+ if (jcv.isFullySigned() && !jcv.getAlreadyTrustPublisher()) {
+ jcv.checkTrustWithUser(file);
}
}
@@ -1226,15 +1219,25 @@ public class JNLPClassLoader extends URLClassLoader {
continue;
}
- JarCertVerifier signer = new JarCertVerifier();
- List<JARDesc> jars = new ArrayList<JARDesc>();
- JARDesc jarDesc = new JARDesc(new File(extractedJarLocation).toURL(), null, null, false, false, false, false);
- jars.add(jarDesc);
tracker.addResource(new File(extractedJarLocation).toURL(), null, null, null);
- signer.verifyJars(jars, tracker);
- if (signer.anyJarsSigned() && !signer.getAlreadyTrustPublisher()) {
- checkTrustWithUser(signer);
+ URL codebase = file.getCodeBase();
+ if (codebase == null) {
+ //FIXME: codebase should be the codebase of the Main Jar not
+ //the location. Although, it still works in the current state.
+ codebase = file.getResources().getMainJAR().getLocation();
+ }
+
+ SecurityDesc jarSecurity = null;
+ if (jcv.isFullySigned()) {
+ // Already trust application, nested jar should be given
+ jarSecurity = new SecurityDesc(file,
+ SecurityDesc.ALL_PERMISSIONS,
+ codebase.getHost());
+ } else {
+ jarSecurity = new SecurityDesc(file,
+ SecurityDesc.SANDBOX_PERMISSIONS,
+ codebase.getHost());
}
try {
@@ -1244,25 +1247,6 @@ public class JNLPClassLoader extends URLClassLoader {
CachedJarFileCallback.getInstance().addMapping(fakeRemote, fileURL);
addURL(fakeRemote);
- SecurityDesc jarSecurity = file.getSecurity();
-
- if (file instanceof PluginBridge) {
-
- URL codebase = null;
-
- if (file.getCodeBase() != null) {
- codebase = file.getCodeBase();
- } else {
- //Fixme: codebase should be the codebase of the Main Jar not
- //the location. Although, it still works in the current state.
- codebase = file.getResources().getMainJAR().getLocation();
- }
-
- jarSecurity = new SecurityDesc(file,
- SecurityDesc.ALL_PERMISSIONS,
- codebase.getHost());
- }
-
jarLocationSecurityMap.put(fakeRemote, jarSecurity);
} catch (MalformedURLException mfue) {
@@ -1475,18 +1459,6 @@ public class JNLPClassLoader extends URLClassLoader {
}
/**
- * Verifies code signing of jars to be used.
- *
- * @param jars the jars to be verified.
- */
- private JarCertVerifier verifyJars(List<JARDesc> jars) throws Exception {
-
- jcv = new JarCertVerifier();
- jcv.verifyJars(jars, tracker);
- return jcv;
- }
-
- /**
* Find the loaded class in this loader or any of its extension loaders.
*/
protected Class findLoadedClassAll(String name) {
@@ -1642,7 +1614,6 @@ public class JNLPClassLoader extends URLClassLoader {
// Verify if needed
- final JarCertVerifier signer = new JarCertVerifier();
final List<JARDesc> jars = new ArrayList<JARDesc>();
jars.add(desc);
@@ -1654,14 +1625,12 @@ public class JNLPClassLoader extends URLClassLoader {
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
public Void run() throws Exception {
- signer.verifyJars(jars, tracker);
+ jcv.add(jars, tracker);
- if (signer.anyJarsSigned() && !signer.getAlreadyTrustPublisher()) {
- checkTrustWithUser(signer);
- }
+ checkTrustWithUser();
final SecurityDesc security;
- if (signer.anyJarsSigned()) {
+ if (jcv.isFullySigned()) {
security = new SecurityDesc(file,
SecurityDesc.ALL_PERMISSIONS,
file.getCodeBase().getHost());
diff --git a/netx/net/sourceforge/jnlp/security/AppVerifier.java b/netx/net/sourceforge/jnlp/security/AppVerifier.java
new file mode 100644
index 0000000..2fc1ec0
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/AppVerifier.java
@@ -0,0 +1,91 @@
+/* AppVerifier.java
+ Copyright (C) 2012 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.security;
+
+import java.security.cert.CertPath;
+import java.util.HashMap;
+
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.LaunchException;
+import net.sourceforge.jnlp.tools.CertInformation;
+import net.sourceforge.jnlp.tools.JarCertVerifier;
+
+/**
+ * An interface that provides various details about an app's signers.
+ */
+public interface AppVerifier {
+
+ /**
+ * Checks if the app has already found trust in its publisher(s).
+ * @param certs The certs to search through and their cert information
+ * @param signedJars A map of all the jars of this app and the number of
+ * signed entries each one has.
+ * @return True if the app trusts its publishers.
+ */
+ public boolean hasAlreadyTrustedPublisher(
+ HashMap<CertPath, CertInformation> certs,
+ HashMap<String, Integer> signedJars);
+
+ /**
+ * Checks if the app has signer(s) whose certs along their chains are in CA certs.
+ * @param certs The certs to search through and their cert information
+ * @param signedJars A map of all the jars of this app and the number of
+ * signed entries each one has.
+ * @return True if the app has a root in the CA certs store.
+ */
+ public boolean hasRootInCacerts(HashMap<CertPath, CertInformation> certs,
+ HashMap<String, Integer> signedJars);
+
+ /**
+ * Checks if the app's jars are covered by the provided certificates, enough
+ * to consider the app fully signed.
+ * @param certs Any possible signer and their respective information regarding this app.
+ * @param signedJars A map of all the jars of this app and the number of
+ * signed entries each one has.
+ * @return
+ */
+ public boolean isFullySigned(HashMap<CertPath, CertInformation> certs,
+ HashMap<String, Integer> signedJars);
+
+ /**
+ * Prompt the user with requests for trusting the certificates used by this app
+ * @throws LaunchException
+ */
+ public void checkTrustWithUser(JarCertVerifier jcv, JNLPFile file)
+ throws LaunchException;
+}
diff --git a/netx/net/sourceforge/jnlp/security/CertVerifier.java b/netx/net/sourceforge/jnlp/security/CertVerifier.java
index 842a865..7769884 100644
--- a/netx/net/sourceforge/jnlp/security/CertVerifier.java
+++ b/netx/net/sourceforge/jnlp/security/CertVerifier.java
@@ -1,5 +1,5 @@
/* CertVerifier.java
- Copyright (C) 2009 Red Hat, Inc.
+ Copyright (C) 2012 Red Hat, Inc.
This file is part of IcedTea.
@@ -39,10 +39,10 @@ package net.sourceforge.jnlp.security;
import java.security.cert.CertPath;
import java.security.cert.Certificate;
-import java.util.ArrayList;
+import java.util.List;
/**
- * An interface that provides various details about a certificate
+ * An interface that provides various details about certificates of an app.
*/
public interface CertVerifier {
@@ -58,36 +58,30 @@ public interface CertVerifier {
public boolean getRootInCacerts();
/**
- * Return if there are signing issues with the certificate(s) being veried
+ * Return if there are signing issues with the certificate being verified
*/
- public boolean hasSigningIssues();
+ public boolean hasSigningIssues(CertPath certPath);
/**
- * Return if there are no signing issues with this cert (!hasSigningIssues())
+ * Get the details regarding issue with this certificate
*/
- public boolean noSigningIssues();
+ public List<String> getDetails(CertPath certPath);
/**
- * Get the details regarding issue(s) with this certificate
- */
- public ArrayList<String> getDetails();
-
- /**
- * Return a valid certificate path to this certificate(s) being verified
+ * Return a valid certificate path to this certificate being verified
* @return The CertPath
*/
- public CertPath getCertPath();
+ public CertPath getCertPath(CertPath certPath);
/**
* Returns the application's publisher's certificate.
*/
- public abstract Certificate getPublisher();
+ public abstract Certificate getPublisher(CertPath certPath);
/**
* Returns the application's root's certificate. This
- * may return the same certificate as getPublisher() in
+ * may return the same certificate as getPublisher(CertPath certPath) in
* the event that the application is self signed.
*/
- public abstract Certificate getRoot();
-
+ public abstract Certificate getRoot(CertPath certPath);
}
diff --git a/netx/net/sourceforge/jnlp/security/CertWarningPane.java b/netx/net/sourceforge/jnlp/security/CertWarningPane.java
index eedd86e..fff814c 100644
--- a/netx/net/sourceforge/jnlp/security/CertWarningPane.java
+++ b/netx/net/sourceforge/jnlp/security/CertWarningPane.java
@@ -96,7 +96,7 @@ public class CertWarningPane extends SecurityDialogPanel {
private void addComponents() {
AccessType type = parent.getAccessType();
JNLPFile file = parent.getFile();
- Certificate c = parent.getCertVerifier().getPublisher();
+ Certificate c = parent.getCertVerifier().getPublisher(null);
String name = "";
String publisher = "";
@@ -253,7 +253,7 @@ public class CertWarningPane extends SecurityDialogPanel {
if (alwaysTrust != null && alwaysTrust.isSelected()) {
try {
KeyStore ks = KeyStores.getKeyStore(Level.USER, Type.CERTS);
- X509Certificate c = (X509Certificate) parent.getCertVerifier().getPublisher();
+ X509Certificate c = (X509Certificate) parent.getCertVerifier().getPublisher(null);
CertificateUtils.addToKeyStore(c, ks);
File keyStoreFile = new File(KeyStores.getKeyStoreLocation(Level.USER, Type.CERTS));
if (!keyStoreFile.isFile()) {
diff --git a/netx/net/sourceforge/jnlp/security/CertsInfoPane.java b/netx/net/sourceforge/jnlp/security/CertsInfoPane.java
index c4f7c67..d22a590 100644
--- a/netx/net/sourceforge/jnlp/security/CertsInfoPane.java
+++ b/netx/net/sourceforge/jnlp/security/CertsInfoPane.java
@@ -84,7 +84,7 @@ public class CertsInfoPane extends SecurityDialogPanel {
* Builds the JTree out of CertPaths.
*/
void buildTree() {
- certPath = parent.getCertVerifier().getCertPath();
+ certPath = parent.getCertVerifier().getCertPath(null);
X509Certificate firstCert =
((X509Certificate) certPath.getCertificates().get(0));
String subjectString =
diff --git a/netx/net/sourceforge/jnlp/security/HttpsCertVerifier.java b/netx/net/sourceforge/jnlp/security/HttpsCertVerifier.java
index 8490cf8..edd5899 100644
--- a/netx/net/sourceforge/jnlp/security/HttpsCertVerifier.java
+++ b/netx/net/sourceforge/jnlp/security/HttpsCertVerifier.java
@@ -80,7 +80,14 @@ public class HttpsCertVerifier implements CertVerifier {
return isTrusted;
}
- public CertPath getCertPath() {
+
+ /* XXX: Most of these methods have a CertPath param that should be passed
+ * from the UI dialogs. However, this is not implemented yet so most of
+ * the params are ignored.
+ */
+
+ @Override
+ public CertPath getCertPath(CertPath certPath) { // Parameter ignored.
ArrayList<X509Certificate> list = new ArrayList<X509Certificate>();
for (int i = 0; i < chain.length; i++)
@@ -99,7 +106,8 @@ public class HttpsCertVerifier implements CertVerifier {
return certPaths.get(0);
}
- public ArrayList<String> getDetails() {
+ @Override
+ public List<String> getDetails(CertPath certPath) { // Parameter ignored.
boolean hasExpiredCert = false;
boolean hasExpiringCert = false;
@@ -192,13 +200,15 @@ public class HttpsCertVerifier implements CertVerifier {
details.add(detail);
}
- public Certificate getPublisher() {
+ @Override
+ public Certificate getPublisher(CertPath certPath) { // Paramater ignored.
if (chain.length > 0)
return (Certificate) chain[0];
return null;
}
- public Certificate getRoot() {
+ @Override
+ public Certificate getRoot(CertPath certPath) { // Parameter ignored.
if (chain.length > 0)
return (Certificate) chain[chain.length - 1];
return null;
@@ -207,18 +217,14 @@ public class HttpsCertVerifier implements CertVerifier {
public boolean getRootInCacerts() {
try {
KeyStore[] caCertsKeyStores = KeyStores.getCAKeyStores();
- return CertificateUtils.inKeyStores((X509Certificate) getRoot(), caCertsKeyStores);
+ return CertificateUtils.inKeyStores((X509Certificate) getRoot(null), caCertsKeyStores);
} catch (Exception e) {
}
return false;
}
- public boolean hasSigningIssues() {
- return false;
- }
-
- public boolean noSigningIssues() {
+ @Override
+ public boolean hasSigningIssues(CertPath certPath) {
return false;
}
-
}
diff --git a/netx/net/sourceforge/jnlp/security/JNLPAppVerifier.java b/netx/net/sourceforge/jnlp/security/JNLPAppVerifier.java
new file mode 100644
index 0000000..9dcfaf5
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/JNLPAppVerifier.java
@@ -0,0 +1,143 @@
+/* JNLPAppVerifier.java
+ Copyright (C) 2012 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+ */
+
+package net.sourceforge.jnlp.security;
+
+import static net.sourceforge.jnlp.runtime.Translator.R;
+
+import java.security.cert.CertPath;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.LaunchException;
+import net.sourceforge.jnlp.security.SecurityDialogs.AccessType;
+import net.sourceforge.jnlp.tools.CertInformation;
+import net.sourceforge.jnlp.tools.JarCertVerifier;
+
+public class JNLPAppVerifier implements AppVerifier {
+
+ @Override
+ public boolean hasAlreadyTrustedPublisher(
+ HashMap<CertPath, CertInformation> certs,
+ HashMap<String, Integer> signedJars) {
+ int sumOfSignableEntries = JarCertVerifier.getTotalJarEntries(signedJars);
+ for (CertInformation certInfo : certs.values()) {
+ Map<String, Integer> certSignedJars = certInfo.getSignedJars();
+
+ if (JarCertVerifier.getTotalJarEntries(certSignedJars) == sumOfSignableEntries
+ && certInfo.isPublisherAlreadyTrusted()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean hasRootInCacerts(HashMap<CertPath, CertInformation> certs,
+ HashMap<String, Integer> signedJars) {
+ int sumOfSignableEntries = JarCertVerifier.getTotalJarEntries(signedJars);
+ for (CertInformation certInfo : certs.values()) {
+ Map<String, Integer> certSignedJars = certInfo.getSignedJars();
+
+ if (JarCertVerifier.getTotalJarEntries(certSignedJars) == sumOfSignableEntries
+ && certInfo.isRootInCacerts()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isFullySigned(HashMap<CertPath, CertInformation> certs,
+ HashMap<String, Integer> signedJars) {
+ int sumOfSignableEntries = JarCertVerifier.getTotalJarEntries(signedJars);
+ for (CertPath cPath : certs.keySet()) {
+ // If this cert has signed everything, return true
+ if (hasCompletelySignedApp(certs.get(cPath), sumOfSignableEntries)) {
+ return true;
+ }
+ }
+
+ // No cert found that signed all entries. Return false.
+ return false;
+ }
+
+ @Override
+ public void checkTrustWithUser(JarCertVerifier jcv, JNLPFile file)
+ throws LaunchException {
+
+ int sumOfSignableEntries = JarCertVerifier.getTotalJarEntries(jcv.getJarSignableEntries());
+ for (CertPath cPath : jcv.getCertsList()) {
+ jcv.setCurrentlyUsedCertPath(cPath);
+ CertInformation info = jcv.getCertInformation(cPath);
+ if (hasCompletelySignedApp(info, sumOfSignableEntries)) {
+ if (info.isPublisherAlreadyTrusted()) {
+ return;
+ }
+
+ AccessType dialogType;
+ if (info.isRootInCacerts() && !info.hasSigningIssues()) {
+ dialogType = AccessType.VERIFIED;
+ } else if (info.isRootInCacerts()) {
+ dialogType = AccessType.SIGNING_ERROR;
+ } else {
+ dialogType = AccessType.UNVERIFIED;
+ }
+
+ boolean wasApproved = SecurityDialogs.showCertWarningDialog(
+ dialogType, file, jcv);
+ if (wasApproved) {
+ return;
+ }
+ }
+ }
+
+ throw new LaunchException(null, null, R("LSFatal"), R("LCLaunching"),
+ R("LCancelOnUserRequest"), "");
+ }
+
+ /**
+ * Find out if the CertPath with the given info has fully signed the app.
+ * @param info The information regarding the CertPath in question
+ * @param sumOfSignableEntries The total number of signable entries in the app.
+ * @return True if the signer has fully signed this app.
+ */
+ public boolean hasCompletelySignedApp(CertInformation info, int sumOfSignableEntries) {
+ return JarCertVerifier.getTotalJarEntries(info.getSignedJars()) == sumOfSignableEntries;
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/security/MoreInfoPane.java b/netx/net/sourceforge/jnlp/security/MoreInfoPane.java
index 3276ea2..1ccd302 100644
--- a/netx/net/sourceforge/jnlp/security/MoreInfoPane.java
+++ b/netx/net/sourceforge/jnlp/security/MoreInfoPane.java
@@ -44,7 +44,7 @@ import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
-import java.util.ArrayList;
+import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
@@ -73,7 +73,7 @@ public class MoreInfoPane extends SecurityDialogPanel {
* Constructs the GUI components of this panel
*/
private void addComponents() {
- ArrayList<String> details = certVerifier.getDetails();
+ List<String> details = certVerifier.getDetails(null);
// Show signed JNLP warning if the signed main jar does not have a
// signed JNLP file and the launching JNLP file contains special properties
diff --git a/netx/net/sourceforge/jnlp/security/PluginAppVerifier.java b/netx/net/sourceforge/jnlp/security/PluginAppVerifier.java
new file mode 100644
index 0000000..a8589d8
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/PluginAppVerifier.java
@@ -0,0 +1,224 @@
+/* PluginAppVerifier.java
+ Copyright (C) 2012 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+ */
+
+package net.sourceforge.jnlp.security;
+
+import static net.sourceforge.jnlp.runtime.Translator.R;
+
+import java.security.cert.CertPath;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.LaunchException;
+import net.sourceforge.jnlp.security.SecurityDialogs.AccessType;
+import net.sourceforge.jnlp.tools.CertInformation;
+import net.sourceforge.jnlp.tools.JarCertVerifier;
+
+public class PluginAppVerifier implements AppVerifier {
+
+ @Override
+ public boolean hasAlreadyTrustedPublisher(
+ HashMap<CertPath, CertInformation> certs,
+ HashMap<String, Integer> signedJars) {
+
+ boolean allPublishersTrusted = true;
+
+ for(String jarName : signedJars.keySet()) {
+ int numbSignableEntries = signedJars.get(jarName);
+ boolean publisherTrusted = false;
+
+ for (CertInformation certInfo : certs.values()) {
+ if(certInfo.isSignerOfJar(jarName)
+ && numbSignableEntries == certInfo.getNumJarEntriesSigned(jarName)
+ && certInfo.isPublisherAlreadyTrusted()) {
+ publisherTrusted = true;
+ break;
+ }
+ }
+
+ allPublishersTrusted &= publisherTrusted;
+ }
+ return allPublishersTrusted;
+ }
+
+ @Override
+ public boolean hasRootInCacerts(HashMap<CertPath, CertInformation> certs,
+ HashMap<String, Integer> signedJars) {
+
+ boolean allRootCAsTrusted = true;
+
+ for(String jarName : signedJars.keySet()) {
+ int numbSignableEntries = signedJars.get(jarName);
+ boolean rootCATrusted = false;
+
+ for (CertInformation certInfo : certs.values()) {
+ if(certInfo.isSignerOfJar(jarName)
+ && numbSignableEntries == certInfo.getNumJarEntriesSigned(jarName)
+ && certInfo.isRootInCacerts()) {
+ rootCATrusted = true;
+ break;
+ }
+ }
+
+ allRootCAsTrusted &= rootCATrusted;
+ }
+ return allRootCAsTrusted;
+ }
+
+ @Override
+ public boolean isFullySigned(HashMap<CertPath, CertInformation> certs,
+ HashMap<String, Integer> signedJars) {
+
+ boolean isFullySigned = true;
+
+ for(String jarName : signedJars.keySet()) {
+ int numbSignableEntries = signedJars.get(jarName);
+ boolean isSigned = false;
+
+ for (CertInformation certInfo : certs.values()) {
+ if(certInfo.isSignerOfJar(jarName)
+ && numbSignableEntries == certInfo.getNumJarEntriesSigned(jarName)) {
+ isSigned = true;
+ break;
+ }
+ }
+
+ isFullySigned &= isSigned;
+ }
+
+ return isFullySigned;
+ }
+
+ @Override
+ public void checkTrustWithUser(JarCertVerifier jcv, JNLPFile file)
+ throws LaunchException {
+ ArrayList<CertPath> certPaths = buildCertPathsList(jcv);
+ ArrayList<CertPath> alreadyApprovedByUser = new ArrayList<CertPath>();
+ for (String jarName : jcv.getJarSignableEntries().keySet()) {
+ boolean trustFoundOrApproved = false;
+ for (CertPath cPathApproved : alreadyApprovedByUser) {
+ jcv.setCurrentlyUsedCertPath(cPathApproved);
+ CertInformation info = jcv.getCertInformation(cPathApproved);
+ if (info.isSignerOfJar(jarName)
+ && alreadyApprovedByUser.contains(cPathApproved)) {
+ trustFoundOrApproved = true;
+ break;
+ }
+ }
+
+ if (trustFoundOrApproved) {
+ continue;
+ }
+
+ for (CertPath cPath : certPaths) {
+ jcv.setCurrentlyUsedCertPath(cPath);
+ CertInformation info = jcv.getCertInformation(cPath);
+ if (info.isSignerOfJar(jarName)) {
+ if (info.isPublisherAlreadyTrusted()) {
+ trustFoundOrApproved = true;
+ alreadyApprovedByUser.add(cPath);
+ break;
+ }
+
+ AccessType dialogType;
+ if (info.isRootInCacerts() && !info.hasSigningIssues()) {
+ dialogType = AccessType.VERIFIED;
+ } else if (info.isRootInCacerts()) {
+ dialogType = AccessType.SIGNING_ERROR;
+ } else {
+ dialogType = AccessType.UNVERIFIED;
+ }
+
+ boolean wasApproved = SecurityDialogs.showCertWarningDialog(
+ dialogType, file, jcv);
+ if (wasApproved) {
+ alreadyApprovedByUser.add(cPath);
+ trustFoundOrApproved = true;
+ break;
+ }
+ }
+ }
+ if (!trustFoundOrApproved) {
+ throw new LaunchException(null, null, R("LSFatal"),
+ R("LCLaunching"), R("LCancelOnUserRequest"), "");
+ }
+ }
+ }
+
+ /**
+ * Build a list of all the CertPaths that were detected in the provided
+ * JCV, placing them in the most trusted possible order.
+ * @param jcv The verifier containing the CertPaths to examine.
+ * @return A list of CertPaths sorted in the following order: Signers with
+ * 1. Already trusted publishers
+ * 2. Roots in the CA store and have no signing issues
+ * 3. Roots in the CA store but have signing issues
+ * 4. Everything else
+ */
+ public ArrayList<CertPath> buildCertPathsList(JarCertVerifier jcv) {
+ ArrayList<CertPath> certPathsList = jcv.getCertsList();
+ ArrayList<CertPath> returnList = new ArrayList<CertPath>();
+
+ for (CertPath cPath : certPathsList) {
+ if (!returnList.contains(cPath)
+ && jcv.getCertInformation(cPath).isPublisherAlreadyTrusted())
+ returnList.add(cPath);
+ }
+
+ for (CertPath cPath : certPathsList) {
+ if (!returnList.contains(cPath)
+ && jcv.getCertInformation(cPath).isRootInCacerts()
+ && !jcv.getCertInformation(cPath).hasSigningIssues())
+ returnList.add(cPath);
+ }
+
+ for (CertPath cPath : certPathsList) {
+ if (!returnList.contains(cPath)
+ && jcv.getCertInformation(cPath).isRootInCacerts()
+ && jcv.getCertInformation(cPath).hasSigningIssues())
+ returnList.add(cPath);
+ }
+
+ for (CertPath cPath : certPathsList) {
+ if (!returnList.contains(cPath))
+ returnList.add(cPath);
+ }
+
+ return returnList;
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/tools/CertInformation.java b/netx/net/sourceforge/jnlp/tools/CertInformation.java
new file mode 100644
index 0000000..6d6d27e
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/tools/CertInformation.java
@@ -0,0 +1,292 @@
+/* CertInformation.java
+ Copyright (C) 2012 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+ */
+
+package net.sourceforge.jnlp.tools;
+
+import static net.sourceforge.jnlp.runtime.Translator.R;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+
+/**
+ * Maintains information about a CertPath that has signed at least one of the
+ * entries provided by a jar of the app.
+ */
+public class CertInformation {
+
+ private boolean hasExpiredCert = false;
+ private boolean hasExpiringCert = false;
+
+ private boolean isNotYetValidCert = false;
+
+ /* Code signer properties of the certificate. */
+ private boolean hasBadKeyUsage = false;
+ private boolean hasBadExtendedKeyUsage = false;
+ private boolean hasBadNetscapeCertType = false;
+
+ private boolean alreadyTrustPublisher = false;
+ private boolean rootInCacerts = false;
+
+ static enum Detail {
+ TRUSTED (R("STrustedCertificate")),
+ UNTRUSTED (R("SUntrustedCertificate")),
+ RUN_WITHOUT_RESTRICTIONS(R("SRunWithoutRestrictions")),
+ EXPIRED (R("SHasExpiredCert")),
+ EXPIRING (R("SHasExpiringCert")),
+ NOT_YET_VALID (R("SNotYetValidCert")),
+ BAD_KEY_USAGE (R("SBadKeyUsage")),
+ BAT_EXTENDED_KEY_USAGE (R("SBadExtendedKeyUsage")),
+ BAD_NETSCAPE_CERT_TYPE (R("SBadNetscapeCertType"));
+
+ private final String message;
+ Detail(String issue) {
+ message = issue;
+ }
+
+ public String message() {
+ return message;
+ }
+ }
+
+ private EnumSet<Detail> details = EnumSet.noneOf(Detail.class);
+
+ /** The jars and their number of entries this cert has signed. */
+ private HashMap<String, Integer> signedJars = new HashMap<String, Integer>();
+
+ /**
+ * Return if there are signing issues with this certificate.
+ * @return true if there are any issues with expiry, validity or bad key usage.
+ */
+ public boolean hasSigningIssues() {
+ return hasExpiredCert || isNotYetValidCert || hasBadKeyUsage
+ || hasBadExtendedKeyUsage || hasBadNetscapeCertType;
+ }
+
+ /**
+ * Return whether or not the publisher is already trusted.
+ *
+ * @return True if the publisher is trusted already.
+ */
+ public boolean isPublisherAlreadyTrusted() {
+ return alreadyTrustPublisher;
+ }
+
+ /**
+ * Set whether or not the publisher is already trusted.
+ *
+ */
+ public void setAlreadyTrustPublisher() {
+ alreadyTrustPublisher = true;
+ }
+
+ /**
+ * Return whether or not the root is in the list of trusted CA certificates.
+ *
+ * @return True if the root is in the list of CA certificates.
+ */
+ public boolean isRootInCacerts() {
+ return rootInCacerts;
+ }
+
+ /**
+ * Set that this cert's root CA is to be trusted.
+ */
+ public void setRootInCacerts() {
+ rootInCacerts = true;
+ details.add(Detail.TRUSTED);
+ }
+
+ /**
+ * Resets any trust of the root and publisher. Also removes unnecessary
+ * details from the list of issues.
+ */
+ public void resetForReverification() {
+ alreadyTrustPublisher = false;
+ rootInCacerts = false;
+ removeFromDetails(Detail.UNTRUSTED);
+ removeFromDetails(Detail.TRUSTED);
+ }
+ /**
+ * Check if this cert is the signer of a jar.
+ * @param jarName The absolute path of the jar this certificate has signed.
+ * @return true if this cert has signed the jar found at jarName.
+ */
+ public boolean isSignerOfJar(String jarName) {
+ return signedJars.containsKey(jarName);
+ }
+
+ /**
+ * Add a jar to the list of jars this certificate has signed along with the
+ * number of entries it has signed in the jar.
+ *
+ * @param jarName The absolute path of the jar this certificate has signed.
+ * @param signedEntriesCount The number of entries this cert has signed in jarName.
+ */
+ public void setNumJarEntriesSigned(String jarName, int signedEntriesCount) {
+ if (signedJars.containsKey(jarName)) {
+ if (JNLPRuntime.isDebug())
+ System.err.println("WARNING: A jar that has already been "
+ + "verified is being yet again verified: " + jarName);
+ } else {
+ signedJars.put(jarName, signedEntriesCount);
+ }
+ }
+
+ /**
+ * Find the number of entries this cert has signed in the specified jar.
+ * @param jarName The absolute path of the jar this certificate has signed.
+ * @return The number of entries this cert has signed in jarName.
+ */
+ public int getNumJarEntriesSigned(String jarName) {
+ return signedJars.get(jarName);
+ }
+
+ /**
+ * Get all the jars this cert has signed along with the number of entries
+ * in each jar.
+ * @return
+ */
+ public Map<String, Integer> getSignedJars() {
+ return signedJars;
+ }
+
+ /**
+ * Get the details regarding issue(s) with this certificate.
+ *
+ * @return A list of all the details/issues with this app.
+ */
+ public List<String> getDetailsAsStrings() {
+ List<String> detailsToStr = new ArrayList<String>();
+ for (Detail issue : details) {
+ detailsToStr.add(issue.message());
+ }
+ return detailsToStr;
+ }
+
+ /**
+ * Remove an issue from the list of details of issues with this certificate.
+ * List is unchanged if detail was not present.
+ *
+ * @param detail The issue to be removed regarding this certificate.
+ */
+ private void removeFromDetails(Detail detail) {
+ details.remove(detail);
+ }
+
+ /**
+ * Set that this cert is expired and add this issue to the list of details.
+ */
+ public void setHasExpiredCert() {
+ hasExpiredCert = true;
+ details.add(Detail.RUN_WITHOUT_RESTRICTIONS);
+ details.add(Detail.EXPIRED);
+ }
+
+ /**
+ * Set that this cert is expiring within 6 months and add this issue to
+ * the list of details.
+ */
+ public void setHasExpiringCert() {
+ hasExpiringCert = true;
+ details.add(Detail.RUN_WITHOUT_RESTRICTIONS);
+ details.add(Detail.EXPIRING);
+ }
+
+ /**
+ * Get whether or not this cert will expire within 6 months.
+ * @return true if the cert will be expired after 6 months.
+ */
+ public boolean hasExpiringCert() {
+ return hasExpiringCert;
+ }
+
+ /**
+ * Set that this cert is not yet valid
+ * and add this issue to the list of details.
+ */
+ public void setNotYetValidCert() {
+ isNotYetValidCert = true;
+ details.add(Detail.RUN_WITHOUT_RESTRICTIONS);
+ details.add(Detail.NOT_YET_VALID);
+ }
+
+
+ /**
+ * Set that this cert has bad key usage
+ * and add this issue to the list of details.
+ */
+ public void setBadKeyUsage() {
+ hasBadKeyUsage = true;
+ details.add(Detail.RUN_WITHOUT_RESTRICTIONS);
+ details.add(Detail.BAD_KEY_USAGE);
+ }
+
+
+ /**
+ * Set that this cert has bad extended key usage
+ * and add this issue to the list of details.
+ */
+ public void setBadExtendedKeyUsage() {
+ hasBadExtendedKeyUsage = true;
+ details.add(Detail.RUN_WITHOUT_RESTRICTIONS);
+ details.add(Detail.BAT_EXTENDED_KEY_USAGE);
+ }
+
+
+ /**
+ * Set that this cert has a bad netscape cert type
+ * and add this issue to the list of details.
+ */
+ public void setBadNetscapeCertType() {
+ hasBadNetscapeCertType = true;
+ details.add(Detail.RUN_WITHOUT_RESTRICTIONS);
+ details.add(Detail.BAD_NETSCAPE_CERT_TYPE);
+ }
+
+ /**
+ * Set that this cert and all of its CAs are untrusted so far.
+ */
+ public void setUntrusted() {
+ details.add(Detail.UNTRUSTED);
+
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/tools/JarCertVerifier.java b/netx/net/sourceforge/jnlp/tools/JarCertVerifier.java
index 520880a..01426f3 100644
--- a/netx/net/sourceforge/jnlp/tools/JarCertVerifier.java
+++ b/netx/net/sourceforge/jnlp/tools/JarCertVerifier.java
@@ -25,25 +25,41 @@
package net.sourceforge.jnlp.tools;
-import static net.sourceforge.jnlp.runtime.Translator.R;
-
-import java.io.*;
-import java.util.*;
-import java.util.jar.*;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.CodeSigner;
+import java.security.KeyStore;
+import java.security.cert.CertPath;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
-import java.security.cert.CertPath;
-import java.security.*;
-import sun.security.x509.*;
-import sun.security.util.*;
-
-import net.sourceforge.jnlp.*;
-import net.sourceforge.jnlp.cache.*;
-import net.sourceforge.jnlp.security.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import net.sourceforge.jnlp.JARDesc;
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.LaunchException;
+import net.sourceforge.jnlp.cache.ResourceTracker;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+import net.sourceforge.jnlp.security.AppVerifier;
+import net.sourceforge.jnlp.security.CertVerifier;
+import net.sourceforge.jnlp.security.CertificateUtils;
+import net.sourceforge.jnlp.security.KeyStores;
+import sun.security.util.DerInputStream;
+import sun.security.util.DerValue;
+import sun.security.x509.NetscapeCertTypeExtension;
/**
- * <p>The jar certificate verifier utility.
- *
+ * <p>
+ * The jar certificate verifier utility.
+ *
* @author Roland Schemers
* @author Jan Luehe
*/
@@ -55,53 +71,39 @@ public class JarCertVerifier implements CertVerifier {
// prefix for new signature-related files in META-INF directory
private static final String SIG_PREFIX = META_INF + "SIG-";
- private static final long SIX_MONTHS = 180 * 24 * 60 * 60 * 1000L; //milliseconds
+ private static final long SIX_MONTHS = 180 * 24 * 60 * 60 * 1000L; // milliseconds
- static enum verifyResult {
+ static enum VerifyResult {
UNSIGNED, SIGNED_OK, SIGNED_NOT_OK
}
- // signer's certificate chain (when composing)
- X509Certificate[] certChain;
+ /** All of the jar files that were verified for signing */
+ private ArrayList<String> verifiedJars = new ArrayList<String>();
+
+ /** All of the jar files that were not verified */
+ private ArrayList<String> unverifiedJars = new ArrayList<String>();
- boolean verbose = false; // verbose output when signing/verifying
- boolean showcerts = false; // show certs when verifying
+ /** The certificates used for jar verification linked to their respective information */
+ private HashMap<CertPath, CertInformation> certs = new HashMap<CertPath, CertInformation>();
- private boolean hasExpiredCert = false;
- private boolean hasExpiringCert = false;
- private boolean notYetValidCert = false;
+ /** Temporary cert path hack to be used to keep track of which one a UI dialog is using */
+ private CertPath currentlyUsed;
- private boolean badKeyUsage = false;
- private boolean badExtendedKeyUsage = false;
- private boolean badNetscapeCertType = false;
+ /** Absolute location to jars and the number of entries which are possibly signable */
+ private HashMap<String, Integer> jarSignableEntries = new HashMap<String, Integer>();
- private boolean alreadyTrustPublisher = false;
- private boolean rootInCacerts = false;
+ /** The application verifier to use by this instance */
+ private AppVerifier appVerifier;
/**
- * The single certPath used in this JarSiging. We're only keeping
- * track of one here, since in practice there's only one signer
- * for a JNLP Application.
+ * Create a new jar certificate verifier utility that uses the provided verifier for its strategy pattern.
+ *
+ * @param verifier
+ * The application verifier to be used by the new instance.
*/
- private CertPath certPath = null;
-
- private boolean noSigningIssues = true;
-
- private boolean anyJarsSigned = false;
-
- /** all of the jar files that were verified */
- private ArrayList<String> verifiedJars = null;
-
- /** all of the jar files that were not verified */
- private ArrayList<String> unverifiedJars = null;
-
- /** the certificates used for jar verification */
- private HashMap<CertPath, Integer> certs = new HashMap<CertPath, Integer>();
-
- /** details of this signing */
- private ArrayList<String> details = new ArrayList<String>();
-
- private int totalSignableEntries = 0;
+ public JarCertVerifier(AppVerifier verifier) {
+ appVerifier = verifier;
+ }
/** Whether a signable entry was found within jars (jars with content more than just META-INF/*) */
private boolean triviallySigned = false;
@@ -113,88 +115,107 @@ public class JarCertVerifier implements CertVerifier {
return triviallySigned;
}
- /* (non-Javadoc)
- * @see net.sourceforge.jnlp.tools.CertVerifier2#getAlreadyTrustPublisher()
- */
public boolean getAlreadyTrustPublisher() {
- return alreadyTrustPublisher;
+ boolean allPublishersTrusted = appVerifier.hasAlreadyTrustedPublisher(
+ certs, jarSignableEntries);
+ if (JNLPRuntime.isDebug()) {
+ System.out.println("App already has trusted publisher: "
+ + allPublishersTrusted);
+ }
+ return allPublishersTrusted;
}
- /* (non-Javadoc)
- * @see net.sourceforge.jnlp.tools.CertVerifier2#getRootInCacerts()
- */
public boolean getRootInCacerts() {
- return rootInCacerts;
- }
-
- public CertPath getCertPath() {
- return certPath;
+ boolean allRootCAsTrusted = appVerifier.hasRootInCacerts(certs,
+ jarSignableEntries);
+ if (JNLPRuntime.isDebug()) {
+ System.out.println("App has trusted root CA: " + allRootCAsTrusted);
+ }
+ return allRootCAsTrusted;
}
- /* (non-Javadoc)
- * @see net.sourceforge.jnlp.tools.CertVerifier2#hasSigningIssues()
- */
- public boolean hasSigningIssues() {
- return hasExpiredCert || notYetValidCert || badKeyUsage
- || badExtendedKeyUsage || badNetscapeCertType;
+ public CertPath getCertPath(CertPath cPath) { // Parameter ignored.
+ return currentlyUsed;
}
- /* (non-Javadoc)
- * @see net.sourceforge.jnlp.tools.CertVerifier2#noSigningIssues()
- */
- public boolean noSigningIssues() {
- return noSigningIssues;
+ public boolean hasSigningIssues(CertPath certPath) {
+ return certs.get(certPath).hasSigningIssues();
}
- public boolean anyJarsSigned() {
- return anyJarsSigned;
+ public List<String> getDetails(CertPath certPath) {
+ if (certPath != null) {
+ currentlyUsed = certPath;
+ }
+ return certs.get(currentlyUsed).getDetailsAsStrings();
}
- /* (non-Javadoc)
- * @see net.sourceforge.jnlp.tools.CertVerifier2#getDetails()
+ /**
+ * Get a list of the cert paths of all signers across the app.
+ *
+ * @return ArrayList of CertPath vars representing each of the signers present on any jar.
*/
- public ArrayList<String> getDetails() {
- return details;
+ public ArrayList<CertPath> getCertsList() {
+ return new ArrayList<CertPath>(certs.keySet());
}
- /* (non-Javadoc)
- * @see net.sourceforge.jnlp.tools.CertVerifier2#getCerts()
+ /**
+ * Find the information the specified cert path has with respect to this application.
+ *
+ * @return All the information the path has with this app.
*/
- public ArrayList<CertPath> getCerts() {
- return new ArrayList<CertPath>(certs.keySet());
+ public CertInformation getCertInformation(CertPath cPath) {
+ return certs.get(cPath);
}
/**
- * Returns whether or not all entries have a common signer.
- *
- * It is possible to create jars where only some entries are signed. In
- * such cases, we should not prompt the user to accept anything, as the whole
- * application must be treated as unsigned. This method should be called by a
- * caller before it is about to ask the user to accept a cert and determine
- * whether the application is trusted or not.
- *
- * @return Whether or not all entries have a common signer
+ * Returns whether or not the app is considered completely signed.
+ *
+ * An app using a JNLP is considered signed if all of the entries of its jars are signed by at least one common signer.
+ *
+ * An applet on the other hand only needs to have each individual jar be fully signed by a signer. The signers can differ between jars.
+ *
+ * @return Whether or not the app is considered signed.
*/
- public boolean isFullySignedByASingleCert() {
-
+ // FIXME: Change javadoc once applets do not need entire jars signed.
+ public boolean isFullySigned() {
if (triviallySigned)
return true;
-
- for (CertPath cPath : certs.keySet()) {
- // If this cert has signed everything, return true
- if (certs.get(cPath) == totalSignableEntries)
- return true;
+ boolean fullySigned = appVerifier.isFullySigned(certs,
+ jarSignableEntries);
+ if (JNLPRuntime.isDebug()) {
+ System.out.println("App already has trusted publisher: "
+ + fullySigned);
}
-
- // No cert found that signed all entries. Return false.
- return false;
+ return fullySigned;
}
- public void verifyJars(List<JARDesc> jars, ResourceTracker tracker)
+ /**
+ * Update the verifier to consider new jars when verifying.
+ *
+ * @param jars
+ * List of new jars to be verified.
+ * @param tracker
+ * Resource tracker used to obtain the the jars from cache
+ * @throws Exception
+ * Caused by issues with obtaining the jars' entries or interacting with the tracker.
+ */
+ public void add(List<JARDesc> jars, ResourceTracker tracker)
throws Exception {
+ verifyJars(jars, tracker);
+ }
- verifiedJars = new ArrayList<String>();
- unverifiedJars = new ArrayList<String>();
+ /**
+ * Verify the jars provided and update the state of this instance to match the new information.
+ *
+ * @param jars
+ * List of new jars to be verified.
+ * @param tracker
+ * Resource tracker used to obtain the the jars from cache
+ * @throws Exception
+ * Caused by issues with obtaining the jars' entries or interacting with the tracker.
+ */
+ private void verifyJars(List<JARDesc> jars, ResourceTracker tracker)
+ throws Exception {
for (JARDesc jar : jars) {
@@ -209,17 +230,22 @@ public class JarCertVerifier implements CertVerifier {
}
String localFile = jarFile.getAbsolutePath();
- verifyResult result = verifyJar(localFile);
+ if (verifiedJars.contains(localFile)
+ || unverifiedJars.contains(localFile)) {
+ continue;
+ }
+
+ VerifyResult result = verifyJar(localFile);
triviallySigned = false;
- if (result == verifyResult.UNSIGNED) {
+ if (result == VerifyResult.UNSIGNED) {
unverifiedJars.add(localFile);
- } else if (result == verifyResult.SIGNED_NOT_OK) {
- noSigningIssues = false;
+ } else if (result == VerifyResult.SIGNED_NOT_OK) {
verifiedJars.add(localFile);
- } else if (result == verifyResult.SIGNED_OK) {
+ } else if (result == VerifyResult.SIGNED_OK) {
verifiedJars.add(localFile);
- triviallySigned = totalSignableEntries <= 0 && certs.size() <= 0;
+ triviallySigned = getTotalJarEntries(jarSignableEntries) <= 0
+ && certs.size() <= 0;
}
} catch (Exception e) {
// We may catch exceptions from using verifyJar()
@@ -228,26 +254,20 @@ public class JarCertVerifier implements CertVerifier {
}
}
- //we really only want the first certPath
- for (CertPath cPath : certs.keySet()) {
-
- if (certs.get(cPath) != totalSignableEntries)
- continue;
- else
- certPath = cPath;
-
- // check if the certs added above are in the trusted path
- checkTrustedCerts();
-
- if (alreadyTrustPublisher || rootInCacerts)
- break;
- }
-
+ for (CertPath certPath : certs.keySet())
+ checkTrustedCerts(certPath);
}
- private verifyResult verifyJar(String jarName) throws Exception {
- boolean anySigned = false;
- boolean hasUnsignedEntry = false;
+ /**
+ * Checks through all the jar entries of jarName for signers, storing all the common ones in the certs hash map.
+ *
+ * @param jarName
+ * The absolute path to the jar file.
+ * @return The return of {@link JarCertVerifier#verifyJarEntryCerts} using the entries found in the jar located at jarName.
+ * @throws Exception
+ * Will be thrown if there are any problems with the jar.
+ */
+ private VerifyResult verifyJar(String jarName) throws Exception {
JarFile jarFile = null;
try {
@@ -262,10 +282,9 @@ public class JarCertVerifier implements CertVerifier {
InputStream is = jarFile.getInputStream(je);
try {
- int n;
- while ((n = is.read(buffer, 0, buffer.length)) != -1) {
+ while (is.read(buffer, 0, buffer.length) != -1) {
// we just read. this will throw a SecurityException
- // if a signature/digest check fails.
+ // if a signature/digest check fails.
}
} finally {
if (is != null) {
@@ -273,95 +292,9 @@ public class JarCertVerifier implements CertVerifier {
}
}
}
+ return verifyJarEntryCerts(jarName, jarFile.getManifest() != null,
+ entriesVec);
- if (jarFile.getManifest() != null) {
- if (verbose)
- System.out.println();
-
- long now = System.currentTimeMillis();
-
- for (JarEntry je : entriesVec) {
- String name = je.getName();
- CodeSigner[] signers = je.getCodeSigners();
- boolean isSigned = (signers != null);
- anySigned |= isSigned;
-
- boolean shouldHaveSignature = !je.isDirectory()
- && !isMetaInfFile(name);
-
- hasUnsignedEntry |= shouldHaveSignature && !isSigned;
-
- if (shouldHaveSignature)
- totalSignableEntries++;
-
- if (shouldHaveSignature && isSigned) {
- for (int i = 0; i < signers.length; i++) {
- CertPath certPath = signers[i].getSignerCertPath();
- if (!certs.containsKey(certPath))
- certs.put(certPath, 1);
- else
- certs.put(certPath, certs.get(certPath) + 1);
-
- Certificate cert = signers[i].getSignerCertPath()
- .getCertificates().get(0);
- if (cert instanceof X509Certificate) {
- checkCertUsage((X509Certificate) cert, null);
- if (!showcerts) {
- long notBefore = ((X509Certificate) cert)
- .getNotBefore().getTime();
- long notAfter = ((X509Certificate) cert)
- .getNotAfter().getTime();
-
- if (now < notBefore) {
- notYetValidCert = true;
- }
-
- if (notAfter < now) {
- hasExpiredCert = true;
- } else if (notAfter < now + SIX_MONTHS) {
- hasExpiringCert = true;
- }
- }
- }
- }
- }
- } //while e has more elements
- } else { //if man not null
-
- // Else increment totalEntries by 1 so that unsigned jars with
- // no manifests can't sneak in
- totalSignableEntries++;
- }
-
- //Alert the user if any of the following are true.
- if (!anySigned) {
- return verifyResult.UNSIGNED;
- } else {
- anyJarsSigned = true;
-
- //warnings
- if (hasUnsignedEntry || hasExpiredCert || hasExpiringCert ||
- badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
- notYetValidCert) {
-
- addToDetails(R("SRunWithoutRestrictions"));
-
- if (badKeyUsage)
- addToDetails(R("SBadKeyUsage"));
- if (badExtendedKeyUsage)
- addToDetails(R("SBadExtendedKeyUsage"));
- if (badNetscapeCertType)
- addToDetails(R("SBadNetscapeCertType"));
- if (hasUnsignedEntry)
- addToDetails(R("SHasUnsignedEntry"));
- if (hasExpiredCert)
- addToDetails(R("SHasExpiredCert"));
- if (hasExpiringCert)
- addToDetails(R("SHasExpiringCert"));
- if (notYetValidCert)
- addToDetails(R("SNotYetValidCert"));
- }
- }
} catch (Exception e) {
e.printStackTrace();
throw e;
@@ -370,51 +303,175 @@ public class JarCertVerifier implements CertVerifier {
jarFile.close();
}
}
-
- //anySigned does not guarantee that all files were signed.
- return (anySigned && !(hasUnsignedEntry || hasExpiredCert
- || badKeyUsage || badExtendedKeyUsage || badNetscapeCertType || notYetValidCert)) ? verifyResult.SIGNED_OK : verifyResult.SIGNED_NOT_OK;
}
/**
- * Checks the user's trusted.certs file and the cacerts file to see
- * if a publisher's and/or CA's certificate exists there.
+ * Checks through all the jar entries for signers, storing all the common ones in the certs hash map.
+ *
+ * @param jarName
+ * The absolute path to the jar file.
+ * @param jarHasManifest
+ * Whether or not the associated jar has a manifest.
+ * @param entries
+ * The list of entries in the associated jar.
+ * @return If there is at least one signable entry that is not signed by a common signer, return UNSIGNED. Otherwise every signable entry is signed by at least one common signer. If the signer has no issues, return SIGNED_OK. If there are any signing issues, return SIGNED_NOT_OK.
+ * @throws Exception
+ * Will be thrown if there are issues with entries.
*/
- private void checkTrustedCerts() throws Exception {
- if (certPath != null) {
- try {
- X509Certificate publisher = (X509Certificate) getPublisher();
- KeyStore[] certKeyStores = KeyStores.getCertKeyStores();
- alreadyTrustPublisher = CertificateUtils.inKeyStores(publisher, certKeyStores);
- X509Certificate root = (X509Certificate) getRoot();
- KeyStore[] caKeyStores = KeyStores.getCAKeyStores();
- // Check entire cert path for a trusted CA
- for (Certificate c : certPath.getCertificates()) {
- if ((rootInCacerts = CertificateUtils.inKeyStores(
- (X509Certificate) c, caKeyStores))) {
- break;
+ VerifyResult verifyJarEntryCerts(String jarName, boolean jarHasManifest,
+ Vector<JarEntry> entries) throws Exception {
+ // Contains number of entries the cert with this CertPath has signed.
+ HashMap<CertPath, Integer> jarSignCount = new HashMap<CertPath, Integer>();
+ int numSignableEntriesInJar = 0;
+
+ // Record current time just before checking the jar begins.
+ long now = System.currentTimeMillis();
+ if (jarHasManifest) {
+
+ for (JarEntry je : entries) {
+ String name = je.getName();
+ CodeSigner[] signers = je.getCodeSigners();
+ boolean isSigned = (signers != null);
+
+ boolean shouldHaveSignature = !je.isDirectory()
+ && !isMetaInfFile(name);
+
+ if (shouldHaveSignature) {
+ numSignableEntriesInJar++;
+ }
+
+ if (shouldHaveSignature && isSigned) {
+ for (int i = 0; i < signers.length; i++) {
+ CertPath certPath = signers[i].getSignerCertPath();
+
+ if (!jarSignCount.containsKey(certPath))
+ jarSignCount.put(certPath, 1);
+ else
+ jarSignCount.put(certPath,
+ jarSignCount.get(certPath) + 1);
}
}
- } catch (Exception e) {
- // TODO: Warn user about not being able to
- // look through their cacerts/trusted.certs
- // file depending on exception.
- throw e;
+ } // while e has more elements
+ } else { // if manifest is null
+
+ // Else increment total entries by 1 so that unsigned jars with
+ // no manifests can't sneak in
+ numSignableEntriesInJar++;
+ }
+
+ jarSignableEntries.put(jarName, numSignableEntriesInJar);
+
+ // Find all signers that have signed every signable entry in this jar.
+ boolean allEntriesSignedBySingleCert = false;
+ for (CertPath certPath : jarSignCount.keySet()) {
+ if (jarSignCount.get(certPath) == numSignableEntriesInJar) {
+ allEntriesSignedBySingleCert = true;
+
+ boolean wasPreviouslyVerified = certs.containsKey(certPath);
+ if (!wasPreviouslyVerified)
+ certs.put(certPath, new CertInformation());
+
+ CertInformation certInfo = certs.get(certPath);
+ if (wasPreviouslyVerified)
+ certInfo.resetForReverification();
+
+ certInfo.setNumJarEntriesSigned(jarName,
+ numSignableEntriesInJar);
+
+ Certificate cert = certPath.getCertificates().get(0);
+ if (cert instanceof X509Certificate) {
+ checkCertUsage(certPath, (X509Certificate) cert, null);
+ long notBefore = ((X509Certificate) cert).getNotBefore().getTime();
+ long notAfter = ((X509Certificate) cert).getNotAfter().getTime();
+ if (now < notBefore) {
+ certInfo.setNotYetValidCert();
+ }
+
+ if (notAfter < now) {
+ certInfo.setHasExpiredCert();
+ } else if (notAfter < now + SIX_MONTHS) {
+ certInfo.setHasExpiringCert();
+ }
+ }
+ }
+ }
+
+ // Every signable entry of this jar needs to be signed by at least
+ // one signer for the jar to be considered successfully signed.
+ VerifyResult result = null;
+ if (allEntriesSignedBySingleCert) {
+
+ // We need to find at least one signer without any issues.
+ for (CertPath entryCertPath : jarSignCount.keySet()) {
+ if (certs.containsKey(entryCertPath)
+ && !hasSigningIssues(entryCertPath)) {
+ result = VerifyResult.SIGNED_OK;
+ break;
+ }
}
+ if (result == null) {
+ // All signers had issues
+ result = VerifyResult.SIGNED_NOT_OK;
+ }
+ } else {
+ result = VerifyResult.UNSIGNED;
+ }
- if (!rootInCacerts)
- addToDetails(R("SUntrustedCertificate"));
- else
- addToDetails(R("STrustedCertificate"));
+ if (JNLPRuntime.isDebug()) {
+ System.out.println("Jar found at " + jarName
+ + "has been verified as " + result);
}
+ return result;
}
- /* (non-Javadoc)
- * @see net.sourceforge.jnlp.tools.CertVerifier2#getPublisher()
+ /**
+ * Checks the user's trusted.certs file and the cacerts file to see if a
+ * publisher's and/or CA's certificate exists there.
+ *
+ * @param certPath
+ * The cert path of the signer being checked for trust.
*/
- public Certificate getPublisher() {
- if (certPath != null) {
- List<? extends Certificate> certList = certPath.getCertificates();
+ private void checkTrustedCerts(CertPath certPath) throws Exception {
+ CertInformation info = certs.get(certPath);
+ try {
+ X509Certificate publisher = (X509Certificate) getPublisher(certPath);
+ KeyStore[] certKeyStores = KeyStores.getCertKeyStores();
+ if (CertificateUtils.inKeyStores(publisher, certKeyStores))
+ info.setAlreadyTrustPublisher();
+ KeyStore[] caKeyStores = KeyStores.getCAKeyStores();
+ // Check entire cert path for a trusted CA
+ for (Certificate c : certPath.getCertificates()) {
+ if (CertificateUtils.inKeyStores((X509Certificate) c,
+ caKeyStores)) {
+ info.setRootInCacerts();
+ return;
+ }
+ }
+ } catch (Exception e) {
+ // TODO: Warn user about not being able to
+ // look through their cacerts/trusted.certs
+ // file depending on exception.
+ if (JNLPRuntime.isDebug()) {
+ System.out.println("WARNING: Unable to read through cert store files.");
+ }
+ throw e;
+ }
+
+ // Otherwise a parent cert was not found to be trusted.
+ info.setUntrusted();
+ }
+
+ public void setCurrentlyUsedCertPath(CertPath cPath) {
+ currentlyUsed = cPath;
+ }
+
+ public Certificate getPublisher(CertPath cPath) {
+ if (cPath != null) {
+ currentlyUsed = cPath;
+ }
+ if (currentlyUsed != null) {
+ List<? extends Certificate> certList = currentlyUsed
+ .getCertificates();
if (certList.size() > 0) {
return certList.get(0);
} else {
@@ -425,12 +482,13 @@ public class JarCertVerifier implements CertVerifier {
}
}
- /* (non-Javadoc)
- * @see net.sourceforge.jnlp.tools.CertVerifier2#getRoot()
- */
- public Certificate getRoot() {
- if (certPath != null) {
- List<? extends Certificate> certList = certPath.getCertificates();
+ public Certificate getRoot(CertPath cPath) {
+ if (cPath != null) {
+ currentlyUsed = cPath;
+ }
+ if (currentlyUsed != null) {
+ List<? extends Certificate> certList = currentlyUsed
+ .getCertificates();
if (certList.size() > 0) {
return certList.get(certList.size() - 1);
} else {
@@ -441,20 +499,10 @@ public class JarCertVerifier implements CertVerifier {
}
}
- private void addToDetails(String detail) {
- if (!details.contains(detail))
- details.add(detail);
- }
-
/**
* Returns whether a file is in META-INF, and thus does not require signing.
- *
- * Signature-related files under META-INF include:
- * . META-INF/MANIFEST.MF
- * . META-INF/SIG-*
- * . META-INF/*.SF
- * . META-INF/*.DSA
- * . META-INF/*.RSA
+ *
+ * Signature-related files under META-INF include: . META-INF/MANIFEST.MF . META-INF/SIG-* . META-INF/*.SF . META-INF/*.DSA . META-INF/*.RSA
*/
static boolean isMetaInfFile(String name) {
String ucName = name.toUpperCase();
@@ -463,15 +511,19 @@ public class JarCertVerifier implements CertVerifier {
/**
* Check if userCert is designed to be a code signer
- * @param userCert the certificate to be examined
- * @param bad 3 booleans to show if the KeyUsage, ExtendedKeyUsage,
- * NetscapeCertType has codeSigning flag turned on.
- * If null, the class field badKeyUsage, badExtendedKeyUsage,
+ *
+ * @param userCert
+ * the certificate to be examined
+ * @param bad
+ * 3 booleans to show if the KeyUsage, ExtendedKeyUsage,
+ * NetscapeCertType has codeSigning flag turned on. If null,
+ * the class field badKeyUsage, badExtendedKeyUsage,
* badNetscapeCertType will be set.
- *
- * Required for verifyJar()
+ *
+ * Required for verifyJar()
*/
- void checkCertUsage(X509Certificate userCert, boolean[] bad) {
+ void checkCertUsage(CertPath certPath, X509Certificate userCert,
+ boolean[] bad) {
// Can act as a signer?
// 1. if KeyUsage, then [0] should be true
@@ -489,7 +541,7 @@ public class JarCertVerifier implements CertVerifier {
if (bad != null) {
bad[0] = true;
} else {
- badKeyUsage = true;
+ certs.get(certPath).setBadKeyUsage();
}
}
}
@@ -502,7 +554,7 @@ public class JarCertVerifier implements CertVerifier {
if (bad != null) {
bad[1] = true;
} else {
- badExtendedKeyUsage = true;
+ certs.get(certPath).setBadExtendedKeyUsage();
}
}
}
@@ -512,24 +564,24 @@ public class JarCertVerifier implements CertVerifier {
try {
// OID_NETSCAPE_CERT_TYPE
- byte[] netscapeEx = userCert.getExtensionValue
- ("2.16.840.1.113730.1.1");
+ byte[] netscapeEx = userCert
+ .getExtensionValue("2.16.840.1.113730.1.1");
if (netscapeEx != null) {
DerInputStream in = new DerInputStream(netscapeEx);
byte[] encoded = in.getOctetString();
encoded = new DerValue(encoded).getUnalignedBitString()
.toByteArray();
- NetscapeCertTypeExtension extn =
- new NetscapeCertTypeExtension(encoded);
+ NetscapeCertTypeExtension extn = new NetscapeCertTypeExtension(
+ encoded);
- Boolean val = (Boolean) extn.get(
- NetscapeCertTypeExtension.OBJECT_SIGNING);
+ Boolean val = (Boolean) extn
+ .get(NetscapeCertTypeExtension.OBJECT_SIGNING);
if (!val) {
if (bad != null) {
bad[2] = true;
} else {
- badNetscapeCertType = true;
+ certs.get(certPath).setBadNetscapeCertType();
}
}
}
@@ -540,11 +592,31 @@ public class JarCertVerifier implements CertVerifier {
/**
* Returns if all jars are signed.
- *
+ *
* @return True if all jars are signed, false if there are one or more unsigned jars
*/
public boolean allJarsSigned() {
return this.unverifiedJars.size() == 0;
}
+ public void checkTrustWithUser(JNLPFile file) throws LaunchException {
+ appVerifier.checkTrustWithUser(this, file);
+ }
+
+ public Map<String, Integer> getJarSignableEntries() {
+ return Collections.unmodifiableMap(jarSignableEntries);
+ }
+
+ /**
+ * Get the total number of entries in the provided map.
+ *
+ * @return The number of entries.
+ */
+ public static int getTotalJarEntries(Map<String, Integer> map) {
+ int sum = 0;
+ for (int value : map.values()) {
+ sum += value;
+ }
+ return sum;
+ }
}
diff --git a/tests/netx/unit/net/sourceforge/jnlp/tools/JarCertVerifierTest.java b/tests/netx/unit/net/sourceforge/jnlp/tools/JarCertVerifierTest.java
index 300b85b..88054ab 100644
--- a/tests/netx/unit/net/sourceforge/jnlp/tools/JarCertVerifierTest.java
+++ b/tests/netx/unit/net/sourceforge/jnlp/tools/JarCertVerifierTest.java
@@ -37,18 +37,484 @@ exception statement from your version.
package net.sourceforge.jnlp.tools;
-import static org.junit.Assert.*;
+import static net.sourceforge.jnlp.runtime.Translator.R;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import java.security.CodeSigner;
+import java.util.Date;
+import java.util.List;
+import java.util.Vector;
+import java.util.jar.JarEntry;
+
+import net.sourceforge.jnlp.JARDesc;
+import net.sourceforge.jnlp.tools.JarCertVerifier.VerifyResult;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
import org.junit.Test;
public class JarCertVerifierTest {
@Test
public void testIsMetaInfFile() {
- final String METAINF ="META-INF";
+ final String METAINF = "META-INF";
assertFalse(JarCertVerifier.isMetaInfFile("some_dir/" + METAINF + "/filename"));
assertFalse(JarCertVerifier.isMetaInfFile(METAINF + "filename"));
assertTrue(JarCertVerifier.isMetaInfFile(METAINF + "/filename"));
}
+ class JarCertVerifierEntry extends JarEntry {
+ CodeSigner[] signers;
+
+ public JarCertVerifierEntry(String name, CodeSigner[] codesigners) {
+ super(name);
+ signers = codesigners;
+ }
+
+ public JarCertVerifierEntry(String name) {
+ this(name, null);
+ }
+
+ public CodeSigner[] getCodeSigners() {
+ return signers == null ? null : signers.clone();
+ }
+ }
+
+ // Empty list to be used with JarCertVerifier constructor.
+ private static final List<JARDesc> emptyJARDescList = new Vector<JARDesc>();
+
+ private static final String DNPARTIAL = ", OU=JarCertVerifier Unit Test, O=IcedTea, L=Toronto, ST=Ontario, C=CA";
+ private static CodeSigner alphaSigner, betaSigner, charlieSigner,
+ expiredSigner, expiringSigner, notYetValidSigner, expiringAndNotYetValidSigner;
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ Date currentDate = new Date();
+ Date pastDate = new Date(currentDate.getTime() - (1000L * 24L * 60L * 60L) - 1000L); // 1 day and 1 second in the past
+ Date futureDate = new Date(currentDate.getTime() + (1000L * 24L * 60L * 60L)); // 1 day in the future
+ alphaSigner = CodeSignerCreator.getOneCodeSigner("CN=Alpha Signer" + DNPARTIAL, currentDate, 365);
+ betaSigner = CodeSignerCreator.getOneCodeSigner("CN=Beta Signer" + DNPARTIAL, currentDate, 365);
+ charlieSigner = CodeSignerCreator.getOneCodeSigner("CN=Charlie Signer" + DNPARTIAL, currentDate, 365);
+ expiredSigner = CodeSignerCreator.getOneCodeSigner("CN=Expired Signer" + DNPARTIAL, pastDate, 1);
+ expiringSigner = CodeSignerCreator.getOneCodeSigner("CN=Expiring Signer" + DNPARTIAL, currentDate, 1);
+ notYetValidSigner = CodeSignerCreator.getOneCodeSigner("CN=Not Yet Valid Signer" + DNPARTIAL, futureDate, 365);
+ expiringAndNotYetValidSigner = CodeSignerCreator.getOneCodeSigner("CN=Expiring and Not Yet Valid Signer" + DNPARTIAL, futureDate, 3);
+ }
+
+ @Test
+ public void testNoManifest() throws Exception {
+ JarCertVerifier jcv = new JarCertVerifier(null);
+ VerifyResult result = jcv.verifyJarEntryCerts("", false, null);
+
+ Assert.assertEquals("No manifest should be considered unsigned.",
+ VerifyResult.UNSIGNED, result);
+ Assert.assertEquals("No manifest means no signers in the verifier.",
+ 0, jcv.getCertsList().size());
+ }
+
+ @Test
+ public void testNoSignableEntries() throws Exception {
+ JarCertVerifier jcv = new JarCertVerifier(null);
+ Vector<JarEntry> entries = new Vector<JarEntry>();
+ entries.add(new JarCertVerifierEntry("OneDirEntry/"));
+ entries.add(new JarCertVerifierEntry("META-INF/MANIFEST.MF"));
+ VerifyResult result = jcv.verifyJarEntryCerts("", true, entries);
+
+ Assert.assertEquals("No signable entry (only dirs/manifests) should be considered unsigned.",
+ VerifyResult.UNSIGNED, result);
+ Assert.assertEquals("No signable entry (only dirs/manifests) means no signers in the verifier.",
+ 0, jcv.getCertsList().size());
+ }
+
+ @Test
+ public void testSingleEntryNoSigners() throws Exception {
+ JarCertVerifier jcv = new JarCertVerifier(null);
+ Vector<JarEntry> entries = new Vector<JarEntry>();
+ entries.add(new JarCertVerifierEntry("firstEntryWithoutSigner"));
+ VerifyResult result = jcv.verifyJarEntryCerts("", true, entries);
+
+ Assert.assertEquals("One unsigned entry should be considered unsigned.",
+ VerifyResult.UNSIGNED, result);
+ Assert.assertEquals("One unsigned entry means no signers in the verifier.",
+ 0, jcv.getCertsList().size());
+ }
+
+ @Test
+ public void testManyEntriesNoSigners() throws Exception {
+ JarCertVerifier jcv = new JarCertVerifier(null);
+ Vector<JarEntry> entries = new Vector<JarEntry>();
+ entries.add(new JarCertVerifierEntry("firstEntryWithoutSigner"));
+ entries.add(new JarCertVerifierEntry("secondEntryWithoutSigner"));
+ entries.add(new JarCertVerifierEntry("thirdEntryWithoutSigner"));
+ VerifyResult result = jcv.verifyJarEntryCerts("", true, entries);
+
+ Assert.assertEquals("Many unsigned entries should be considered unsigned.",
+ VerifyResult.UNSIGNED, result);
+ Assert.assertEquals("Many unsigned entries means no signers in the verifier.", 0,
+ jcv.getCertsList().size());
+ }
+
+ @Test
+ public void testSingleEntrySingleValidSigner() throws Exception {
+ JarCertVerifier jcv = new JarCertVerifier(null);
+ CodeSigner[] signers = { alphaSigner };
+ Vector<JarEntry> entries = new Vector<JarEntry>();
+ entries.add(new JarCertVerifierEntry("firstSignedByOne", signers));
+ VerifyResult result = jcv.verifyJarEntryCerts("", true, entries);
+
+ Assert.assertEquals("One signed entry should be considered signed and okay.",
+ VerifyResult.SIGNED_OK, result);
+ Assert.assertEquals("One signed entry means one signer in the verifier.",
+ 1, jcv.getCertsList().size());
+ Assert.assertTrue("One signed entry means one signer in the verifier.",
+ jcv.getCertsList().contains(alphaSigner.getSignerCertPath()));
+ }
+
+ @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 testNotAllEntriesSigned() throws Exception {
+ JarCertVerifier jcv = new JarCertVerifier(null);
+ CodeSigner[] alphaSigners = { alphaSigner };
+ Vector<JarEntry> entries = new Vector<JarEntry>();
+ entries.add(new JarCertVerifierEntry("firstSignedByAlpha", alphaSigners));
+ entries.add(new JarCertVerifierEntry("secondSignedByAlpha", alphaSigners));
+ entries.add(new JarCertVerifierEntry("thirdUnsigned"));
+ VerifyResult result = jcv.verifyJarEntryCerts("", true, entries);
+
+ Assert.assertEquals("First two entries signed by alpha signer, third entry not signed, should be considered unisgned.",
+ VerifyResult.UNSIGNED, result);
+ Assert.assertEquals("First two entries signed by alpha signer, third entry not signed, means no signers in the verifier.",
+ 0, jcv.getCertsList().size());
+ }
+
+ @Test
+ public void testSingleEntryExpiredSigner() throws Exception {
+ JarCertVerifier jcv = new JarCertVerifier(null);
+ CodeSigner[] expiredSigners = { expiredSigner };
+ Vector<JarEntry> entries = new Vector<JarEntry>();
+ entries.add(new JarCertVerifierEntry("firstSignedByExpired", expiredSigners));
+ VerifyResult result = jcv.verifyJarEntryCerts("", true, entries);
+
+ Assert.assertEquals("One entry signed by expired cert, should be considered signed but not okay.",
+ VerifyResult.SIGNED_NOT_OK, result);
+ Assert.assertEquals("One entry signed by expired cert means one signer in the verifier.",
+ 1, jcv.getCertsList().size());
+ Assert.assertTrue("One entry signed by expired cert means one signer in the verifier.",
+ jcv.getCertsList().contains(expiredSigner.getSignerCertPath()));
+ }
+
+ @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 testSingleEntryExpiringSigner() throws Exception {
+ JarCertVerifier jcv = new JarCertVerifier(null);
+ CodeSigner[] expiringSigners = { expiringSigner };
+ Vector<JarEntry> entries = new Vector<JarEntry>();
+ entries.add(new JarCertVerifierEntry("firstSignedByExpiring", expiringSigners));
+ VerifyResult result = jcv.verifyJarEntryCerts("", true, entries);
+
+ Assert.assertEquals("One entry signed by expiring cert, should be considered signed and okay.",
+ VerifyResult.SIGNED_OK, result);
+ Assert.assertEquals("One entry signed by expiring cert means one signer in the verifier.",
+ 1, jcv.getCertsList().size());
+ Assert.assertTrue("One entry signed by expiring cert means one signer in the verifier.",
+ jcv.getCertsList().contains(expiringSigner.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 testSingleEntryNotYetValidSigner() throws Exception {
+ JarCertVerifier jcv = new JarCertVerifier(null);
+ CodeSigner[] notYetValidSigners = { notYetValidSigner };
+ Vector<JarEntry> entries = new Vector<JarEntry>();
+ entries.add(new JarCertVerifierEntry("firstSignedByNotYetValid", notYetValidSigners));
+ VerifyResult result = jcv.verifyJarEntryCerts("", true, entries);
+
+ Assert.assertEquals("One entry signed by cert that is not yet valid, should be considered signed but not okay.",
+ VerifyResult.SIGNED_NOT_OK, result);
+ Assert.assertEquals("One entry signed by cert that is not yet valid means one signer in the verifier.",
+ 1, jcv.getCertsList().size());
+ Assert.assertTrue("One entry signed by cert that is not yet valid means one signer in the verifier.",
+ jcv.getCertsList().contains(notYetValidSigner.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 testSingleEntryExpiringAndNotYetValidSigner() throws Exception {
+ JarCertVerifier jcv = new JarCertVerifier(null);
+ CodeSigner[] expiringAndNotYetValidSigners = { expiringAndNotYetValidSigner };
+ Vector<JarEntry> entries = new Vector<JarEntry>();
+ entries.add(new JarCertVerifierEntry("firstSignedByExpiringNotYetValid", expiringAndNotYetValidSigners));
+ VerifyResult result = jcv.verifyJarEntryCerts("", true, entries);
+
+ Assert.assertEquals("One entry 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("One entry signed by cert that is not yet valid but also expiring means one signer in the verifier.",
+ 1, jcv.getCertsList().size());
+ Assert.assertTrue("One entry signed by cert that is not yet valid but also expiring means one signer in the verifier.",
+ jcv.getCertsList().contains(expiringAndNotYetValidSigner.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()));
+ }
+
}
diff --git a/tests/test-extensions/net/sourceforge/jnlp/tools/CodeSignerCreator.java b/tests/test-extensions/net/sourceforge/jnlp/tools/CodeSignerCreator.java
new file mode 100644
index 0000000..dcf1d39
--- /dev/null
+++ b/tests/test-extensions/net/sourceforge/jnlp/tools/CodeSignerCreator.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package net.sourceforge.jnlp.tools;
+
+import java.security.CodeSigner;
+import java.security.PrivateKey;
+import java.security.Timestamp;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Date;
+
+import sun.security.x509.AlgorithmId;
+import sun.security.x509.CertAndKeyGen;
+import sun.security.x509.CertificateAlgorithmId;
+import sun.security.x509.CertificateIssuerName;
+import sun.security.x509.CertificateSerialNumber;
+import sun.security.x509.CertificateSubjectName;
+import sun.security.x509.CertificateValidity;
+import sun.security.x509.CertificateVersion;
+import sun.security.x509.X500Name;
+import sun.security.x509.X509CertImpl;
+import sun.security.x509.X509CertInfo;
+
+public class CodeSignerCreator {
+
+ /**
+ * Create an X509 Certificate signed using SHA1withRSA with a 2048 bit key.
+ * @param dname Domain Name to represent the certificate
+ * @param notBefore The date by which the certificate starts being valid. Cannot be null.
+ * @param validity The number of days the certificate is valid after notBefore.
+ * @return An X509 certificate setup with properties using the specified parameters.
+ * @throws Exception
+ */
+ public static X509Certificate createCert(String dname, Date notBefore, int validity)
+ throws Exception {
+ int keysize = 2048;
+ String keyAlgName = "RSA";
+ String sigAlgName = "SHA1withRSA";
+
+ if (dname == null)
+ throw new Exception("Required DN is null. Please specify cert Domain Name via dname");
+ if (notBefore == null)
+ throw new Exception("Required start date is null. Please specify the date at which the cert is valid via notBefore");
+ if (validity < 0)
+ throw new Exception("Required validity is negative. Please specify the number of days for which the cert is valid after the start date.");
+
+ // KeyTool#doGenKeyPair
+ X500Name x500Name = new X500Name(dname);
+
+ CertAndKeyGen keypair = new CertAndKeyGen(keyAlgName, sigAlgName);
+
+ keypair.generate(keysize);
+ PrivateKey privKey = keypair.getPrivateKey();
+
+ X509Certificate oldCert = keypair.getSelfCertificate(x500Name,
+ notBefore, validity * 24L * 60L * 60L);
+
+ // KeyTool#doSelfCert
+ byte[] encoded = oldCert.getEncoded();
+ X509CertImpl certImpl = new X509CertImpl(encoded);
+ X509CertInfo certInfo = (X509CertInfo) certImpl.get(X509CertImpl.NAME
+ + "." + X509CertImpl.INFO);
+
+ Date notAfter = new Date(notBefore.getTime() + validity*1000L*24L*60L*60L);
+
+ CertificateValidity interval = new CertificateValidity(notBefore,
+ notAfter);
+
+ certInfo.set(X509CertInfo.VALIDITY, interval);
+ certInfo.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(
+ new java.util.Random().nextInt() & 0x7fffffff));
+ certInfo.set(X509CertInfo.SUBJECT + "." + CertificateSubjectName.DN_NAME, x500Name);
+ certInfo.set(X509CertInfo.ISSUER + "." + CertificateIssuerName.DN_NAME, x500Name);
+
+ // The inner and outer signature algorithms have to match.
+ // The way we achieve that is really ugly, but there seems to be no
+ // other solution: We first sign the cert, then retrieve the
+ // outer sigalg and use it to set the inner sigalg
+ X509CertImpl newCert = new X509CertImpl(certInfo);
+ newCert.sign(privKey, sigAlgName);
+ AlgorithmId sigAlgid = (AlgorithmId)newCert.get(X509CertImpl.SIG_ALG);
+ certInfo.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, sigAlgid);
+
+ certInfo.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
+
+ // FIXME Figure out extensions
+// CertificateExtensions ext = createV3Extensions(
+// null,
+// (CertificateExtensions)certInfo.get(X509CertInfo.EXTENSIONS),
+// v3ext,
+// oldCert.getPublicKey(),
+// null);
+// certInfo.set(X509CertInfo.EXTENSIONS, ext);
+
+ newCert = new X509CertImpl(certInfo);
+ newCert.sign(privKey, sigAlgName);
+
+ return newCert;
+ }
+
+ /**
+ * Create a new code signer with the specified information.
+ * @param domainName Domain Name to represent the certificate
+ * @param notBefore The date by which the certificate starts being valid. Cannot be null.
+ * @param validity The number of days the certificate is valid after notBefore.
+ * @return A code signer with the properties passed through its parameters.
+ */
+ public static CodeSigner getOneCodeSigner(String domainName, Date notBefore, int validity)
+ throws Exception {
+ X509Certificate jarEntryCert = createCert(domainName, notBefore, validity);
+
+ ArrayList<X509Certificate> certs = new ArrayList<X509Certificate>(1);
+ certs.add(jarEntryCert);
+
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ CertPath certPath = cf.generateCertPath(certs);
+ Timestamp certTimestamp = new Timestamp(jarEntryCert.getNotBefore(), certPath);
+ return new CodeSigner(certPath, certTimestamp);
+ }
+}