8244683: A TSA server used by tests
Reviewed-by: weijun
This commit is contained in:
parent
ec4240b342
commit
13d30235e1
@ -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 @@ import sun.security.x509.X500Name;
|
||||
*/
|
||||
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();
|
||||
@Override
|
||||
public String getSigAlgo(String sigAlgo) throws Exception {
|
||||
return "SHA256withRSA";
|
||||
}
|
||||
|
||||
tst.putInteger(1);
|
||||
tst.putOID(policyId);
|
||||
@Override
|
||||
public TsaParam getRespParam(TsaParam reqParam) {
|
||||
TsaParam respParam = RespInterceptor.super.getRespParam(reqParam);
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start TSA service.
|
||||
*/
|
||||
void start() {
|
||||
httpServer.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop TSA service.
|
||||
*/
|
||||
void stop() {
|
||||
httpServer.stop(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return server port number.
|
||||
*/
|
||||
int getPort() {
|
||||
return httpServer.getAddress().getPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
stop();
|
||||
return respParam;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
private static class Handler extends TsaHandler {
|
||||
|
||||
try (Handler tsa = Handler.init(0, "ks");) {
|
||||
Handler(String keystore) throws Exception {
|
||||
super(KeyStore.getInstance(new File(keystore),
|
||||
PASSWORD.toCharArray()), PASSWORD);
|
||||
}
|
||||
|
||||
public TsaSigner createSigner(HttpExchange exchange)
|
||||
throws Exception {
|
||||
String path = exchange.getRequestURI().getPath().substring(1);
|
||||
|
||||
SignerEntry signerEntry = createSignerEntry(
|
||||
path.startsWith("ts") ? path : "ts");
|
||||
byte[] requestData = exchange.getRequestBody().readAllBytes();
|
||||
RespInterceptor interceptor = new Interceptor(path);
|
||||
return new TsaSigner(signerEntry, requestData, interceptor);
|
||||
}
|
||||
}
|
||||
|
||||
private static TsaServer initServer(String keystore) throws Exception {
|
||||
return new TsaServer(new Handler(keystore));
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
prepare();
|
||||
|
||||
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,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*
|
||||
* 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 jdk.test.lib.security.timestamp;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A default implementation for {@link RespInterceptor}.
|
||||
*/
|
||||
public class DefaultRespInterceptor<T extends TsaParam>
|
||||
implements RespInterceptor {
|
||||
|
||||
private final T param;
|
||||
|
||||
public DefaultRespInterceptor(T param) {
|
||||
Objects.requireNonNull(param);
|
||||
|
||||
this.param = param;
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getSignerCertChain(
|
||||
X509Certificate[] signerCertChain, boolean certReq)
|
||||
throws Exception {
|
||||
X509Certificate[] certChain = RespInterceptor.super.getSignerCertChain(
|
||||
signerCertChain, certReq);
|
||||
if (param.certReq() == null) {
|
||||
return certChain;
|
||||
} else if (param.certReq()) {
|
||||
return signerCertChain;
|
||||
} else {
|
||||
return new X509Certificate[0];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSigAlgo(String sigAlgo) throws Exception {
|
||||
return param.sigAlgo() == null ? sigAlgo : param.sigAlgo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TsaParam getRespParam(TsaParam reqParam) {
|
||||
TsaParam respParam = RespInterceptor.super.getRespParam(reqParam);
|
||||
respParam.version(param.version() == null
|
||||
? respParam.version() : param.version());
|
||||
respParam.status(param.status() == null
|
||||
? respParam.status() : param.status());
|
||||
respParam.policyId(param.policyId() == null
|
||||
? respParam.policyId() : param.policyId());
|
||||
respParam.digestAlgo(param.digestAlgo() == null
|
||||
? respParam.digestAlgo() : param.digestAlgo());
|
||||
respParam.hashedMessage(param.hashedMessage() == null
|
||||
? respParam.hashedMessage() : param.hashedMessage());
|
||||
respParam.genTime(param.genTime() == null
|
||||
? respParam.genTime() : param.genTime());
|
||||
return respParam;
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*
|
||||
* 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 jdk.test.lib.security.timestamp;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* This interceptor defines some extensions for generating time-stamping
|
||||
* response.
|
||||
*/
|
||||
public interface RespInterceptor {
|
||||
|
||||
/**
|
||||
* Return an alternative signer certificate chain if necessary.
|
||||
* In default case, the returned certificate chain is empty if the server
|
||||
* certificate chain is not required.
|
||||
*
|
||||
* @param signerCertChain the original signer certificate chain
|
||||
* @param certReq indicate if require {@code TSA} server certificate
|
||||
* @return the signer certificate chain
|
||||
* @throws Exception the exception
|
||||
*/
|
||||
default X509Certificate[] getSignerCertChain(
|
||||
X509Certificate[] signerCertChain, boolean certReq)
|
||||
throws Exception {
|
||||
return certReq ? signerCertChain : new X509Certificate[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an alternative signature algorithm if necessary.
|
||||
*
|
||||
* @param sigAlgo the original signature algorithm
|
||||
* @return the signature algorithm
|
||||
* @throws Exception the exception
|
||||
*/
|
||||
default public String getSigAlgo(String sigAlgo) throws Exception {
|
||||
return sigAlgo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the TSA response parameters.
|
||||
*
|
||||
* @param reqParam the TSA request parameters.
|
||||
* @return the TSA response parameters.
|
||||
*/
|
||||
default public TsaParam getRespParam(TsaParam reqParam) {
|
||||
TsaParam respParam = TsaParam.newInstance();
|
||||
respParam.version(1);
|
||||
respParam.status(0);
|
||||
respParam.policyId(reqParam.policyId() == null
|
||||
? "2.3.4" : reqParam.policyId());
|
||||
respParam.digestAlgo(reqParam.digestAlgo());
|
||||
respParam.hashedMessage(reqParam.hashedMessage());
|
||||
respParam.serialNumber(BigInteger.ONE);
|
||||
respParam.genTime(Date.from(Instant.now()));
|
||||
respParam.nonce(reqParam.nonce());
|
||||
return respParam;
|
||||
}
|
||||
}
|
47
test/lib/jdk/test/lib/security/timestamp/SignerEntry.java
Normal file
47
test/lib/jdk/test/lib/security/timestamp/SignerEntry.java
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*
|
||||
* 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 jdk.test.lib.security.timestamp;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A carrier for the TSA signer private key and certificate chain.
|
||||
*/
|
||||
public class SignerEntry {
|
||||
|
||||
public final PrivateKey privateKey;
|
||||
public final X509Certificate[] certChain;
|
||||
public final X509Certificate cert;
|
||||
|
||||
public SignerEntry(PrivateKey privateKey, X509Certificate[] certChain) {
|
||||
Objects.requireNonNull(privateKey);
|
||||
Objects.requireNonNull(certChain);
|
||||
|
||||
this.privateKey = privateKey;
|
||||
this.certChain = certChain;
|
||||
this.cert = certChain[0];
|
||||
}
|
||||
}
|
232
test/lib/jdk/test/lib/security/timestamp/TsaHandler.java
Normal file
232
test/lib/jdk/test/lib/security/timestamp/TsaHandler.java
Normal file
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*
|
||||
* 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 jdk.test.lib.security.timestamp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URI;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
|
||||
import sun.security.util.KnownOIDs;
|
||||
import sun.security.x509.AlgorithmId;
|
||||
|
||||
/**
|
||||
* A {@link HttpHandler} receiving time-stamping request and returning response.
|
||||
*
|
||||
* <p> It can be initialized by the given key store and key passphrase if any.
|
||||
* At runtime, the handler can accept the below parameters via {@code HTTP}
|
||||
* query string.
|
||||
*
|
||||
* <ul>
|
||||
* <li><b>alias</b>: Certificate alias. The default value is the alias of the
|
||||
* first found time stamping certificate in the key store.</li>
|
||||
* <li><b>sigAlgo</b>: Signature algorithm. The default value is determined by
|
||||
* the selected certificate.</li>
|
||||
* <li><b>version</b>: The time-stamping request/token version.</li>
|
||||
* <li><b>status</b>: The time-stamping status.</li>
|
||||
* <li><b>policyId</b>: The policy ID.</li>
|
||||
* <li><b>digestAlgo</b>: The digest algorithm used by the hashed message.</li>
|
||||
* <li><b>hashedMessage</b>: The hashed message.</li>
|
||||
* <li><b>serialNumber</b>: The serial number.</li>
|
||||
* <li><b>genTime</b>: The time at which the time-stamp token has been created.
|
||||
* The format is {@code yyyy-MM-dd}.</li>
|
||||
* <li><b>nonce</b>: The nonce.</li>
|
||||
* <li><b>certReq</b>: Indicate if require {@code TSA} server certificate.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p> All the parameters are optional.
|
||||
*
|
||||
* <p> The {@code HTTP} query string format looks like: <br>
|
||||
* {@literal alias=<certificate alias>&sigAlgo=<signature algorithm>&date=<yyyy-MM-dd>&status=<status number>}
|
||||
*/
|
||||
public class TsaHandler implements HttpHandler {
|
||||
|
||||
private static final DateTimeFormatter FORMATTER
|
||||
= DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
|
||||
public final KeyStore keyStore;
|
||||
public final char[] passphrase;
|
||||
|
||||
/**
|
||||
* Create a handler with a given key store and private key passphrase.
|
||||
*
|
||||
* @param keyStore a key store
|
||||
* @param passphrase the private key passphrase
|
||||
*/
|
||||
public TsaHandler(KeyStore keyStore, String passphrase) {
|
||||
this.keyStore = keyStore;
|
||||
this.passphrase = passphrase == null
|
||||
? null
|
||||
: passphrase.toCharArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange exchange) throws IOException {
|
||||
try {
|
||||
byte[] responseData = createSigner(exchange).sign();
|
||||
exchange.getResponseHeaders().set(
|
||||
"Content-Type", "application/timestamp-reply");
|
||||
exchange.sendResponseHeaders(200, responseData.length);
|
||||
exchange.getResponseBody().write(responseData);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
exchange.sendResponseHeaders(500, 0);
|
||||
} finally {
|
||||
exchange.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link TsaSigner} instance with the time-stamping request and
|
||||
* application parameters from a given {@link HttpExchange} instance.
|
||||
*
|
||||
* @param exchange a {@link HttpExchange} instance
|
||||
* @return a {@link TsaSigner} instance
|
||||
* @throws Exception the exception
|
||||
*/
|
||||
protected TsaSigner createSigner(HttpExchange exchange) throws Exception {
|
||||
TsaParam param = getParam(exchange.getRequestURI());
|
||||
|
||||
String alias = getSignerAlias(param.alias(), param.sigAlgo());
|
||||
SignerEntry signerEntry = createSignerEntry(alias);
|
||||
|
||||
byte[] requestData = exchange.getRequestBody().readAllBytes();
|
||||
return new TsaSigner(signerEntry, requestData, param);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link SignerEntry} from a given key store alias.
|
||||
*
|
||||
* @param alias the keys tore alias
|
||||
* @return the {@link SignerEntry} instance
|
||||
* @throws Exception the exception
|
||||
*/
|
||||
protected SignerEntry createSignerEntry(String alias) throws Exception {
|
||||
PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, passphrase);
|
||||
Certificate[] certChain = keyStore.getCertificateChain(alias);
|
||||
X509Certificate[] x509CertChain = Arrays.copyOf(
|
||||
certChain, certChain.length, X509Certificate[].class);
|
||||
SignerEntry signerEntry = new SignerEntry(privateKey, x509CertChain);
|
||||
return signerEntry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse parameters from {@link URI}.
|
||||
*
|
||||
* @param uri the {@link URI} from {@code HTTP} request
|
||||
* @return a {@link TsaParam} instance wrapping application parameters
|
||||
*/
|
||||
protected TsaParam getParam(URI uri) {
|
||||
String query = uri.getQuery();
|
||||
|
||||
TsaParam param = TsaParam.newInstance();
|
||||
if (query != null) {
|
||||
for (String bufParam : query.split("&")) {
|
||||
String[] pair = bufParam.split("=");
|
||||
if ("alias".equalsIgnoreCase(pair[0])) {
|
||||
param.alias(pair[1]);
|
||||
System.out.println("alias: " + param.alias());
|
||||
} else if ("sigAlgo".equalsIgnoreCase(pair[0])) {
|
||||
param.sigAlgo(pair[1]);
|
||||
System.out.println("sigAlgo: " + param.sigAlgo());
|
||||
} else if ("version".equalsIgnoreCase(pair[0])) {
|
||||
param.version(Integer.valueOf(pair[1]));
|
||||
System.out.println("version: " + param.version());
|
||||
} else if ("status".equalsIgnoreCase(pair[0])) {
|
||||
param.status(Integer.valueOf(pair[1]));
|
||||
System.out.println("status: " + param.status());
|
||||
} else if ("policyId".equalsIgnoreCase(pair[0])) {
|
||||
param.policyId(pair[1]);
|
||||
System.out.println("policyId: " + param.policyId());
|
||||
} else if ("digestAlgo".equalsIgnoreCase(pair[0])) {
|
||||
param.digestAlgo(pair[1]);
|
||||
System.out.println("digestAlgo: " + param.digestAlgo());
|
||||
} else if ("serialNumber".equalsIgnoreCase(pair[0])) {
|
||||
param.serialNumber(new BigInteger(pair[1]));
|
||||
System.out.println("serialNumber: " + param.serialNumber());
|
||||
} else if ("nonce".equalsIgnoreCase(pair[0])) {
|
||||
param.nonce(new BigInteger(pair[1]));
|
||||
System.out.println("nonce: " + param.nonce());
|
||||
} else if ("date".equalsIgnoreCase(pair[0])) {
|
||||
Date genTime = Date.from(LocalDate.parse(pair[1], FORMATTER)
|
||||
.atStartOfDay().atZone(ZoneId.systemDefault())
|
||||
.toInstant());
|
||||
param.genTime(genTime);
|
||||
System.out.println("genTime: " + param.genTime());
|
||||
} else if ("certReq".equalsIgnoreCase(pair[0])) {
|
||||
param.certReq(Boolean.valueOf(pair[1]));
|
||||
System.out.println("certReq: " + param.certReq());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the signer certificate alias.
|
||||
*
|
||||
* @param alias the specified alias
|
||||
* @param sigAlgo the specified signature algorithm
|
||||
* @return the signer alias
|
||||
* @throws Exception the exception
|
||||
*/
|
||||
protected String getSignerAlias(String alias, String sigAlgo)
|
||||
throws Exception {
|
||||
if (alias == null) {
|
||||
String keyAlgo = sigAlgo == null
|
||||
? null
|
||||
: AlgorithmId.getEncAlgFromSigAlg(sigAlgo);
|
||||
Enumeration<String> aliases = keyStore.aliases();
|
||||
while(aliases.hasMoreElements()) {
|
||||
String bufAlias = aliases.nextElement();
|
||||
X509Certificate bufCert
|
||||
= (X509Certificate) keyStore.getCertificate(bufAlias);
|
||||
// The certificate must have critical extended key usage time
|
||||
// stamping, and must match the key algorithm if any.
|
||||
List<String> eku = bufCert.getExtendedKeyUsage();
|
||||
if (eku != null && eku.contains(KnownOIDs.KP_TimeStamping.value())
|
||||
&& (keyAlgo == null || keyAlgo.equalsIgnoreCase(
|
||||
bufCert.getPublicKey().getAlgorithm()))) {
|
||||
return bufAlias;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return alias;
|
||||
}
|
||||
}
|
180
test/lib/jdk/test/lib/security/timestamp/TsaParam.java
Normal file
180
test/lib/jdk/test/lib/security/timestamp/TsaParam.java
Normal file
@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*
|
||||
* 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 jdk.test.lib.security.timestamp;
|
||||
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* The application parameters. They are used for generating {@code TSA}
|
||||
* response.
|
||||
*/
|
||||
public class TsaParam {
|
||||
|
||||
/********** Signing internals **********/
|
||||
|
||||
// The signer certificate alias in the key store
|
||||
private String alias;
|
||||
|
||||
// The signature algorithm used by time-stamping
|
||||
private String sigAlgo;
|
||||
|
||||
/********** TSA fields **********/
|
||||
|
||||
// The time-stamping token version
|
||||
private Integer version;
|
||||
|
||||
// The time-stamping response status
|
||||
private Integer status;
|
||||
|
||||
// The policy ID
|
||||
private String policyId;
|
||||
|
||||
// The digest algorithm in messageImprint
|
||||
private String digestAlgo;
|
||||
|
||||
// The hashed message in messageImprint
|
||||
private byte[] hashedMessage;
|
||||
|
||||
// The serial number
|
||||
private BigInteger serialNumber;
|
||||
|
||||
// The time-stamping token generation time
|
||||
private Date genTime;
|
||||
|
||||
// The nonce
|
||||
private BigInteger nonce;
|
||||
|
||||
// Indicate if request TSA server certificate
|
||||
private Boolean certReq;
|
||||
|
||||
public static TsaParam newInstance() {
|
||||
return new TsaParam();
|
||||
}
|
||||
|
||||
public String alias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
public TsaParam alias(String alias) {
|
||||
this.alias = alias;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String sigAlgo() {
|
||||
return sigAlgo;
|
||||
}
|
||||
|
||||
public TsaParam sigAlgo(String sigAlgo) {
|
||||
this.sigAlgo = sigAlgo;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Integer version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public TsaParam version(Integer version) {
|
||||
this.version = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Integer status() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public TsaParam status(Integer status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String policyId() {
|
||||
return policyId;
|
||||
}
|
||||
|
||||
public TsaParam policyId(String policyId) {
|
||||
this.policyId = policyId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String digestAlgo() {
|
||||
return digestAlgo;
|
||||
}
|
||||
|
||||
public TsaParam digestAlgo(String digestAlgo) {
|
||||
this.digestAlgo = digestAlgo;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date genTime() {
|
||||
return genTime;
|
||||
}
|
||||
|
||||
public TsaParam genTime(Date genTime) {
|
||||
this.genTime = genTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public byte[] hashedMessage() {
|
||||
return hashedMessage;
|
||||
}
|
||||
|
||||
public TsaParam hashedMessage(byte[] hashedMessage) {
|
||||
this.hashedMessage = hashedMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TsaParam hashedMessage(String hashedMessageHex) {
|
||||
return hashedMessage(Utils.toByteArray(hashedMessageHex));
|
||||
}
|
||||
|
||||
public BigInteger serialNumber() {
|
||||
return serialNumber;
|
||||
}
|
||||
|
||||
public TsaParam serialNumber(BigInteger serialNumber) {
|
||||
this.serialNumber = serialNumber;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BigInteger nonce() {
|
||||
return nonce;
|
||||
}
|
||||
|
||||
public TsaParam nonce(BigInteger nonce) {
|
||||
this.nonce = nonce;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Boolean certReq() {
|
||||
return certReq;
|
||||
}
|
||||
|
||||
public TsaParam certReq(Boolean certReq) {
|
||||
this.certReq = certReq;
|
||||
return this;
|
||||
}
|
||||
}
|
118
test/lib/jdk/test/lib/security/timestamp/TsaServer.java
Normal file
118
test/lib/jdk/test/lib/security/timestamp/TsaServer.java
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*
|
||||
* 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 jdk.test.lib.security.timestamp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
|
||||
/**
|
||||
* A {@link HttpServer}-based {@code TSA} server working with/without a
|
||||
* time-stamping request. With a set of specific parameters defined in
|
||||
* {@link TsaParam} and delivered by {@code HTTP} query (see {@link TsaHandler}),
|
||||
* an application could affect the time-stamping response generating process
|
||||
* directly. The application parameters can override the similar fields in the
|
||||
* time-stamping request. Furthermore an application can provide an alternative
|
||||
* {@link RespInterceptor} or even extend {@link TsaSigner} for generating custom
|
||||
* time-stamping response.
|
||||
*/
|
||||
public class TsaServer implements AutoCloseable {
|
||||
|
||||
private final HttpServer server;
|
||||
|
||||
/**
|
||||
* Create {@code TSA} server with the given port, key store and the
|
||||
* associated passphrase if any. A default {@link TsaHandler} is initialized
|
||||
* along with this server.
|
||||
*
|
||||
* @param port port
|
||||
* @param keyStore a key store
|
||||
* @param passphrase the private key passphrase
|
||||
* @throws IOException the I/O exception
|
||||
*/
|
||||
public TsaServer(int port, KeyStore keyStore, String passphrase)
|
||||
throws IOException {
|
||||
Objects.requireNonNull(keyStore, "Key store cannot be null");
|
||||
|
||||
server = HttpServer.create(new InetSocketAddress(port), 0);
|
||||
server.createContext("/", new TsaHandler(keyStore, passphrase));
|
||||
}
|
||||
|
||||
public TsaServer(KeyStore keyStore, String passphrase) throws IOException {
|
||||
this(0, keyStore, passphrase);
|
||||
}
|
||||
|
||||
public TsaServer(int port, KeyStore keyStore) throws IOException {
|
||||
this(port, keyStore, null);
|
||||
}
|
||||
|
||||
public TsaServer(KeyStore keyStore) throws IOException {
|
||||
this(0, keyStore, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create {@code TSA} server with the given port and {@link TsaHandler}.
|
||||
*
|
||||
* @param port the port number
|
||||
* @param handler a {@link TsaHandler} instance
|
||||
* @throws IOException the I/O exception
|
||||
*/
|
||||
public TsaServer(int port, TsaHandler handler) throws IOException {
|
||||
server = HttpServer.create(new InetSocketAddress(port), 0);
|
||||
server.createContext("/", handler);
|
||||
}
|
||||
|
||||
public TsaServer(TsaHandler handler) throws IOException {
|
||||
this(0, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start {@code TSA} server.
|
||||
*/
|
||||
public void start() {
|
||||
server.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop {@code TSA} server.
|
||||
*/
|
||||
public void stop() {
|
||||
server.stop(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the server port.
|
||||
*/
|
||||
public int getPort() {
|
||||
return server.getAddress().getPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
stop();
|
||||
}
|
||||
}
|
260
test/lib/jdk/test/lib/security/timestamp/TsaSigner.java
Normal file
260
test/lib/jdk/test/lib/security/timestamp/TsaSigner.java
Normal file
@ -0,0 +1,260 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*
|
||||
* 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 jdk.test.lib.security.timestamp;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.security.Signature;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.test.lib.hexdump.HexPrinter;
|
||||
import sun.security.pkcs.ContentInfo;
|
||||
import sun.security.pkcs.PKCS7;
|
||||
import sun.security.pkcs.SignerInfo;
|
||||
import sun.security.util.DerOutputStream;
|
||||
import sun.security.util.DerValue;
|
||||
import sun.security.util.KnownOIDs;
|
||||
import sun.security.util.ObjectIdentifier;
|
||||
import sun.security.x509.AlgorithmId;
|
||||
import sun.security.x509.X500Name;
|
||||
|
||||
/**
|
||||
* Process time-stamping request and generate signed data.
|
||||
*/
|
||||
public class TsaSigner {
|
||||
|
||||
private static final boolean DEBUG = Boolean.getBoolean("test.debug");
|
||||
private static final HexPrinter HEX_PRINTER = HexPrinter.simple();
|
||||
|
||||
protected final SignerEntry signerEntry;
|
||||
protected final byte[] requestData;
|
||||
private final RespInterceptor interceptor;
|
||||
|
||||
/**
|
||||
* Initialization.
|
||||
*
|
||||
* @param signerEntry a {@link SignerEntry} instance
|
||||
* @param requestData the time-stamping request data
|
||||
* @param interceptor the interceptor for customizing signing
|
||||
*/
|
||||
public TsaSigner(SignerEntry signerEntry,
|
||||
byte[] requestData, RespInterceptor interceptor) {
|
||||
Objects.requireNonNull(signerEntry);
|
||||
Objects.requireNonNull(interceptor);
|
||||
|
||||
this.signerEntry = signerEntry;
|
||||
this.requestData = requestData;
|
||||
this.interceptor = interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization.
|
||||
*
|
||||
* @param signerEntry a {@link SignerEntry} instance
|
||||
* @param requestData the time-stamping request
|
||||
* @param param the application parameters
|
||||
*/
|
||||
public TsaSigner(SignerEntry signerEntry, byte[] requestData,
|
||||
TsaParam param) {
|
||||
this(signerEntry, requestData,
|
||||
new DefaultRespInterceptor<TsaParam>(param));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign data.
|
||||
*
|
||||
* @returns the time-stamping response data
|
||||
*/
|
||||
public byte[] sign() throws Exception {
|
||||
TsaParam requestParam = parseRequestParam();
|
||||
byte[] responseSeqData = createResponse(requestParam);
|
||||
return responseSeqData;
|
||||
}
|
||||
|
||||
// Parse the parameters from the time-stamping request data.
|
||||
private TsaParam parseRequestParam() throws Exception {
|
||||
TsaParam param = TsaParam.newInstance();
|
||||
|
||||
if (requestData == null) {
|
||||
return param;
|
||||
}
|
||||
|
||||
System.out.println("===== Request ====");
|
||||
debug("Request", requestData);
|
||||
|
||||
DerValue request = new DerValue(requestData);
|
||||
|
||||
param.version(request.data.getInteger());
|
||||
print("reqVersion", param.version());
|
||||
|
||||
DerValue messageImprintValue = request.data.getDerValue();
|
||||
debug("messageImprintValue", messageImprintValue.toByteArray());
|
||||
|
||||
DerValue digestAlgoValue = messageImprintValue.data.getDerValue();
|
||||
debug("digestAlgoValue", digestAlgoValue.toByteArray());
|
||||
|
||||
param.digestAlgo(AlgorithmId.parse(digestAlgoValue).getName());
|
||||
print("reqDigestAlgo", param.digestAlgo());
|
||||
|
||||
param.hashedMessage(messageImprintValue.data.getOctetString());
|
||||
debug("reqHashedMessage", param.hashedMessage());
|
||||
|
||||
while (request.data.available() > 0) {
|
||||
DerValue value = request.data.getDerValue();
|
||||
if (value.tag == DerValue.tag_Integer) {
|
||||
param.nonce(value.getBigInteger());
|
||||
print("reqNonce", param.nonce());
|
||||
} else if (value.tag == DerValue.tag_Boolean) {
|
||||
param.certReq(value.getBoolean());
|
||||
print("certReq", param.certReq());
|
||||
} else if (value.tag == DerValue.tag_ObjectId) {
|
||||
param.policyId(value.getOID().toString());
|
||||
print("reqPolicyId", param.policyId());
|
||||
}
|
||||
}
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
// Create the time-stamping response data with the given the time-stamping
|
||||
// request parameters.
|
||||
private byte[] createResponse(TsaParam requestParam) throws Exception {
|
||||
System.out.println("===== Response ====");
|
||||
TsaParam respParam = interceptor.getRespParam(requestParam);
|
||||
|
||||
DerOutputStream statusInfoOut = new DerOutputStream();
|
||||
int status = respParam.status();
|
||||
print("Status", status);
|
||||
statusInfoOut.putInteger(status);
|
||||
|
||||
DerOutputStream responseOut = new DerOutputStream();
|
||||
responseOut.write(DerValue.tag_Sequence, statusInfoOut);
|
||||
debug("Status info", statusInfoOut.toByteArray());
|
||||
System.out.println("Generated status info");
|
||||
|
||||
// Here, when the status is either 0 or 1, the response will contains
|
||||
// the signed data. Note that even though the signed data is not
|
||||
// generated, no failure info will be sent.
|
||||
if (status == 0 || status == 1) {
|
||||
System.out.println("Signer: "
|
||||
+ signerEntry.cert.getSubjectX500Principal().getName());
|
||||
String issuerName = signerEntry.cert.getIssuerX500Principal().getName();
|
||||
print("Issuer", issuerName);
|
||||
|
||||
DerOutputStream tstInfoOut = new DerOutputStream();
|
||||
|
||||
int version = respParam.version();
|
||||
print("version", version);
|
||||
tstInfoOut.putInteger(version);
|
||||
|
||||
String policyId = respParam.policyId();
|
||||
print("policyId", policyId);
|
||||
tstInfoOut.putOID(ObjectIdentifier.of(policyId));
|
||||
|
||||
String digestAlgo = respParam.digestAlgo();
|
||||
print("digestAlgo", digestAlgo);
|
||||
DerOutputStream digestAlgoOut = new DerOutputStream();
|
||||
AlgorithmId digestAlgoId = AlgorithmId.get(digestAlgo);
|
||||
digestAlgoId.encode(digestAlgoOut);
|
||||
byte[] hashedMessage = respParam.hashedMessage();
|
||||
debug("hashedMessage", hashedMessage);
|
||||
digestAlgoOut.putOctetString(hashedMessage);
|
||||
tstInfoOut.write(DerValue.tag_Sequence, digestAlgoOut);
|
||||
|
||||
BigInteger serialNumber = respParam.serialNumber();
|
||||
print("serialNumber", serialNumber);
|
||||
tstInfoOut.putInteger(serialNumber);
|
||||
|
||||
Date genTime = respParam.genTime();
|
||||
print("genTime", genTime);
|
||||
tstInfoOut.putGeneralizedTime(genTime);
|
||||
|
||||
BigInteger nonce = respParam.nonce();
|
||||
if (nonce != null) {
|
||||
tstInfoOut.putInteger(nonce);
|
||||
}
|
||||
|
||||
DerOutputStream tstInfoSeqOut = new DerOutputStream();
|
||||
tstInfoSeqOut.write(DerValue.tag_Sequence, tstInfoOut);
|
||||
byte[] tstInfoSeqData = tstInfoSeqOut.toByteArray();
|
||||
debug("TST Info", tstInfoSeqData);
|
||||
|
||||
DerOutputStream eContentOut = new DerOutputStream();
|
||||
eContentOut.putOctetString(tstInfoSeqData);
|
||||
|
||||
ContentInfo eContentInfo = new ContentInfo(
|
||||
ObjectIdentifier.of(KnownOIDs.TimeStampTokenInfo),
|
||||
new DerValue(eContentOut.toByteArray()));
|
||||
|
||||
String defaultSigAlgo = AlgorithmId.getDefaultSigAlgForKey(
|
||||
signerEntry.privateKey);
|
||||
String sigAlgo = interceptor.getSigAlgo(defaultSigAlgo);
|
||||
Signature signature = Signature.getInstance(sigAlgo);
|
||||
System.out.println(
|
||||
"Signature algorithm: " + signature.getAlgorithm());
|
||||
signature.initSign(signerEntry.privateKey);
|
||||
signature.update(tstInfoSeqData);
|
||||
|
||||
SignerInfo signerInfo = new SignerInfo(
|
||||
new X500Name(issuerName),
|
||||
signerEntry.cert.getSerialNumber(),
|
||||
AlgorithmId.get(
|
||||
AlgorithmId.getDigAlgFromSigAlg(sigAlgo)),
|
||||
AlgorithmId.get(sigAlgo),
|
||||
signature.sign());
|
||||
|
||||
X509Certificate[] signerCertChain = interceptor.getSignerCertChain(
|
||||
signerEntry.certChain, requestParam.certReq());
|
||||
PKCS7 p7 = new PKCS7(new AlgorithmId[] { digestAlgoId },
|
||||
eContentInfo, signerCertChain,
|
||||
new SignerInfo[] { signerInfo });
|
||||
ByteArrayOutputStream signedDataOut = new ByteArrayOutputStream();
|
||||
p7.encodeSignedData(signedDataOut);
|
||||
byte[] signedData = signedDataOut.toByteArray();
|
||||
debug("Signed data", signedData);
|
||||
responseOut.putDerValue(new DerValue(signedData));
|
||||
System.out.println("Generated signed data");
|
||||
}
|
||||
|
||||
DerOutputStream responseSeqOut = new DerOutputStream();
|
||||
responseSeqOut.write(DerValue.tag_Sequence, responseOut);
|
||||
byte[] responseSeqData = responseSeqOut.toByteArray();
|
||||
debug("Response", responseSeqData);
|
||||
System.out.println("Generated response");
|
||||
return responseSeqData;
|
||||
}
|
||||
|
||||
private static void print(String name, Object value) {
|
||||
System.out.println(name + ": " + value);
|
||||
}
|
||||
|
||||
private static void debug(String name, byte[] bytes) {
|
||||
if (DEBUG) {
|
||||
System.out.println(name + ":");
|
||||
HEX_PRINTER.format(bytes);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user