提交 4ecfdc7a 编写于 作者: K kohsuke

integrated signature validation code, but off by default until we verify that it's working.

git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@23181 71c3de6d-444a-0410-be80-ed276b4c234a
上级 03d866c1
......@@ -298,6 +298,11 @@ THE SOFTWARE.
<artifactId>cli</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jvnet.hudson</groupId>
<artifactId>crypto-util</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.jvnet.hudson</groupId>
<artifactId>jtidy</artifactId>
......
......@@ -41,9 +41,12 @@ import org.acegisecurity.Authentication;
import org.apache.commons.io.input.CountingInputStream;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.NullOutputStream;
import org.apache.commons.io.output.TeeOutputStream;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.jvnet.hudson.crypto.SignatureOutputStream;
import org.jvnet.hudson.crypto.CertificateUtil;
import javax.servlet.ServletException;
import javax.net.ssl.SSLHandshakeException;
......@@ -52,6 +55,8 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
......@@ -70,6 +75,14 @@ import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.security.MessageDigest;
import java.security.DigestOutputStream;
import java.security.GeneralSecurityException;
import java.security.Signature;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import com.trilead.ssh2.crypto.Base64;
/**
* Controls update center capability.
......@@ -181,7 +194,7 @@ public class UpdateCenter extends AbstractModelObject {
/**
* This is the endpoint that receives the update center data file from the browser.
*/
public void doPostBack(StaplerRequest req) throws IOException {
public void doPostBack(StaplerRequest req) throws IOException, GeneralSecurityException {
dataTimestamp = System.currentTimeMillis();
String p = req.getParameter("json");
JSONObject o = JSONObject.fromObject(p);
......@@ -192,10 +205,63 @@ public class UpdateCenter extends AbstractModelObject {
return;
}
if (signatureCheck)
verifySignature(o);
LOGGER.info("Obtained the latest update center data file");
getDataFile().write(p);
}
/**
* Verifies the signature in the update center data file.
*/
private boolean verifySignature(JSONObject o) throws GeneralSecurityException, IOException {
JSONObject signature = o.getJSONObject("signature");
if (signature.isNullObject()) {
LOGGER.severe("No signature block found");
return false;
}
o.remove("signature");
List<X509Certificate> certs = new ArrayList<X509Certificate>();
{// load and verify certificates
CertificateFactory cf = CertificateFactory.getInstance("X509");
for (Object cert : o.getJSONArray("certificates")) {
X509Certificate c = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(Base64.decode(cert.toString().toCharArray())));
c.checkValidity();
certs.add(c);
}
CertificateUtil.validatePath(certs);
}
// this is for computing a digest to check sanity
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
DigestOutputStream dos = new DigestOutputStream(new NullOutputStream(),sha1);
// this is for computing a signature
Signature sig = Signature.getInstance("SHA1withRSA");
sig.initVerify(certs.get(0));
SignatureOutputStream sos = new SignatureOutputStream(sig);
o.writeCanonical(new OutputStreamWriter(new TeeOutputStream(dos,sos),"UTF-8"));
// did the digest match? this is not a part of the signature validation, but if we have a bug in the c14n
// (which is more likely than someone tampering with update center), we can tell
String computedDigest = new String(Base64.encode(sha1.digest()));
String providedDigest = signature.getString("digest");
if (!computedDigest.equalsIgnoreCase(providedDigest)) {
LOGGER.severe("Digest mismatch: "+computedDigest+" vs "+providedDigest);
return false;
}
if (!sig.verify(Base64.decode(signature.getString("signature").toCharArray()))) {
LOGGER.severe("Signature in the update center doesn't match with the certificate");
return false;
}
return true;
}
/**
* Schedules a Hudson upgrade.
*/
......@@ -1016,4 +1082,9 @@ public class UpdateCenter extends AbstractModelObject {
private static final Logger LOGGER = Logger.getLogger(UpdateCenter.class.getName());
public static boolean neverUpdate = Boolean.getBoolean(UpdateCenter.class.getName()+".never");
/**
* Off by default until we know this is reasonably working.
*/
public static boolean signatureCheck = Boolean.getBoolean(UpdateCenter.class.getName()+".signatureCheck");
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册