Skip to content
Permalink
Browse files
8244683: A TSA server used by tests
Reviewed-by: weijun
  • Loading branch information
John Jiang committed Jun 5, 2020
1 parent ec4240b commit 13d30235e10df247391254fa9826f25f182a7aa2
Showing 8 changed files with 1,077 additions and 211 deletions.
@@ -21,49 +21,41 @@
* questions.
*/

import com.sun.net.httpserver.*;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import com.sun.net.httpserver.HttpExchange;

import jdk.test.lib.SecurityTools;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.security.timestamp.*;
import jdk.test.lib.util.JarUtils;
import sun.security.pkcs.ContentInfo;
import sun.security.pkcs.PKCS7;
import sun.security.pkcs.PKCS9Attribute;
import sun.security.pkcs.SignerInfo;
import sun.security.timestamp.TimestampToken;
import sun.security.util.DerOutputStream;
import sun.security.util.DerValue;
import sun.security.util.ObjectIdentifier;
import sun.security.x509.AlgorithmId;
import sun.security.x509.X500Name;

/*
* @test
* @bug 6543842 6543440 6939248 8009636 8024302 8163304 8169911 8180289 8172404
* 8242151
* @summary checking response of timestamp
* @modules java.base/sun.security.pkcs
* java.base/sun.security.timestamp
@@ -85,241 +77,112 @@
*/
public class TimestampCheck {

static final String defaultPolicyId = "2.3.4";
static String host = null;
private static final String PASSWORD = "changeit";
private static final String defaultPolicyId = "2.3.4";
private static String host = null;

static class Handler implements HttpHandler, AutoCloseable {
private static class Interceptor implements RespInterceptor {

private final HttpServer httpServer;
private final String keystore;
private final String path;

@Override
public void handle(HttpExchange t) throws IOException {
int len = 0;
for (String h: t.getRequestHeaders().keySet()) {
if (h.equalsIgnoreCase("Content-length")) {
len = Integer.valueOf(t.getRequestHeaders().get(h).get(0));
}
}
byte[] input = new byte[len];
t.getRequestBody().read(input);

try {
String path = t.getRequestURI().getPath().substring(1);
byte[] output = sign(input, path);
Headers out = t.getResponseHeaders();
out.set("Content-Type", "application/timestamp-reply");

t.sendResponseHeaders(200, output.length);
OutputStream os = t.getResponseBody();
os.write(output);
} catch (Exception e) {
e.printStackTrace();
t.sendResponseHeaders(500, 0);
}
t.close();
Interceptor(String path) {
this.path = path;
}

/**
* @param input The data to sign
* @param path different cases to simulate, impl on URL path
* @returns the signed
*/
byte[] sign(byte[] input, String path) throws Exception {

DerValue value = new DerValue(input);
System.out.println("#\n# Incoming Request\n===================");
System.out.println("# Version: " + value.data.getInteger());
DerValue messageImprint = value.data.getDerValue();
AlgorithmId aid = AlgorithmId.parse(
messageImprint.data.getDerValue());
System.out.println("# AlgorithmId: " + aid);

ObjectIdentifier policyId = ObjectIdentifier.of(defaultPolicyId);
BigInteger nonce = null;
while (value.data.available() > 0) {
DerValue v = value.data.getDerValue();
if (v.tag == DerValue.tag_Integer) {
nonce = v.getBigInteger();
System.out.println("# nonce: " + nonce);
} else if (v.tag == DerValue.tag_Boolean) {
System.out.println("# certReq: " + v.getBoolean());
} else if (v.tag == DerValue.tag_ObjectId) {
policyId = v.getOID();
System.out.println("# PolicyID: " + policyId);
}
}

System.out.println("#\n# Response\n===================");
KeyStore ks = KeyStore.getInstance(
new File(keystore), "changeit".toCharArray());

// If path starts with "ts", use the TSA it points to.
// Otherwise, always use "ts".
String alias = path.startsWith("ts") ? path : "ts";

if (path.equals("diffpolicy")) {
policyId = ObjectIdentifier.of(defaultPolicyId);
}

DerOutputStream statusInfo = new DerOutputStream();
statusInfo.putInteger(0);

AlgorithmId[] algorithms = {aid};
Certificate[] chain = ks.getCertificateChain(alias);
X509Certificate[] signerCertificateChain;
X509Certificate signer = (X509Certificate)chain[0];

if (path.equals("fullchain")) { // Only case 5 uses full chain
signerCertificateChain = new X509Certificate[chain.length];
for (int i=0; i<chain.length; i++) {
signerCertificateChain[i] = (X509Certificate)chain[i];
}
@Override
public X509Certificate[] getSignerCertChain(
X509Certificate[] signerCertChain, boolean certReq)
throws Exception {
if (path.equals("fullchain")) { // Only case 5 uses full chain
return signerCertChain;
} else if (path.equals("nocert")) {
signerCertificateChain = new X509Certificate[0];
return new X509Certificate[0];
} else {
signerCertificateChain = new X509Certificate[1];
signerCertificateChain[0] = (X509Certificate)chain[0];
return new X509Certificate[] { signerCertChain[0] };
}
}

DerOutputStream tst = new DerOutputStream();

tst.putInteger(1);
tst.putOID(policyId);
@Override
public String getSigAlgo(String sigAlgo) throws Exception {
return "SHA256withRSA";
}

if (!path.equals("baddigest") && !path.equals("diffalg")) {
tst.putDerValue(messageImprint);
} else {
byte[] data = messageImprint.toByteArray();
if (path.equals("diffalg")) {
data[6] = (byte)0x01;
} else {
data[data.length-1] = (byte)0x01;
data[data.length-2] = (byte)0x02;
data[data.length-3] = (byte)0x03;
}
tst.write(data);
@Override
public TsaParam getRespParam(TsaParam reqParam) {
TsaParam respParam = RespInterceptor.super.getRespParam(reqParam);

String policyId
= respParam.policyId() == null || path.equals("diffpolicy")
? TimestampCheck.defaultPolicyId
: respParam.policyId();
respParam.policyId(policyId);

String digestAlgo = respParam.digestAlgo();
if (path.equals("diffalg")) {
digestAlgo = digestAlgo.contains("256")
? "SHA-1" : "SHA-256";
}
respParam.digestAlgo(digestAlgo);

tst.putInteger(1);
byte[] hashedMessage = respParam.hashedMessage();
if (path.equals("baddigest")) {
hashedMessage[hashedMessage.length - 1] = (byte) 0x01;
hashedMessage[hashedMessage.length - 2] = (byte) 0x02;
hashedMessage[hashedMessage.length - 3] = (byte) 0x03;
}
respParam.hashedMessage(hashedMessage);

Instant instant = Instant.now();
if (path.equals("tsold")) {
instant = instant.minus(20, ChronoUnit.DAYS);
}
tst.putGeneralizedTime(Date.from(instant));
respParam.genTime(Date.from(instant));

BigInteger nonce = respParam.nonce();
if (path.equals("diffnonce")) {
tst.putInteger(1234);
nonce = BigInteger.valueOf(1234);
} else if (path.equals("nononce")) {
// no noce
} else {
tst.putInteger(nonce);
nonce = null;
}
respParam.nonce(nonce);

DerOutputStream tstInfo = new DerOutputStream();
tstInfo.write(DerValue.tag_Sequence, tst);

DerOutputStream tstInfo2 = new DerOutputStream();
tstInfo2.putOctetString(tstInfo.toByteArray());

// Always use the same algorithm at timestamp signing
// so it is different from the hash algorithm.
String sigAlg = "SHA256withRSA";
Signature sig = Signature.getInstance(sigAlg);
sig.initSign((PrivateKey)(ks.getKey(
alias, "changeit".toCharArray())));
sig.update(tstInfo.toByteArray());

ContentInfo contentInfo = new ContentInfo(ObjectIdentifier.of(
"1.2.840.113549.1.9.16.1.4"),
new DerValue(tstInfo2.toByteArray()));

System.out.println("# Signing...");
System.out.println("# " + new X500Name(signer
.getIssuerX500Principal().getName()));
System.out.println("# " + signer.getSerialNumber());

SignerInfo signerInfo = new SignerInfo(
new X500Name(signer.getIssuerX500Principal().getName()),
signer.getSerialNumber(),
AlgorithmId.get(AlgorithmId.getDigAlgFromSigAlg(sigAlg)),
AlgorithmId.get(AlgorithmId.getEncAlgFromSigAlg(sigAlg)),
sig.sign());

SignerInfo[] signerInfos = {signerInfo};
PKCS7 p7 = new PKCS7(algorithms, contentInfo,
signerCertificateChain, signerInfos);
ByteArrayOutputStream p7out = new ByteArrayOutputStream();
p7.encodeSignedData(p7out);

DerOutputStream response = new DerOutputStream();
response.write(DerValue.tag_Sequence, statusInfo);
response.putDerValue(new DerValue(p7out.toByteArray()));

DerOutputStream out = new DerOutputStream();
out.write(DerValue.tag_Sequence, response);

return out.toByteArray();
}

private Handler(HttpServer httpServer, String keystore) {
this.httpServer = httpServer;
this.keystore = keystore;
return respParam;
}
}

/**
* Initialize TSA instance.
*
* Extended Key Info extension of certificate that is used for
* signing TSA responses should contain timeStamping value.
*/
static Handler init(int port, String keystore) throws IOException {
HttpServer httpServer = HttpServer.create(
new InetSocketAddress(port), 0);
Handler tsa = new Handler(httpServer, keystore);
httpServer.createContext("/", tsa);
return tsa;
}
private static class Handler extends TsaHandler {

/**
* Start TSA service.
*/
void start() {
httpServer.start();
Handler(String keystore) throws Exception {
super(KeyStore.getInstance(new File(keystore),
PASSWORD.toCharArray()), PASSWORD);
}

/**
* Stop TSA service.
*/
void stop() {
httpServer.stop(0);
}
public TsaSigner createSigner(HttpExchange exchange)
throws Exception {
String path = exchange.getRequestURI().getPath().substring(1);

/**
* Return server port number.
*/
int getPort() {
return httpServer.getAddress().getPort();
SignerEntry signerEntry = createSignerEntry(
path.startsWith("ts") ? path : "ts");
byte[] requestData = exchange.getRequestBody().readAllBytes();
RespInterceptor interceptor = new Interceptor(path);
return new TsaSigner(signerEntry, requestData, interceptor);
}
}

@Override
public void close() throws Exception {
stop();
}
private static TsaServer initServer(String keystore) throws Exception {
return new TsaServer(new Handler(keystore));
}

public static void main(String[] args) throws Throwable {
prepare();

try (Handler tsa = Handler.init(0, "ks");) {
try (TsaServer tsa = initServer("ks");) {
tsa.start();
int port = tsa.getPort();
host = "http://localhost:" + port + "/";

if (args.length == 0) { // Run this test

prepare();

sign("normal")
.shouldNotContain("Warning")
.shouldContain("The signer certificate will expire on")

0 comments on commit 13d3023

Please sign in to comment.