Цифровая подпись с отметкой времени в Java

У меня проблема с созданием действительной подписи CMS в Bouncy Castle с использованием надежной временной метки. Создание подписи работает хорошо (я хочу включить подпись в файл PDF), подпись действительна. Но после того, как я включил доверенную временную метку в таблицу атрибутов без знака подписи, подпись все еще остается действительной, но Reader сообщает, что подпись включает встроенную временную метку, но она недействительна. Это заставляет меня поверить, что временная метка хэша I не является правильной, но я не могу понять, в чем ее проблема.

Код подписи:

Store store = new JcaCertStore(Arrays.asList(certContainer.getChain()));

CMSSignedDataGenerator signedDataGenerator = new CMSSignedDataGenerator();
JcaSignerInfoGeneratorBuilder infoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build());
JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder("SHA1withRSA");
signedDataGenerator.addSignerInfoGenerator(
                       infoGeneratorBuilder.build(contentSignerBuilder.build(certContainer.getPrivateKey()), (X509Certificate)certContainer.getSignatureCertificate()));
signedDataGenerator.addCertificates(store);
CMSTypedData cmsData = new CMSProcessableByteArray(data);
signedData = signedDataGenerator.generate(cmsData, false);
Collection<SignerInformation> ss = signedData.getSignerInfos().getSigners();
SignerInformation si = ss.iterator().next(); // get first signer (should be only one)
ASN1EncodableVector timestampVector = new ASN1EncodableVector();
Attribute token = createTSToken(si.getSignature());
timestampVector.add(token);
AttributeTable at = new AttributeTable(timestampVector);
si = SignerInformation.replaceUnsignedAttributes(si, at);
ss.clear();
ss.add(si);
SignerInformationStore newSignerStore = new SignerInformationStore(ss);
CMSSignedData newSignedData = CMSSignedData.replaceSigners(signedData, newSignerStore);

createTSToken код:

public Attribute createTSToken(byte[] data) throws NoSuchProviderException, NoSuchAlgorithmException, IOException {
    // Generate timestamp
    MessageDigest digest = MessageDigest.getInstance("SHA1", "BC");
    TimeStampResponse response = timestampData(digest.digest(data));
    TimeStampToken timestampToken = response.getTimeStampToken();
    // Create timestamp attribute

    Attribute a = new Attribute(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken, new DERSet(ASN1Primitive.fromByteArray(timestampToken.getEncoded())));
    return a;
}

timestampData:

TimeStampRequestGenerator reqgen = new TimeStampRequestGenerator();
TimeStampRequest req = reqgen.generate(TSPAlgorithms.SHA1, data);
byte request[] = req.getEncoded();

URL url = new URL("http://time.certum.pl");
HttpURLConnection con = (HttpURLConnection) url.openConnection();

con.setDoOutput(true);
con.setDoInput(true);
con.setRequestMethod("POST");
con.setRequestProperty("Content-type", "application/timestamp-query");
con.setRequestProperty("Content-length", String.valueOf(request.length));

OutputStream out = con.getOutputStream();
out.write(request);
out.flush();

if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
    throw new IOException("Received HTTP error: " + con.getResponseCode() + " - " +                 con.getResponseMessage());
}
InputStream in = con.getInputStream();
TimeStampResp resp = TimeStampResp.getInstance(new ASN1InputStream(in).readObject());
response = new TimeStampResponse(resp);
response.validate(req);
if(response.getStatus() != 0) {
    System.out.println(response.getStatusString());
    return null;
}
return response;

Спасибо за вашу помощь!

Файлы примеров:

Подписанный PDF

Неподписанный PDF

Подписанный PDF с iText

Подписанный PDF с LTV - отредактированный

1 ответ

Решение

signature_lipsum.pdf, первая версия

Метка времени ссылается как подписавшая какая-то

CN=e-Szigno Test TSA2,OU=e-Szigno CA,O=Microsec Ltd.,L= Будапешт,C=HU

который был выпущен

CN=Microsec e-Szigno Test Root CA 2008,OU=e-Szigno CA,O=Microsec Ltd.,L= Будапешт,C=HU

с серийным номером 7.

Однако он не предоставляет сам этот сертификат и не предоставляется ни контейнером CMS с инкапсулирующей подписью, ни в некотором разделе PDF-документа с информацией, связанной с проверкой.

Таким образом, по крайней мере, на моем компьютере нет никакой возможности проверить маркер метки времени, и Adobe Reader совершенно прав, чтобы не принимать метку времени.

Предоставили ли вы соответствующий сертификат на вашем компьютере способом, подходящим для вашего Adobe Reader? Если у вас есть, и он все еще не работает, пожалуйста, предоставьте его для дальнейших испытаний. Если нет, попробуйте найти и предоставить их.

Возможно, вы захотите усилить сам маркер метки времени, чтобы включить этот сертификат, прежде чем включать его в подпись.

signature_lipsum.pdf, вторая версия

В обновленном файле signature_lipsum.pdf отметка времени подписи содержит сертификат TSA, но это неверный сертификат!

Как и в первой версии, отметка времени ссылается на сертификат подписывающего

  • Испытуемый CN=e-Szigno Test TSA2,OU=e-Szigno CA,O=Microsec Ltd.,L= Будапешт,C=HU
  • CN эмитента = Microsec e-Szigno Test Root CA 2008, OU = e-Szigno CA, O = Microsec Ltd., L = Будапешт,C=HU
  • Серийный номер 7.

Содержащийся сертификат, с другой стороны, имеет

  • Испытуемый CN=e-Szigno Test TSA2,OU=e-Szigno CA,O=Microsec Ltd.,L= Будапешт,C=HU
  • CN эмитента = Microsec e-Szigno Test Root CA 2008, OU = e-Szigno CA, O = Microsec Ltd., L = Будапешт,C=HU
  • Серийный номер 5.

Я предполагаю, что тестовый TSA использует несколько подписывающих устройств / программных токенов с индивидуальными сертификатами, а OP содержит неправильный сертификат.

Поэтому вы можете вместо этого включить правильный сертификат.

Кстати, метка времени в PDF, подписанная iText, содержит сертификат, соответствующий ссылкам в марке...

В запросах метки времени RFC 3161 TSA может автоматически включить сертификат подписавшего. Надувной замок позволяет установить этот флаг следующим образом:

TimeStampRequestGenerator reqgen = new TimeStampRequestGenerator();
reqgen.setCertReq(true); // <<<<<<<<<<<<<<<<<<<<<<<<<<
TimeStampRequest req = reqgen.generate(TSPAlgorithms.SHA1, data);

Вместо того, чтобы включать сертификат самостоятельно, вы можете попробовать это.

LTV включен

Из комментариев:

Просто из любопытства, что еще нужно добавить, чтобы включить PDF LTV?

Цитируя Леонарда Розентхола (PDF-гуру в Adobe):

LTV включен означает, что вся информация, необходимая для проверки файла (за исключением корневых сертификатов), содержится внутри. Так что это утверждение [...] было бы правдой.

PDF подписан правильно и содержит все необходимые сертификаты, действительный ответ CRL или OSCP для каждого сертификата

( 10 января 2013 г.; 7:07 вечера; Леонард Розентол на главном форуме)

Другие вопросы по тегам