java - que - ¿Cómo validar si un jar firmado contiene una marca de tiempo?
identidad de marca (4)
Desde https://blogs.oracle.com/mullan/entry/how_to_determine_if_a :
Puede usar la utilidad jarsigner para determinar si un JAR firmado se ha marcado con el código de tiempo de la siguiente manera:
jarsigner -verify -verbose -certs signed.jar
donde
signed.jar
es el nombre de tu JAR firmado. Si tiene marca de tiempo, el resultado incluirá líneas de las siguientes que indican la hora en que se firmó:
[entry was signed on 8/2/13 3:48 PM]
Si el JAR no tiene marca de tiempo, la salida no incluirá esas líneas.
Después de que se firma un jar y se usa la opción -tsa, ¿cómo puedo validar que se haya incluido la marca de tiempo? Lo intenté:
jarsigner -verify -verbose -certs myApp.jar
Pero el resultado no especifica nada sobre la marca de tiempo. Lo estoy preguntando porque incluso si tengo un error tipográfico en la ruta de la URL -tsa, el jarsigner tiene éxito. Esta es la URL de GlobalSign TSA: http://timestamp.globalsign.com/scripts/timstamp.dll y el servidor detrás de ella aparentemente acepta cualquier ruta (es decir, timestamp.globalsign.com/foobar), así que al final estoy no estoy seguro de que mi frasco tenga el sello del tiempo o no.
La keytool
de keytool
de Java puede confirmar si un JAR firmado tiene marca de tiempo y también puede mostrar el certificado de TSA:
$ keytool -printcert -jarfile myApp.jar
...
Timestamp:
Owner: CN=GeoTrust Timestamping Signer 1, O=GeoTrust Inc, C=US
Issuer: CN=Thawte Timestamping CA, OU=Thawte Certification, O=Thawte, L=Durbanville, ST=Western Cape, C=ZA
Serial number: 5e8d2daca44665546bb587978191a8bf
Valid from: Wed Oct 31 00:00:00 GMT 2007 until: Mon Oct 30 23:59:59 GMT 2017
Certificate fingerprints:
MD5: E5:30:07:8E:91:8D:A0:6C:18:6D:91:2A:B6:D2:3A:56
SHA1: 22:3C:DA:27:07:96:73:81:6B:60:8A:1B:8C:B0:AB:02:30:10:7F:CC
SHA256: D7:B8:44:BD:39:5A:17:36:02:39:51:C6:4D:6C:81:65:45:93:AD:29:1D:DC:E4:6C:8D:79:B6:65:DF:31:0C:F6
Signature algorithm name: SHA1withRSA
Version: 3
...
Solo pasé las últimas 2 horas buscando este problema y finalmente encontré una manera de identificar si un archivo jar realmente tiene información de sello de tiempo en el archivo Signature Block incluido. Pude ver el certificado GlobalSign en el editor hexadecimal del archivo /META-INF/FOO.DSA, pero no encontré ninguna herramienta que imprimiera la información que necesita.
Puede cambiar el nombre del archivo FOO.DSA a foo.p7b para abrirlo en el CertMgr de Windows, pero tampoco muestra ninguna información de marca de tiempo. Tampoco pude usar OpenSSL para verificar el archivo DSA (es el formato de archivo PKCS # 7).
Así que se me ocurrió el siguiente código que mostrará Time Stamp SignerInfo y la fecha en que se creó Timestamp. Espero que sea un buen comienzo para ti. Necesita bcprov-jdk16-144.jar, bctsp-jdk16-144.jar y bcmail-jdk16-144.jar en classpath. Obtenlos de Bouncycastle
package de.mhaller.bouncycastle;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.Security;
import java.util.Collection;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import org.bouncycastle.asn1.DEREncodable;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerId;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.tsp.TSPException;
import org.bouncycastle.tsp.TimeStampToken;
import org.bouncycastle.tsp.TimeStampTokenInfo;
public class VerifyTimestampSignature {
private static boolean found;
public static void main(String[] args) throws Exception {
if (args == null || args.length != 1) {
System.out.println("usage: java " + VerifyTimestampSignature.class.getName()
+ " [jar-file|dsa-file]");
return;
}
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);
String filename = args[0];
if (filename.toLowerCase().endsWith(".dsa")) {
InputStream dsa = new FileInputStream(filename);
printDSAInfos(filename, dsa);
return;
}
if (filename.toLowerCase().endsWith(".jar")) {
InputStream jar = new FileInputStream(filename);
JarInputStream jarInputStream = new JarInputStream(jar);
JarEntry nextJarEntry;
do {
nextJarEntry = jarInputStream.getNextJarEntry();
if (nextJarEntry == null) {
break;
}
if (nextJarEntry.getName().toLowerCase().endsWith(".dsa")) {
printDSAInfos(nextJarEntry.getName(), jarInputStream);
}
} while (nextJarEntry != null);
}
if (!found) {
System.out.println("No certificate with time stamp information found in " + filename);
} else {
System.out.println("Found at least one time stamp info");
System.out.println("Note: But it was NOT verified for validity!");
}
}
private static void printDSAInfos(String file, InputStream dsa) throws CMSException,
IOException, TSPException {
System.out.println("Retrieving time stamp token from: " + file);
CMSSignedData signature = new CMSSignedData(dsa);
SignerInformationStore store = signature.getSignerInfos();
Collection<?> signers = store.getSigners();
for (Object object : signers) {
SignerInformation signerInform = (SignerInformation) object;
AttributeTable attrs = signerInform.getUnsignedAttributes();
if (attrs == null) {
System.err
.println("Signer Information does not contain any unsigned attributes. A signed jar file with Timestamp information should contain unsigned attributes.");
continue;
}
Attribute attribute = attrs.get(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken);
DEREncodable dob = attribute.getAttrValues().getObjectAt(0);
CMSSignedData signedData = new CMSSignedData(dob.getDERObject().getEncoded());
TimeStampToken tst = new TimeStampToken(signedData);
SignerId signerId = tst.getSID();
System.out.println("Signer: " + signerId.toString());
TimeStampTokenInfo tstInfo = tst.getTimeStampInfo();
System.out.println("Timestamp generated: " + tstInfo.getGenTime());
found = true;
}
}
}
mhaller proporciona un excelente código (printDSAInfos). Me ayuda mucho en mi trabajo. Sin embargo, se requieren un par de cambios. La clase DEREncodable ahora se cambia a ASN1Encodable y el método getDERObject () se cambian a toASN1Primitive. Entonces el código se ve así
ASN1Encodable dob = attribute.getAttrValues().getObjectAt(0);
CMSSignedData signedData = new CMSSignedData(dob.toASN1Primitive().getEncoded());