I've just sign document. and Add LTV too (with Document Secure Story and TSA); but adobe reader tells me that LTV is not enabled.
I found the problem. Adobe reader tells me that OCSP is not embedded in the document.
After adding time stamp I just create DSS dictionary and add certificates and ocsp responses.
PDDocumentCatalog catalog = template.getDocumentCatalog();
COSDictionary catalogDictionary = catalog.getCOSDictionary();
COSDictionary dssDictionary = new COSDictionary();
COSArray cosOcsps = CertUtils.getOcspResponseCosArray(ocspResp);
COSArray cosCerts = CertUtils.getCertificateCosArray(certs);
dssDictionary.setItem(COSName.getPDFName("Certs"), cosCerts);
dssDictionary.setItem(COSName.getPDFName("OCSPs"), cosOcsps);
catalogDictionary.setItem(COSName.getPDFName("DSS"), dssDictionary);
is not that enough to add OCSPs?
I sign document like Pades-BES. does it needs VRI? I know that id does not need.
that's sample
PDF Document
The specification ETSI TS 102 778-4 (aka PAdES part 4) in Annex A.1 Document Security Store requires the value of the OCSPs entry in a DSS dictionary to be
An array of (indirect references to) streams, each containing a BER-encoded Online Certificate Status Protocol (OCSP) response (see RFC 2560 [8]). This array contains OCSPs that may be used in the validation of any signatures in the document.
You, on the other hand, only used an array of the BasicOCSPResponse objects which were contained in the original OCSPResponse objects you received.
OCSPResponse ::= SEQUENCE {
responseStatus OCSPResponseStatus,
responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
ResponseBytes ::= SEQUENCE {
responseType OBJECT IDENTIFIER,
response OCTET STRING }
For a basic OCSP responder, responseType will be id-pkix-ocsp-basic.
The value for response SHALL be the DER encoding of BasicOCSPResponse.
BasicOCSPResponse ::= SEQUENCE {
tbsResponseData ResponseData,
signatureAlgorithm AlgorithmIdentifier,
signature BIT STRING,
certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
(from section 4.2.1 ASN.1 Specification of the OCSP Response of RFC 2560)
Probably you were not aware that you only used this inner object because many security libraries after requesting an OCSP response unwrap the original OCSPResponse, check the contained OCSPResponseStatus, and (if it indicates success) only return the contained BasicOCSPResponse or (otherwise) throw some exception.
If that's the case, you can simply wrap your BasicOCSPResponse in an OCSPResponse using the OCSPResponseStatus value successful (0) before putting it into the document.
Related
I am trying to sign a PDF with BouncyCastle CMS. Signing works, but Adobe Reader tells me it is not LTV-ready.
As far as I can see, the CRLs are embedded in the CMS SignedData. The certficates are embedded too. A timestamp also is embedded.
The signature is a detached signature and put in the "reserved space".
Why is the signature still not LTV-ready? Am I doing something obvious wrong?
Signed Test-PDF: http://www.filedropper.com/outputx
Code:
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
// CertificateChain
List<Certificate> certList = Arrays.asList(certChain);
try {
Hashtable signedAttrs = new Hashtable();
X509Certificate signingCert = (X509Certificate) certList.get(0);
gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC")
.setSignedAttributeGenerator(new AttributeTable(signedAttrs))
.build("SHA256withRSA", privKey, signingCert));
gen.addCertificates(new JcaCertStore(certList));
boolean embedCrls = true;
if (embedCrls) {
X509CRL[] crls = fetchCRLs(signingCert);
for (X509CRL crl : crls) {
gen.addCRL(new JcaX509CRLHolder(crl));
}
}
// gen.addOtherRevocationInfo(arg0, arg1);
CMSProcessableByteArray processable = new CMSProcessableByteArray(IOUtils.toByteArray(content));
CMSSignedData signedData = gen.generate(processable, false);
if (tsaClient != null) {
signedData = signTimeStamps(signedData);
}
return signedData.getEncoded();
} catch (Exception e) {
throw new RuntimeException(e);
}
In interoperable PDF signatures there are two ways to add validation related information, and neither of them uses the default CMS way of adding CRLs that your code uses.
Adding Pre-Fetched Data In Signed Attributes
The old, ISO 32000-1 style "PKCS#7 Signatures as used in ISO 32000" (later based on CMS instead of PKCS#7) are specified to include revocation information in a dedicated signed attribute for an OID registered by Adobe:
12.8.3.3.2 Revocation Information
The adbe Revocation Information attribute:
adbe-revocationInfoArchival OBJECT IDENTIFIER ::=
{ adbe(1.2.840.113583) acrobat(1) security(1) 8 }
The value of the revocation information attribute can include any of the following data types:
Certificate Revocation Lists (CRLs), described in RFC 3280 (see the Bibliography): CRLs are generally large and therefore should not be embedded in the PKCS#7 object.
Online Certificate Status Protocol (OCSP) Responses, described in RFC 2560, X.509 Internet Public Key Infrastructure Online Certificate Status Protocol—OCSP (see the Bibliography): These are generally small and constant in size and should be the data type included in the PKCS#7 object.
Custom revocation information: The format is not prescribed by this specification, other than that it be encoded as an OCTET STRING. The application should be able to determine the type of data contained within the OCTET STRING by looking at the associated OBJECT IDENTIFIER.
adbe's Revocation Information attribute value has ASN.1 type RevocationInfoArchival:
RevocationInfoArchival ::= SEQUENCE {
crl [0] EXPLICIT SEQUENCE of CRLs, OPTIONAL
ocsp [1] EXPLICIT SEQUENCE of OCSP Responses, OPTIONAL
otherRevInfo [2] EXPLICIT SEQUENCE of OtherRevInfo, OPTIONAL
}
OtherRevInfo ::= SEQUENCE {
Type OBJECT IDENTIFIER
Value OCTET STRING
}
(Obviously this is merely Pseudo-ASN.1...)
As this structure is the value of a signed attribute, revocation information must be fetched before signing to be added like this.
Adding Data In Document Security Stores (DSS)
For their PAdES signatures ETSI specified extra PDF structures to be added to signed PDF documents which can carry extra validation related information, see ETSI EN 319 142. These structures later were integrated in ISO 32000-2:
12.8.4.3 Document Security Store (DSS)
The document security store (DSS), when present, shall be a dictionary that shall be the value of a DSS key in the document catalog dictionary (see 7.7.2, "Document catalog dictionary"). This dictionary may contain:
an array of all certificates used for the signatures, including timestamp signatures, that occur in the document. It shall also hold all the auxiliary certificates required to validate the certificates participating in certificate chain validations.
an array of all Certificate Revocation Lists (CRL) (see Internet RFC 5280) used for some of the signatures, and
an array of all Certificate Status Protocol (OCSP) responses (see Internet RFC 6960) used for some of the signatures.
a VRI key whose value shall be a dictionary containing one VRI dictionary (validation-related information) for each signature represented in CMS format.
Any VRI dictionaries, if present, shall be located in document incremental update sections. If the signature dictionary to which a VRI dictionary applies is itself in an incremental update section, the DSS/VRI update shall be done later than the signature update. The inclusion of VRI dictionary entries is optional. All validation material referenced in VRI entries is included in DSS entries too.
...
As you can read already here, this structure is designed for adding information after signing. This goes along with the ETSI favoring revocation information generated after the signature for validation.
And In PDFBox
You mention PDFBox in your tags, so you appear to use PDFBox for signing.
To add pre-fetched validation data to the signed adbe-revocationInfoArchival attribute, you merely have to add an attribute as defines above to the signedAttrs of your code.
To add data in document security stores (DSS) you can use the code of the PDFBox example AddValidationInformation as mentioned by #Tilman in a comment.
I am using iTextSharp 5.5.13 in order to sign and validate PDF files. Few days ago I ended up with exception in OcspVerifier.Verify which stated "certificate expired on 20181225...GMT+00:00"
All PDF files are signed with embeded OCSP response and CLR. Validation was done using signing date and it used to work fine until the ocsp signers certificate expired.
Despite the fact that all certificates were valid at the time of signing, the OcspVerifier.Verify started to throw exception which states "cert expired...".
if (pkcs7.Ocsp != null)
ocsps.Add(pkcs7.Ocsp);
PdfOcspVerifier ocspVerifier = new PdfOcspVerifier(null, ocsps);
ocspVerifier.OnlineCheckingAllowed = false;
List<VerificationOK> verification = ocspVerifier.Verify(signCert, issuerCert, signDate.ToUniversalTime());
EXCEPTION: "certificate expired on 20181225...GMT+00:00"
It looks like bug to me? Is there any reason why OCSP signer certificate is not verified against signing date?
Class OscpVerifier - original, with line which validate cert against current date:
virtual public void IsValidResponse(BasicOcspResp ocspResp, X509Certificate issuerCert)
{
....
//check if lifetime of certificate is ok
responderCert.CheckValidity();
}
Modified version of OscpVerifier.cs:
// old definition with old functionality
virtual public void IsValidResponse(BasicOcspResp ocspResp, X509Certificate issuerCert)
{
IsValidResponse(ocspResp, issuerCert, DateTime.UtcNow);
}
// with signDate parameter:
virtual public void IsValidResponse(BasicOcspResp ocspResp, X509Certificate issuerCert, DateTime signDate)
{
...
//check if lifetime of certificate is ok
responderCert.CheckValidity(signDate);
//responderCert.CheckValidity();
}
with corresponding method call change fom:
virtual public bool Verify(BasicOcspResp ocspResp, X509Certificate signCert, X509Certificate issuerCert, DateTime signDate)
{
...
... {
// check if the OCSP response was genuine
IsValidResponse(ocspResp, issuerCert);
return true;
}
...
}
to:
virtual public bool Verify(BasicOcspResp ocspResp, X509Certificate signCert, X509Certificate issuerCert, DateTime signDate)
{
...
... {
// check if the OCSP response was genuine
IsValidResponse(ocspResp, issuerCert, signDate);
return true;
}
...
}
I included this variation of the OscpVerifier class directly in the project and it validates now old signatures as expected.
However, I am not sure if I hit the bug or there is reason why those signatures should be considered as not valid?
OCSP signer certificate must be validated at current time except if the OCSP response has been protected with a time stamp, so itext is working properly.
Summarizing, an OCSP response contains (See RFC6960 ):
target certificate and validity interfal
time when the response was generated
A digital signature of a CA trusted responder, and its certificate
The criteria for accepting an OCSP response is established in the RFC, but it does not clarify your question, because it does not establish how to validate the response over time.
3.2. Signed Response Acceptance Requirements
Prior to accepting a signed response for a particular certificate as
valid, OCSP clients SHALL confirm that:
The certificate identified in a received response corresponds to the certificate that was identified in the corresponding request;
The signature on the response is valid;
The identity of the signer matches the intended recipient of the request;
The signer is currently authorized to provide a response for the certificate in question;
The time at which the status being indicated is known to be correct (thisUpdate) is sufficiently recent;
When available, the time at or before which newer information will be available about the status of the certificate (nextUpdate) is greater than the current time.
However, it does indicate that the digital signature must be valid (2). In general, for a signature to be considered valid, it must comply with:
Cryptographic integrity
Validity of the certificate (expiration and revocation)
An OCSP response with an expired certificate will not meet the second criterion and therefore should be rejected. To extend the period in which a signature is valid, a time stamp must be added on the content. The PAdES standard specifies how
There is also an ETSI guide with the details on how to perform the verification of a PAdES and CAdES signatures, but unfortunately I have not found the link now
I sign document with PADES LTV Profile. Signer library is written base on Pdfbox library.
I have one problem.
In PADES LTV profile, the final revision must be checked in online (It means that OCSP responses, CRLS and certificates of this revision must not be in document secure store (DSS)).
For testing, I add 12 revision to my document.
Every time when I add new revision, I do not add certificates and ocsp responses of this current revision to the DSS. I add previous revisions certificates and ocsp responses to the DSS. I do this because last revision must be checked in online. So I must not add OCSP response of last revision to the DSS. I do so, but sometimes Adobe reader thinks that last revision have embedded OCSP response in the document.
The problem might be is that following:
Each ocsp response must be unique, even if when we generate them with the same certificate. In the other words, If we request ocsp object twice, they must be unique.
For this I do that following but it does not work:
private OCSPReq buildOcspRequest(X509Certificate issuerCert, BigInteger serialNumber, File inputDocument) {
InputStream is = new FileInputStream(inputDocument);
X509CertificateHolder certificateHolder;
certificateHolder = new X509CertificateHolder(issuerCert.getEncoded());
CertificateID id = new CertificateID(new BcDigestCalculatorProvider().get(CertificateID.HASH_SHA1), certificateHolder, serialNumber);
OCSPReqBuilder ocspReqBuilder = new OCSPReqBuilder();
ocspReqBuilder.addRequest(id);
DigestCalculatorProvider digestCalculatorProvider;
JcaDigestCalculatorProviderBuilder digestCalcProvBuilder = new JcaDigestCalculatorProviderBuilder().setProvider("BC");
digestCalculatorProvider = digestCalcProvBuilder.build();
// get SHA 256
DigestCalculator digest = digestCalculatorProvider.get(AlgorithmIdentifier.getInstance("2.16.840.1.101.3.4.2.1"));
OutputStream os = digest.getOutputStream();
IOUtils.copy(is, os);
byte[] messageImprint = digest.getDigest();
BigInteger time = BigInteger.valueOf(System.currentTimeMillis());
// time + imprint
byte[] nonce = ArrayUtils.addAll(time.toByteArray(), messageImprint);
Extension extension = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, true, nonce);
ocspReqBuilder.setRequestExtensions(new Extensions(extension));
return ocspReqBuilder.build();
}
this is testing pdf link SAMPLE PDF
I might create 4 revision and everything might be all right ... I don't know , sometimes it happens... When I was testing, the problem occours when I create many revisions... THE LAST REVISION THINKS THAT OCSP RESPONSE OF PREVIOUS REVISIONS IS IT'S OWN!
In PADES LTV profile, the final revision must be checked in online
No.
PAdES, i.e. ETSI TS 102 778, in part 4 (PAdES-LTV Profile) merely recommends this:
4.3 Validation Process
It is recommended that that validation process be as follows:
1) The "latest" document Time-stamp should be validated at current time with validation data collected at the current time.
The word should indicates a recommendation, and the sentence before actually spells it out.
But let's assume you want to follow the recommendation. Your interpretation
(It means that OCSP responses, CRLS and certificates of this revision must not be in document secure store (DSS)).
is not correct still: There may be validation related information (OCSP responses, CRLs, certificates) in the document which (seen as isolated information) apply to the outer time stamp or signature. If you time stamp your document twice in short succession using the same time stamp service and add a CRL for certificates used in the first time stamp in the time between, chances are that the outer time stamp involves certificates covered by this CRL and is applied in the validity interval indicated by the CRL.
No, it is the responsibility of the verifier not to use these information if the verifier chose to follow the PAdES-LTV recommendation quoted above.
Adobe Reader does not claim to follow this recommendation (at least as far as I know). Thus, if you verify the signatures and time stamps using Adobe Reader, information contained in the document may be used to verify the outer time stamp, too.
Actually Adobe originally loved the concept of first retrieving validation related information and signing and time stamping everything including those information thereafter.
The problem might be is that following:
Each ocsp response must be unique, even if when we generate them with the same certificate. In the other words, If we request ocsp object twice, they must be unique.
No. While it is good style to use nonces, your observation has nothing to do with them.
The reason why Adobe Reader sometimes (and only sometimes) makes use of already embedded information for verifying outer signatures / time stamps is that OCSP responses and CRLs have validity intervals defined by their thisUpdate and nextUpdate fields which sometimes (if you add multiple time stamps by the same TSA) in spite of being embedded for the validation of an older time stamp still encompass the time of the newly added one (and, thus, are also used for their validation).
If you want to prevent this, you have to check the OCSP responses and CRLs already contained in the PDF or just now added to it by you, inspect those whose respective validity interval has not yet ended, and not apply any time stamp to the document whose certificates can be validated with them.
I'm struggling adding authenticated attributes (OCSP data) to my message using CryptoApi. I first used CryptoApi's simplified message functions, but now switch to the low-level message functions, thinking that I would be able to control the message structure better. But I am once again stuck. My process is as follows:
Initialize CMSG_SIGNER_ENCODE_INFO and CMSG_SIGNED_ENCODE_INFO structure
I create a CRYPT_ATTRIBUTE for the ocsp date and specifies it in the CMSG_SIGNER_ENCODE_INFO structure
I then call CryptMsgCalculateEncodedLength to get the size
CryptMsgOpenToEncode with CMSG_SIGNED as the message type
CryptMsgUpdate, to insert my content into the message
CryptMsgGetParam with CMSG_CONTENT_PARAM to get the encoded blob
CryptMsgClose, I'm done with the message for now.
I open the message again to get the CMSG_ENCRYPTED_DIGEST, which is sent to a TSA and the result is added as an unaunthenticated attribute using CryptMsgControl.
I'm using this to sign signature tags in Adobe. So when there is no authenticated attributes, I receive three green check from Adobe:
The document has not been modified...
The document is signed by the current user
The signature includes an embedded timestamp (and the timestamp is validate)
But as soon as the authenticated attribute is added the signer's identity is invalidated and the timestamp data in incorrect. The CMSG_COMPUTED_HASH_PARAM when authenticated attributes are added and when not, differs. Should this not be the same? Since the document digest is of the content of the document and not of the authenticated attribute.
Is there another way to add authenticated attributes? I've tried to add it as a signer using CryptMsgControl, but that did not help either...
how about this step on adding the authenticated attributes for signing, example time stamping,
CryptEncodeObject(PKCS_7_ASN_ENCODING, szOID_RSA_signingTime, &curtime, pTime, &szTime);
pTime = (BYTE *)LocalAlloc(GPTR, szTime);
CryptEncodeObject(PKCS_7_ASN_ENCODING, szOID_RSA_signingTime, &curtime, pTime, &szTime);
time_blob.cbData = szTime;
time_blob.pbData = pTime;
attrib[0].pszObjId = szOID_RSA_signingTime;
attrib[0].cValue = 1;
attrib[0].rgValue = &time_blob;
CosignerInfo.cAuthAttr = 1;
CosignerInfo.rgAuthAttr = attrib;
and that Cosigner params is from CMSG_SIGNER_ENCODE_INFO CosignerInfo;
I am signing some data with a self signed X509Certificate in c#. Signature results as a binary byte[]. I want to save this signature to a pkcs#7 format file with extenstion .p7b. When I save this using FileStream as required. It generates an invalid p7b file.
Can anyone help save this signature as a valid external file? Or point out if there is something wrong with the approach?
// EDIT: Adding the code as asked by Eugene Mayevski
// Open Store Location & fetch certificate
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
X509Certificate2Collection certifcates = store.Certificates;
X509Certificate2 certificate = certifcates[0];`
// fetch private key
string publicKeyString = certificate.GetPublicKeyString();
RSACryptoServiceProvider privateKey = certificate.PrivateKey as RSACryptoServiceProvider;
// get binary of data & sign it.
byte[] buffer = Encoding.Default.GetBytes("Sample data to sign. Although it would be a document.");
byte[] signature = privateKey.SignData(buffer, new SHA1Managed());
You are not generating a Cryptographic Message Syntax (CMS) message format which is defined in PKCS#7. You generate just the signature, which can be placed in the container format.
To create a CMS formatted message yourself, please take a look at the Microsoft documentation or use the C# version of Bouncy Castle.