예제 #1
0
        public static Rfc3161TimestampRequest CreateFromSignerInfo(
            SignerInfo signerInfo,
            HashAlgorithmName hashAlgorithm,
            Oid?requestedPolicyId              = null,
            ReadOnlyMemory <byte>?nonce        = null,
            bool requestSignerCertificates     = false,
            X509ExtensionCollection?extensions = null)
        {
            if (signerInfo == null)
            {
                throw new ArgumentNullException(nameof(signerInfo));
            }

            // https://tools.ietf.org/html/rfc3161, Appendix A.
            //
            // The value of messageImprint field within TimeStampToken shall be a
            // hash of the value of signature field within SignerInfo for the
            // signedData being time-stamped.
            return(CreateFromData(
                       signerInfo.GetSignature(),
                       hashAlgorithm,
                       requestedPolicyId,
                       nonce,
                       requestSignerCertificates,
                       extensions));
        }
예제 #2
0
        internal Pkcs12SecretBag(Oid secretTypeOid, ReadOnlyMemory <byte> secretValue)
            : this(EncodeBagValue(secretTypeOid, secretValue))
        {
            _secretTypeOid = new Oid(secretTypeOid);

            _decoded = SecretBagAsn.Decode(EncodedBagValue, AsnEncodingRules.BER);
        }
