mirror of
https://github.com/openjdk/jdk.git
synced 2026-04-17 10:20:33 +00:00
8163304: jarsigner -verbose -verify should print the algorithms used to sign the jar
Reviewed-by: mullan
This commit is contained in:
parent
4b919bee81
commit
debfdef758
@ -267,6 +267,8 @@ module java.base {
|
||||
jdk.crypto.pkcs11;
|
||||
exports sun.security.ssl to
|
||||
java.security.jgss;
|
||||
exports sun.security.timestamp to
|
||||
jdk.jartool;
|
||||
exports sun.security.tools to
|
||||
jdk.jartool;
|
||||
exports sun.security.util to
|
||||
|
||||
@ -498,6 +498,23 @@ public class SignerInfo implements DerEncoder {
|
||||
return unauthenticatedAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timestamp PKCS7 data unverified.
|
||||
* @return a PKCS7 object
|
||||
*/
|
||||
public PKCS7 getTsToken() throws IOException {
|
||||
if (unauthenticatedAttributes == null) {
|
||||
return null;
|
||||
}
|
||||
PKCS9Attribute tsTokenAttr =
|
||||
unauthenticatedAttributes.getAttribute(
|
||||
PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID);
|
||||
if (tsTokenAttr == null) {
|
||||
return null;
|
||||
}
|
||||
return new PKCS7((byte[])tsTokenAttr.getValue());
|
||||
}
|
||||
|
||||
/*
|
||||
* Extracts a timestamp from a PKCS7 SignerInfo.
|
||||
*
|
||||
@ -525,19 +542,12 @@ public class SignerInfo implements DerEncoder {
|
||||
if (timestamp != null || !hasTimestamp)
|
||||
return timestamp;
|
||||
|
||||
if (unauthenticatedAttributes == null) {
|
||||
hasTimestamp = false;
|
||||
return null;
|
||||
}
|
||||
PKCS9Attribute tsTokenAttr =
|
||||
unauthenticatedAttributes.getAttribute(
|
||||
PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID);
|
||||
if (tsTokenAttr == null) {
|
||||
PKCS7 tsToken = getTsToken();
|
||||
if (tsToken == null) {
|
||||
hasTimestamp = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
PKCS7 tsToken = new PKCS7((byte[])tsTokenAttr.getValue());
|
||||
// Extract the content (an encoded timestamp token info)
|
||||
byte[] encTsTokenInfo = tsToken.getContentInfo().getData();
|
||||
// Extract the signer (the Timestamping Authority)
|
||||
|
||||
@ -50,6 +50,9 @@ import java.util.Map.Entry;
|
||||
|
||||
import jdk.security.jarsigner.JarSigner;
|
||||
import jdk.security.jarsigner.JarSignerException;
|
||||
import sun.security.pkcs.PKCS7;
|
||||
import sun.security.pkcs.SignerInfo;
|
||||
import sun.security.timestamp.TimestampToken;
|
||||
import sun.security.tools.KeyStoreUtil;
|
||||
import sun.security.x509.*;
|
||||
import sun.security.util.*;
|
||||
@ -87,6 +90,15 @@ public class Main {
|
||||
|
||||
private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds
|
||||
|
||||
private static final DisabledAlgorithmConstraints DISABLED_CHECK =
|
||||
new DisabledAlgorithmConstraints(
|
||||
DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS);
|
||||
|
||||
private static final Set<CryptoPrimitive> DIGEST_PRIMITIVE_SET = Collections
|
||||
.unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST));
|
||||
private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections
|
||||
.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
|
||||
|
||||
// Attention:
|
||||
// This is the entry that get launched by the security tool jarsigner.
|
||||
public static void main(String args[]) throws Exception {
|
||||
@ -163,6 +175,8 @@ public class Main {
|
||||
|
||||
private Throwable chainNotValidatedReason = null;
|
||||
|
||||
private boolean seeWeak = false;
|
||||
|
||||
CertificateFactory certificateFactory;
|
||||
CertPathValidator validator;
|
||||
PKIXParameters pkixParameters;
|
||||
@ -628,6 +642,10 @@ public class Main {
|
||||
{
|
||||
boolean anySigned = false; // if there exists entry inside jar signed
|
||||
JarFile jf = null;
|
||||
Map<String,String> digestMap = new HashMap<>();
|
||||
Map<String,PKCS7> sigMap = new HashMap<>();
|
||||
Map<String,String> sigNameMap = new HashMap<>();
|
||||
Map<String,String> unparsableSignatures = new HashMap<>();
|
||||
|
||||
try {
|
||||
jf = new JarFile(jarName, true);
|
||||
@ -638,16 +656,44 @@ public class Main {
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry je = entries.nextElement();
|
||||
entriesVec.addElement(je);
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = jf.getInputStream(je);
|
||||
while (is.read(buffer, 0, buffer.length) != -1) {
|
||||
// we just read. this will throw a SecurityException
|
||||
// if a signature/digest check fails.
|
||||
}
|
||||
} finally {
|
||||
if (is != null) {
|
||||
is.close();
|
||||
try (InputStream is = jf.getInputStream(je)) {
|
||||
String name = je.getName();
|
||||
if (signatureRelated(name)
|
||||
&& SignatureFileVerifier.isBlockOrSF(name)) {
|
||||
String alias = name.substring(name.lastIndexOf('/') + 1,
|
||||
name.lastIndexOf('.'));
|
||||
try {
|
||||
if (name.endsWith(".SF")) {
|
||||
Manifest sf = new Manifest(is);
|
||||
boolean found = false;
|
||||
for (Object obj : sf.getMainAttributes().keySet()) {
|
||||
String key = obj.toString();
|
||||
if (key.endsWith("-Digest-Manifest")) {
|
||||
digestMap.put(alias,
|
||||
key.substring(0, key.length() - 16));
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
unparsableSignatures.putIfAbsent(alias,
|
||||
String.format(
|
||||
rb.getString("history.unparsable"),
|
||||
name));
|
||||
}
|
||||
} else {
|
||||
sigNameMap.put(alias, name);
|
||||
sigMap.put(alias, new PKCS7(is));
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
unparsableSignatures.putIfAbsent(alias, String.format(
|
||||
rb.getString("history.unparsable"), name));
|
||||
}
|
||||
} else {
|
||||
while (is.read(buffer, 0, buffer.length) != -1) {
|
||||
// we just read. this will throw a SecurityException
|
||||
// if a signature/digest check fails.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -805,10 +851,11 @@ public class Main {
|
||||
System.out.println(rb.getString(
|
||||
".X.not.signed.by.specified.alias.es."));
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
if (man == null)
|
||||
if (man == null) {
|
||||
System.out.println();
|
||||
System.out.println(rb.getString("no.manifest."));
|
||||
}
|
||||
|
||||
// If signer is a trusted cert or private entry in user's own
|
||||
// keystore, it can be self-signed.
|
||||
@ -816,8 +863,99 @@ public class Main {
|
||||
signerSelfSigned = false;
|
||||
}
|
||||
|
||||
// Even if the verbose option is not specified, all out strings
|
||||
// must be generated so seeWeak can be updated.
|
||||
if (!digestMap.isEmpty()
|
||||
|| !sigMap.isEmpty()
|
||||
|| !unparsableSignatures.isEmpty()) {
|
||||
if (verbose != null) {
|
||||
System.out.println();
|
||||
}
|
||||
for (String s : sigMap.keySet()) {
|
||||
if (!digestMap.containsKey(s)) {
|
||||
unparsableSignatures.putIfAbsent(s, String.format(
|
||||
rb.getString("history.nosf"), s));
|
||||
}
|
||||
}
|
||||
for (String s : digestMap.keySet()) {
|
||||
PKCS7 p7 = sigMap.get(s);
|
||||
if (p7 != null) {
|
||||
String history;
|
||||
try {
|
||||
SignerInfo si = p7.getSignerInfos()[0];
|
||||
X509Certificate signer = si.getCertificate(p7);
|
||||
String digestAlg = digestMap.get(s);
|
||||
String sigAlg = AlgorithmId.makeSigAlg(
|
||||
si.getDigestAlgorithmId().getName(),
|
||||
si.getDigestEncryptionAlgorithmId().getName());
|
||||
PublicKey key = signer.getPublicKey();
|
||||
PKCS7 tsToken = si.getTsToken();
|
||||
if (tsToken != null) {
|
||||
SignerInfo tsSi = tsToken.getSignerInfos()[0];
|
||||
X509Certificate tsSigner = tsSi.getCertificate(tsToken);
|
||||
byte[] encTsTokenInfo = tsToken.getContentInfo().getData();
|
||||
TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo);
|
||||
PublicKey tsKey = tsSigner.getPublicKey();
|
||||
String tsDigestAlg = tsTokenInfo.getHashAlgorithm().getName();
|
||||
String tsSigAlg = AlgorithmId.makeSigAlg(
|
||||
tsSi.getDigestAlgorithmId().getName(),
|
||||
tsSi.getDigestEncryptionAlgorithmId().getName());
|
||||
Calendar c = Calendar.getInstance(
|
||||
TimeZone.getTimeZone("UTC"),
|
||||
Locale.getDefault(Locale.Category.FORMAT));
|
||||
c.setTime(tsTokenInfo.getDate());
|
||||
history = String.format(
|
||||
rb.getString("history.with.ts"),
|
||||
signer.getSubjectX500Principal(),
|
||||
withWeak(digestAlg, DIGEST_PRIMITIVE_SET),
|
||||
withWeak(sigAlg, SIG_PRIMITIVE_SET),
|
||||
withWeak(key),
|
||||
c,
|
||||
tsSigner.getSubjectX500Principal(),
|
||||
withWeak(tsDigestAlg, DIGEST_PRIMITIVE_SET),
|
||||
withWeak(tsSigAlg, SIG_PRIMITIVE_SET),
|
||||
withWeak(tsKey));
|
||||
} else {
|
||||
history = String.format(
|
||||
rb.getString("history.without.ts"),
|
||||
signer.getSubjectX500Principal(),
|
||||
withWeak(digestAlg, DIGEST_PRIMITIVE_SET),
|
||||
withWeak(sigAlg, SIG_PRIMITIVE_SET),
|
||||
withWeak(key));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// The only usage of sigNameMap, remember the name
|
||||
// of the block file if it's invalid.
|
||||
history = String.format(
|
||||
rb.getString("history.unparsable"),
|
||||
sigNameMap.get(s));
|
||||
}
|
||||
if (verbose != null) {
|
||||
System.out.println(history);
|
||||
}
|
||||
} else {
|
||||
unparsableSignatures.putIfAbsent(s, String.format(
|
||||
rb.getString("history.nobk"), s));
|
||||
}
|
||||
}
|
||||
if (verbose != null) {
|
||||
for (String s : unparsableSignatures.keySet()) {
|
||||
System.out.println(unparsableSignatures.get(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println();
|
||||
if (!anySigned) {
|
||||
if (hasSignature) {
|
||||
if (seeWeak) {
|
||||
if (verbose != null) {
|
||||
System.out.println(rb.getString("jar.treated.unsigned.see.weak.verbose"));
|
||||
System.out.println("\n " +
|
||||
DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS +
|
||||
"=" + Security.getProperty(DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS));
|
||||
} else {
|
||||
System.out.println(rb.getString("jar.treated.unsigned.see.weak"));
|
||||
}
|
||||
} else if (hasSignature) {
|
||||
System.out.println(rb.getString("jar.treated.unsigned"));
|
||||
} else {
|
||||
System.out.println(rb.getString("jar.is.unsigned"));
|
||||
@ -845,7 +983,9 @@ public class Main {
|
||||
if (weakAlg != 0) {
|
||||
// In fact, jarsigner verification did not catch this
|
||||
// since it has not read the JarFile content itself.
|
||||
// Everything is done with JarFile API.
|
||||
// Everything is done with JarFile API. The signing
|
||||
// history (digestMap etc) will show these info and
|
||||
// print out proper warnings.
|
||||
}
|
||||
|
||||
if (badKeyUsage) {
|
||||
@ -936,6 +1076,26 @@ public class Main {
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
private String withWeak(String alg, Set<CryptoPrimitive> primitiveSet) {
|
||||
if (DISABLED_CHECK.permits(primitiveSet, alg, null)) {
|
||||
return alg;
|
||||
} else {
|
||||
seeWeak = true;
|
||||
return String.format(rb.getString("with.weak"), alg);
|
||||
}
|
||||
}
|
||||
|
||||
private String withWeak(PublicKey key) {
|
||||
if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
|
||||
return String.format(
|
||||
rb.getString("key.bit"), KeyUtil.getKeySize(key));
|
||||
} else {
|
||||
seeWeak = true;
|
||||
return String.format(
|
||||
rb.getString("key.bit.weak"), KeyUtil.getKeySize(key));
|
||||
}
|
||||
}
|
||||
|
||||
private static MessageFormat validityTimeForm = null;
|
||||
private static MessageFormat notYetTimeForm = null;
|
||||
private static MessageFormat expiredTimeForm = null;
|
||||
@ -1125,22 +1285,22 @@ public class Main {
|
||||
void signJar(String jarName, String alias)
|
||||
throws Exception {
|
||||
|
||||
DisabledAlgorithmConstraints dac =
|
||||
new DisabledAlgorithmConstraints(
|
||||
DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS);
|
||||
|
||||
if (digestalg != null && !dac.permits(
|
||||
Collections.singleton(CryptoPrimitive.MESSAGE_DIGEST), digestalg, null)) {
|
||||
if (digestalg != null && !DISABLED_CHECK.permits(
|
||||
DIGEST_PRIMITIVE_SET, digestalg, null)) {
|
||||
weakAlg |= 1;
|
||||
}
|
||||
if (tSADigestAlg != null && !dac.permits(
|
||||
Collections.singleton(CryptoPrimitive.MESSAGE_DIGEST), tSADigestAlg, null)) {
|
||||
if (tSADigestAlg != null && !DISABLED_CHECK.permits(
|
||||
DIGEST_PRIMITIVE_SET, tSADigestAlg, null)) {
|
||||
weakAlg |= 4;
|
||||
}
|
||||
if (sigalg != null && !dac.permits(
|
||||
Collections.singleton(CryptoPrimitive.SIGNATURE), sigalg, null)) {
|
||||
if (sigalg != null && !DISABLED_CHECK.permits(
|
||||
SIG_PRIMITIVE_SET , sigalg, null)) {
|
||||
weakAlg |= 2;
|
||||
}
|
||||
if (!DISABLED_CHECK.permits(
|
||||
SIG_PRIMITIVE_SET, privateKey)) {
|
||||
weakAlg |= 8;
|
||||
}
|
||||
|
||||
boolean aliasUsed = false;
|
||||
X509Certificate tsaCert = null;
|
||||
@ -1385,6 +1545,11 @@ public class Main {
|
||||
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."),
|
||||
tSADigestAlg, "-tsadigestalg"));
|
||||
}
|
||||
if ((weakAlg & 8) == 8) {
|
||||
System.out.println(String.format(
|
||||
rb.getString("The.1.signing.key.has.a.keysize.of.2.which.is.considered.a.security.risk."),
|
||||
privateKey.getAlgorithm(), KeyUtil.getKeySize(privateKey)));
|
||||
}
|
||||
} else {
|
||||
System.out.println(rb.getString("jar.signed."));
|
||||
}
|
||||
|
||||
@ -145,11 +145,26 @@ public class Resources extends java.util.ListResourceBundle {
|
||||
{"jar.is.unsigned",
|
||||
"jar is unsigned."},
|
||||
{"jar.treated.unsigned",
|
||||
"Signature not parsable or verifiable. The jar will be treated as unsigned. The jar may have been signed with a weak algorithm that is now disabled. For more information, rerun jarsigner with debug enabled (-J-Djava.security.debug=jar)."},
|
||||
"WARNING: Signature is either not parsable or not verifiable, and the jar will be treated as unsigned. For more information, re-run jarsigner with debug enabled (-J-Djava.security.debug=jar)."},
|
||||
{"jar.treated.unsigned.see.weak",
|
||||
"The jar will be treated as unsigned, because it is signed with a weak algorithm that is now disabled.\n\nRe-run jarsigner with the -verbose option for more details."},
|
||||
{"jar.treated.unsigned.see.weak.verbose",
|
||||
"WARNING: The jar will be treated as unsigned, because it is signed with a weak algorithm that is now disabled by the security property:"},
|
||||
{"jar.signed.", "jar signed."},
|
||||
{"jar.signed.with.signer.errors.", "jar signed, with signer errors."},
|
||||
{"jar.verified.", "jar verified."},
|
||||
{"jar.verified.with.signer.errors.", "jar verified, with signer errors."},
|
||||
|
||||
{"history.with.ts", "- Signed by \"%1$s\"\n Digest algorithm: %2$s\n Signature algorithm: %3$s, %4$s\n Timestamped by \"%6$s\" on %5$tc\n Timestamp digest algorithm: %7$s\n Timestamp signature algorithm: %8$s, %9$s"},
|
||||
{"history.without.ts", "- Signed by \"%1$s\"\n Digest algorithm: %2$s\n Signature algorithm: %3$s, %4$s"},
|
||||
{"history.unparsable", "- Unparsable signature-related file %s"},
|
||||
{"history.nosf", "- Missing signature-related file META-INF/%s.SF"},
|
||||
{"history.nobk", "- Missing block file for signature-related file META-INF/%s.SF"},
|
||||
|
||||
{"with.weak", "%s (weak)"},
|
||||
{"key.bit", "%d-bit key"},
|
||||
{"key.bit.weak", "%d-bit key (weak)"},
|
||||
|
||||
{"jarsigner.", "jarsigner: "},
|
||||
{"signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or.",
|
||||
"signature filename must consist of the following characters: A-Z, 0-9, _ or -"},
|
||||
@ -248,6 +263,8 @@ public class Resources extends java.util.ListResourceBundle {
|
||||
"The signer's certificate is self-signed."},
|
||||
{"The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk.",
|
||||
"The %1$s algorithm specified for the %2$s option is considered a security risk."},
|
||||
{"The.1.signing.key.has.a.keysize.of.2.which.is.considered.a.security.risk.",
|
||||
"The %s signing key has a keysize of %d which is considered a security risk."},
|
||||
{"This.jar.contains.entries.whose.certificate.chain.is.not.validated.reason.1",
|
||||
"This jar contains entries whose certificate chain is not validated. Reason: %s"},
|
||||
{"no.timestamp.signing",
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
package jdk.testlibrary;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@ -40,7 +41,8 @@ import java.util.jar.Manifest;
|
||||
public final class JarUtils {
|
||||
|
||||
/**
|
||||
* Create jar file with specified files.
|
||||
* Create jar file with specified files. If a specified file does not exist,
|
||||
* a new jar entry will be created with the file name itself as the content.
|
||||
*/
|
||||
public static void createJar(String dest, String... files)
|
||||
throws IOException {
|
||||
@ -54,6 +56,8 @@ public final class JarUtils {
|
||||
jos.putNextEntry(new JarEntry(file));
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
fis.transferTo(jos);
|
||||
} catch (FileNotFoundException e) {
|
||||
jos.write(file.getBytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -61,7 +65,17 @@ public final class JarUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add specified files to existing jar file.
|
||||
* Add or remove specified files to existing jar file. If a specified file
|
||||
* to be updated or added does not exist, the jar entry will be created
|
||||
* with the file name itself as the content.
|
||||
*
|
||||
* @param src the original jar file name
|
||||
* @param dest the new jar file name
|
||||
* @param files the files to update. The list is broken into 2 groups
|
||||
* by a "-" string. The files before in the 1st group will
|
||||
* be either updated or added. The files in the 2nd group
|
||||
* will be removed. If no "-" exists, all files belong to
|
||||
* the 1st group.
|
||||
*/
|
||||
public static void updateJar(String src, String dest, String... files)
|
||||
throws IOException {
|
||||
@ -77,8 +91,11 @@ public final class JarUtils {
|
||||
JarEntry entry = entries.nextElement();
|
||||
String name = entry.getName();
|
||||
boolean found = false;
|
||||
boolean update = true;
|
||||
for (String file : files) {
|
||||
if (name.equals(file)) {
|
||||
if (file.equals("-")) {
|
||||
update = false;
|
||||
} else if (name.equals(file)) {
|
||||
updatedFiles.add(file);
|
||||
found = true;
|
||||
break;
|
||||
@ -86,11 +103,18 @@ public final class JarUtils {
|
||||
}
|
||||
|
||||
if (found) {
|
||||
System.out.println(String.format("Updating %s with %s",
|
||||
dest, name));
|
||||
jos.putNextEntry(new JarEntry(name));
|
||||
try (FileInputStream fis = new FileInputStream(name)) {
|
||||
fis.transferTo(jos);
|
||||
if (update) {
|
||||
System.out.println(String.format("Updating %s with %s",
|
||||
dest, name));
|
||||
jos.putNextEntry(new JarEntry(name));
|
||||
try (FileInputStream fis = new FileInputStream(name)) {
|
||||
fis.transferTo(jos);
|
||||
} catch (FileNotFoundException e) {
|
||||
jos.write(name.getBytes());
|
||||
}
|
||||
} else {
|
||||
System.out.println(String.format("Removing %s from %s",
|
||||
name, dest));
|
||||
}
|
||||
} else {
|
||||
System.out.println(String.format("Copying %s to %s",
|
||||
@ -103,12 +127,17 @@ public final class JarUtils {
|
||||
|
||||
// append new files
|
||||
for (String file : files) {
|
||||
if (file.equals("-")) {
|
||||
break;
|
||||
}
|
||||
if (!updatedFiles.contains(file)) {
|
||||
System.out.println(String.format("Adding %s with %s",
|
||||
dest, file));
|
||||
jos.putNextEntry(new JarEntry(file));
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
fis.transferTo(jos);
|
||||
} catch (FileNotFoundException e) {
|
||||
jos.write(file.getBytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,25 +22,29 @@
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
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.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.Locale;
|
||||
|
||||
import jdk.testlibrary.*;
|
||||
import jdk.testlibrary.JarUtils;
|
||||
import sun.security.pkcs.ContentInfo;
|
||||
import sun.security.pkcs.PKCS7;
|
||||
import sun.security.pkcs.PKCS9Attribute;
|
||||
@ -52,11 +56,22 @@ import sun.security.util.ObjectIdentifier;
|
||||
import sun.security.x509.AlgorithmId;
|
||||
import sun.security.x509.X500Name;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6543842 6543440 6939248 8009636 8024302 8163304
|
||||
* @summary checking response of timestamp
|
||||
* @modules java.base/sun.security.pkcs
|
||||
* java.base/sun.security.timestamp
|
||||
* java.base/sun.security.x509
|
||||
* java.base/sun.security.util
|
||||
* java.base/sun.security.tools.keytool
|
||||
* @library /lib/testlibrary
|
||||
* @run main/timeout=600 TimestampCheck
|
||||
*/
|
||||
public class TimestampCheck {
|
||||
static final String TSKS = "tsks";
|
||||
static final String JAR = "old.jar";
|
||||
|
||||
static final String defaultPolicyId = "2.3.4.5";
|
||||
static final String defaultPolicyId = "2.3.4";
|
||||
static String host = null;
|
||||
|
||||
static class Handler implements HttpHandler, AutoCloseable {
|
||||
|
||||
@ -75,11 +90,7 @@ public class TimestampCheck {
|
||||
t.getRequestBody().read(input);
|
||||
|
||||
try {
|
||||
int path = 0;
|
||||
if (t.getRequestURI().getPath().length() > 1) {
|
||||
path = Integer.parseInt(
|
||||
t.getRequestURI().getPath().substring(1));
|
||||
}
|
||||
String path = t.getRequestURI().getPath().substring(1);
|
||||
byte[] output = sign(input, path);
|
||||
Headers out = t.getResponseHeaders();
|
||||
out.set("Content-Type", "application/timestamp-reply");
|
||||
@ -97,24 +108,10 @@ public class TimestampCheck {
|
||||
/**
|
||||
* @param input The data to sign
|
||||
* @param path different cases to simulate, impl on URL path
|
||||
* 0: normal
|
||||
* 1: Missing nonce
|
||||
* 2: Different nonce
|
||||
* 3: Bad digets octets in messageImprint
|
||||
* 4: Different algorithmId in messageImprint
|
||||
* 5: whole chain in cert set
|
||||
* 6: extension is missing
|
||||
* 7: extension is non-critical
|
||||
* 8: extension does not have timestamping
|
||||
* 9: no cert in response
|
||||
* 10: normal
|
||||
* 11: always return default policy id
|
||||
* 12: normal
|
||||
* otherwise: normal
|
||||
* @returns the signed
|
||||
*/
|
||||
byte[] sign(byte[] input, int path) throws Exception {
|
||||
// Read TSRequest
|
||||
byte[] sign(byte[] input, String path) throws Exception {
|
||||
|
||||
DerValue value = new DerValue(input);
|
||||
System.err.println("\nIncoming Request\n===================");
|
||||
System.err.println("Version: " + value.data.getInteger());
|
||||
@ -138,36 +135,33 @@ public class TimestampCheck {
|
||||
}
|
||||
}
|
||||
|
||||
// Write TSResponse
|
||||
System.err.println("\nResponse\n===================");
|
||||
KeyStore ks = KeyStore.getInstance("JKS");
|
||||
try (FileInputStream fis = new FileInputStream(keystore)) {
|
||||
ks.load(fis, "changeit".toCharArray());
|
||||
}
|
||||
KeyStore ks = KeyStore.getInstance(
|
||||
new File(keystore), "changeit".toCharArray());
|
||||
|
||||
String alias = "ts";
|
||||
if (path == 6) alias = "tsbad1";
|
||||
if (path == 7) alias = "tsbad2";
|
||||
if (path == 8) alias = "tsbad3";
|
||||
if (path.startsWith("bad") || path.equals("weak")) {
|
||||
alias = "ts" + path;
|
||||
}
|
||||
|
||||
if (path == 11) {
|
||||
if (path.equals("diffpolicy")) {
|
||||
policyId = new ObjectIdentifier(defaultPolicyId);
|
||||
}
|
||||
|
||||
DerOutputStream statusInfo = new DerOutputStream();
|
||||
statusInfo.putInteger(0);
|
||||
|
||||
DerOutputStream token = new DerOutputStream();
|
||||
AlgorithmId[] algorithms = {aid};
|
||||
Certificate[] chain = ks.getCertificateChain(alias);
|
||||
X509Certificate[] signerCertificateChain = null;
|
||||
X509Certificate[] signerCertificateChain;
|
||||
X509Certificate signer = (X509Certificate)chain[0];
|
||||
if (path == 5) { // Only case 5 uses full chain
|
||||
|
||||
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];
|
||||
}
|
||||
} else if (path == 9) {
|
||||
} else if (path.equals("nocert")) {
|
||||
signerCertificateChain = new X509Certificate[0];
|
||||
} else {
|
||||
signerCertificateChain = new X509Certificate[1];
|
||||
@ -179,11 +173,11 @@ public class TimestampCheck {
|
||||
tst.putInteger(1);
|
||||
tst.putOID(policyId);
|
||||
|
||||
if (path != 3 && path != 4) {
|
||||
if (!path.equals("baddigest") && !path.equals("diffalg")) {
|
||||
tst.putDerValue(messageImprint);
|
||||
} else {
|
||||
byte[] data = messageImprint.toByteArray();
|
||||
if (path == 4) {
|
||||
if (path.equals("diffalg")) {
|
||||
data[6] = (byte)0x01;
|
||||
} else {
|
||||
data[data.length-1] = (byte)0x01;
|
||||
@ -198,10 +192,10 @@ public class TimestampCheck {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
tst.putGeneralizedTime(cal.getTime());
|
||||
|
||||
if (path == 2) {
|
||||
if (path.equals("diffnonce")) {
|
||||
tst.putInteger(1234);
|
||||
} else if (path == 1) {
|
||||
// do nothing
|
||||
} else if (path.equals("nononce")) {
|
||||
// no noce
|
||||
} else {
|
||||
tst.putInteger(nonce);
|
||||
}
|
||||
@ -212,6 +206,8 @@ public class TimestampCheck {
|
||||
DerOutputStream tstInfo2 = new DerOutputStream();
|
||||
tstInfo2.putOctetString(tstInfo.toByteArray());
|
||||
|
||||
// Always use the same algorithm at timestamp signing
|
||||
// so it is different from the hash algorithm.
|
||||
Signature sig = Signature.getInstance("SHA1withRSA");
|
||||
sig.initSign((PrivateKey)(ks.getKey(
|
||||
alias, "changeit".toCharArray())));
|
||||
@ -229,12 +225,11 @@ public class TimestampCheck {
|
||||
SignerInfo signerInfo = new SignerInfo(
|
||||
new X500Name(signer.getIssuerX500Principal().getName()),
|
||||
signer.getSerialNumber(),
|
||||
aid, AlgorithmId.get("RSA"), sig.sign());
|
||||
AlgorithmId.get("SHA-1"), AlgorithmId.get("RSA"), sig.sign());
|
||||
|
||||
SignerInfo[] signerInfos = {signerInfo};
|
||||
PKCS7 p7 =
|
||||
new PKCS7(algorithms, contentInfo, signerCertificateChain,
|
||||
signerInfos);
|
||||
PKCS7 p7 = new PKCS7(algorithms, contentInfo,
|
||||
signerCertificateChain, signerInfos);
|
||||
ByteArrayOutputStream p7out = new ByteArrayOutputStream();
|
||||
p7.encodeSignedData(p7out);
|
||||
|
||||
@ -294,44 +289,68 @@ public class TimestampCheck {
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
try (Handler tsa = Handler.init(0, TSKS);) {
|
||||
public static void main(String[] args) throws Throwable {
|
||||
|
||||
prepare();
|
||||
|
||||
try (Handler tsa = Handler.init(0, "tsks");) {
|
||||
tsa.start();
|
||||
int port = tsa.getPort();
|
||||
|
||||
String cmd;
|
||||
// Use -J-Djava.security.egd=file:/dev/./urandom to speed up
|
||||
// nonce generation in timestamping request. Not avaibale on
|
||||
// Windows and defaults to thread seed generator, not too bad.
|
||||
if (System.getProperty("java.home").endsWith("jre")) {
|
||||
cmd = System.getProperty("java.home") + "/../bin/jarsigner";
|
||||
} else {
|
||||
cmd = System.getProperty("java.home") + "/bin/jarsigner";
|
||||
}
|
||||
|
||||
cmd += " " + System.getProperty("test.tool.vm.opts")
|
||||
+ " -J-Djava.security.egd=file:/dev/./urandom"
|
||||
+ " -J-Duser.language=en -J-Duser.country=US"
|
||||
+ " -debug -keystore " + TSKS + " -storepass changeit"
|
||||
+ " -tsa http://localhost:" + port + "/%d"
|
||||
+ " -signedjar new_%d.jar " + JAR + " old";
|
||||
host = "http://localhost:" + port + "/";
|
||||
|
||||
if (args.length == 0) { // Run this test
|
||||
jarsigner(cmd, 0, true); // Success, normal call
|
||||
jarsigner(cmd, 1, false); // These 4 should fail
|
||||
jarsigner(cmd, 2, false);
|
||||
jarsigner(cmd, 3, false);
|
||||
jarsigner(cmd, 4, false);
|
||||
jarsigner(cmd, 5, true); // Success, 6543440 solved.
|
||||
jarsigner(cmd, 6, false); // tsbad1
|
||||
jarsigner(cmd, 7, false); // tsbad2
|
||||
jarsigner(cmd, 8, false); // tsbad3
|
||||
jarsigner(cmd, 9, false); // no cert in timestamp
|
||||
jarsigner(cmd + " -tsapolicyid 1.2.3.4", 10, true);
|
||||
checkTimestamp("new_10.jar", "1.2.3.4", "SHA-256");
|
||||
jarsigner(cmd + " -tsapolicyid 1.2.3.5", 11, false);
|
||||
jarsigner(cmd + " -tsadigestalg SHA", 12, true);
|
||||
checkTimestamp("new_12.jar", defaultPolicyId, "SHA-1");
|
||||
sign("none")
|
||||
.shouldContain("is not timestamped")
|
||||
.shouldHaveExitValue(0);
|
||||
|
||||
sign("badku")
|
||||
.shouldHaveExitValue(0);
|
||||
checkBadKU("badku.jar");
|
||||
|
||||
sign("normal")
|
||||
.shouldNotContain("is not timestamped")
|
||||
.shouldHaveExitValue(0);
|
||||
|
||||
sign("nononce")
|
||||
.shouldHaveExitValue(1);
|
||||
sign("diffnonce")
|
||||
.shouldHaveExitValue(1);
|
||||
sign("baddigest")
|
||||
.shouldHaveExitValue(1);
|
||||
sign("diffalg")
|
||||
.shouldHaveExitValue(1);
|
||||
sign("fullchain")
|
||||
.shouldHaveExitValue(0); // Success, 6543440 solved.
|
||||
sign("bad1")
|
||||
.shouldHaveExitValue(1);
|
||||
sign("bad2")
|
||||
.shouldHaveExitValue(1);
|
||||
sign("bad3")
|
||||
.shouldHaveExitValue(1);
|
||||
sign("nocert")
|
||||
.shouldHaveExitValue(1);
|
||||
|
||||
sign("policy", "-tsapolicyid", "1.2.3")
|
||||
.shouldHaveExitValue(0);
|
||||
checkTimestamp("policy.jar", "1.2.3", "SHA-256");
|
||||
|
||||
sign("diffpolicy", "-tsapolicyid", "1.2.3")
|
||||
.shouldHaveExitValue(1);
|
||||
|
||||
sign("tsaalg", "-tsadigestalg", "SHA")
|
||||
.shouldHaveExitValue(0);
|
||||
checkTimestamp("tsaalg.jar", defaultPolicyId, "SHA-1");
|
||||
|
||||
sign("weak", "-digestalg", "MD5",
|
||||
"-sigalg", "MD5withRSA", "-tsadigestalg", "MD5")
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldMatch("MD5.*-digestalg.*risk")
|
||||
.shouldMatch("MD5.*-tsadigestalg.*risk")
|
||||
.shouldMatch("MD5withRSA.*-sigalg.*risk");
|
||||
checkWeak("weak.jar");
|
||||
|
||||
// When .SF or .RSA is missing or invalid
|
||||
checkMissingOrInvalidFiles("normal.jar");
|
||||
} else { // Run as a standalone server
|
||||
System.err.println("Press Enter to quit server");
|
||||
System.in.read();
|
||||
@ -339,6 +358,95 @@ public class TimestampCheck {
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkMissingOrInvalidFiles(String s)
|
||||
throws Throwable {
|
||||
JarUtils.updateJar(s, "1.jar", "-", "META-INF/OLD.SF");
|
||||
verify("1.jar", "-verbose")
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldContain("treated as unsigned")
|
||||
.shouldContain("Missing signature-related file META-INF/OLD.SF");
|
||||
JarUtils.updateJar(s, "2.jar", "-", "META-INF/OLD.RSA");
|
||||
verify("2.jar", "-verbose")
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldContain("treated as unsigned")
|
||||
.shouldContain("Missing block file for signature-related file META-INF/OLD.SF");
|
||||
JarUtils.updateJar(s, "3.jar", "META-INF/OLD.SF");
|
||||
verify("3.jar", "-verbose")
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldContain("treated as unsigned")
|
||||
.shouldContain("Unparsable signature-related file META-INF/OLD.SF");
|
||||
JarUtils.updateJar(s, "4.jar", "META-INF/OLD.RSA");
|
||||
verify("4.jar", "-verbose")
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldContain("treated as unsigned")
|
||||
.shouldContain("Unparsable signature-related file META-INF/OLD.RSA");
|
||||
}
|
||||
|
||||
static OutputAnalyzer jarsigner(List<String> extra)
|
||||
throws Throwable {
|
||||
JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jarsigner")
|
||||
.addVMArg("-Duser.language=en")
|
||||
.addVMArg("-Duser.country=US")
|
||||
.addToolArg("-keystore")
|
||||
.addToolArg("tsks")
|
||||
.addToolArg("-storepass")
|
||||
.addToolArg("changeit");
|
||||
for (String s : extra) {
|
||||
if (s.startsWith("-J")) {
|
||||
launcher.addVMArg(s.substring(2));
|
||||
} else {
|
||||
launcher.addToolArg(s);
|
||||
}
|
||||
}
|
||||
return ProcessTools.executeCommand(launcher.getCommand());
|
||||
}
|
||||
|
||||
static OutputAnalyzer verify(String file, String... extra)
|
||||
throws Throwable {
|
||||
List<String> args = new ArrayList<>();
|
||||
args.add("-verify");
|
||||
args.add(file);
|
||||
args.addAll(Arrays.asList(extra));
|
||||
return jarsigner(args);
|
||||
}
|
||||
|
||||
static void checkBadKU(String file) throws Throwable {
|
||||
verify(file)
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldContain("treated as unsigned")
|
||||
.shouldContain("re-run jarsigner with debug enabled");
|
||||
verify(file, "-verbose")
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldContain("Signed by")
|
||||
.shouldContain("treated as unsigned")
|
||||
.shouldContain("re-run jarsigner with debug enabled");
|
||||
verify(file, "-J-Djava.security.debug=jar")
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldContain("SignatureException: Key usage restricted")
|
||||
.shouldContain("treated as unsigned")
|
||||
.shouldContain("re-run jarsigner with debug enabled");
|
||||
}
|
||||
|
||||
static void checkWeak(String file) throws Throwable {
|
||||
verify(file)
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldContain("treated as unsigned")
|
||||
.shouldMatch("weak algorithm that is now disabled.")
|
||||
.shouldMatch("Re-run jarsigner with the -verbose option for more details");
|
||||
verify(file, "-verbose")
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldContain("treated as unsigned")
|
||||
.shouldMatch("weak algorithm that is now disabled by")
|
||||
.shouldMatch("Digest algorithm: .*weak")
|
||||
.shouldMatch("Signature algorithm: .*weak")
|
||||
.shouldMatch("Timestamp digest algorithm: .*weak")
|
||||
.shouldNotMatch("Timestamp signature algorithm: .*weak.*weak")
|
||||
.shouldMatch("Timestamp signature algorithm: .*key.*weak");
|
||||
verify(file, "-J-Djava.security.debug=jar")
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldMatch("SignatureException:.*Disabled");
|
||||
}
|
||||
|
||||
static void checkTimestamp(String file, String policyId, String digestAlg)
|
||||
throws Exception {
|
||||
try (JarFile jf = new JarFile(file)) {
|
||||
@ -365,41 +473,62 @@ public class TimestampCheck {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cmd the command line (with a hole to plug in)
|
||||
* @param path the path in the URL, i.e, http://localhost/path
|
||||
* @param expected if this command should succeed
|
||||
*/
|
||||
static void jarsigner(String cmd, int path, boolean expected)
|
||||
throws Exception {
|
||||
System.err.println("Test " + path);
|
||||
Process p = Runtime.getRuntime().exec(String.format(Locale.ROOT,cmd, path, path));
|
||||
BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(p.getErrorStream()));
|
||||
while (true) {
|
||||
String s = reader.readLine();
|
||||
if (s == null) break;
|
||||
System.err.println(s);
|
||||
}
|
||||
static int which = 0;
|
||||
|
||||
// Will not see noTimestamp warning
|
||||
boolean seeWarning = false;
|
||||
reader = new BufferedReader(
|
||||
new InputStreamReader(p.getInputStream()));
|
||||
while (true) {
|
||||
String s = reader.readLine();
|
||||
if (s == null) break;
|
||||
System.err.println(s);
|
||||
if (s.indexOf("Warning:") >= 0) {
|
||||
seeWarning = true;
|
||||
}
|
||||
/**
|
||||
* @param extra more args given to jarsigner
|
||||
*/
|
||||
static OutputAnalyzer sign(String path, String... extra)
|
||||
throws Throwable {
|
||||
which++;
|
||||
System.err.println("\n>> Test #" + which + ": " + Arrays.toString(extra));
|
||||
List<String> args = List.of("-J-Djava.security.egd=file:/dev/./urandom",
|
||||
"-debug", "-signedjar", path + ".jar", "old.jar",
|
||||
path.equals("badku") ? "badku" : "old");
|
||||
args = new ArrayList<>(args);
|
||||
if (!path.equals("none") && !path.equals("badku")) {
|
||||
args.add("-tsa");
|
||||
args.add(host + path);
|
||||
}
|
||||
int result = p.waitFor();
|
||||
if (expected && result != 0 || !expected && result == 0) {
|
||||
throw new Exception("Failed");
|
||||
}
|
||||
if (seeWarning) {
|
||||
throw new Exception("See warning");
|
||||
args.addAll(Arrays.asList(extra));
|
||||
return jarsigner(args);
|
||||
}
|
||||
|
||||
static void prepare() throws Exception {
|
||||
jdk.testlibrary.JarUtils.createJar("old.jar", "A");
|
||||
Files.deleteIfExists(Paths.get("tsks"));
|
||||
keytool("-alias ca -genkeypair -ext bc -dname CN=CA");
|
||||
keytool("-alias old -genkeypair -dname CN=old");
|
||||
keytool("-alias badku -genkeypair -dname CN=badku");
|
||||
keytool("-alias ts -genkeypair -dname CN=ts");
|
||||
keytool("-alias tsweak -genkeypair -keysize 512 -dname CN=tsbad1");
|
||||
keytool("-alias tsbad1 -genkeypair -dname CN=tsbad1");
|
||||
keytool("-alias tsbad2 -genkeypair -dname CN=tsbad2");
|
||||
keytool("-alias tsbad3 -genkeypair -dname CN=tsbad3");
|
||||
|
||||
gencert("old");
|
||||
gencert("badku", "-ext ku:critical=keyAgreement");
|
||||
gencert("ts", "-ext eku:critical=ts");
|
||||
gencert("tsweak", "-ext eku:critical=ts");
|
||||
gencert("tsbad1");
|
||||
gencert("tsbad2", "-ext eku=ts");
|
||||
gencert("tsbad3", "-ext eku:critical=cs");
|
||||
}
|
||||
|
||||
static void gencert(String alias, String... extra) throws Exception {
|
||||
keytool("-alias " + alias + " -certreq -file " + alias + ".req");
|
||||
String genCmd = "-gencert -alias ca -infile " +
|
||||
alias + ".req -outfile " + alias + ".cert";
|
||||
for (String s : extra) {
|
||||
genCmd += " " + s;
|
||||
}
|
||||
keytool(genCmd);
|
||||
keytool("-alias " + alias + " -importcert -file " + alias + ".cert");
|
||||
}
|
||||
|
||||
static void keytool(String cmd) throws Exception {
|
||||
cmd = "-keystore tsks -storepass changeit -keypass changeit " +
|
||||
"-keyalg rsa -validity 200 " + cmd;
|
||||
sun.security.tools.keytool.Main.main(cmd.split(" "));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,103 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2007, 2013, 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.
|
||||
#
|
||||
|
||||
# @test
|
||||
# @bug 6543842 6543440 6939248 8009636 8024302
|
||||
# @summary checking response of timestamp
|
||||
# @modules java.base/sun.security.pkcs
|
||||
# java.base/sun.security.timestamp
|
||||
# java.base/sun.security.x509
|
||||
# java.base/sun.security.util
|
||||
#
|
||||
# @run shell/timeout=600 ts.sh
|
||||
|
||||
# Run for a long time because jarsigner with timestamp needs to create a
|
||||
# 64-bit random number and it might be extremely slow on a machine with
|
||||
# not enough entropy pool
|
||||
|
||||
# set platform-dependent variables
|
||||
OS=`uname -s`
|
||||
case "$OS" in
|
||||
Windows_* )
|
||||
FS="\\"
|
||||
;;
|
||||
* )
|
||||
FS="/"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "${TESTSRC}" = "" ] ; then
|
||||
TESTSRC="."
|
||||
fi
|
||||
if [ "${TESTJAVA}" = "" ] ; then
|
||||
JAVAC_CMD=`which javac`
|
||||
TESTJAVA=`dirname $JAVAC_CMD`/..
|
||||
fi
|
||||
|
||||
JAR="${TESTJAVA}${FS}bin${FS}jar ${TESTTOOLVMOPTS}"
|
||||
JAVA="${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS}"
|
||||
JAVAC="${TESTJAVA}${FS}bin${FS}javac ${TESTTOOLVMOPTS} ${TESTJAVACOPTS}"
|
||||
KT="${TESTJAVA}${FS}bin${FS}keytool ${TESTTOOLVMOPTS} -keystore tsks -storepass changeit -keypass changeit -keyalg rsa -validity 200"
|
||||
|
||||
rm tsks
|
||||
echo Nothing > A
|
||||
rm old.jar
|
||||
$JAR cvf old.jar A
|
||||
|
||||
# ca is CA
|
||||
# old is signer for code
|
||||
# ts is signer for timestamp
|
||||
# tsbad1 has no extendedKeyUsage
|
||||
# tsbad2's extendedKeyUsage is non-critical
|
||||
# tsbad3's extendedKeyUsage has no timestamping
|
||||
|
||||
$KT -alias ca -genkeypair -ext bc -dname CN=CA
|
||||
$KT -alias old -genkeypair -dname CN=old
|
||||
$KT -alias ts -genkeypair -dname CN=ts
|
||||
$KT -alias tsbad1 -genkeypair -dname CN=tsbad1
|
||||
$KT -alias tsbad2 -genkeypair -dname CN=tsbad2
|
||||
$KT -alias tsbad3 -genkeypair -dname CN=tsbad3
|
||||
|
||||
$KT -alias old -certreq | \
|
||||
$KT -alias ca -gencert | \
|
||||
$KT -alias old -importcert
|
||||
$KT -alias ts -certreq | \
|
||||
$KT -alias ca -gencert -ext eku:critical=ts | \
|
||||
$KT -alias ts -importcert
|
||||
$KT -alias tsbad1 -certreq | \
|
||||
$KT -alias ca -gencert | \
|
||||
$KT -alias tsbad1 -importcert
|
||||
$KT -alias tsbad2 -certreq | \
|
||||
$KT -alias ca -gencert -ext eku=ts | \
|
||||
$KT -alias tsbad2 -importcert
|
||||
$KT -alias tsbad3 -certreq | \
|
||||
$KT -alias ca -gencert -ext eku:critical=cs | \
|
||||
$KT -alias tsbad3 -importcert
|
||||
|
||||
EXTRAOPTS="--add-exports java.base/sun.security.pkcs=ALL-UNNAMED \
|
||||
--add-exports java.base/sun.security.timestamp=ALL-UNNAMED \
|
||||
--add-exports java.base/sun.security.x509=ALL-UNNAMED \
|
||||
--add-exports java.base/sun.security.util=ALL-UNNAMED"
|
||||
$JAVAC ${EXTRAOPTS} -d . ${TESTSRC}/TimestampCheck.java
|
||||
$JAVA ${TESTVMOPTS} ${EXTRAOPTS} "-Dtest.tool.vm.opts=${TESTTOOLVMOPTS}" TimestampCheck
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user