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();
 }
Пример #3
0
        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);
        }
Пример #4
0
        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);
        }
Пример #5
0
        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;
 }