예제 #3
0
        internal ECKey(ISystemClock clock, ICKBinaryReader r)
            : base(clock, r)
        {
            r.ReadByte();
            Oid?curveName = HelperAndExtensions.ReadNullableOid(r);

            Debug.Assert(curveName != null);
            _parameters = new ECParameters()
            {
                Curve = ECCurve.CreateFromOid(curveName),
                D     = ReadBytes(r),
                Q     = new ECPoint()
                {
                    X = ReadBytes(r), Y = ReadBytes(r)
                }
            };
            if (r.ReadBoolean())
            {
                _ecdh = ECDiffieHellman.Create(_parameters);
            }
            else
            {
                _ec = ECDsa.Create(_parameters);
            }
            JWKCurveName = GetJWKCurveName(_parameters.Curve.Oid);
예제 #4
0
        public static Oid?CopyOid(this Oid?oid)
        {
#if NETCOREAPP
            return(oid);
#else
            return(oid is null ? null : new Oid(oid));
#endif
        }
예제 #5
0
        public Oid GetSecretType()
        {
            if (_secretTypeOid == null)
            {
                _secretTypeOid = new Oid(_decoded.SecretTypeId);
            }

            return(new Oid(_secretTypeOid));
        }
예제 #6
0
        public Oid GetBagId()
        {
            if (_bagOid == null)
            {
                _bagOid = new Oid(_bagIdValue);
            }

            return(new Oid(_bagOid));
        }
예제 #7
0
        public Oid GetCertificateType()
        {
            if (_certTypeOid == null)
            {
                _certTypeOid = new Oid(_decoded.CertId);
            }

            return(_certTypeOid.CopyOid());
        }
예제 #8
0
 public static Oid?CopyOid(this Oid?oid)
 {
     if (s_oidIsInitOnceOnly)
     {
         return(oid);
     }
     else
     {
         return(oid is null ? null : new Oid(oid));
     }
 }
예제 #9
0
        /// <summary>
        /// Create a CertBag for a specified certificate type and encoding.
        /// </summary>
        /// <param name="certificateType">The identifier for the certificate type</param>
        /// <param name="encodedCertificate">The encoded value</param>
        /// <remarks>
        /// No validation is done to ensure that the <paramref name="encodedCertificate"/> value is
        /// correct for the indicated <paramref name="certificateType"/>.  Note that for X.509
        /// public-key certificates the correct encoding for a CertBag value is to wrap the
        /// DER-encoded certificate in an OCTET STRING.
        /// </remarks>
        public Pkcs12CertBag(Oid certificateType, ReadOnlyMemory <byte> encodedCertificate)
            : base(
                Oids.Pkcs12CertBag,
                EncodeBagValue(certificateType, encodedCertificate),
                skipCopy: true)
        {
            _certTypeOid = certificateType.CopyOid();

            _decoded = CertBagAsn.Decode(EncodedBagValue, AsnEncodingRules.BER);

            IsX509Certificate = _decoded.CertId == Oids.Pkcs12X509CertBagType;
        }
예제 #10
0
 internal AsnEncodedData(Oid?oid, byte[] rawData, bool skipCopy)
 {
     if (skipCopy)
     {
         ArgumentNullException.ThrowIfNull(rawData);
         Oid      = oid;
         _rawData = rawData;
     }
     else
     {
         Reset(oid, rawData);
     }
 }
예제 #11
0
        public override void Reset()
        {
            _lazyRawData            = null;
            _lazySignatureAlgorithm = null;
            _lazyVersion            = 0;
            _lazySubjectName        = null;
            _lazyIssuerName         = null;
            _lazyPublicKey          = null;
            _lazyPrivateKey         = null;
            _lazyExtensions         = null;

            base.Reset();
        }
예제 #12
0
        internal static Oid GetSharedOrNewOid(ref AsnValueReader asnValueReader)
        {
            Oid?ret = GetSharedOrNullOid(ref asnValueReader);

            if (ret is not null)
            {
                return(ret);
            }

            string oidValue = asnValueReader.ReadObjectIdentifier();

            return(new Oid(oidValue, null));
        }
예제 #13
0
        protected override string?FormatNative(Oid?oid, byte[] rawData, bool multiLine)
        {
            if (oid == null || string.IsNullOrEmpty(oid.Value))
            {
                return(EncodeSpaceSeparatedHexString(rawData));
            }

            switch (oid.Value)
            {
            case "2.5.29.17":
                return(FormatSubjectAlternativeName(rawData));
            }

            return(null);
        }
예제 #14
0
        protected override string?FormatNative(Oid?oid, byte[] rawData, bool multiLine)
        {
            // If OID is not present, then we can force CryptFormatObject
            // to use hex formatting by providing an empty OID string.
            string oidValue = string.Empty;

            if (oid != null && oid.Value != null)
            {
                oidValue = oid.Value;
            }

            int       dwFormatStrType   = multiLine ? Interop.Crypt32.CRYPT_FORMAT_STR_MULTI_LINE : Interop.Crypt32.CRYPT_FORMAT_STR_NONE;
            int       cbFormat          = 0;
            const int X509_ASN_ENCODING = 0x00000001;

            unsafe
            {
                IntPtr oidValuePtr = Marshal.StringToHGlobalAnsi(oidValue);
                char[]? pooledarray = null;
                try
                {
                    if (Interop.Crypt32.CryptFormatObject(X509_ASN_ENCODING, 0, dwFormatStrType, IntPtr.Zero, (byte *)oidValuePtr, rawData, rawData.Length, null, ref cbFormat))
                    {
                        int         charLength = (cbFormat + 1) / 2;
                        Span <char> buffer     = charLength <= 256 ?
                                                 stackalloc char[256] :
                                                 (pooledarray = ArrayPool <char> .Shared.Rent(charLength));
                        fixed(char *bufferPtr = buffer)
                        {
                            if (Interop.Crypt32.CryptFormatObject(X509_ASN_ENCODING, 0, dwFormatStrType, IntPtr.Zero, (byte *)oidValuePtr, rawData, rawData.Length, bufferPtr, ref cbFormat))
                            {
                                return(new string(bufferPtr));
                            }
                        }
                    }
                }
                finally
                {
                    Marshal.FreeHGlobal(oidValuePtr);
                    if (pooledarray != null)
                    {
                        ArrayPool <char> .Shared.Return(pooledarray);
                    }
                }
            }

            return(null);
        }
예제 #15
0
        public static Rfc3161TimestampRequest CreateFromHash(
            ReadOnlyMemory <byte> hash,
            HashAlgorithmName hashAlgorithm,
            Oid?requestedPolicyId              = null,
            ReadOnlyMemory <byte>?nonce        = null,
            bool requestSignerCertificates     = false,
            X509ExtensionCollection?extensions = null)
        {
            string oidStr = PkcsHelpers.GetOidFromHashAlgorithm(hashAlgorithm);

            return(CreateFromHash(
                       hash,
                       new Oid(oidStr, oidStr),
                       requestedPolicyId,
                       nonce,
                       requestSignerCertificates,
                       extensions));
        }
예제 #16
0
        private static ECCurve CreateFromValueAndName(string?oidValue, string?oidFriendlyName)
        {
            Oid?oid = null;

            if (oidValue == null && oidFriendlyName != null)
            {
                try
                {
                    oid = Oid.FromFriendlyName(oidFriendlyName, OidGroup.PublicKeyAlgorithm);
                }
                catch (CryptographicException)
                {
                }
            }

            oid ??= new Oid(oidValue, oidFriendlyName);
            return(ECCurve.Create(oid));
        }
예제 #17
0
        internal X500RelativeDistinguishedName(ReadOnlyMemory <byte> rawData)
        {
            RawData = rawData;

            ReadOnlySpan <byte> rawDataSpan = rawData.Span;
            AsnValueReader      outer       = new AsnValueReader(rawDataSpan, AsnEncodingRules.DER);

            // Windows does not enforce the sort order on multi-value RDNs.
            AsnValueReader rdn          = outer.ReadSetOf(skipSortOrderValidation: true);
            AsnValueReader typeAndValue = rdn.ReadSequence();

            Oid firstType = Oids.GetSharedOrNewOid(ref typeAndValue);
            ReadOnlySpan <byte> firstValue = typeAndValue.ReadEncodedValue();

            typeAndValue.ThrowIfNotEmpty();

            if (rdn.HasData)
            {
                do
                {
                    typeAndValue = rdn.ReadSequence();

                    // Check that the attribute type is a valid OID,
                    // if it's from the cache, even better (faster, lower alloc).
                    if (Oids.GetSharedOrNullOid(ref typeAndValue) is null)
                    {
                        typeAndValue.ReadObjectIdentifier();
                    }

                    typeAndValue.ReadEncodedValue();
                    typeAndValue.ThrowIfNotEmpty();
                }while (rdn.HasData);
            }
            else
            {
                _singleElementType = firstType;

                bool overlaps = rawDataSpan.Overlaps(firstValue, out int offset);
                Debug.Assert(overlaps, "AsnValueReader.ReadEncodedValue returns a slice of the source");
                Debug.Assert(offset > 0);

                _singleElementValue = rawData.Slice(offset, firstValue.Length);
            }
        }
예제 #18
0
        public static Rfc3161TimestampRequest CreateFromData(
            ReadOnlySpan <byte> data,
            HashAlgorithmName hashAlgorithm,
            Oid?requestedPolicyId              = null,
            ReadOnlyMemory <byte>?nonce        = null,
            bool requestSignerCertificates     = false,
            X509ExtensionCollection?extensions = null)
        {
            using (IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithm))
            {
                hasher.AppendData(data);
                byte[] digest = hasher.GetHashAndReset();

                return(CreateFromHash(
                           digest,
                           hashAlgorithm,
                           requestedPolicyId,
                           nonce,
                           requestSignerCertificates,
                           extensions));
            }
        }
