public static bool TryDecode( ReadOnlyMemory <byte> source, out Rfc3161TimestampTokenInfo timestampTokenInfo, out int bytesConsumed) { if (TryDecode(source, false, out Rfc3161TstInfo tstInfo, out bytesConsumed, out byte[] copiedBytes)) { timestampTokenInfo = new Rfc3161TimestampTokenInfo(copiedBytes, tstInfo); return(true); } bytesConsumed = 0; timestampTokenInfo = null; return(false); }
/// <param name="encodedBytes" /> /// <param name="timestampTokenInfo" /> /// <param name="bytesConsumed" /> public static bool TryDecode(ReadOnlyMemory <byte> encodedBytes, out Rfc3161TimestampTokenInfo timestampTokenInfo, out int bytesConsumed) { throw new PlatformNotSupportedException(); }
private Rfc3161RequestResponseStatus ValidateResponse( Rfc3161TimestampToken token, bool shouldThrow) { Debug.Assert(token != null); // This method validates the acceptance criteria sprinkled throughout the // field descriptions in https://tools.ietf.org/html/rfc3161#section-2.4.1 and // https://tools.ietf.org/html/rfc3161#section-2.4.2 if (!token.VerifyHash(GetMessageHash().Span, HashAlgorithmId.Value)) { if (shouldThrow) { throw new CryptographicException(SR.Cryptography_BadHashValue); } return(Rfc3161RequestResponseStatus.HashMismatch); } Rfc3161TimestampTokenInfo tokenInfo = token.TokenInfo; // We only understand V1 messaging and validation if (tokenInfo.Version != 1) { if (shouldThrow) { throw new CryptographicException(SR.Cryptography_TimestampReq_BadResponse); } return(Rfc3161RequestResponseStatus.VersionTooNew); } // reqPolicy is what the policy SHOULD be, so we can't reject it here. ReadOnlyMemory <byte>?requestNonce = GetNonce(); ReadOnlyMemory <byte>?responseNonce = tokenInfo.GetNonce(); // The RFC says that if a nonce was in the request it MUST be present in // the response and it MUST be equal. // // It does not say that if no nonce was requested that the response MUST NOT include one, so // don't check anything if no nonce was requested. if (requestNonce != null) { if (responseNonce == null || !requestNonce.Value.Span.SequenceEqual(responseNonce.Value.Span)) { if (shouldThrow) { throw new CryptographicException(SR.Cryptography_TimestampReq_BadNonce); } return(Rfc3161RequestResponseStatus.NonceMismatch); } } SignedCms tokenCms = token.AsSignedCms(); if (RequestSignerCertificate) { // If the certificate was requested it // A) MUST be present in token.AsSignedCms().Certificates // B) the ESSCertID(2) identifier MUST be correct. // // Other certificates are permitted, and will not be validated. if (tokenCms.SignerInfos[0].Certificate == null) { if (shouldThrow) { throw new CryptographicException(SR.Cryptography_TimestampReq_NoCertFound); } return(Rfc3161RequestResponseStatus.RequestedCertificatesMissing); } } else { // If no certificate was requested then the CMS Certificates collection // MUST be empty. if (tokenCms.Certificates.Count != 0) { if (shouldThrow) { throw new CryptographicException(SR.Cryptography_TimestampReq_UnexpectedCertFound); } return(Rfc3161RequestResponseStatus.UnexpectedCertificates); } } return(Rfc3161RequestResponseStatus.Accepted); }
public static bool TryDecode(ReadOnlyMemory <byte> source, out Rfc3161TimestampToken token, out int bytesConsumed) { bytesConsumed = 0; token = null; try { ContentInfoAsn contentInfo = AsnSerializer.Deserialize <ContentInfoAsn>(source, AsnEncodingRules.BER, out int bytesActuallyRead); // https://tools.ietf.org/html/rfc3161#section-2.4.2 // // A TimeStampToken is as follows. It is defined as a ContentInfo // ([CMS]) and SHALL encapsulate a signed data content type. // // TimeStampToken::= ContentInfo // --contentType is id-signedData([CMS]) // --content is SignedData ([CMS]) if (contentInfo.ContentType != Oids.Pkcs7Signed) { return(false); } SignedCms cms = new SignedCms(); cms.Decode(source); // The fields of type EncapsulatedContentInfo of the SignedData // construct have the following meanings: // // eContentType is an object identifier that uniquely specifies the // content type. For a time-stamp token it is defined as: // // id-ct-TSTInfo OBJECT IDENTIFIER ::= { iso(1) member-body(2) // us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) ct(1) 4} // // eContent is the content itself, carried as an octet string. // The eContent SHALL be the DER-encoded value of TSTInfo. if (cms.ContentInfo.ContentType.Value != Oids.TstInfo) { return(false); } // RFC3161: // The time-stamp token MUST NOT contain any signatures other than the // signature of the TSA. The certificate identifier (ESSCertID) of the // TSA certificate MUST be included as a signerInfo attribute inside a // SigningCertificate attribute. // RFC5816 says that ESSCertIDv2 should be allowed instead. SignerInfoCollection signerInfos = cms.SignerInfos; if (signerInfos.Count != 1) { return(false); } SignerInfo signer = signerInfos[0]; EssCertId certId; EssCertIdV2 certId2; if (!TryGetCertIds(signer, out certId, out certId2)) { return(false); } X509Certificate2 signerCert = signer.Certificate; if (signerCert == null && signer.SignerIdentifier.Type == SubjectIdentifierType.IssuerAndSerialNumber) { // If the cert wasn't provided, but the identifier was IssuerAndSerialNumber, // and the ESSCertId(V2) has specified an issuerSerial value, ensure it's a match. X509IssuerSerial issuerSerial = (X509IssuerSerial)signer.SignerIdentifier.Value; if (certId?.IssuerSerial != null) { if (!IssuerAndSerialMatch( certId.IssuerSerial.Value, issuerSerial.IssuerName, issuerSerial.SerialNumber)) { return(false); } } if (certId2?.IssuerSerial != null) { if (!IssuerAndSerialMatch( certId2.IssuerSerial.Value, issuerSerial.IssuerName, issuerSerial.SerialNumber)) { return(false); } } } Rfc3161TimestampTokenInfo tokenInfo; if (Rfc3161TimestampTokenInfo.TryDecode(cms.ContentInfo.Content, out tokenInfo, out _)) { if (signerCert != null && !CheckCertificate(signerCert, signer, certId, certId2, tokenInfo)) { return(false); } token = new Rfc3161TimestampToken { _parsedDocument = cms, _signerInfo = signer, _essCertId = certId, _essCertIdV2 = certId2, TokenInfo = tokenInfo, }; bytesConsumed = bytesActuallyRead; return(true); } } catch (CryptographicException) { } return(false); }
private static bool CheckCertificate( X509Certificate2 tsaCertificate, SignerInfo signer, EssCertId certId, EssCertIdV2 certId2, Rfc3161TimestampTokenInfo tokenInfo) { Debug.Assert(tsaCertificate != null); Debug.Assert(signer != null); Debug.Assert(tokenInfo != null); // certId and certId2 are allowed to be null, they get checked in CertMatchesIds. if (!CertMatchesIds(tsaCertificate, certId, certId2)) { return(false); } // Nothing in RFC3161 actually mentions checking the certificate's validity // against the TSTInfo timestamp value, but it seems sensible. // // Accuracy is ignored here, for better replicability in user code. if (tsaCertificate.NotAfter < tokenInfo.Timestamp || tsaCertificate.NotBefore > tokenInfo.Timestamp) { return(false); } // https://tools.ietf.org/html/rfc3161#section-2.3 // // The TSA MUST sign each time-stamp message with a key reserved // specifically for that purpose. A TSA MAY have distinct private keys, // e.g., to accommodate different policies, different algorithms, // different private key sizes or to increase the performance. The // corresponding certificate MUST contain only one instance of the // extended key usage field extension as defined in [RFC2459] Section // 4.2.1.13 with KeyPurposeID having value: // // id-kp-timeStamping. This extension MUST be critical. using (var ekuExts = tsaCertificate.Extensions.OfType <X509EnhancedKeyUsageExtension>().GetEnumerator()) { if (!ekuExts.MoveNext()) { return(false); } X509EnhancedKeyUsageExtension ekuExt = ekuExts.Current; if (!ekuExt.Critical) { return(false); } bool hasPurpose = false; foreach (Oid oid in ekuExt.EnhancedKeyUsages) { if (oid.Value == Oids.TimeStampingPurpose) { hasPurpose = true; break; } } if (!hasPurpose) { return(false); } if (ekuExts.MoveNext()) { return(false); } } try { signer.CheckSignature(new X509Certificate2Collection(tsaCertificate), true); return(true); } catch (CryptographicException) { return(false); } }
public static bool TryDecode(ReadOnlyMemory <byte> encodedBytes, out Rfc3161TimestampTokenInfo timestampTokenInfo, out int bytesConsumed) { throw null; }