blob: da8a3d99fff1d91f782f3eb9c89200396202300c [file] [log] [blame]
// ========================================================================
// Copyright (c) 2009-2009 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.webapp.verifier.rules;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.webapp.verifier.AbstractRule;
/**
* Signed Jar Verifier
*/
public class JarSignatureRule extends AbstractRule
{
private String _keystoreLocation = System.getProperty("java.home") + "/lib/security/cacerts";
private String _type = "JKS"; // default
private String _alias = "verisignclass3ca"; // default
private KeyStore _keystore;
private static X509Certificate[] _trustedCertificates;
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.webapp.verifier.AbstractRule#getDescription()
*/
public String getDescription()
{
return "verifies that the given keystore contains the certificates required for all jar files present";
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.webapp.verifier.AbstractRule#getName()
*/
public String getName()
{
return "jar-signature";
}
/* ------------------------------------------------------------ */
/**
* @param jar
* @return
*/
private List<JarEntry> resolveJar(JarFile jar)
{
List<JarEntry> entries = new ArrayList<JarEntry>();
Enumeration<JarEntry> e = jar.entries();
while (e.hasMoreElements())
{
JarEntry jarEntry = e.nextElement();
try
{
entries.add(jarEntry); // for further verification
IO.toString(jar.getInputStream(jar.getEntry(jarEntry.getName())));
}
catch (IOException e1)
{
throw new SecurityException(e1);
}
}
return entries;
}
/* ------------------------------------------------------------ */
/**
* Set the _keystore.
*
* @param keyStoreLocation
* the _keystore to set
*/
public void setKeystoreLocation( String keyStoreLocation )
{
_keystoreLocation = keyStoreLocation;
}
@Override
public void initialize()
{
try
{
_keystore = KeyStore.getInstance(_type);
InputStream istream = new File( _keystoreLocation ).toURL().openStream();
_keystore.load(istream,null);
_trustedCertificates = new X509Certificate[]
{ (X509Certificate)_keystore.getCertificate(_alias) };
}
catch (Throwable t)
{
exception(_keystoreLocation, t.getMessage(), t);
}
}
public void setType(String type)
{
_type = type;
}
/**
* @see org.eclipse.jetty.webapp.verifier.Rule#visitWebInfLibJar(java.lang.String, java.io.File, java.util.jar.JarFile)
*/
@Override
public void visitWebInfLibJar(String path, File archive, JarFile jar)
{
try
{
if (jar.getManifest() == null)
{
error(jar.toString(),"missing manifest.mf, can not be signed");
}
List<JarEntry> entries = resolveJar(jar);
for (JarEntry jarEntry : entries)
{
if (!jarEntry.isDirectory() && !jarEntry.getName().startsWith("META-INF"))
{
Certificate[] certs = jarEntry.getCertificates();
if (certs == null || certs.length == 0)
{
error(jarEntry.getName(),"entry has not been signed");
}
else
{
X509Certificate[] chainRoots = getChainRoots(certs);
boolean signed = false;
for (int i = 0; i < chainRoots.length; i++)
{
if (isTrusted(chainRoots[i]))
{
signed = true;
break;
}
}
if (!signed)
{
error(jarEntry.getName(),"Untrusted provider's JAR");
}
}
}
}
}
catch (Exception e)
{
exception(jar.getName(), e.getMessage(), e);
}
}
private boolean isTrusted(X509Certificate certificate)
{
for (int i = 0; i < _trustedCertificates.length; i++)
{
if (certificate.getSubjectDN().equals(_trustedCertificates[i].getSubjectDN()))
{
if (certificate.equals(_trustedCertificates[i]))
{
return true;
}
}
}
for (int i = 0; i < _trustedCertificates.length; i++)
{
if (certificate.getIssuerDN().equals(_trustedCertificates[i].getSubjectDN()))
{
try
{
certificate.verify(_trustedCertificates[i].getPublicKey());
return true;
}
catch (Exception e)
{
}
}
}
return false;
}
/**
* Returns a array of certificates with the root certificate of each chain
*
* @param certificates an array of X509 certificate's
* @return an array of X509 certificate's with the root certificate of each chain
*/
private X509Certificate[] getChainRoots(Certificate[] certificates)
{
List<X509Certificate> chainRoots = new ArrayList<X509Certificate>();
for (int i = 0; i < certificates.length - 1; i++)
{
if (!((X509Certificate)certificates[i + 1]).getSubjectDN().equals(((X509Certificate)certificates[i]).getIssuerDN()))
{
chainRoots.add((X509Certificate)certificates[i]);
}
}
chainRoots.add((X509Certificate)certificates[certificates.length - 1]);
return chainRoots.toArray(new X509Certificate[chainRoots.size()]);
}
}