예제 #19
0
 /// <summary>
 ///   Initializes a new instance of the <see cref="AsnEncodedData"/> class from an object identifier
 ///   (OID) and existing encoded data.
 /// </summary>
 /// <param name="oid">
 ///   The object identifier for this data.
 /// </param>
 /// <param name="rawData">
 ///   The Abstract Syntax Notation One (ASN.1)-encoded data.
 /// </param>
 public AsnEncodedData(Oid?oid, ReadOnlySpan <byte> rawData)
 {
     Reset(oid, rawData);
 }
예제 #20
0
 public AsnEncodedData(Oid?oid, byte[] rawData)
 {
     Reset(oid, rawData);
 }
예제 #21
0
 private void Reset(Oid?oid, byte[] rawData)
 {
     this.Oid     = oid;
     this.RawData = rawData;
 }
예제 #22
0
        protected override string? FormatNative(Oid? oid, byte[] rawData, bool multiLine)
        {
            if (oid == null || string.IsNullOrEmpty(oid.Value))
            {
                return EncodeHexString(rawData, true);
            }

            // The established behavior for this method is to return the native answer, if possible,
            // or to return null and let rawData get hex-encoded.  CryptographicException should not
            // be raised.

            bool clearErrors = true;
            try
            {
                using (SafeAsn1ObjectHandle asnOid = Interop.Crypto.ObjTxt2Obj(oid.Value))
                using (SafeAsn1OctetStringHandle octetString = Interop.Crypto.Asn1OctetStringNew())
                {
                    if (asnOid.IsInvalid || octetString.IsInvalid)
                    {
                        return null;
                    }

                    if (!Interop.Crypto.Asn1OctetStringSet(octetString, rawData, rawData.Length))
                    {
                        return null;
                    }

                    using (SafeBioHandle bio = Interop.Crypto.CreateMemoryBio())
                    using (SafeX509ExtensionHandle x509Ext = Interop.Crypto.X509ExtensionCreateByObj(asnOid, false, octetString))
                    {
                        if (bio.IsInvalid || x509Ext.IsInvalid)
                        {
                            return null;
                        }

                        if (!Interop.Crypto.X509V3ExtPrint(bio, x509Ext))
                        {
                            return null;
                        }

                        // X509V3ExtPrint might contaminate the error queue on success, always clear now.
                        Interop.Crypto.ErrClearError();

                        // Errors past here are handled by throws, don't need to double-lock
                        // the success path.
                        clearErrors = false;

                        int printLen = Interop.Crypto.GetMemoryBioSize(bio);

                        // Account for the null terminator that it'll want to write.
                        var buf = new byte[printLen + 1];
                        int read = Interop.Crypto.BioGets(bio, buf, buf.Length);

                        if (read < 0)
                        {
                            throw Interop.Crypto.CreateOpenSslCryptographicException();
                        }

                        return Encoding.UTF8.GetString(buf, 0, read);
                    }
                }
            }
            finally
            {
                // All of the return null paths might have errors that we are ignoring.
                if (clearErrors)
                {
                    Interop.Crypto.ErrClearError();
                }
            }
        }
예제 #23
0
        /// <summary>
        ///   Decodes the specified Certificate Revocation List (CRL) and produces
        ///   a <see cref="CertificateRevocationListBuilder" /> with all of the revocation
        ///   entries from the decoded CRL.
        /// </summary>
        /// <param name="currentCrl">
        ///   The DER-encoded CRL to decode.
        /// </param>
        /// <param name="currentCrlNumber">
        ///   When this method returns, contains the CRL sequence number from the decoded CRL.
        ///   This parameter is treated as uninitialized.
        /// </param>
        /// <param name="bytesConsumed">
        ///   When this method returns, contains the number of bytes that were read from
        ///   <paramref name="currentCrl"/> while decoding.
        /// </param>
        /// <returns>
        ///   A new builder that has the same revocation entries as the decoded CRL.
        /// </returns>
        /// <exception cref="CryptographicException">
        ///   <paramref name="currentCrl" /> could not be decoded.
        /// </exception>
        public static CertificateRevocationListBuilder Load(
            ReadOnlySpan <byte> currentCrl,
            out BigInteger currentCrlNumber,
            out int bytesConsumed)
        {
            List <RevokedCertificate> list = new();
            BigInteger crlNumber           = 0;
            int        payloadLength;

            try
            {
                AsnValueReader reader = new AsnValueReader(currentCrl, AsnEncodingRules.DER);
                payloadLength = reader.PeekEncodedValue().Length;

                AsnValueReader certificateList = reader.ReadSequence();
                AsnValueReader tbsCertList     = certificateList.ReadSequence();
                AlgorithmIdentifierAsn.Decode(ref certificateList, ReadOnlyMemory <byte> .Empty, out _);

                if (!certificateList.TryReadPrimitiveBitString(out _, out _))
                {
                    throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
                }

                certificateList.ThrowIfNotEmpty();

                int version = 0;

                if (tbsCertList.PeekTag().HasSameClassAndValue(Asn1Tag.Integer))
                {
                    // https://datatracker.ietf.org/doc/html/rfc5280#section-5.1 says the only
                    // version values are v1 (0) and v2 (1).
                    //
                    // Since v1 (0) is supposed to not write down the version value, v2 (1) is the
                    // only legal value to read.
                    if (!tbsCertList.TryReadInt32(out version) || version != 1)
                    {
                        throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
                    }
                }

                AlgorithmIdentifierAsn.Decode(ref tbsCertList, ReadOnlyMemory <byte> .Empty, out _);
                // X500DN
                tbsCertList.ReadSequence();

                // thisUpdate
                ReadX509Time(ref tbsCertList);

                // nextUpdate
                ReadX509TimeOpt(ref tbsCertList);

                AsnValueReader revokedCertificates = default;

                if (tbsCertList.HasData && tbsCertList.PeekTag().HasSameClassAndValue(Asn1Tag.Sequence))
                {
                    revokedCertificates = tbsCertList.ReadSequence();
                }

                if (version > 0 && tbsCertList.HasData)
                {
                    AsnValueReader crlExtensionsExplicit = tbsCertList.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0));
                    AsnValueReader crlExtensions         = crlExtensionsExplicit.ReadSequence();
                    crlExtensionsExplicit.ThrowIfNotEmpty();

                    while (crlExtensions.HasData)
                    {
                        AsnValueReader extension = crlExtensions.ReadSequence();
                        Oid?           extnOid   = Oids.GetSharedOrNullOid(ref extension);

                        if (extnOid is null)
                        {
                            extension.ReadObjectIdentifier();
                        }

                        if (extension.PeekTag().HasSameClassAndValue(Asn1Tag.Boolean))
                        {
                            extension.ReadBoolean();
                        }

                        if (!extension.TryReadPrimitiveOctetString(out ReadOnlySpan <byte> extnValue))
                        {
                            throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
                        }

                        // Since we're only matching against OIDs that come from GetSharedOrNullOid
                        // we can use ReferenceEquals and skip the Value string equality check in
                        // the Oid.ValueEquals extension method (as it will always be preempted by
                        // the ReferenceEquals or will evaulate to false).
                        if (ReferenceEquals(extnOid, Oids.CrlNumberOid))
                        {
                            AsnValueReader crlNumberReader = new AsnValueReader(
                                extnValue,
                                AsnEncodingRules.DER);

                            crlNumber = crlNumberReader.ReadInteger();
                            crlNumberReader.ThrowIfNotEmpty();
                        }
                    }
                }

                tbsCertList.ThrowIfNotEmpty();

                while (revokedCertificates.HasData)
                {
                    RevokedCertificate revokedCertificate = new RevokedCertificate(ref revokedCertificates, version);
                    list.Add(revokedCertificate);
                }
            }
            catch (AsnContentException e)
            {
                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e);
            }

            bytesConsumed    = payloadLength;
            currentCrlNumber = crlNumber;
            return(new CertificateRevocationListBuilder(list));
        }
예제 #24
0
 public AsnEncodedData(Oid?oid, byte[] rawData) : this(oid, rawData, skipCopy : false)
 {
 }
예제 #25
0
        private static List <byte[]> ParseDistinguishedName(
            string stringForm,
            List <char> dnSeparators,
            bool noQuotes)
        {
            // 16 is way more RDNs than we should ever need. A fairly standard set of values is
            // { E, CN, O, OU, L, S, C } = 7;
            // The EV values add in
            // {
            //   STREET, PostalCode, SERIALNUMBER, 2.5.4.15,
            //   1.3.6.1.4.1.311.60.2.1.2, 1.3.6.1.4.1.311.60.2.1.3
            // } = 6
            //
            // 7 + 6 = 13, round up to the nearest power-of-two.
            const int           InitalRdnSize = 16;
            List <byte[]>       encodedSets   = new List <byte[]>(InitalRdnSize);
            ReadOnlySpan <char> chars         = stringForm;

            int pos;
            int end = chars.Length;

            int  tagStart        = -1;
            int  tagEnd          = -1;
            Oid? tagOid          = null;
            int  valueStart      = -1;
            int  valueEnd        = -1;
            bool hadEscapedQuote = false;

            const char KeyValueSeparator = '=';
            const char QuotedValueChar   = '"';

            ParseState state = ParseState.SeekTag;

            for (pos = 0; pos < end; pos++)
            {
                char c = chars[pos];

                switch (state)
                {
                case ParseState.SeekTag:
                    if (char.IsWhiteSpace(c))
                    {
                        continue;
                    }

                    if (char.IsControl(c))
                    {
                        state = ParseState.Invalid;
                        break;
                    }

                    // The first character in the tag start.
                    // We know that there's at least one valid
                    // character, so make end be start+1.
                    //
                    // Single letter values with no whitespace padding them
                    // (e.g. E=) would otherwise be ambiguous with length.
                    // (SeekEquals can't set the tagEnd value because it
                    // doesn't know if it was preceded by whitespace)

                    // Note that we make no check here for the dnSeparator(s).
                    // Two separators in a row is invalid (except for UseNewlines,
                    // and they are only allowed because they are whitespace).
                    //
                    // But the throw for an invalid value will come from when the
                    // OID fails to encode.
                    tagStart = pos;
                    tagEnd   = pos + 1;
                    state    = ParseState.SeekTagEnd;
                    break;

                case ParseState.SeekTagEnd:
                    if (c == KeyValueSeparator)
                    {
                        goto case ParseState.SeekEquals;
                    }

                    if (char.IsWhiteSpace(c))
                    {
                        // Tag values aren't permitted whitespace, but there
                        // can be whitespace between the tag and the separator.
                        state = ParseState.SeekEquals;
                        break;
                    }

                    if (char.IsControl(c))
                    {
                        state = ParseState.Invalid;
                        break;
                    }

                    // We found another character in the tag, so move the
                    // end (non-inclusive) to the next character.
                    tagEnd = pos + 1;
                    break;

                case ParseState.SeekEquals:
                    if (c == KeyValueSeparator)
                    {
                        Debug.Assert(tagStart >= 0);
                        tagOid   = ParseOid(stringForm, tagStart, tagEnd);
                        tagStart = -1;

                        state = ParseState.SeekValueStart;
                        break;
                    }

                    if (!char.IsWhiteSpace(c))
                    {
                        state = ParseState.Invalid;
                        break;
                    }

                    break;

                case ParseState.SeekValueStart:
                    if (char.IsWhiteSpace(c))
                    {
                        continue;
                    }

                    // If the first non-whitespace character is a quote,
                    // this is a quoted string.  Unless the flags say to
                    // not interpret quoted strings.
                    if (c == QuotedValueChar && !noQuotes)
                    {
                        state      = ParseState.SeekEndQuote;
                        valueStart = pos + 1;
                        break;
                    }

                    // It's possible to just write "CN=,O=". So we might
                    // run into the RDN separator here.
                    if (dnSeparators.Contains(c))
                    {
                        valueStart = pos;
                        valueEnd   = pos;
                        goto case ParseState.SeekComma;
                    }

                    state      = ParseState.SeekValueEnd;
                    valueStart = pos;
                    valueEnd   = pos + 1;
                    break;

                case ParseState.SeekEndQuote:
                    // The only escape sequence in DN parsing is that a quoted
                    // value can embed quotes via "", the same as a C# verbatim
                    // string.  So, if we see a quote while looking for a close
                    // quote we need to remember that this might have been the
                    // end, but be open to the possibility that there's another
                    // quote coming.
                    if (c == QuotedValueChar)
                    {
                        state    = ParseState.MaybeEndQuote;
                        valueEnd = pos;
                        break;
                    }

                    // Everything else is okay.
                    break;

                case ParseState.MaybeEndQuote:
                    if (c == QuotedValueChar)
                    {
                        state           = ParseState.SeekEndQuote;
                        hadEscapedQuote = true;
                        valueEnd        = -1;
                        break;
                    }

                    // If the character wasn't another quote:
                    //   dnSeparator: process value, state transition to SeekTag
                    //   whitespace: state transition to SeekComma
                    //   anything else: invalid.
                    // since that's the same table as SeekComma, just change state
                    // and go there.
                    state = ParseState.SeekComma;
                    goto case ParseState.SeekComma;

                case ParseState.SeekValueEnd:
                    // Every time we see a non-whitespace character we need to mark it
                    if (dnSeparators.Contains(c))
                    {
                        goto case ParseState.SeekComma;
                    }

                    if (char.IsWhiteSpace(c))
                    {
                        continue;
                    }

                    // Including control characters.
                    valueEnd = pos + 1;

                    break;

                case ParseState.SeekComma:
                    if (dnSeparators.Contains(c))
                    {
                        Debug.Assert(tagOid != null);
                        Debug.Assert(valueEnd != -1);
                        Debug.Assert(valueStart != -1);

                        encodedSets.Add(ParseRdn(tagOid, chars[valueStart..valueEnd], hadEscapedQuote));
예제 #26
0
 public string Format(Oid?oid, byte[] rawData, bool multiLine)
 {
     return(FormatNative(oid, rawData, multiLine) ?? Convert.ToHexString(rawData));
 }
예제 #27
0
 public string Format(Oid?oid, byte[] rawData, bool multiLine)
 {
     return(FormatNative(oid, rawData, multiLine) ?? HexConverter.ToString(rawData.AsSpan(), HexConverter.Casing.Upper));
 }
예제 #28
0
        /// <summary>
        /// Create a timestamp request using a pre-computed hash value.
        /// </summary>
        /// <param name="hash">The pre-computed hash value to be timestamped.</param>
        /// <param name="hashAlgorithmId">
        ///   The Object Identifier (OID) for the hash algorithm which produced <paramref name="hash"/>.
        /// </param>
        /// <param name="requestedPolicyId">
        ///   The Object Identifier (OID) for a timestamp policy the Timestamp Authority (TSA) should use,
        ///   or <c>null</c> to express no preference.
        /// </param>
        /// <param name="nonce">
        ///   An optional nonce (number used once) to uniquely identify this request to pair it with the response.
        ///   The value is interpreted as an unsigned big-endian integer and may be normalized to the encoding format.
        /// </param>
        /// <param name="requestSignerCertificates">
        ///   Indicates whether the Timestamp Authority (TSA) must (<c>true</c>) or must not (<c>false</c>) include
        ///   the signing certificate in the issued timestamp token.
        /// </param>
        /// <param name="extensions">RFC3161 extensions to present with the request.</param>
        /// <returns>
        ///   An <see cref="Rfc3161TimestampRequest"/> representing the chosen values.
        /// </returns>
        /// <seealso cref="Encode"/>
        /// <seealso cref="TryEncode"/>
        public static Rfc3161TimestampRequest CreateFromHash(
            ReadOnlyMemory <byte> hash,
            Oid hashAlgorithmId,
            Oid?requestedPolicyId              = null,
            ReadOnlyMemory <byte>?nonce        = null,
            bool requestSignerCertificates     = false,
            X509ExtensionCollection?extensions = null)
        {
            // Normalize the nonce:
            if (nonce.HasValue)
            {
                ReadOnlyMemory <byte> nonceMemory = nonce.Value;
                ReadOnlySpan <byte>   nonceSpan   = nonceMemory.Span;

                // If it's empty, or it would be negative, insert the requisite byte.
                if (nonceSpan.Length == 0 || nonceSpan[0] >= 0x80)
                {
                    byte[] temp = new byte[nonceSpan.Length + 1];
                    nonceSpan.CopyTo(temp.AsSpan(1));
                    nonce = temp;
                }
                else
                {
                    int slice = 0;

                    // Find all extra leading 0x00 values and trim them off.
                    while (slice < nonceSpan.Length && nonceSpan[slice] == 0)
                    {
                        slice++;
                    }

                    // Back up one if it was all zero, or we turned the number negative.
                    if (slice == nonceSpan.Length || nonceSpan[slice] >= 0x80)
                    {
                        slice--;
                    }

                    nonce = nonceMemory.Slice(slice);
                }
            }

            var req = new Rfc3161TimeStampReq
            {
                Version        = 1,
                MessageImprint = new MessageImprint
                {
                    HashAlgorithm =
                    {
                        Algorithm  = hashAlgorithmId,
                        Parameters = AlgorithmIdentifierAsn.ExplicitDerNull,
                    },

                    HashedMessage = hash,
                },
                ReqPolicy = requestedPolicyId,
                CertReq   = requestSignerCertificates,
                Nonce     = nonce,
            };

            if (extensions != null)
            {
                req.Extensions =
                    extensions.OfType <X509Extension>().Select(e => new X509ExtensionAsn(e)).ToArray();
            }

            // The RFC implies DER (see TryParse), and DER is the most widely understood given that
            // CER isn't specified.
            const AsnEncodingRules ruleSet = AsnEncodingRules.DER;

            using (AsnWriter writer = new AsnWriter(ruleSet))
            {
                req.Encode(writer);

                byte[] encodedBytes = writer.Encode();

                // Make sure everything normalizes
                req = Rfc3161TimeStampReq.Decode(encodedBytes, ruleSet);

                return(new Rfc3161TimestampRequest
                {
                    _encodedBytes = writer.Encode(),
                    _parsedData = req,
                });
            }
        }
예제 #29
0
 protected abstract string?FormatNative(Oid?oid, byte[] rawData, bool multiLine);
        public static PublicKey ParsePublicKeyFromX509Format(byte[] x509SubjectPublicKeyInfoBlob)
        {
            Oid?           Oid        = null;
            AsnEncodedData?Parameters = null;
            AsnEncodedData?KeyValue   = null;

            using MemoryStream Stream = new MemoryStream(x509SubjectPublicKeyInfoBlob);
            using BinaryReader Reader = new BinaryReader(Stream);

            try
            {
                while (Stream.Position < Stream.Length)
                {
                    byte Token = Reader.ReadByte();
                    switch (Token)
                    {
                    case 0x30:     // SEQUENCE
                    case 0x03:     // BIT STRING
                    case 0x06:     // OID
                        byte PayloadLength = Reader.ReadByte();
                        if (PayloadLength > 0x7F)
                        {
                            throw new InvalidOperationException($"Payload lengths > 0x74 are not supported. Found at position [{Stream.Position - 1}]");
                        }

                        if (Token == 0x30)
                        {
                            continue;
                        }

                        byte UnusedBits = 0;
                        if (Token == 0x03)
                        {
                            UnusedBits = Reader.ReadByte();
                            if (UnusedBits > 0)
                            {
                                throw new InvalidOperationException($"Bit strings with unused bits are not supported. Found at position [{Stream.Position - 1}]");
                            }
                            PayloadLength--;
                        }

                        byte[] Payload = new byte[PayloadLength];

                        int BytesRead = Reader.Read(Payload, 0, Payload.Length);
                        if (BytesRead != PayloadLength)
                        {
                            throw new InvalidOperationException($"Payload length did not match. Found at position [{Stream.Position - 1}]");
                        }

                        if (Token == 0x06)
                        {
                            if (Oid == null)
                            {
                                Oid = new Oid(ConvertOidByteArrayToStringValue(Payload));
                            }
                            else
                            {
                                Parameters = new AsnEncodedData(Payload);
                            }
                        }
                        if (Token == 0x03)
                        {
                            KeyValue = new AsnEncodedData(Payload);
                        }
                        break;

                    default:
                        throw new InvalidOperationException($"Unknown token byte [{Token:X2}] found at position [{Stream.Position - 1}].");
                    }
                }

                if (Oid == null || Parameters == null || KeyValue == null)
                {
                    throw new InvalidOperationException("Required information could not be found in blob.");
                }

                return(new PublicKey(Oid, Parameters, KeyValue));
            }
            catch (Exception ParseException)
            {
                throw new InvalidOperationException("Public key could not be parsed from X.509 format blob.", ParseException);
            }
        }