using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
+using System.Security.Cryptography.Asn1;
using System.Text;
using Microsoft.Win32.SafeHandles;
(handle, buf) => EncodeAsn1Integer(handle, buf),
asn1Integer);
- DerSequenceReader reader = DerSequenceReader.CreateForPayload(derEncoded);
- return reader.ReadIntegerBytes();
+ AsnReader reader = new AsnReader(derEncoded, AsnEncodingRules.DER);
+ return reader.GetIntegerBytes().ToArray();
}
}
}
namespace System.Security.Cryptography.Asn1
{
- internal partial struct AlgorithmIdentifierAsn
- {
+ internal partial struct AlgorithmIdentifierAsn
+ {
internal static readonly ReadOnlyMemory<byte> ExplicitDerNull = new byte[] { 0x05, 0x00 };
internal bool Equals(ref AlgorithmIdentifierAsn other)
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Runtime.InteropServices;
-using System.Security.Cryptography.X509Certificates;
-using Internal.Cryptography;
-
-namespace System.Security.Cryptography.Asn1
-{
- [StructLayout(LayoutKind.Sequential)]
- internal partial struct X509ExtensionAsn
- {
- [ObjectIdentifier]
- internal string ExtnId;
-
- [DefaultValue(0x01, 0x01, 0x00)]
- internal bool Critical;
-
- [OctetString]
- internal ReadOnlyMemory<byte> ExtnValue;
-
- public X509ExtensionAsn(X509Extension extension, bool copyValue=true)
- {
- if (extension == null)
- {
- throw new ArgumentNullException(nameof(extension));
- }
-
- ExtnId = extension.Oid.Value;
- Critical = extension.Critical;
- ExtnValue = copyValue ? extension.RawData.CloneByteArray() : extension.RawData;
- }
-
- internal void Encode(AsnWriter writer)
- {
- AsnSerializer.Serialize(this, writer);
- }
-
- internal static void Decode(AsnReader reader, out X509ExtensionAsn decoded)
- {
- if (reader == null)
- throw new ArgumentNullException(nameof(reader));
-
- ReadOnlyMemory<byte> value = reader.GetEncodedValue();
- decoded = AsnSerializer.Deserialize<X509ExtensionAsn>(value, reader.RuleSet);
- }
- }
-}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.InteropServices;
+using System.Security.Cryptography.X509Certificates;
+
+namespace System.Security.Cryptography.Asn1
+{
+ internal partial struct X509ExtensionAsn
+ {
+ public X509ExtensionAsn(X509Extension extension)
+ {
+ if (extension == null)
+ {
+ throw new ArgumentNullException(nameof(extension));
+ }
+
+ ExtnId = extension.Oid;
+ Critical = extension.Critical;
+ ExtnValue = extension.RawData;
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<asn:Sequence
+ xmlns:asn="http://schemas.dot.net/asnxml/201808/"
+ name="X509ExtensionAsn"
+ namespace="System.Security.Cryptography.Asn1">
+
+ <!--
+ https://tools.ietf.org/html/rfc5280#section-4.1
+
+ Extension ::= SEQUENCE {
+ extnID OBJECT IDENTIFIER,
+ critical BOOLEAN DEFAULT FALSE,
+ extnValue OCTET STRING
+ - contains the DER encoding of an ASN.1 value
+ - corresponding to the extension type identified
+ - by extnID
+ }
+ -->
+ <asn:ObjectIdentifier name="ExtnId" />
+ <asn:Boolean name="Critical" defaultDerInit="0x01, 0x01, 0x00" />
+ <asn:OctetString name="ExtnValue" />
+</asn:Sequence>
--- /dev/null
+using System;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Asn1;
+
+namespace System.Security.Cryptography.Asn1
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal partial struct X509ExtensionAsn
+ {
+ private static byte[] s_defaultCritical = { 0x01, 0x01, 0x00 };
+
+ internal Oid ExtnId;
+ internal bool Critical;
+ internal ReadOnlyMemory<byte> ExtnValue;
+
+#if DEBUG
+ static X509ExtensionAsn()
+ {
+ X509ExtensionAsn decoded = default;
+ AsnReader reader;
+
+ reader = new AsnReader(s_defaultCritical, AsnEncodingRules.DER);
+ decoded.Critical = reader.ReadBoolean();
+ reader.ThrowIfNotEmpty();
+ }
+#endif
+
+ internal void Encode(AsnWriter writer)
+ {
+ Encode(writer, Asn1Tag.Sequence);
+ }
+
+ internal void Encode(AsnWriter writer, Asn1Tag tag)
+ {
+ writer.PushSequence(tag);
+
+ writer.WriteObjectIdentifier(ExtnId);
+
+ // DEFAULT value handler for Critical.
+ {
+ using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER))
+ {
+ tmp.WriteBoolean(Critical);
+ ReadOnlySpan<byte> encoded = tmp.EncodeAsSpan();
+
+ if (!encoded.SequenceEqual(s_defaultCritical))
+ {
+ writer.WriteEncodedValue(encoded.ToArray());
+ }
+ }
+ }
+
+ writer.WriteOctetString(ExtnValue.Span);
+ writer.PopSequence(tag);
+ }
+
+ internal static X509ExtensionAsn Decode(ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+ {
+ return Decode(Asn1Tag.Sequence, encoded, ruleSet);
+ }
+
+ internal static X509ExtensionAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+ {
+ AsnReader reader = new AsnReader(encoded, ruleSet);
+
+ Decode(reader, expectedTag, out X509ExtensionAsn decoded);
+ reader.ThrowIfNotEmpty();
+ return decoded;
+ }
+
+ internal static void Decode(AsnReader reader, out X509ExtensionAsn decoded)
+ {
+ if (reader == null)
+ throw new ArgumentNullException(nameof(reader));
+
+ Decode(reader, Asn1Tag.Sequence, out decoded);
+ }
+
+ internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out X509ExtensionAsn decoded)
+ {
+ if (reader == null)
+ throw new ArgumentNullException(nameof(reader));
+
+ decoded = default;
+ AsnReader sequenceReader = reader.ReadSequence(expectedTag);
+ AsnReader defaultReader;
+
+ decoded.ExtnId = sequenceReader.ReadObjectIdentifier();
+
+ if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(Asn1Tag.Boolean))
+ {
+ decoded.Critical = sequenceReader.ReadBoolean();
+ }
+ else
+ {
+ defaultReader = new AsnReader(s_defaultCritical, AsnEncodingRules.DER);
+ decoded.Critical = defaultReader.ReadBoolean();
+ }
+
+
+ if (sequenceReader.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory<byte> tmpExtnValue))
+ {
+ decoded.ExtnValue = tmpExtnValue;
+ }
+ else
+ {
+ decoded.ExtnValue = sequenceReader.ReadOctetString();
+ }
+
+
+ sequenceReader.ThrowIfNotEmpty();
+ }
+ }
+}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Collections;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Numerics;
-using System.Text;
-
-namespace System.Security.Cryptography
-{
- /// <summary>
- /// Writes data encoded via the Distinguished Encoding Rules for Abstract
- /// Syntax Notation 1 (ASN.1) data.
- /// </summary>
- internal static class DerEncoder
- {
- private const byte ConstructedFlag = 0x20;
- private const byte ConstructedSequenceTag = ConstructedFlag | (byte)DerSequenceReader.DerTag.Sequence;
- private const byte ConstructedSetTag = ConstructedFlag | (byte)DerSequenceReader.DerTag.Set;
-
- private static readonly byte[][] s_nullTlv =
- {
- new byte[] { (byte)DerSequenceReader.DerTag.Null },
- new byte[] { 0 },
- Array.Empty<byte>(),
- };
-
- private static byte[] EncodeLength(int length)
- {
- Debug.Assert(length >= 0);
-
- byte low = unchecked((byte)length);
-
- // If the length value fits in 7 bits, it's an answer all by itself.
- if (length < 0x80)
- {
- return new[] { low };
- }
-
- // If the length is more than 0x7F then it is stored as
- // 0x80 | lengthLength
- // big
- // endian
- // length
-
- // So:
- // 0 => 0x00.
- // 1 => 0x01.
- // 127 => 0x7F.
- // 128 => 0x81 0x80
- // 255 => 0x81 0xFF
- // 256 => 0x82 0x01 0x00
- // 65535 => 0x82 0xFF 0xFF
- // 65536 => 0x83 0x01 0x00 0x00
- // ...
- // int.MaxValue => 0x84 0x7F 0xFF 0xFF 0xFF
- //
- // Technically DER lengths can go longer than int.MaxValue, but since our
- // encoding input here is an int, our output will be no larger than that.
-
- if (length <= 0xFF)
- {
- return new byte[] { 0x81, low };
- }
-
- int remainder = length >> 8;
- byte midLow = unchecked((byte)remainder);
-
- if (length <= 0xFFFF)
- {
- return new byte[] { 0x82, midLow, low };
- }
-
- remainder >>= 8;
- byte midHigh = unchecked((byte)remainder);
-
- if (length <= 0xFFFFFF)
- {
- return new byte[] { 0x83, midHigh, midLow, low };
- }
-
- remainder >>= 8;
- byte high = unchecked((byte)remainder);
-
- // Since we know this was a non-negative signed number, the highest
- // legal value here is 0x7F.
- Debug.Assert(remainder < 0x80);
-
- return new byte[] { 0x84, high, midHigh, midLow, low };
- }
-
- /// <summary>
- /// Encode the segments { tag, length, value } of a boolean.
- /// </summary>
- /// <param name="value">The boolean to encode</param>
- /// <returns>The encoded segments { tag, length, value }</returns>
- internal static byte[][] SegmentedEncodeBoolean(bool value)
- {
- // BER says FALSE is zero, TRUE is other.
- // DER says TRUE is 0xFF.
- byte[] data =
- {
- (byte)(value ? 0xFF : 0x00),
- };
-
- return new byte[][]
- {
- new byte[] { (byte)DerSequenceReader.DerTag.Boolean },
- new byte[] { 0x01 },
- data,
- };
- }
-
- /// <summary>
- /// Encode the segments { tag, length, value } of an unsigned integer.
- /// </summary>
- /// <param name="value">The value to encode.</param>
- /// <returns>The encoded segments { tag, length, value }</returns>
- internal static byte[][] SegmentedEncodeUnsignedInteger(uint value)
- {
- byte[] bytes = BitConverter.GetBytes(value);
-
- if (BitConverter.IsLittleEndian)
- {
- Array.Reverse(bytes);
- }
-
- return SegmentedEncodeUnsignedInteger(bytes);
- }
-
- /// <summary>
- /// Encode the segments { tag, length, value } of an unsigned integer.
- /// </summary>
- /// <param name="bigEndianBytes">The value to encode, in big integer representation.</param>
- /// <returns>The encoded segments { tag, length, value }</returns>
- internal static byte[][] SegmentedEncodeUnsignedInteger(ReadOnlySpan<byte> bigEndianBytes)
- {
- Debug.Assert(!bigEndianBytes.IsEmpty, "The span must not be empty.");
-
- int start = 0;
- int end = start + bigEndianBytes.Length;
-
- // Remove any leading zeroes.
- while (start < end && bigEndianBytes[start] == 0)
- {
- start++;
- }
-
- // All zeroes, just back up one and let it flow through the normal flow.
- if (start == end)
- {
- start--;
- Debug.Assert(start >= 0);
- }
-
- // If the first byte is bigger than 0x7F it will look like a negative number, since
- // we're unsigned, insert a zero-padding byte.
- int length = end - start;
- int writeStart = bigEndianBytes[start] > 0x7F ? 1 : 0;
- var dataBytes = new byte[length + writeStart];
- bigEndianBytes.Slice(start, length).CopyTo(new Span<byte>(dataBytes).Slice(writeStart));
-
- return new[]
- {
- new[] { (byte)DerSequenceReader.DerTag.Integer },
- EncodeLength(dataBytes.Length),
- dataBytes,
- };
- }
-
- /// <summary>
- /// Encode the segments { tag, length, value } of a BIT STRING which is wrapped over
- /// other DER-encoded data.
- /// </summary>
- /// <param name="childSegments"></param>
- /// <remarks>
- /// Despite containing other DER-encoded data this does not get the constructed bit,
- /// because it doesn't when encoding public keys in SubjectPublicKeyInfo</remarks>
- /// <returns></returns>
- internal static byte[][] SegmentedEncodeBitString(params byte[][][] childSegments)
- {
- return SegmentedEncodeBitString(ConcatenateArrays(childSegments));
- }
-
- /// <summary>
- /// Encode the segments { tag, length, value } of a bit string where all bits are significant.
- /// </summary>
- /// <param name="data">The data to encode</param>
- /// <returns>The encoded segments { tag, length, value }</returns>
- internal static byte[][] SegmentedEncodeBitString(byte[] data)
- {
- return SegmentedEncodeBitString(0, data);
- }
-
- /// <summary>
- /// Encode the segments { tag, length, value } of a bit string where the least significant
- /// <paramref name="unusedBits"/> of the last byte are padding.
- /// </summary>
- /// <param name="unusedBits">The number of padding bits (0-7) in the last byte</param>
- /// <param name="data">The data to encode</param>
- /// <returns>The encoded segments { tag, length, value }</returns>
- internal static byte[][] SegmentedEncodeBitString(int unusedBits, byte[] data)
- {
- Debug.Assert(data != null);
- Debug.Assert(unusedBits >= 0);
- Debug.Assert(unusedBits <= 7);
- Debug.Assert(unusedBits == 0 || data.Length > 0);
-
- byte[] encodedData = new byte[data.Length + 1];
-
- // Copy data to encodedData, but leave a one byte gap for unusedBits.
- Buffer.BlockCopy(data, 0, encodedData, 1, data.Length);
- encodedData[0] = (byte)unusedBits;
-
- // We need to make a mask of the bits to keep around for the last
- // byte, ensuring we clear out any bits that were set, but reported
- // as padding by unusedBits
- //
- // For example:
- // unusedBits 0 => mask 0b11111111
- // unusedBits 1 => mask 0b11111110
- // unusedBits 7 => mask 0b10000000
- byte lastByteSemanticMask = unchecked((byte)(-1 << unusedBits));
-
- // Since encodedData.Length is data.Length + 1, "encodedDate.Length - 1" is just "data.Length".
- encodedData[data.Length] &= lastByteSemanticMask;
-
- return new byte[][]
- {
- new byte[] { (byte)DerSequenceReader.DerTag.BitString },
- EncodeLength(encodedData.Length),
- encodedData,
- };
- }
-
- /// <summary>
- /// Encode the segments { tag, length, value } of a bit string value based upon a NamedBitList.
- /// ((<paramref name="bigEndianBytes"/>[0] >> 7) & 1) is considered the "leading" bit, proceeding
- /// through the array for up to <paramref name="namedBitsCount"/>.
- /// </summary>
- /// <param name="bigEndianBytes">
- /// The data in big endian order, the most significant bit of byte 0 is the leading bit
- /// (corresponds to the named value for "bit 0"). Any bits beyond <paramref name="namedBitsCount"/>
- /// are ignored, and any missing bits are assumed to be unset.
- /// </param>
- /// <param name="namedBitsCount">
- /// The total number of named bits. Since the bits are numbered with a zero index, this should be
- /// one higher than the largest defined bit. (namedBitsCount=10 covers bits 0-9)
- /// </param>
- /// <returns>
- /// A triplet of { tag }, { length }, { data }. All trailing unset named bits are removed.
- /// </returns>
- internal static byte[][] SegmentedEncodeNamedBitList(byte[] bigEndianBytes, int namedBitsCount)
- {
- Debug.Assert(bigEndianBytes != null, "bigEndianBytes != null");
- Debug.Assert(namedBitsCount > 0, "namedBitsCount > 0");
-
- // The encoding that this follows is the DER encoding for NamedBitList, which is different than
- // (unnamed) BIT STRING.
- //
- // X.690 (08/2015) section 11.2.2 (Unused bits) says:
- // Where ITU-T Rec. X.680 | ISO/IEC 8824-1, 22.7, applies, the bitstring shall have all
- // trailing 0 bits removed before it is encoded.
- // NOTE 1 – In the case where a size constraint has been applied, the abstract value
- // delivered by a decoder to the application will be one of those satisfying the
- // size constraint and differing from the transmitted value only in the number of
- // trailing 0 bits.
- // NOTE 2 – If a bitstring value has no 1 bits, then an encoder shall encode the value
- // with a length of 1 and an initial octet set to 0.
- //
- // X.680 (08/2015) section 22.7 says:
- // When a "NamedBitList" is used in defining a bitstring type ASN.1 encoding rules are free
- // to add (or remove) arbitrarily any trailing 0 bits to (or from) values that are being
- // encoded or decoded
- //
- // Therefore, if 16 bits are defined, and only bit 7 is set, instead of { 00 01 00 } the encoding
- // should be { 00 01 }
- //
- // And, if 8 bits are defined, and only bit 6 is set, instead of { 00 02 } it should be { 01 02 },
- // signifying that the last bit was omitted.
-
- int lastSetBit = -1;
-
- int lastBitProvided = (bigEndianBytes.Length * 8) - 1;
- int lastPossibleBit = Math.Min(lastBitProvided, namedBitsCount - 1);
-
- for (int currentBit = lastPossibleBit; currentBit >= 0; currentBit--)
- {
- int currentByte = currentBit / 8;
-
- // As we loop through the numbered bits we need to figure out
- // 1) which indexed byte it would be in (currentByte)
- // 2) How many bits from the right it is (shiftIndex)
- //
- // For example:
- // currentBit 0 => currentByte 0, shiftIndex 7 (1 << 7)
- // currentBit 1 => currentByte 0, shiftIndex 6 (1 << 6)
- // currentBit 7 => currentByte 0, shiftIndex 0 (1 << 0)
- // currentBit 8 => currentByte 1, shiftIndex 7 (1 << 7)
- // etc
- int shiftIndex = 7 - (currentBit % 8);
- int testValue = 1 << shiftIndex;
- byte dataByte = bigEndianBytes[currentByte];
-
- if ((dataByte & testValue) == testValue)
- {
- lastSetBit = currentBit;
- break;
- }
- }
-
- byte[] dataSegment;
-
- if (lastSetBit >= 0)
- {
- // Bits are zero-indexed, so lastSetBit=0 means "1 semantic bit", and
- // "1 semantic bit" requires a byte to write it down.
- int semanticBits = lastSetBit + 1;
- int semanticBytes = (7 + semanticBits) / 8;
-
- // For a lastSetBit of : 0 1 2 3 4 5 6 7 8 9 A B C D E F
- // unused bits should be: 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
- int unusedBits = 7 - (lastSetBit % 8);
-
- // We need to make a mask of the bits to keep around for the last
- // byte, ensuring we clear out any bits that were set, but beyond
- // the namedBitsCount limit.
- //
- // For example:
- // lastSetBit 0 => mask 0b10000000
- // lastSetBit 1 => mask 0b11000000
- // lastSetBit 7 => mask 0b11111111
- // lastSetBit 8 => mask 0b10000000
- byte lastByteSemanticMask = unchecked((byte)(-1 << unusedBits));
-
- // Semantic bytes plus the "how many unused bits" prefix byte.
- dataSegment = new byte[semanticBytes + 1];
- dataSegment[0] = (byte)unusedBits;
-
- Debug.Assert(semanticBytes <= bigEndianBytes.Length);
-
- Buffer.BlockCopy(bigEndianBytes, 0, dataSegment, 1, semanticBytes);
-
- // But the last byte might have too many bits set, trim it down
- // to only the ones we knew about (all "don't care" values must be 0)
- dataSegment[semanticBytes] &= lastByteSemanticMask;
- }
- else
- {
- // No bits being set is encoded as just "no unused bits",
- // with no semantic payload.
- dataSegment = new byte[] { 0x00 };
- }
-
- return new byte[][]
- {
- new byte[] { (byte)DerSequenceReader.DerTag.BitString },
- EncodeLength(dataSegment.Length),
- dataSegment
- };
- }
-
- /// <summary>
- /// Encode the segments { tag, length, value } of an octet string (byte array).
- /// </summary>
- /// <param name="data">The data to encode</param>
- /// <returns>The encoded segments { tag, length, value }</returns>
- internal static byte[][] SegmentedEncodeOctetString(byte[] data)
- {
- Debug.Assert(data != null);
-
- // Because this is not currently public API the data array is not being cloned.
- return new byte[][]
- {
- new byte[] { (byte)DerSequenceReader.DerTag.OctetString },
- EncodeLength(data.Length),
- data,
- };
- }
-
- internal static byte[][] SegmentedEncodeNull()
- {
- return s_nullTlv;
- }
-
- /// <summary>
- /// Encode an object identifier (Oid).
- /// </summary>
- /// <returns>The encoded OID</returns>
- internal static byte[] EncodeOid(string oidValue)
- {
- return ConcatenateArrays(SegmentedEncodeOid(oidValue));
- }
-
- /// <summary>
- /// Encode the segments { tag, length, value } of an object identifier (Oid).
- /// </summary>
- /// <returns>The encoded segments { tag, length, value }</returns>
- internal static byte[][] SegmentedEncodeOid(Oid oid)
- {
- Debug.Assert(oid != null);
-
- // All exceptions past this point should just be "CryptographicException", because that's
- // how they'd come back from Desktop/Windows, since it was a non-success result of calling
- // CryptEncodeObject.
- string oidValue = oid.Value;
-
- return SegmentedEncodeOid(oidValue);
- }
-
- /// <summary>
- /// Encode the segments { tag, length, value } of an object identifier (Oid).
- /// </summary>
- /// <returns>The encoded segments { tag, length, value }</returns>
- internal static byte[][] SegmentedEncodeOid(string oidValue)
- {
- // All exceptions herein should just be "CryptographicException", because that's
- // how they'd come back from Desktop/Windows, since it was a non-success result of calling
- // CryptEncodeObject.
-
- if (string.IsNullOrEmpty(oidValue))
- throw new CryptographicException(SR.Argument_InvalidOidValue);
- if (oidValue.Length < 3 /* "1.1" is the shortest value */)
- throw new CryptographicException(SR.Argument_InvalidOidValue);
- if (oidValue[1] != '.')
- throw new CryptographicException(SR.Argument_InvalidOidValue);
-
- int firstRid;
-
- switch (oidValue[0])
- {
- case '0':
- firstRid = 0;
- break;
- case '1':
- firstRid = 1;
- break;
- case '2':
- firstRid = 2;
- break;
- default:
- throw new CryptographicException(SR.Argument_InvalidOidValue);
- }
-
- int startPos = 2;
-
- // The first two RIDs are special:
- // ITU X.690 8.19.4:
- // The numerical value of the first subidentifier is derived from the values of the first two
- // object identifier components in the object identifier value being encoded, using the formula:
- // (X*40) + Y
- // where X is the value of the first object identifier component and Y is the value of the
- // second object identifier component.
- // NOTE – This packing of the first two object identifier components recognizes that only
- // three values are allocated from the root node, and at most 39 subsequent values from
- // nodes reached by X = 0 and X = 1.
-
- BigInteger rid = ParseOidRid(oidValue, ref startPos);
- rid += 40 * firstRid;
-
- // The worst case is "1.1.1.1.1", which takes 4 bytes (5 rids, with the first two condensed)
- // Longer numbers get smaller: "2.1.127" is only 2 bytes. (81d (0x51) and 127 (0x7F))
- // So length / 2 should prevent any reallocations.
- List<byte> encodedBytes = new List<byte>(oidValue.Length / 2);
-
- EncodeRid(encodedBytes, ref rid);
-
- while (startPos < oidValue.Length)
- {
- rid = ParseOidRid(oidValue, ref startPos);
-
- EncodeRid(encodedBytes, ref rid);
- }
-
- return new byte[][]
- {
- new byte[] { (byte)DerSequenceReader.DerTag.ObjectIdentifier },
- EncodeLength(encodedBytes.Count),
- encodedBytes.ToArray(),
- };
- }
-
- /// <summary>
- /// Encode a character string as a UTF8String value.
- /// </summary>
- /// <param name="chars">The characters to be encoded.</param>
- /// <returns>The encoded segments { tag, length, value }</returns>
- internal static byte[][] SegmentedEncodeUtf8String(char[] chars)
- {
- Debug.Assert(chars != null);
-
- return SegmentedEncodeUtf8String(chars, 0, chars.Length);
- }
-
- /// <summary>
- /// Encode a substring as a UTF8String value.
- /// </summary>
- /// <param name="chars">The characters whose substring is to be encoded.</param>
- /// <param name="offset">The character offset into <paramref name="chars"/> at which to start.</param>
- /// <param name="count">The total number of characters from <paramref name="chars"/> to read.</param>
- /// <returns>The encoded segments { tag, length, value }</returns>
- internal static byte[][] SegmentedEncodeUtf8String(char[] chars, int offset, int count)
- {
- Debug.Assert(chars != null);
- Debug.Assert(offset >= 0);
- Debug.Assert(offset <= chars.Length);
- Debug.Assert(count >= 0);
- Debug.Assert(count <= chars.Length - offset);
-
- // ITU-T X.690 says that ISO/IEC 10646 Annex D should be used; but no announcers
- // or escape sequences, and each character shall be encoded in the smallest number of
- // bytes possible.
- //
- // Thankfully, that's just Encoding.UTF8.
-
- byte[] encodedBytes = System.Text.Encoding.UTF8.GetBytes(chars, offset, count);
-
- return new byte[][]
- {
- new byte[] { (byte)DerSequenceReader.DerTag.UTF8String },
- EncodeLength(encodedBytes.Length),
- encodedBytes,
- };
- }
-
- /// <summary>
- /// Make a constructed SEQUENCE of the byte-triplets of the contents, but leave
- /// the value in a segmented form (to be included in a larger SEQUENCE).
- /// </summary>
- /// <param name="items">Series of Tag-Length-Value triplets to build into one sequence.</param>
- /// <returns>The encoded segments { tag, length, value }</returns>
- internal static byte[][] ConstructSegmentedSequence(params byte[][][] items)
- {
- return ConstructSegmentedSequence((IEnumerable<byte[][]>)items);
- }
-
- internal static byte[][] ConstructSegmentedSequence(IEnumerable<byte[][]> items)
- {
- Debug.Assert(items != null);
-
- byte[] data = ConcatenateArrays(items);
-
- return new byte[][]
- {
- new byte[] { ConstructedSequenceTag },
- EncodeLength(data.Length),
- data,
- };
- }
-
- /// <summary>
- /// Make a context-specific tagged value which is constructed of other DER encoded values.
- /// Logically the same as a SEQUENCE, but providing context as to data interpretation (and usually
- /// indicates an optional element adjacent to another SEQUENCE).
- /// </summary>
- /// <param name="contextId">The value's context ID</param>
- /// <param name="items">Series of Tag-Length-Value triplets to build into one sequence.</param>
- /// <returns>The encoded segments { tag, length, value }</returns>
- internal static byte[][] ConstructSegmentedContextSpecificValue(int contextId, params byte[][][] items)
- {
- Debug.Assert(items != null);
- Debug.Assert(contextId >= 0 && contextId <= 30);
-
- byte[] data = ConcatenateArrays(items);
-
- byte tagId = (byte)(
- DerSequenceReader.ConstructedFlag |
- DerSequenceReader.ContextSpecificTagFlag |
- contextId);
-
- return new byte[][]
- {
- new byte[] { tagId },
- EncodeLength(data.Length),
- data,
- };
- }
-
- /// <summary>
- /// Make a constructed SET of the byte-triplets of the contents, but leave
- /// the value in a segmented form (to be included in a larger SEQUENCE).
- /// </summary>
- /// <param name="items">Series of Tag-Length-Value triplets to build into one set.</param>
- /// <returns>The encoded segments { tag, length, value }</returns>
- internal static byte[][] ConstructSegmentedSet(params byte[][][] items)
- {
- Debug.Assert(items != null);
-
- byte[][][] sortedItems = (byte[][][])items.Clone();
- Array.Sort(sortedItems, AsnSetValueComparer.Instance);
- byte[] data = ConcatenateArrays(sortedItems);
-
- return new byte[][]
- {
- new byte[] { ConstructedSetTag },
- EncodeLength(data.Length),
- data,
- };
- }
-
- /// <summary>
- /// Make a constructed SET of the byte-triplets of the contents, but leave
- /// the value in a segmented form (to be included in a larger SEQUENCE).
- ///
- /// This method assumes that the data is presorted, and writes it as-is.
- /// </summary>
- /// <param name="items">Series of Tag-Length-Value triplets to build into one set.</param>
- /// <returns>The encoded segments { tag, length, value }</returns>
- internal static byte[][] ConstructSegmentedPresortedSet(params byte[][][] items)
- {
- Debug.Assert(items != null);
-
- byte[] data = ConcatenateArrays(items);
-
- return new byte[][]
- {
- new byte[] { ConstructedSetTag },
- EncodeLength(data.Length),
- data,
- };
- }
-
- /// <summary>
- /// Test to see if the input characters contains only characters permitted by the ASN.1
- /// PrintableString restricted character set.
- /// </summary>
- /// <param name="chars">The characters to test.</param>
- /// <returns>
- /// <c>true</c> if all of the characters in <paramref name="chars"/> are valid PrintableString characters,
- /// <c>false</c> otherwise.
- /// </returns>
- internal static bool IsValidPrintableString(char[] chars)
- {
- Debug.Assert(chars != null);
-
- return IsValidPrintableString(chars, 0, chars.Length);
- }
-
- /// <summary>
- /// Test to see if the input substring contains only characters permitted by the ASN.1
- /// PrintableString restricted character set.
- /// </summary>
- /// <param name="chars">The character string to test.</param>
- /// <param name="offset">The starting character position within <paramref name="chars"/>.</param>
- /// <param name="count">The number of characters from <paramref name="chars"/> to read.</param>
- /// <returns>
- /// <c>true</c> if all of the indexed characters in <paramref name="chars"/> are valid PrintableString
- /// characters, <c>false</c> otherwise.
- /// </returns>
- internal static bool IsValidPrintableString(char[] chars, int offset, int count)
- {
- Debug.Assert(chars != null);
- Debug.Assert(offset >= 0);
- Debug.Assert(offset <= chars.Length);
- Debug.Assert(count >= 0);
- Debug.Assert(count <= chars.Length - offset);
-
- int end = count + offset;
-
- for (int i = offset; i < end; i++)
- {
- if (!IsPrintableStringCharacter(chars[i]))
- {
- return false;
- }
- }
-
- return true;
- }
-
- /// <summary>
- /// Encode a character string as a PrintableString value.
- /// </summary>
- /// <param name="chars">The characters to be encoded.</param>
- /// <returns>The encoded segments { tag, length, value }</returns>
- internal static byte[][] SegmentedEncodePrintableString(char[] chars)
- {
- Debug.Assert(chars != null);
-
- return SegmentedEncodePrintableString(chars, 0, chars.Length);
- }
-
- /// <summary>
- /// Encode a substring as a PrintableString value.
- /// </summary>
- /// <param name="chars">The character string whose substring is to be encoded.</param>
- /// <param name="offset">The character offset into <paramref name="chars"/> at which to start.</param>
- /// <param name="count">The total number of characters from <paramref name="chars"/> to read.</param>
- /// <returns>The encoded segments { tag, length, value }</returns>
- internal static byte[][] SegmentedEncodePrintableString(char[] chars, int offset, int count)
- {
- Debug.Assert(chars != null);
- Debug.Assert(offset >= 0);
- Debug.Assert(offset <= chars.Length);
- Debug.Assert(count >= 0);
- Debug.Assert(count <= chars.Length);
- Debug.Assert(offset + count <= chars.Length);
-
- Debug.Assert(IsValidPrintableString(chars, offset, count));
-
- // ITU-T X.690 (08/2015) 8.23.5 says to encode PrintableString as ISO/IEC 2022 using an implicit
- // context of G0=6 (ANSI/ASCII) and no explicit escape sequences are allowed.
- //
- // That's a long-winded way of saying that it's just ASCII. Since we've already established
- // that it fits the PrintableString character restrictions, we can just convert char to byte.
- byte[] encodedString = new byte[count];
-
- for (int i = 0; i < count; i++)
- {
- encodedString[i] = (byte)chars[i + offset];
- }
-
- return new byte[][]
- {
- new byte[] { (byte)DerSequenceReader.DerTag.PrintableString },
- EncodeLength(encodedString.Length),
- encodedString,
- };
- }
-
- /// <summary>
- /// Encode a string of characters as a IA5String value.
- /// </summary>
- /// <param name="chars">The characters to be encoded.</param>
- /// <returns>The encoded segments { tag, length, value }</returns>
- internal static byte[][] SegmentedEncodeIA5String(char[] chars)
- {
- Debug.Assert(chars != null);
-
- return SegmentedEncodeIA5String(chars, 0, chars.Length);
- }
-
- /// <summary>
- /// Encode a substring as a IA5String value.
- /// </summary>
- /// <param name="chars">The characters whose substring is to be encoded.</param>
- /// <param name="offset">The character offset into <paramref name="chars"/> at which to start.</param>
- /// <param name="count">The total number of characters from <paramref name="chars"/> to read.</param>
- /// <returns>The encoded segments { tag, length, value }</returns>
- internal static byte[][] SegmentedEncodeIA5String(char[] chars, int offset, int count)
- {
- Debug.Assert(chars != null);
- Debug.Assert(offset >= 0);
- Debug.Assert(offset <= chars.Length);
- Debug.Assert(count >= 0);
- Debug.Assert(count <= chars.Length);
- Debug.Assert(offset + count <= chars.Length);
-
- // ITU-T X.690 (08/2015) 8.23.5 says to encode IA5String as ISO/IEC 2022 using an implicit
- // context of G0=6 (ANSI/ASCII) and no explicit escape sequences are allowed.
- //
- // That's a long-winded way of saying that it's just ASCII 0x00-0x7F.
- byte[] encodedString = new byte[count];
-
- for (int i = 0; i < count; i++)
- {
- char c = chars[i + offset];
-
- // IA5 is ASCII 0x00-0x7F.
- if (c > 127)
- {
- throw new CryptographicException(SR.Cryptography_Invalid_IA5String);
- }
-
- encodedString[i] = (byte)c;
- }
-
- return new byte[][]
- {
- new byte[] { (byte)DerSequenceReader.DerTag.IA5String },
- EncodeLength(encodedString.Length),
- encodedString,
- };
- }
-
- internal static byte[][] SegmentedEncodeUtcTime(DateTime utcTime)
- {
- Debug.Assert(utcTime.Kind == DateTimeKind.Utc);
-
- // Low-allocating encoding of YYMMDDHHmmssZ
- byte[] asciiDateBytes = new byte[13];
-
- int year = utcTime.Year;
- int month = utcTime.Month;
- int day = utcTime.Day;
- int hour = utcTime.Hour;
- int minute = utcTime.Minute;
- int second = utcTime.Second;
-
- const byte Char0 = (byte)'0';
-
- asciiDateBytes[1] = (byte)(Char0 + (year % 10));
- year /= 10;
- asciiDateBytes[0] = (byte)(Char0 + (year % 10));
-
- asciiDateBytes[3] = (byte)(Char0 + (month % 10));
- month /= 10;
- asciiDateBytes[2] = (byte)(Char0 + (month % 10));
-
- asciiDateBytes[5] = (byte)(Char0 + (day % 10));
- day /= 10;
- asciiDateBytes[4] = (byte)(Char0 + (day % 10));
-
- asciiDateBytes[7] = (byte)(Char0 + (hour % 10));
- hour /= 10;
- asciiDateBytes[6] = (byte)(Char0 + (hour % 10));
-
- asciiDateBytes[9] = (byte)(Char0 + (minute % 10));
- minute /= 10;
- asciiDateBytes[8] = (byte)(Char0 + (minute % 10));
-
- asciiDateBytes[11] = (byte)(Char0 + (second % 10));
- second /= 10;
- asciiDateBytes[10] = (byte)(Char0 + (second % 10));
-
- asciiDateBytes[12] = (byte)'Z';
-
- return new[]
- {
- new byte[] { (byte)DerSequenceReader.DerTag.UTCTime },
- EncodeLength(asciiDateBytes.Length),
- asciiDateBytes,
- };
- }
-
- internal static byte[][] SegmentedEncodeGeneralizedTime(DateTime utcTime)
- {
- Debug.Assert(utcTime.Kind == DateTimeKind.Utc);
-
- // Low-allocating encoding of YYYYMMDDHHmmssZ
- byte[] asciiDateBytes = new byte[15];
-
- int year = utcTime.Year;
- int month = utcTime.Month;
- int day = utcTime.Day;
- int hour = utcTime.Hour;
- int minute = utcTime.Minute;
- int second = utcTime.Second;
-
- const byte Char0 = (byte)'0';
-
- asciiDateBytes[3] = (byte)(Char0 + (year % 10));
- year /= 10;
- asciiDateBytes[2] = (byte)(Char0 + (year % 10));
- year /= 10;
- asciiDateBytes[1] = (byte)(Char0 + (year % 10));
- year /= 10;
- asciiDateBytes[0] = (byte)(Char0 + (year % 10));
-
- asciiDateBytes[5] = (byte)(Char0 + (month % 10));
- month /= 10;
- asciiDateBytes[4] = (byte)(Char0 + (month % 10));
-
- asciiDateBytes[7] = (byte)(Char0 + (day % 10));
- day /= 10;
- asciiDateBytes[6] = (byte)(Char0 + (day % 10));
-
- asciiDateBytes[9] = (byte)(Char0 + (hour % 10));
- hour /= 10;
- asciiDateBytes[8] = (byte)(Char0 + (hour % 10));
-
- asciiDateBytes[11] = (byte)(Char0 + (minute % 10));
- minute /= 10;
- asciiDateBytes[10] = (byte)(Char0 + (minute % 10));
-
- asciiDateBytes[13] = (byte)(Char0 + (second % 10));
- second /= 10;
- asciiDateBytes[12] = (byte)(Char0 + (second % 10));
-
- asciiDateBytes[14] = (byte)'Z';
-
- return new[]
- {
- new byte[] { (byte)DerSequenceReader.DerTag.GeneralizedTime },
- EncodeLength(asciiDateBytes.Length),
- asciiDateBytes,
- };
- }
-
- /// <summary>
- /// Make a constructed SEQUENCE of the byte-triplets of the contents.
- /// Each byte[][] should be a byte[][3] of {tag (1 byte), length (1-5 bytes), payload (variable)}.
- /// </summary>
- internal static byte[] ConstructSequence(params byte[][][] items)
- {
- return ConstructSequence((IEnumerable<byte[][]>)items);
- }
-
- /// <summary>
- /// Make a constructed SEQUENCE of the byte-triplets of the contents.
- /// Each byte[][] should be a byte[][3] of {tag (1 byte), length (1-5 bytes), payload (variable)}.
- /// </summary>
- internal static byte[] ConstructSequence(IEnumerable<byte[][]> items)
- {
- // A more robust solution would be required for public API. DerInteger(int), DerBoolean, etc,
- // which do not allow the user to specify lengths, but only the payload. But for efficiency things
- // are tracked as just little segments of bytes, and they're not glued together until this method.
-
- int payloadLength = 0;
-
- foreach (byte[][] segments in items)
- {
- foreach (byte[] segment in segments)
- {
- payloadLength += segment.Length;
- }
- }
-
- byte[] encodedLength = EncodeLength(payloadLength);
-
- // The tag (1) + the length of the length + the length of the payload
- byte[] encodedSequence = new byte[1 + encodedLength.Length + payloadLength];
-
- encodedSequence[0] = ConstructedSequenceTag;
-
- int writeStart = 1;
-
- Buffer.BlockCopy(encodedLength, 0, encodedSequence, writeStart, encodedLength.Length);
-
- writeStart += encodedLength.Length;
-
- foreach (byte[][] segments in items)
- {
- Debug.Assert(segments != null);
- Debug.Assert(segments.Length == 3);
-
- foreach (byte[] segment in segments)
- {
- Debug.Assert(segment != null);
-
- Buffer.BlockCopy(segment, 0, encodedSequence, writeStart, segment.Length);
- writeStart += segment.Length;
- }
- }
-
- return encodedSequence;
- }
-
- private static BigInteger ParseOidRid(string oidValue, ref int startIndex)
- {
- Debug.Assert(startIndex < oidValue.Length);
-
- int endIndex = oidValue.IndexOf('.', startIndex);
-
- if (endIndex == -1)
- {
- endIndex = oidValue.Length;
- }
-
- Debug.Assert(endIndex > startIndex);
-
- // The following code is equivalent to
- // BigInteger.TryParse(temp, NumberStyles.None, CultureInfo.InvariantCulture, out value)
- // but doesn't require creating the intermediate substring (TryParse doesn't take start+end/length values)
-
- BigInteger value = BigInteger.Zero;
-
- for (int position = startIndex; position < endIndex; position++)
- {
- value *= 10;
- value += AtoI(oidValue[position]);
- }
-
- startIndex = endIndex + 1;
- return value;
- }
-
- private static int AtoI(char c)
- {
- if (c >= '0' && c <= '9')
- return c - '0';
-
- throw new CryptographicException(SR.Argument_InvalidOidValue);
- }
-
- private static void EncodeRid(List<byte> encodedData, ref BigInteger rid)
- {
- BigInteger divisor = new BigInteger(128);
- BigInteger unencoded = rid;
-
- // The encoding is 7 bits of value and the 8th bit signifies "keep reading".
- // So, for the input 1079 (0b0000 0100 0011 0111) the partitioning is
- // 00|00 0100 0|011 0111, and the leading two bits are ignored.
- // Therefore the encoded is 0b1000 1000 0011 0111 = 0x8837
-
- Stack<byte> littleEndianBytes = new Stack<byte>();
- byte continuance = 0;
-
- do
- {
- BigInteger remainder;
- unencoded = BigInteger.DivRem(unencoded, divisor, out remainder);
-
- byte octet = (byte)remainder;
- octet |= continuance;
-
- // Any remaining (preceding) bytes need the continuance bit set.
- continuance = 0x80;
- littleEndianBytes.Push(octet);
- }
- while (unencoded != BigInteger.Zero);
-
- encodedData.AddRange(littleEndianBytes);
- }
-
- private static bool IsPrintableStringCharacter(char c)
- {
- // ITU-T X.680 (11/2008)
- // 41.4 (PrintableString)
-
- // Latin Capital, Latin Small, Digits
- if ((c >= 'A' && c <= 'Z') ||
- (c >= 'a' && c <= 'z') ||
- (c >= '0' && c <= '9'))
- {
- return true;
- }
-
- // Individually included characters
- switch (c)
- {
- case ' ':
- case '\'':
- case '(':
- case ')':
- case '+':
- case ',':
- case '-':
- case '.':
- case '/':
- case ':':
- case '=':
- case '?':
- return true;
- }
-
- return false;
- }
-
- private static byte[] ConcatenateArrays(params byte[][][] segments)
- {
- return ConcatenateArrays((IEnumerable<byte[][]>)segments);
- }
-
- private static byte[] ConcatenateArrays(IEnumerable<byte[][]> segments)
- {
- int length = 0;
-
- foreach (byte[][] middleSegments in segments)
- {
- foreach (byte[] segment in middleSegments)
- {
- length += segment.Length;
- }
- }
-
- byte[] concatenated = new byte[length];
-
- int offset = 0;
-
- foreach (byte[][] middleSegments in segments)
- {
- foreach (byte[] segment in middleSegments)
- {
- Buffer.BlockCopy(segment, 0, concatenated, offset, segment.Length);
- offset += segment.Length;
- }
- }
-
- return concatenated;
- }
-
- private class AsnSetValueComparer : IComparer<byte[][]>, IComparer
- {
- public static AsnSetValueComparer Instance { get; } = new AsnSetValueComparer();
-
- public int Compare(byte[][] x, byte[][] y)
- {
- Debug.Assert(x != null && y != null, "Comparing one more more null values");
- Debug.Assert(x.Length == 3, $"x.Length is {x.Length} when it should be 3");
- Debug.Assert(y.Length == 3, $"y.Length is {y.Length} when it should be 3");
-
- // ITU-T X.690 11.6 Set-of components (CER/DER)
- // The encodings of the component values of a set-of value shall appear in
- // ascending order, the encodings being compared as octet strings with the
- // shorter components being padded at their trailing end with 0-octets.
- //
- // ITU-T X.690 10.3 Set components (DER)
- // The encodings of the component values of a set value shall appear in an
- // order determined by their tags as specified in 8.6 of
- // Rec. ITU-T X.680 | ISO/IEC 8824-1.
- //
- // ITU-T X.680 8.6
- // The canonical order for tags is based on the outermost tag of each type
- // and is defined as follows:
- // a) those elements or alternatives with universal class tags shall appear
- // first, followed by those with application class tags, followed by those
- // with context-specific tags, followed by those with private class tags;
- // b) within each class of tags, the elements or alternatives shall appear
- // in ascending order of their tag numbers.
- //
- // ITU-T X.690 8.1.2.2 says:
- // Universal: 0b00xxxxxx
- // Application: 0b01xxxxxx
- // Context-Specific: 0b10xxxxxx
- // Private: 0b11xxxxxx
- //
- // The net result is that the DER order is just lexicographic ordering by
- // the TLV (tag-length-value) encoding. The comment in 11.6 about treating
- // a shorter value as if it was right-padded with zeroes doesn't apply to DER,
- // due to the length encoding. But for indefinite-length CER it would.
- Debug.Assert(x[0].Length == 1, $"x[0].Length is {x[0].Length} when it should be 1");
- Debug.Assert(y[0].Length == 1, $"y[0].Length is {y[0].Length} when it should be 1");
-
- int comparison = x[0][0] - y[0][0];
-
- if (comparison != 0)
- return comparison;
-
- // The length encoding will always sort lexicographically based on its value, due to the
- // length-or-length-length first byte. So skip over the [1] values and compare [2].Length.
- comparison = x[2].Length - y[2].Length;
-
- if (comparison != 0)
- return comparison;
-
- for (int i = 0; i < x[2].Length; i++)
- {
- comparison = x[2][i] - y[2][i];
-
- if (comparison != 0)
- {
- return comparison;
- }
- }
-
- return 0;
- }
-
- public int Compare(object x, object y)
- {
- return Compare(x as byte[][], y as byte[][]);
- }
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Diagnostics;
-using System.Globalization;
-using System.Numerics;
-using System.Text;
-using System.Threading;
-
-namespace System.Security.Cryptography
-{
- /// <summary>
- /// Reads data encoded via the Distinguished Encoding Rules for Abstract
- /// Syntax Notation 1 (ASN.1) data.
- /// </summary>
- internal class DerSequenceReader
- {
- internal const byte ContextSpecificTagFlag = 0x80;
- internal const byte ConstructedFlag = 0x20;
- internal const byte ContextSpecificConstructedTag0 = ContextSpecificTagFlag | ConstructedFlag;
- internal const byte ContextSpecificConstructedTag1 = ContextSpecificConstructedTag0 | 1;
- internal const byte ContextSpecificConstructedTag2 = ContextSpecificConstructedTag0 | 2;
- internal const byte ContextSpecificConstructedTag3 = ContextSpecificConstructedTag0 | 3;
- internal const byte ConstructedSequence = ConstructedFlag | (byte)DerTag.Sequence;
-
- // 0b1100_0000
- internal const byte TagClassMask = 0xC0;
- internal const byte TagNumberMask = 0x1F;
-
- internal static DateTimeFormatInfo s_validityDateTimeFormatInfo;
-
- private static System.Text.Encoding s_utf8EncodingWithExceptionFallback;
- private static System.Text.Encoding s_latin1Encoding;
-
- private readonly byte[] _data;
- private readonly int _end;
- private int _position;
-
- internal int ContentLength { get; private set; }
-
- private DerSequenceReader(bool startAtPayload, byte[] data, int offset, int length)
- {
- Debug.Assert(startAtPayload, "This overload is only for bypassing the sequence tag");
- Debug.Assert(data != null, "Data is null");
- Debug.Assert(offset >= 0, "Offset is negative");
-
- _data = data;
- _position = offset;
- _end = offset + length;
-
- ContentLength = length;
- }
-
- internal DerSequenceReader(byte[] data)
- : this(data, 0, data.Length)
- {
- }
-
- internal DerSequenceReader(byte[] data, int offset, int length)
- : this(DerTag.Sequence, data, offset, length)
- {
- }
-
- private DerSequenceReader(DerTag tagToEat, byte[] data, int offset, int length)
- {
- Debug.Assert(data != null, "Data is null");
-
- if (offset < 0 || length < 2 || length > data.Length - offset)
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
-
- _data = data;
- _end = offset + length;
- _position = offset;
- EatTag(tagToEat);
- int contentLength = EatLength();
- Debug.Assert(_end - contentLength >= _position);
- ContentLength = contentLength;
-
- // If the sequence reports being smaller than the buffer, shrink the end-of-validity.
- _end = _position + contentLength;
- }
-
- internal static DerSequenceReader CreateForPayload(byte[] payload)
- {
- return new DerSequenceReader(true, payload, 0, payload.Length);
- }
-
- internal bool HasData
- {
- get { return _position < _end; }
- }
-
- internal byte PeekTag()
- {
- if (!HasData)
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
-
- byte tag = _data[_position];
-
- if ((tag & TagNumberMask) == TagNumberMask)
- {
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
- }
-
- return tag;
- }
-
- internal bool HasTag(DerTag expectedTag)
- {
- return HasTag((byte)expectedTag);
- }
-
- internal bool HasTag(byte expectedTag)
- {
- return HasData && _data[_position] == expectedTag;
- }
-
- internal void SkipValue()
- {
- EatTag((DerTag)PeekTag());
- int contentLength = EatLength();
- _position += contentLength;
- }
-
- internal void ValidateAndSkipDerValue()
- {
- byte tag = PeekTag();
-
- // If the tag is in the UNIVERSAL class
- if ((tag & TagClassMask) == 0)
- {
- // Tag 0 is special ("reserved for use by the encoding rules"), but mainly is used
- // as the End-of-Contents marker for the indefinite length encodings, which DER prohibits.
- //
- // Tag 15 is reserved.
- //
- // So either of these are invalid.
-
- if (tag == 0 || tag == 15)
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
-
- // DER limits the constructed encoding to SEQUENCE and SET, as well as anything which gets
- // a defined encoding as being an IMPLICIT SEQUENCE.
-
- bool expectConstructed = false;
-
- switch (tag & TagNumberMask)
- {
- case 0x08: // External or Instance-Of
- case 0x0B: // EmbeddedPDV
- case (byte)DerTag.Sequence:
- case (byte)DerTag.Set:
- case 0x1D: // Unrestricted Character String
- expectConstructed = true;
- break;
- }
-
- bool isConstructed = (tag & ConstructedFlag) == ConstructedFlag;
-
- if (expectConstructed != isConstructed)
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
- }
-
- EatTag((DerTag)tag);
- int contentLength = EatLength();
-
- if (contentLength > 0 && (tag & ConstructedFlag) == ConstructedFlag)
- {
- var childReader = new DerSequenceReader(true, _data, _position, _end - _position);
-
- while (childReader.HasData)
- {
- childReader.ValidateAndSkipDerValue();
- }
- }
-
- _position += contentLength;
- }
-
- /// <summary>
- /// Returns the next value encoded (this includes tag and length)
- /// </summary>
- internal byte[] ReadNextEncodedValue()
- {
- // Check that the tag is legal, but the value isn't relevant.
- PeekTag();
-
- int lengthLength;
- int contentLength = ScanContentLength(_data, _position + 1, _end, out lengthLength);
- // Length of tag, encoded length, and the content
- int totalLength = 1 + lengthLength + contentLength;
- Debug.Assert(_end - totalLength >= _position);
-
- byte[] encodedValue = new byte[totalLength];
- Buffer.BlockCopy(_data, _position, encodedValue, 0, totalLength);
-
- _position += totalLength;
- return encodedValue;
- }
-
- internal bool ReadBoolean()
- {
- EatTag(DerTag.Boolean);
-
- int length = EatLength();
-
- if (length != 1)
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
-
- bool value = _data[_position] != 0;
- _position += length;
- return value;
- }
-
- internal int ReadInteger()
- {
- byte[] integerBytes = ReadIntegerBytes();
-
- // integerBytes is currently Big-Endian, need to reverse it for
- // Little-Endian to pass into BigInteger.
- Array.Reverse(integerBytes);
- BigInteger bigInt = new BigInteger(integerBytes);
- return (int)bigInt;
- }
-
- internal byte[] ReadIntegerBytes()
- {
- EatTag(DerTag.Integer);
-
- return ReadContentAsBytes();
- }
-
- internal byte[] ReadBitString()
- {
- EatTag(DerTag.BitString);
-
- int contentLength = EatLength();
-
- if (contentLength < 1)
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
-
- byte unusedBits = _data[_position];
-
- if (unusedBits > 7)
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
-
- // skip the "unused bits" byte
- contentLength--;
- _position++;
-
- byte[] octets = new byte[contentLength];
- Buffer.BlockCopy(_data, _position, octets, 0, contentLength);
-
- _position += contentLength;
- return octets;
- }
-
- internal byte[] ReadOctetString()
- {
- EatTag(DerTag.OctetString);
-
- return ReadContentAsBytes();
- }
-
- internal string ReadOidAsString()
- {
- EatTag(DerTag.ObjectIdentifier);
- int contentLength = EatLength();
-
- if (contentLength < 1)
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
-
- // Each byte could cause 3 decimal characters to be written, plus a period. Over-allocate
- // and avoid re-alloc.
- StringBuilder builder = new StringBuilder(contentLength * 4);
-
- // The first byte is ((X * 40) + Y), where X is the first segment and Y the second.
- // ISO/IEC 8825-1:2003 section 8.19.4
-
- byte firstByte = _data[_position];
- byte first = (byte)(firstByte / 40);
- byte second = (byte)(firstByte % 40);
-
- builder.Append(first);
- builder.Append('.');
- builder.Append(second);
-
- // For the rest of the segments, the high bit on the byte is a continuation marker,
- // and data is loaded into a BigInteger 7 bits at a time.
- //
- // When the high bit is 0, the segment ends, so emit a '.' between it and the next one.
- //
- // ISO/IEC 8825-1:2003 section 8.19.2, and the .NET representation of Oid.Value.
- bool needDot = true;
- BigInteger bigInt = new BigInteger(0);
-
- for (int i = 1; i < contentLength; i++)
- {
- byte current = _data[_position + i];
- byte data = (byte)(current & 0x7F);
-
- if (needDot)
- {
- builder.Append('.');
- needDot = false;
- }
-
- bigInt <<= 7;
- bigInt += data;
-
- if (current == data)
- {
- builder.Append(bigInt);
- bigInt = 0;
- needDot = true;
- }
- }
-
- _position += contentLength;
- return builder.ToString();
- }
-
- internal Oid ReadOid()
- {
- return new Oid(ReadOidAsString());
- }
-
- internal string ReadUtf8String()
- {
- EatTag(DerTag.UTF8String);
- int contentLength = EatLength();
-
- string str = System.Text.Encoding.UTF8.GetString(_data, _position, contentLength);
- _position += contentLength;
-
- return TrimTrailingNulls(str);
- }
-
- private DerSequenceReader ReadCollectionWithTag(DerTag expected)
- {
- // DerSequenceReader wants to read its own tag, so don't EatTag here.
- CheckTag(expected, _data, _position);
-
- int lengthLength;
- int contentLength = ScanContentLength(_data, _position + 1, _end, out lengthLength);
- int totalLength = 1 + lengthLength + contentLength;
-
- DerSequenceReader reader = new DerSequenceReader(expected, _data, _position, totalLength);
- _position += totalLength;
- return reader;
- }
-
- internal DerSequenceReader ReadSequence()
- {
- return ReadCollectionWithTag(DerTag.Sequence);
- }
-
- internal DerSequenceReader ReadSet()
- {
- return ReadCollectionWithTag(DerTag.Set);
- }
-
- internal string ReadPrintableString()
- {
- EatTag(DerTag.PrintableString);
- int contentLength = EatLength();
-
- // PrintableString is a subset of ASCII, so just return the ASCII interpretation.
- string str = System.Text.Encoding.ASCII.GetString(_data, _position, contentLength);
- _position += contentLength;
-
- return TrimTrailingNulls(str);
- }
-
- internal string ReadIA5String()
- {
- EatTag(DerTag.IA5String);
- int contentLength = EatLength();
-
- // IA5 (International Alphabet - 5) is functionally equivalent to 7-bit ASCII.
-
- string ia5String = System.Text.Encoding.ASCII.GetString(_data, _position, contentLength);
- _position += contentLength;
-
- return TrimTrailingNulls(ia5String);
- }
-
- internal string ReadT61String()
- {
- EatTag(DerTag.T61String);
- int contentLength = EatLength();
- string t61String;
-
- // Technically the T.61 encoding (code page 20261) should be used here, but many
- // implementations don't follow that and use different character sets. CryptoAPI
- // on NetFX seems to interpret it as UTF-8 with fallback to ISO 8859-1. OpenSSL
- // seems to interpret it as ISO 8859-1 with no support for UTF-8.
- // https://github.com/dotnet/corefx/issues/27466
-
- System.Text.Encoding utf8EncodingWithExceptionFallback = LazyInitializer.EnsureInitialized(
- ref s_utf8EncodingWithExceptionFallback,
- () => new UTF8Encoding(false, true));
- System.Text.Encoding latin1Encoding = LazyInitializer.EnsureInitialized(
- ref s_latin1Encoding,
- () => System.Text.Encoding.GetEncoding("iso-8859-1"));
-
- try
- {
- t61String = utf8EncodingWithExceptionFallback.GetString(_data, _position, contentLength);
- }
- catch (DecoderFallbackException)
- {
- t61String = latin1Encoding.GetString(_data, _position, contentLength);
- }
- _position += contentLength;
-
- return TrimTrailingNulls(t61String);
- }
-
- internal DateTime ReadX509Date()
- {
- byte tag = PeekTag();
-
- switch ((DerTag)tag)
- {
- case DerTag.UTCTime:
- return ReadUtcTime();
- case DerTag.GeneralizedTime:
- return ReadGeneralizedTime();
- }
-
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
- }
-
- internal DateTime ReadUtcTime()
- {
- return ReadTime(DerTag.UTCTime, "yyMMddHHmmss'Z'");
- }
-
- internal DateTime ReadGeneralizedTime()
- {
- // Currently only supports reading times with no fractional seconds or time differentials
- // as RFC 2630 doesn't allow these. In case this is done, the format string has to be parsed
- // to follow rules on X.680 and X.690.
- return ReadTime(DerTag.GeneralizedTime, "yyyyMMddHHmmss'Z'");
- }
-
- internal string ReadBMPString()
- {
- EatTag(DerTag.BMPString);
- int contentLength = EatLength();
-
- // BMPString or Basic Multilingual Plane, is equal to UCS-2.
- // And since this is cryptography, it's Big Endian.
- string str = System.Text.Encoding.BigEndianUnicode.GetString(_data, _position, contentLength);
- _position += contentLength;
-
- return TrimTrailingNulls(str);
- }
-
- private static string TrimTrailingNulls(string value)
- {
- // .NET's string comparisons start by checking the length, so a trailing
- // NULL character which was literally embedded in the DER would cause a
- // failure in .NET whereas it wouldn't have with strcmp.
- if (value?.Length > 0)
- {
- int newLength = value.Length;
-
- while (newLength > 0 && value[newLength - 1] == 0)
- {
- newLength--;
- }
-
- if (newLength != value.Length)
- {
- return value.Substring(0, newLength);
- }
- }
-
- return value;
- }
-
- private DateTime ReadTime(DerTag timeTag, string formatString)
- {
- EatTag(timeTag);
- int contentLength = EatLength();
-
- string decodedTime = System.Text.Encoding.ASCII.GetString(_data, _position, contentLength);
- _position += contentLength;
-
- Debug.Assert(
- decodedTime[decodedTime.Length - 1] == 'Z',
- $"The date doesn't follow the X.690 format, ending with {decodedTime[decodedTime.Length - 1]}");
-
- DateTime time;
-
- DateTimeFormatInfo fi = LazyInitializer.EnsureInitialized(
- ref s_validityDateTimeFormatInfo,
- () =>
- {
- var clone = (DateTimeFormatInfo)CultureInfo.InvariantCulture.DateTimeFormat.Clone();
- clone.Calendar.TwoDigitYearMax = 2049;
-
- return clone;
- });
-
- if (!DateTime.TryParseExact(
- decodedTime,
- formatString,
- fi,
- DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
- out time))
- {
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
- }
-
- return time;
- }
-
- private byte[] ReadContentAsBytes()
- {
- int contentLength = EatLength();
-
- byte[] octets = new byte[contentLength];
- Buffer.BlockCopy(_data, _position, octets, 0, contentLength);
-
- _position += contentLength;
- return octets;
- }
-
- private void EatTag(DerTag expected)
- {
- if (!HasData)
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
-
- CheckTag(expected, _data, _position);
- _position++;
- }
-
- private static void CheckTag(DerTag expected, byte[] data, int position)
- {
- if (position >= data.Length)
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
-
- byte actual = data[position];
- byte relevant = (byte)(actual & TagNumberMask);
-
- // Multi-byte tags are not supported by this implementation.
- if (relevant == TagNumberMask)
- {
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
- }
-
- // Context-specific datatypes cannot be tag-verified
- if ((actual & ContextSpecificTagFlag) != 0)
- {
- return;
- }
-
- byte expectedByte = (byte)((byte)expected & TagNumberMask);
-
- if (expectedByte != relevant)
- {
- throw new CryptographicException(
- SR.Cryptography_Der_Invalid_Encoding
-#if DEBUG
- ,
- new InvalidOperationException(
- "Expected tag '0x" + expectedByte.ToString("X2") +
- "', got '0x" + actual.ToString("X2") +
- "' at position " + position)
-#endif
- );
- }
- }
-
- private int EatLength()
- {
- int bytesConsumed;
- int answer = ScanContentLength(_data, _position, _end, out bytesConsumed);
-
- _position += bytesConsumed;
- return answer;
- }
-
- private static int ScanContentLength(byte[] data, int offset, int end, out int bytesConsumed)
- {
- Debug.Assert(end <= data.Length);
-
- if (offset >= end)
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
-
- byte lengthOrLengthLength = data[offset];
-
- if (lengthOrLengthLength < 0x80)
- {
- bytesConsumed = 1;
-
- if (lengthOrLengthLength > end - offset - bytesConsumed)
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
-
- return lengthOrLengthLength;
- }
-
- int lengthLength = (lengthOrLengthLength & 0x7F);
-
- if (lengthLength > sizeof(int))
- {
- // .NET Arrays cannot exceed int.MaxValue in length. Since we're bounded by an
- // array we know that this is invalid data.
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
- }
-
- // The one byte which was lengthLength, plus the number of bytes it said to consume.
- bytesConsumed = 1 + lengthLength;
-
- if (bytesConsumed > end - offset)
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
-
- // CER indefinite length is not supported.
- if (bytesConsumed == 1)
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
-
- int lengthEnd = offset + bytesConsumed;
- int accum = 0;
-
- // data[offset] is lengthLength, so start at data[offset + 1] and stop before
- // data[offset + 1 + lengthLength], aka data[end].
- for (int i = offset + 1; i < lengthEnd; i++)
- {
- accum <<= 8;
- accum |= data[i];
- }
-
- if (accum < 0)
- {
- // .NET Arrays cannot exceed int.MaxValue in length. Since we're bounded by an
- // array we know that this is invalid data.
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
- }
-
- if (accum > end - offset - bytesConsumed)
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
-
- return accum;
- }
-
- internal enum DerTag : byte
- {
- Boolean = 0x01,
- Integer = 0x02,
- BitString = 0x03,
- OctetString = 0x04,
- Null = 0x05,
- ObjectIdentifier = 0x06,
- UTF8String = 0x0C,
- Sequence = 0x10,
- Set = 0x11,
- PrintableString = 0x13,
- T61String = 0x14,
- IA5String = 0x16,
- UTCTime = 0x17,
- GeneralizedTime = 0x18,
- BMPString = 0x1E,
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Text;
-using Test.Cryptography;
-using Xunit;
-
-namespace System.Security.Cryptography.Encoding.Tests
-{
- public class DerEncoderTests
- {
- [Theory]
- [InlineData("00", 0x02, "01", "00")]
- [InlineData("01", 0x02, "01", "01")]
- [InlineData("7F", 0x02, "01", "7F")]
- [InlineData("80", 0x02, "02", "0080")]
- [InlineData("FF", 0x02, "02", "00FF")]
- [InlineData("00FF", 0x02, "02", "00FF")]
- [InlineData("01FF", 0x02, "02", "01FF")]
- [InlineData("7FFF", 0x02, "02", "7FFF")]
- [InlineData("8000", 0x02, "03", "008000")]
- [InlineData("FFFF", 0x02, "03", "00FFFF")]
- [InlineData("00FFFF", 0x02, "03", "00FFFF")]
- [InlineData("7FFFFF", 0x02, "03", "7FFFFF")]
- [InlineData("800000", 0x02, "04", "00800000")]
- [InlineData("FFFFFF", 0x02, "04", "00FFFFFF")]
- [InlineData("00FFFFFF", 0x02, "04", "00FFFFFF")]
- [InlineData("7FFFFFFF", 0x02, "04", "7FFFFFFF")]
- [InlineData("80000000", 0x02, "05", "0080000000")]
- [InlineData("FFFFFFFF", 0x02, "05", "00FFFFFFFF")]
- [InlineData("0123456789ABCDEF", 0x02, "08", "0123456789ABCDEF")]
- [InlineData("FEDCBA9876543210", 0x02, "09", "00FEDCBA9876543210")]
- public static void ValidateUintEncodings(string hexRaw, byte tag, string hexLength, string hexValue)
- {
- byte[] raw = hexRaw.HexToByteArray();
- byte[] length = hexLength.HexToByteArray();
- byte[] value = hexValue.HexToByteArray();
-
- byte[][] segments = DerEncoder.SegmentedEncodeUnsignedInteger(raw);
-
- Assert.Equal(3, segments.Length);
-
- Assert.Equal(new[] { tag }, segments[0]);
- Assert.Equal(length, segments[1]);
- Assert.Equal(value, segments[2]);
- }
-
- [Theory]
- [InlineData("", 0, "01", "00")]
- [InlineData("00", 0, "02", "0000")]
- [InlineData("00", 7, "02", "0700")]
- [InlineData("0000", 0, "03", "000000")]
- [InlineData("007F", 7, "03", "070000")]
- [InlineData("007F", 6, "03", "060040")]
- [InlineData("007F", 5, "03", "050060")]
- [InlineData("007F", 4, "03", "040070")]
- [InlineData("007F", 3, "03", "030078")]
- [InlineData("007F", 2, "03", "02007C")]
- [InlineData("007F", 1, "03", "01007E")]
- [InlineData("007F", 0, "03", "00007F")]
- public static void ValidateBitStringEncodings(string hexRaw, int unusedBits, string hexLength, string encodedData)
- {
- byte[] input = hexRaw.HexToByteArray();
- const byte tag = 0x03;
- byte[] length = hexLength.HexToByteArray();
- byte[] expectedOutput = encodedData.HexToByteArray();
-
- byte[][] segments = DerEncoder.SegmentedEncodeBitString(unusedBits, input);
-
- Assert.Equal(3, segments.Length);
-
- Assert.Equal(new[] { tag }, segments[0]);
- Assert.Equal(length, segments[1]);
- Assert.Equal(expectedOutput, segments[2]);
- }
-
- [Theory]
- [InlineData("", 9, "01", "00")]
- [InlineData("00", 9, "01", "00")]
- [InlineData("0000", 9, "01", "00")]
- [InlineData("007F", 9, "01", "00")]
- [InlineData("8000", 3, "02", "0780")]
- [InlineData("8FF0", 3, "02", "0780")]
- [InlineData("8FF0", 7, "02", "018E")]
- [InlineData("8FF0", 8, "02", "008F")]
- [InlineData("8FF0", 9, "03", "078F80")]
- public static void ValidateNamedBitEncodings(string hexRaw, int namedBits, string hexLength, string encodedData)
- {
- byte[] input = hexRaw.HexToByteArray();
- const byte tag = 0x03;
- byte[] length = hexLength.HexToByteArray();
- byte[] expectedOutput = encodedData.HexToByteArray();
-
- byte[][] segments = DerEncoder.SegmentedEncodeNamedBitList(input, namedBits);
-
- Assert.Equal(3, segments.Length);
-
- Assert.Equal(new[] { tag }, segments[0]);
- Assert.Equal(length, segments[1]);
- Assert.Equal(expectedOutput, segments[2]);
- }
-
- [Theory]
- [InlineData("010203040506070809", "09")]
- [InlineData("", "00")]
- public static void ValidateOctetStringEncodings(string hexData, string hexLength)
- {
- byte[] input = hexData.HexToByteArray();
- const byte tag = 0x04;
- byte[] length = hexLength.HexToByteArray();
-
- byte[][] segments = DerEncoder.SegmentedEncodeOctetString(input);
-
- Assert.Equal(3, segments.Length);
-
- Assert.Equal(new[] { tag }, segments[0]);
- Assert.Equal(length, segments[1]);
- Assert.Equal(input, segments[2]);
- }
-
- [Theory]
- [InlineData("1.3.6.1.5.5.7.3.1", "08", "2B06010505070301")]
- [InlineData("1.3.6.1.5.5.7.3.2", "08", "2B06010505070302")]
- [InlineData("1.3.132.0.34", "05", "2B81040022")]
- [InlineData("2.999.3", "03", "883703")]
- [InlineData("2.999.19427512891.25", "08", "8837C8AFE1A43B19")]
- public static void ValidateOidEncodings(string oidValue, string hexLength, string encodedData)
- {
- Oid oid = new Oid(oidValue, oidValue);
- const byte tag = 0x06;
- byte[] length = hexLength.HexToByteArray();
- byte[] expectedOutput = encodedData.HexToByteArray();
-
- byte[][] segments = DerEncoder.SegmentedEncodeOid(oid);
-
- Assert.Equal(3, segments.Length);
-
- Assert.Equal(new[] { tag }, segments[0]);
- Assert.Equal(length, segments[1]);
- Assert.Equal(expectedOutput, segments[2]);
- }
-
- [Fact]
- public static void ConstructSequence()
- {
- byte[] expected =
- {
- /* SEQUENCE */ 0x30, 0x07,
- /* INTEGER(0) */ 0x02, 0x01, 0x00,
- /* INTEGER(256) */ 0x02, 0x02, 0x01, 0x00,
- };
-
- byte[] encoded = DerEncoder.ConstructSequence(
- DerEncoder.SegmentedEncodeUnsignedInteger(new byte[] { 0x00 }),
- DerEncoder.SegmentedEncodeUnsignedInteger(new byte[] { 0x01, 0x00 }));
-
- Assert.Equal(expected, encoded);
- }
-
- [Theory]
- [InlineData("", true)]
- [InlineData("This is a PrintableString.", true)]
- [InlineData("This is not @ PrintableString.", false)]
- [InlineData("\u65E5\u672C\u8A9E is not a PrintableString", false)]
- public static void CheckPrintableString(string candidate, bool expected)
- {
- Assert.Equal(expected, DerEncoder.IsValidPrintableString(candidate.ToCharArray()));
- }
-
- // No bounds check tests are done here, because the method is currently assert-based,
- // not exception-based.
- [Theory]
- [InlineData("", 0, 0, true)]
- [InlineData("This is a PrintableString.", 0, 26, true)]
- [InlineData("This is not @ PrintableString.", 0, 30, false)]
- [InlineData("This is not @ PrintableString.", 0, 12, true)]
- [InlineData("This is not @ PrintableString.", 12, 0, true)]
- [InlineData("This is not @ PrintableString.", 12, 1, false)]
- [InlineData("This is not @ PrintableString.", 13, 17, true)]
- [InlineData("\u65E5\u672C\u8A9E is not a PrintableString", 0, 27, false)]
- [InlineData("\u65E5\u672C\u8A9E is not a PrintableString", 0, 0, true)]
- [InlineData("\u65E5\u672C\u8A9E is not a PrintableString", 3, 24, true)]
- public static void CheckPrintableSubstring(string candidate, int offset, int length, bool expected)
- {
- Assert.Equal(expected, DerEncoder.IsValidPrintableString(candidate.ToCharArray(), offset, length));
- }
-
- [Theory]
- [InlineData("", "")]
- [InlineData("Hello", "48656C6C6F")]
- [InlineData("banana", "62616E616E61")]
- public static void CheckIA5StringEncoding(string input, string expectedHex)
- {
- byte[][] encodedString = DerEncoder.SegmentedEncodeIA5String(input.ToCharArray());
-
- Assert.NotNull(encodedString);
- Assert.Equal(3, encodedString.Length);
-
- // Check the tag
- Assert.NotNull(encodedString[0]);
- Assert.Equal(1, encodedString[0].Length);
- Assert.Equal(0x16, encodedString[0][0]);
-
- // Check the length. Since the input cases are all less than 0x7F bytes
- // the length is only one byte.
- Assert.NotNull(encodedString[1]);
- Assert.Equal(1, encodedString[1].Length);
- Assert.Equal(expectedHex.Length / 2, encodedString[1][0]);
-
- // Check the value
- Assert.Equal(expectedHex.HexToByteArray(), encodedString[2]);
-
- // And, full roundtrip
- Assert.Equal(input, Text.Encoding.ASCII.GetString(encodedString[2]));
- }
-
- [Theory]
- [InlineData("Hello", 3, 2, "6C6F")]
- [InlineData("banana", 1, 4, "616E616E")]
- public static void CheckIA5SubstringEncoding(string input, int offset, int length, string expectedHex)
- {
- byte[][] encodedString = DerEncoder.SegmentedEncodeIA5String(input.ToCharArray(), offset, length);
-
- Assert.NotNull(encodedString);
- Assert.Equal(3, encodedString.Length);
-
- // Check the tag
- Assert.NotNull(encodedString[0]);
- Assert.Equal(1, encodedString[0].Length);
- Assert.Equal(0x16, encodedString[0][0]);
-
- // Check the length. Since the input cases are all less than 0x7F bytes
- // the length is only one byte.
- Assert.NotNull(encodedString[1]);
- Assert.Equal(1, encodedString[1].Length);
- Assert.Equal(expectedHex.Length / 2, encodedString[1][0]);
-
- // Check the value
- Assert.Equal(expectedHex.HexToByteArray(), encodedString[2]);
-
- // And, full roundtrip
- Assert.Equal(input.Substring(offset, length), Text.Encoding.ASCII.GetString(encodedString[2]));
- }
-
- [Theory]
- [InlineData("", "")]
- [InlineData("Hello", "48656C6C6F")]
- [InlineData("banana", "62616E616E61")]
- public static void CheckPrintableStringEncoding(string input, string expectedHex)
- {
- byte[][] encodedString = DerEncoder.SegmentedEncodePrintableString(input.ToCharArray());
-
- Assert.NotNull(encodedString);
- Assert.Equal(3, encodedString.Length);
-
- // Check the tag
- Assert.NotNull(encodedString[0]);
- Assert.Equal(1, encodedString[0].Length);
- Assert.Equal(0x13, encodedString[0][0]);
-
- // Check the length. Since the input cases are all less than 0x7F bytes
- // the length is only one byte.
- Assert.NotNull(encodedString[1]);
- Assert.Equal(1, encodedString[1].Length);
- Assert.Equal(expectedHex.Length / 2, encodedString[1][0]);
-
- // Check the value
- Assert.Equal(expectedHex.HexToByteArray(), encodedString[2]);
-
- // And, full roundtrip
- Assert.Equal(input, Text.Encoding.ASCII.GetString(encodedString[2]));
- }
-
- [Theory]
- [InlineData("Hello", 3, 2, "6C6F")]
- [InlineData("banana", 1, 4, "616E616E")]
- public static void CheckPrintableSubstringEncoding(string input, int offset, int length, string expectedHex)
- {
- byte[][] encodedString = DerEncoder.SegmentedEncodePrintableString(input.ToCharArray(), offset, length);
-
- Assert.NotNull(encodedString);
- Assert.Equal(3, encodedString.Length);
-
- // Check the tag
- Assert.NotNull(encodedString[0]);
- Assert.Equal(1, encodedString[0].Length);
- Assert.Equal(0x13, encodedString[0][0]);
-
- // Check the length. Since the input cases are all less than 0x7F bytes
- // the length is only one byte.
- Assert.NotNull(encodedString[1]);
- Assert.Equal(1, encodedString[1].Length);
- Assert.Equal(expectedHex.Length / 2, encodedString[1][0]);
-
- // Check the value
- Assert.Equal(expectedHex.HexToByteArray(), encodedString[2]);
-
- // And, full roundtrip
- Assert.Equal(input.Substring(offset, length), Text.Encoding.ASCII.GetString(encodedString[2]));
- }
-
- [Theory]
- [InlineData("", "")]
- [InlineData("Hello", "48656C6C6F")]
- [InlineData("banana", "62616E616E61")]
- [InlineData("\u65E5\u672C\u8A9E", "E697A5E69CACE8AA9E")]
- public static void CheckUTF8StringEncoding(string input, string expectedHex)
- {
- byte[][] encodedString = DerEncoder.SegmentedEncodeUtf8String(input.ToCharArray());
-
- Assert.NotNull(encodedString);
- Assert.Equal(3, encodedString.Length);
-
- // Check the tag
- Assert.NotNull(encodedString[0]);
- Assert.Equal(1, encodedString[0].Length);
- Assert.Equal(0x0C, encodedString[0][0]);
-
- // Check the length. Since the input cases are all less than 0x7F bytes
- // the length is only one byte.
- Assert.NotNull(encodedString[1]);
- Assert.Equal(1, encodedString[1].Length);
- Assert.Equal(expectedHex.Length / 2, encodedString[1][0]);
-
- // Check the value
- Assert.Equal(expectedHex.HexToByteArray(), encodedString[2]);
-
- // And, full roundtrip
- Assert.Equal(input, Text.Encoding.UTF8.GetString(encodedString[2]));
- }
-
- [Theory]
- [InlineData("Hello", 3, 2, "6C6F")]
- [InlineData("banana", 1, 4, "616E616E")]
- [InlineData("\u65E5\u672C\u8A9E", 1, 1, "E69CAC")]
- public static void CheckUTF8SubstringEncoding(string input, int offset, int length, string expectedHex)
- {
- byte[][] encodedString = DerEncoder.SegmentedEncodeUtf8String(input.ToCharArray(), offset, length);
- Assert.NotNull(encodedString);
- Assert.Equal(3, encodedString.Length);
-
- // Check the tag
- Assert.NotNull(encodedString[0]);
- Assert.Equal(1, encodedString[0].Length);
- Assert.Equal(0x0C, encodedString[0][0]);
-
- // Check the length. Since the input cases are all less than 0x7F bytes
- // the length is only one byte.
- Assert.NotNull(encodedString[1]);
- Assert.Equal(1, encodedString[1].Length);
- Assert.Equal(expectedHex.Length / 2, encodedString[1][0]);
-
- // Check the value
- Assert.Equal(expectedHex.HexToByteArray(), encodedString[2]);
-
- // And, full roundtrip
- Assert.Equal(input.Substring(offset, length), Text.Encoding.UTF8.GetString(encodedString[2]));
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Numerics;
-using Test.Cryptography;
-using Xunit;
-
-namespace System.Security.Cryptography.Encoding.Tests
-{
- public class DerSequenceReaderTests
- {
- [Fact]
- public static void ReadIntegers()
- {
- byte[] derEncoded =
- {
- /* SEQUENCE */ 0x30, 23,
- /* INTEGER(0) */ 0x02, 0x01, 0x00,
- /* INTEGER(256) */ 0x02, 0x02, 0x01, 0x00,
- /* INTEGER(-1) */ 0x02, 0x01, 0xFF,
- /* Big integer */ 0x02, 0x0B, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
- };
-
- DerSequenceReader reader = new DerSequenceReader(derEncoded);
- Assert.True(reader.HasData);
- Assert.Equal(23, reader.ContentLength);
-
- int first = reader.ReadInteger();
- Assert.Equal(0, first);
-
- int second = reader.ReadInteger();
- Assert.Equal(256, second);
-
- int third = reader.ReadInteger();
- Assert.Equal(-1, third);
-
- // Reader reads Big-Endian, BigInteger reads Little-Endian
- byte[] fourthBytes = reader.ReadIntegerBytes();
- Array.Reverse(fourthBytes);
- BigInteger fourth = new BigInteger(fourthBytes);
- Assert.Equal(BigInteger.Parse("3645759592820458633497613"), fourth);
-
- // And... done.
- Assert.False(reader.HasData);
- }
-
- [Fact]
- public static void ReadOids()
- {
- byte[] derEncoded =
- {
- // Noise
- 0x10, 0x20, 0x30, 0x04, 0x05,
-
- // Data
- 0x30, 34,
- 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B,
- 0x06, 0x03, 0x55, 0x04, 0x03,
- 0x06, 0x09, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x07,
- 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22,
-
- // More noise.
- 0x85, 0x71, 0x23, 0x74, 0x01,
- };
-
- DerSequenceReader reader = new DerSequenceReader(derEncoded, 5, derEncoded.Length - 10);
- Assert.True(reader.HasData);
- Assert.Equal(34, reader.ContentLength);
-
- Oid first = reader.ReadOid();
- Assert.Equal("1.2.840.113549.1.1.11", first.Value);
-
- Oid second = reader.ReadOid();
- Assert.Equal("2.5.4.3", second.Value);
-
- Oid third = reader.ReadOid();
- Assert.Equal("1.3.6.1.4.1.311.21.7", third.Value);
-
- Oid fourth = reader.ReadOid();
- Assert.Equal("1.3.132.0.34", fourth.Value);
-
- // And... done.
- Assert.False(reader.HasData);
- }
-
- [Theory]
- [InlineData("Universal 31", "1F1F" + "0C" + "50323031375930324D313844")]
- [InlineData("Universal 32", "1F20" + "0A" + "5030304830304D303053")]
- [InlineData("Universal 127", "1F7F" + "01" + "00")]
- [InlineData("Universal 128", "1F8100" + "01" + "00")]
- [InlineData("Application 31", "5F1F" + "01" + "00")]
- [InlineData("Application 32", "5F20" + "01" + "00")]
- [InlineData("Application 127", "5F7F" + "01" + "00")]
- [InlineData("Application 128", "5F8100" + "01" + "00")]
- [InlineData("Context 31", "9F1F" + "01" + "00")]
- [InlineData("Context 32", "9F20" + "01" + "00")]
- [InlineData("Context 127", "9F7F" + "01" + "00")]
- [InlineData("Context 128", "9F8100" + "01" + "00")]
- [InlineData("Private 31", "DF1F" + "01" + "00")]
- [InlineData("Private 32", "DF20" + "01" + "00")]
- [InlineData("Private 127", "DF7F" + "01" + "00")]
- [InlineData("Private 128", "DF8100" + "01" + "00")]
- public static void NoSupportForMultiByteTags(string caseName, string hexInput)
- {
- byte[] bytes = hexInput.HexToByteArray();
- DerSequenceReader reader = DerSequenceReader.CreateForPayload(bytes);
-
- Assert.Throws<CryptographicException>(() => reader.PeekTag());
- Assert.Throws<CryptographicException>(() => reader.SkipValue());
- Assert.Throws<CryptographicException>(() => reader.ReadNextEncodedValue());
- }
-
- [Theory]
- [InlineData("0401")]
- [InlineData("0485")]
- [InlineData("048500000000")]
- [InlineData("04850000000000")]
- [InlineData("048480000000")]
- [InlineData("0484FFFFFFFF")]
- [InlineData("0484FFFFFFFA")]
- [InlineData("0485FF00000000")]
- public static void InvalidLengthSpecified(string hexInput)
- {
- byte[] bytes = hexInput.HexToByteArray();
- DerSequenceReader reader = DerSequenceReader.CreateForPayload(bytes);
-
- // Doesn't throw.
- reader.PeekTag();
-
- // Since EatTag will have succeeded the reader needs to be reconstructed after each test.
- Assert.Throws<CryptographicException>(() => reader.SkipValue());
- reader = DerSequenceReader.CreateForPayload(bytes);
-
- Assert.Throws<CryptographicException>(() => reader.ReadOctetString());
- reader = DerSequenceReader.CreateForPayload(bytes);
-
- Assert.Throws<CryptographicException>(() => reader.ReadNextEncodedValue());
- }
-
- [Fact]
- public static void InteriorLengthTooLong()
- {
- byte[] bytes =
- {
- // CONSTRUCTED SEQUENCE (8 bytes)
- 0x30, 0x08,
-
- // CONSTRUCTED SEQUENCE (2 bytes)
- 0x30, 0x02,
-
- // OCTET STRING (0 bytes)
- 0x04, 0x00,
-
- // OCTET STRING (after the inner sequence, 3 bytes, but that exceeds the sequence bounds)
- 0x04, 0x03, 0x01, 0x02, 0x03
- };
-
- DerSequenceReader reader = new DerSequenceReader(bytes);
- DerSequenceReader nested = reader.ReadSequence();
- Assert.Equal(0, nested.ReadOctetString().Length);
- Assert.False(nested.HasData);
- Assert.Throws<CryptographicException>(() => reader.ReadOctetString());
- }
-
- [Fact]
- public static void InteriorLengthTooLong_Nested()
- {
- byte[] bytes =
- {
- // CONSTRUCTED SEQUENCE (9 bytes)
- 0x30, 0x09,
-
- // CONSTRUCTED SEQUENCE (2 bytes)
- 0x30, 0x02,
-
- // OCTET STRING (1 byte, but 0 remain for the inner sequence)
- 0x04, 0x01,
-
- // OCTET STRING (in the outer sequence, after the inner sequence, 3 bytes)
- 0x04, 0x03, 0x01, 0x02, 0x03
- };
-
- DerSequenceReader reader = new DerSequenceReader(bytes);
- DerSequenceReader nested = reader.ReadSequence();
- Assert.Throws<CryptographicException>(() => nested.ReadOctetString());
- }
-
- [Fact]
- public static void LengthTooLong_ForBounds()
- {
- byte[] bytes =
- {
- // CONSTRUCTED SEQUENCE (9 bytes)
- 0x30, 0x09,
-
- // CONSTRUCTED SEQUENCE (2 bytes)
- 0x30, 0x02,
-
- // OCTET STRING (0 bytes)
- 0x04, 0x00,
-
- // OCTET STRING (after the inner sequence, 3 bytes)
- 0x04, 0x03, 0x01, 0x02, 0x03
- };
-
- Assert.Throws<CryptographicException>(() => new DerSequenceReader(bytes, 0, bytes.Length - 1));
- }
- }
-}
<Compile Include="AsnEncodedData.cs" />
<Compile Include="AsnEncodedDataCollectionTests.cs" />
<Compile Include="Base64TransformsTests.cs" />
- <Compile Include="DerEncoderTests.cs" />
- <Compile Include="DerSequenceReaderTests.cs" />
<Compile Include="Oid.cs" />
<Compile Include="OidCollectionTests.cs" />
<Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1V2.cs">
<Compile Include="$(CommonPath)\System\Security\Cryptography\AsnWriter.cs">
<Link>Common\System\Security\Cryptography\AsnWriter.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\System\Security\Cryptography\DerEncoder.cs">
- <Link>Common\System\Security\Cryptography\DerEncoder.cs</Link>
- </Compile>
- <Compile Include="$(CommonPath)\System\Security\Cryptography\DerSequenceReader.cs">
- <Link>Common\System\Security\Cryptography\DerSequenceReader.cs</Link>
- </Compile>
<Compile Include="$(CommonTestPath)\System\Security\Cryptography\ByteUtils.cs">
<Link>CommonTest\System\Security\Cryptography\ByteUtils.cs</Link>
</Compile>
<data name="Cryptography_Asn_UnusedBitCountRange" xml:space="preserve">
<value>Unused bit count must be between 0 and 7, inclusive.</value>
</data>
- <data name="Cryptography_AsnSerializer_AmbiguousFieldType" xml:space="preserve">
- <value>Field '{0}' of type '{1}' has ambiguous type '{2}', an attribute derived from AsnTypeAttribute is required.</value>
- </data>
- <data name="Cryptography_AsnSerializer_Choice_AllowNullNonNullable" xml:space="preserve">
- <value>[Choice].AllowNull=true is not valid because type '{0}' cannot have a null value.</value>
- </data>
- <data name="Cryptography_AsnSerializer_Choice_ConflictingTagMapping" xml:space="preserve">
- <value>The tag ({0} {1}) for field '{2}' on type '{3}' already is associated in this context with field '{4}' on type '{5}'.</value>
- </data>
- <data name="Cryptography_AsnSerializer_Choice_DefaultValueDisallowed" xml:space="preserve">
- <value>Field '{0}' on [Choice] type '{1}' has a default value, which is not permitted.</value>
- </data>
- <data name="Cryptography_AsnSerializer_Choice_NoChoiceWasMade" xml:space="preserve">
- <value>An instance of [Choice] type '{0}' has no non-null fields.</value>
- </data>
- <data name="Cryptography_AsnSerializer_Choice_NonNullableField" xml:space="preserve">
- <value>Field '{0}' on [Choice] type '{1}' can not be assigned a null value.</value>
- </data>
- <data name="Cryptography_AsnSerializer_Choice_TooManyValues" xml:space="preserve">
- <value>Fields '{0}' and '{1}' on type '{2}' are both non-null when only one value is permitted.</value>
- </data>
- <data name="Cryptography_AsnSerializer_Choice_TypeCycle" xml:space="preserve">
- <value>Field '{0}' on [Choice] type '{1}' has introduced a type chain cycle.</value>
- </data>
- <data name="Cryptography_AsnSerializer_MultipleAsnTypeAttributes" xml:space="preserve">
- <value>Field '{0}' on type '{1}' has multiple attributes deriving from '{2}' when at most one is permitted.</value>
- </data>
- <data name="Cryptography_AsnSerializer_NoJaggedArrays" xml:space="preserve">
- <value>Type '{0}' cannot be serialized or deserialized because it is an array of arrays.</value>
- </data>
- <data name="Cryptography_AsnSerializer_NoMultiDimensionalArrays" xml:space="preserve">
- <value>Type '{0}' cannot be serialized or deserialized because it is a multi-dimensional array.</value>
- </data>
- <data name="Cryptography_AsnSerializer_NoOpenTypes" xml:space="preserve">
- <value>Type '{0}' cannot be serialized or deserialized because it is not sealed or has unbound generic parameters.</value>
- </data>
- <data name="Cryptography_AsnSerializer_Optional_NonNullableField" xml:space="preserve">
- <value>Field '{0}' on type '{1}' is declared [OptionalValue], but it can not be assigned a null value.</value>
- </data>
- <data name="Cryptography_AsnSerializer_SetValueException" xml:space="preserve">
- <value>Unable to set field {0} on type {1}.</value>
- </data>
- <data name="Cryptography_AsnSerializer_SpecificTagChoice" xml:space="preserve">
- <value>Field '{0}' on type '{1}' has specified an implicit tag value via [ExpectedTag] for [Choice] type '{2}'. ExplicitTag must be true, or the [ExpectedTag] attribute removed.</value>
- </data>
- <data name="Cryptography_AsnSerializer_UnexpectedTypeForAttribute" xml:space="preserve">
- <value>Field '{0}' of type '{1}' has an effective type of '{2}' when one of ({3}) was expected.</value>
- </data>
- <data name="Cryptography_AsnSerializer_UtcTimeTwoDigitYearMaxTooSmall" xml:space="preserve">
- <value>Field '{0}' on type '{1}' has a [UtcTime] TwoDigitYearMax value ({2}) smaller than the minimum (99).</value>
- </data>
- <data name="Cryptography_AsnSerializer_UnhandledType" xml:space="preserve">
- <value>Could not determine how to serialize or deserialize type '{0}'.</value>
- </data>
<data name="Cryptography_AsnWriter_EncodeUnbalancedStack" xml:space="preserve">
<value>Encode cannot be called while a Sequence or SetOf is still open.</value>
</data>
<Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1V2.cs">
<Link>Common\System\Security\Cryptography\Asn1V2.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1V2.Serializer.cs">
- <Link>Common\System\Security\Cryptography\Asn1V2.Serializer.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\System\Security\Cryptography\AsnReader.cs">
<Link>Common\System\Security\Cryptography\AsnReader.cs</Link>
</Compile>
<Link>Common\System\Security\Cryptography\Asn1\SubjectPublicKeyInfoAsn.xml.cs</Link>
<DependentUpon>Common\System\Security\Cryptography\Asn1\SubjectPublicKeyInfoAsn.xml</DependentUpon>
</Compile>
- <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\X509ExtensionAsn.cs">
- <Link>Common\System\Security\Cryptography\Asn1\X509ExtensionAsn.cs</Link>
+ <AsnXml Include="$(CommonPath)\System\Security\Cryptography\Asn1\X509ExtensionAsn.xml">
+ <Link>Common\System\Security\Cryptography\Asn1\X509ExtensionAsn.xml</Link>
+ </AsnXml>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\X509ExtensionAsn.xml.cs">
+ <Link>Common\System\Security\Cryptography\Asn1\X509ExtensionAsn.xml.cs</Link>
+ <DependentUpon>Common\System\Security\Cryptography\Asn1\X509ExtensionAsn.xml</DependentUpon>
+ </Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\X509ExtensionAsn.manual.cs">
+ <Link>Common\System\Security\Cryptography\Asn1\X509ExtensionAsn.manual.cs</Link>
+ <DependentUpon>Common\System\Security\Cryptography\Asn1\X509ExtensionAsn.xml</DependentUpon>
</Compile>
<AsnXml Include="System\Security\Cryptography\Pkcs\Asn1\CadesIssuerSerial.xml" />
<Compile Include="System\Security\Cryptography\Pkcs\Asn1\CadesIssuerSerial.xml.cs">
if (extensions != null)
{
tstInfo.Extensions = extensions.OfType<X509Extension>().
- Select(ex => new X509ExtensionAsn(ex, copyValue: false)).ToArray();
+ Select(ex => new X509ExtensionAsn(ex)).ToArray();
}
using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER))
}
}
+ internal static class DictionaryStringHelper
+ {
+ internal static string ReadDirectoryOrIA5String(this AsnReader tavReader)
+ {
+ Asn1Tag tag = tavReader.PeekTag();
+
+ if (tag.TagClass != TagClass.Universal)
+ {
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+ }
+
+ switch ((UniversalTagNumber)tag.TagValue)
+ {
+ case UniversalTagNumber.BMPString:
+ case UniversalTagNumber.IA5String:
+ case UniversalTagNumber.PrintableString:
+ case UniversalTagNumber.UTF8String:
+ case UniversalTagNumber.T61String:
+ // .NET's string comparisons start by checking the length, so a trailing
+ // NULL character which was literally embedded in the DER would cause a
+ // failure in .NET whereas it wouldn't have with strcmp.
+ return tavReader.GetCharacterString((UniversalTagNumber)tag.TagValue).TrimEnd('\0');
+
+ default:
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+ }
+ }
+ }
+
internal struct PinAndClear : IDisposable
{
private byte[] _data;
otherOid == null || otherOid == Oids.UserPrincipalName,
$"otherOid ({otherOid}) is not supported");
- // SubjectAltName ::= GeneralNames
- //
- // IssuerAltName ::= GeneralNames
- //
- // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
- //
- // GeneralName ::= CHOICE {
- // otherName [0] OtherName,
- // rfc822Name [1] IA5String,
- // dNSName [2] IA5String,
- // x400Address [3] ORAddress,
- // directoryName [4] Name,
- // ediPartyName [5] EDIPartyName,
- // uniformResourceIdentifier [6] IA5String,
- // iPAddress [7] OCTET STRING,
- // registeredID [8] OBJECT IDENTIFIER }
- //
- // OtherName::= SEQUENCE {
- // type - id OBJECT IDENTIFIER,
- // value[0] EXPLICIT ANY DEFINED BY type - id }
-
- byte expectedTag = (byte)(DerSequenceReader.ContextSpecificTagFlag | (byte)matchType);
+ AsnReader reader = new AsnReader(extensionBytes, AsnEncodingRules.DER);
+ AsnReader sequenceReader = reader.ReadSequence();
+ reader.ThrowIfNotEmpty();
- if (matchType == GeneralNameType.OtherName)
+ while (sequenceReader.HasData)
{
- expectedTag |= DerSequenceReader.ConstructedFlag;
- }
-
- DerSequenceReader altNameReader = new DerSequenceReader(extensionBytes);
-
- while (altNameReader.HasData)
- {
- if (altNameReader.PeekTag() != expectedTag)
- {
- altNameReader.SkipValue();
- continue;
- }
+ GeneralNameAsn.Decode(sequenceReader, out GeneralNameAsn generalName);
switch (matchType)
{
case GeneralNameType.OtherName:
- {
- DerSequenceReader otherNameReader = altNameReader.ReadSequence();
- string oid = otherNameReader.ReadOidAsString();
-
- if (oid == otherOid)
+ // If the OtherName OID didn't match, move to the next entry.
+ if (generalName.OtherName.HasValue && generalName.OtherName.Value.TypeId == otherOid)
{
- // Payload is value[0] EXPLICIT, meaning
- // a) it'll be tagged as ContextSpecific0
- // b) that's interpretable as a Sequence (EXPLICIT)
- // c) the payload will then be retagged as the correct type (EXPLICIT)
- if (otherNameReader.PeekTag() != DerSequenceReader.ContextSpecificConstructedTag0)
- {
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
- }
-
- otherNameReader = otherNameReader.ReadSequence();
-
// Currently only UPN is supported, which is a UTF8 string per
// https://msdn.microsoft.com/en-us/library/ff842518.aspx
- return otherNameReader.ReadUtf8String();
+ AsnReader nameReader = new AsnReader(generalName.OtherName.Value.Value, AsnEncodingRules.DER);
+ string udnName = nameReader.GetCharacterString(UniversalTagNumber.UTF8String);
+ nameReader.ThrowIfNotEmpty();
+ return udnName;
}
+ break;
- // If the OtherName OID didn't match, move to the next entry.
- continue;
- }
case GeneralNameType.Rfc822Name:
+ if (generalName.Rfc822Name != null)
+ {
+ return generalName.Rfc822Name;
+ }
+ break;
+
case GeneralNameType.DnsName:
+ if (generalName.DnsName != null)
+ {
+ return generalName.DnsName;
+ }
+ break;
+
case GeneralNameType.UniformResourceIdentifier:
- return altNameReader.ReadIA5String();
- default:
- altNameReader.SkipValue();
- continue;
+ if (generalName.Uri != null)
+ {
+ return generalName.Uri;
+ }
+ break;
}
}
private static IEnumerable<KeyValuePair<string, string>> ReadReverseRdns(X500DistinguishedName name)
{
- DerSequenceReader x500NameReader = new DerSequenceReader(name.RawData);
- var rdnReaders = new Stack<DerSequenceReader>();
+ AsnReader x500NameReader = new AsnReader(name.RawData, AsnEncodingRules.DER);
+ AsnReader sequenceReader = x500NameReader.ReadSequence();
+ var rdnReaders = new Stack<AsnReader>();
+ x500NameReader.ThrowIfNotEmpty();
- while (x500NameReader.HasData)
+ while (sequenceReader.HasData)
{
- rdnReaders.Push(x500NameReader.ReadSet());
+ rdnReaders.Push(sequenceReader.ReadSetOf());
}
-
while (rdnReaders.Count > 0)
{
- DerSequenceReader rdnReader = rdnReaders.Pop();
-
+ AsnReader rdnReader = rdnReaders.Pop();
while (rdnReader.HasData)
{
- DerSequenceReader tavReader = rdnReader.ReadSequence();
- string oid = tavReader.ReadOidAsString();
-
- var tag = (DerSequenceReader.DerTag)tavReader.PeekTag();
- string value = null;
-
- switch (tag)
- {
- case DerSequenceReader.DerTag.BMPString:
- value = tavReader.ReadBMPString();
- break;
- case DerSequenceReader.DerTag.IA5String:
- value = tavReader.ReadIA5String();
- break;
- case DerSequenceReader.DerTag.PrintableString:
- value = tavReader.ReadPrintableString();
- break;
- case DerSequenceReader.DerTag.UTF8String:
- value = tavReader.ReadUtf8String();
- break;
- case DerSequenceReader.DerTag.T61String:
- value = tavReader.ReadT61String();
- break;
-
- // Ignore anything we don't know how to read.
- }
-
- if (value != null)
- {
- yield return new KeyValuePair<string, string>(oid, value);
- }
+ AsnReader tavReader = rdnReader.ReadSequence();
+ string oid = tavReader.ReadObjectIdentifierAsString();
+ string value = tavReader.ReadDirectoryOrIA5String();
+ tavReader.ThrowIfNotEmpty();
+ yield return new KeyValuePair<string, string>(oid, value);
}
}
}
using System;
using System.Collections.Generic;
+using System.Runtime.InteropServices;
using System.Security.Cryptography;
+using System.Security.Cryptography.Asn1;
using System.Security.Cryptography.X509Certificates;
+using System.Security.Cryptography.X509Certificates.Asn1;
namespace Internal.Cryptography.Pal
{
- internal struct CertificatePolicyMapping
- {
- public string IssuerDomainPolicy;
- public string SubjectDomainPolicy;
- }
-
internal sealed class CertificatePolicy
{
public bool ImplicitAnyCertificatePolicy { get; set; }
public bool SpecifiedAnyApplicationPolicy { get; set; }
public ISet<string> DeclaredApplicationPolicies { get; set; }
public int? InhibitAnyDepth { get; set; }
- public List<CertificatePolicyMapping> PolicyMapping { get; set; }
+ public List<CertificatePolicyMappingAsn> PolicyMapping { get; set; }
public int? InhibitMappingDepth { get; set; }
public int? RequireExplicitPolicyDepth { get; set; }
{
for (int iMapping = 0; iMapping < policy.PolicyMapping.Count; iMapping++)
{
- CertificatePolicyMapping mapping = policy.PolicyMapping[iMapping];
-
+ CertificatePolicyMappingAsn mapping = policy.PolicyMapping[iMapping];
if (StringComparer.Ordinal.Equals(mapping.IssuerDomainPolicy, oidToCheck))
{
nextOid = mapping.SubjectDomainPolicy;
private static int ReadInhibitAnyPolicyExtension(X509Extension extension)
{
- DerSequenceReader reader = DerSequenceReader.CreateForPayload(extension.RawData);
- return reader.ReadInteger();
+ AsnReader reader = new AsnReader(extension.RawData, AsnEncodingRules.DER);
+ int inhibitAnyPolicy;
+ reader.TryReadInt32(out inhibitAnyPolicy);
+ reader.ThrowIfNotEmpty();
+ return inhibitAnyPolicy;
}
private static void ReadCertPolicyConstraintsExtension(X509Extension extension, CertificatePolicy policy)
{
- DerSequenceReader reader = new DerSequenceReader(extension.RawData);
+ PolicyConstraintsAsn constraints = PolicyConstraintsAsn.Decode(
+ extension.RawData,
+ AsnEncodingRules.DER);
- while (reader.HasData)
- {
- // Policy Constraints context specific tag values are defined in RFC 3280 4.2.1.12,
- // and restated (unchanged) in RFC 5280 4.2.1.11.
- switch (reader.PeekTag())
- {
- case DerSequenceReader.ContextSpecificTagFlag | 0:
- policy.RequireExplicitPolicyDepth = reader.ReadInteger();
- break;
- case DerSequenceReader.ContextSpecificTagFlag | 1:
- policy.InhibitMappingDepth = reader.ReadInteger();
- break;
- default:
- if (extension.Critical)
- {
- // If an unknown value is read, but we're marked as critical,
- // then we don't know what we're doing and MUST fail validation
- // (RFC 3280).
- // If it isn't critical then it means we're allowed to be ignorant
- // of data defined more recently than we understand.
- throw new CryptographicException();
- }
-
- break;
- }
- }
+ policy.RequireExplicitPolicyDepth = constraints.RequireExplicitPolicyDepth;
+ policy.InhibitMappingDepth = constraints.InhibitMappingDepth;
}
private static ISet<string> ReadExtendedKeyUsageExtension(X509Extension extension)
internal static ISet<string> ReadCertPolicyExtension(X509Extension extension)
{
- DerSequenceReader reader = new DerSequenceReader(extension.RawData);
- HashSet<string> policies = new HashSet<string>();
+ AsnReader reader = new AsnReader(extension.RawData, AsnEncodingRules.DER);
+ AsnReader sequenceReader = reader.ReadSequence();
+ reader.ThrowIfNotEmpty();
- while (reader.HasData)
+ HashSet<string> policies = new HashSet<string>();
+ while (sequenceReader.HasData)
{
- DerSequenceReader policyInformation = reader.ReadSequence();
- policies.Add(policyInformation.ReadOidAsString());
+ PolicyInformationAsn.Decode(sequenceReader, out PolicyInformationAsn policyInformation);
+ policies.Add(policyInformation.PolicyIdentifier);
// There is an optional policy qualifier here, but it is for information
// purposes, there is no logic that would be changed.
return policies;
}
- private static List<CertificatePolicyMapping> ReadCertPolicyMappingsExtension(X509Extension extension)
+ private static List<CertificatePolicyMappingAsn> ReadCertPolicyMappingsExtension(X509Extension extension)
{
- DerSequenceReader reader = new DerSequenceReader(extension.RawData);
- List<CertificatePolicyMapping> mappings = new List<CertificatePolicyMapping>();
+ AsnReader reader = new AsnReader(extension.RawData, AsnEncodingRules.DER);
+ AsnReader sequenceReader = reader.ReadSequence();
+ reader.ThrowIfNotEmpty();
- while (reader.HasData)
+ List<CertificatePolicyMappingAsn> mappings = new List<CertificatePolicyMappingAsn>();
+ while (sequenceReader.HasData)
{
- DerSequenceReader mappingSequence = reader.ReadSequence();
- mappings.Add(
- new CertificatePolicyMapping
- {
- IssuerDomainPolicy = mappingSequence.ReadOidAsString(),
- SubjectDomainPolicy = mappingSequence.ReadOidAsString(),
- });
+ CertificatePolicyMappingAsn.Decode(sequenceReader, out CertificatePolicyMappingAsn mapping);
+ mappings.Add(mapping);
}
return mappings;
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
+using System.Security.Cryptography.Asn1;
using System.Security.Cryptography.X509Certificates;
+using System.Security.Cryptography.X509Certificates.Asn1;
using Microsoft.Win32.SafeHandles;
namespace Internal.Cryptography.Pal
return null;
}
- // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
- //
- // DistributionPoint ::= SEQUENCE {
- // distributionPoint [0] DistributionPointName OPTIONAL,
- // reasons [1] ReasonFlags OPTIONAL,
- // cRLIssuer [2] GeneralNames OPTIONAL }
- //
- // DistributionPointName ::= CHOICE {
- // fullName [0] GeneralNames,
- // nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
- //
- // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
- //
- // GeneralName ::= CHOICE {
- // otherName [0] OtherName,
- // rfc822Name [1] IA5String,
- // dNSName [2] IA5String,
- // x400Address [3] ORAddress,
- // directoryName [4] Name,
- // ediPartyName [5] EDIPartyName,
- // uniformResourceIdentifier [6] IA5String,
- // iPAddress [7] OCTET STRING,
- // registeredID [8] OBJECT IDENTIFIER }
-
- DerSequenceReader cdpSequence = new DerSequenceReader(crlDistributionPoints);
-
- while (cdpSequence.HasData)
- {
- const byte ContextSpecificFlag = 0x80;
- const byte ContextSpecific0 = ContextSpecificFlag;
- const byte ConstructedFlag = 0x20;
- const byte ContextSpecificConstructed0 = ContextSpecific0 | ConstructedFlag;
- const byte GeneralNameUri = ContextSpecificFlag | 0x06;
+ AsnReader reader = new AsnReader(crlDistributionPoints, AsnEncodingRules.DER);
+ AsnReader sequenceReader = reader.ReadSequence();
+ reader.ThrowIfNotEmpty();
- DerSequenceReader distributionPointReader = cdpSequence.ReadSequence();
- byte tag = distributionPointReader.PeekTag();
+ while (sequenceReader.HasData)
+ {
+ DistributionPointAsn.Decode(sequenceReader, out DistributionPointAsn distributionPoint);
// Only distributionPoint is supported
- if (tag != ContextSpecificConstructed0)
- {
- continue;
- }
-
- // The DistributionPointName is a CHOICE, not a SEQUENCE, but the reader is the same.
- DerSequenceReader dpNameReader = distributionPointReader.ReadSequence();
- tag = dpNameReader.PeekTag();
-
- // Only fullName is supported,
- // nameRelativeToCRLIssuer is for LDAP-based lookup.
- if (tag != ContextSpecificConstructed0)
- {
- continue;
- }
-
- DerSequenceReader fullNameReader = dpNameReader.ReadSequence();
-
- while (fullNameReader.HasData)
+ // Only fullName is supported, nameRelativeToCRLIssuer is for LDAP-based lookup.
+ if (distributionPoint.DistributionPoint.HasValue &&
+ distributionPoint.DistributionPoint.Value.FullName != null)
{
- tag = fullNameReader.PeekTag();
-
- if (tag != GeneralNameUri)
+ foreach (GeneralNameAsn name in distributionPoint.DistributionPoint.Value.FullName)
{
- fullNameReader.SkipValue();
- continue;
- }
-
- string uri = fullNameReader.ReadIA5String();
-
- Uri parsedUri = new Uri(uri);
-
- if (!StringComparer.Ordinal.Equals(parsedUri.Scheme, "http"))
- {
- continue;
+ if (name.Uri != null &&
+ Uri.TryCreate(name.Uri, UriKind.Absolute, out Uri uri) &&
+ uri.Scheme == "http")
+ {
+ return name.Uri;
+ }
}
-
- return uri;
}
}
using System.Diagnostics;
using System.Numerics;
using System.Security.Cryptography;
+using System.Security.Cryptography.Asn1;
using System.Security.Cryptography.X509Certificates;
+using System.Security.Cryptography.X509Certificates.Asn1;
namespace Internal.Cryptography.Pal
{
FindCore(cert => cert.NotAfter < normalized);
}
- private string DerStringToManagedString(byte[] anyString)
- {
- DerSequenceReader reader = DerSequenceReader.CreateForPayload(anyString);
-
- var tag = (DerSequenceReader.DerTag)reader.PeekTag();
- string value = null;
-
- switch (tag)
- {
- case DerSequenceReader.DerTag.BMPString:
- value = reader.ReadBMPString();
- break;
- case DerSequenceReader.DerTag.IA5String:
- value = reader.ReadIA5String();
- break;
- case DerSequenceReader.DerTag.PrintableString:
- value = reader.ReadPrintableString();
- break;
- case DerSequenceReader.DerTag.UTF8String:
- value = reader.ReadUtf8String();
- break;
- case DerSequenceReader.DerTag.T61String:
- value = reader.ReadT61String();
- break;
-
- // Ignore anything we don't know how to read.
- }
-
- return value;
- }
-
public void FindByTemplateName(string templateName)
{
FindCore(
if (ext != null)
{
// Try a V1 template structure, just a string:
- string decodedName = DerStringToManagedString(ext.RawData);
+ AsnReader reader = new AsnReader(ext.RawData, AsnEncodingRules.DER);
+ string decodedName = reader.ReadDirectoryOrIA5String();
+ reader.ThrowIfNotEmpty();
// If this doesn't match, maybe a V2 template will
if (StringComparer.OrdinalIgnoreCase.Equals(templateName, decodedName))
if (ext != null)
{
- DerSequenceReader reader = new DerSequenceReader(ext.RawData);
- // SEQUENCE (
- // OID oid,
- // INTEGER major,
- // INTEGER minor OPTIONAL
- // )
-
- if (reader.PeekTag() == (byte)DerSequenceReader.DerTag.ObjectIdentifier)
+ CertificateTemplateAsn template = CertificateTemplateAsn.Decode(ext.RawData, AsnEncodingRules.DER);
+ if (StringComparer.Ordinal.Equals(templateName, template.TemplateID))
{
- Oid oid = reader.ReadOid();
-
- if (StringComparer.Ordinal.Equals(templateName, oid.Value))
- {
- return true;
- }
+ return true;
}
}
using System.Collections.Generic;
using System.Diagnostics;
using System.Security.Cryptography;
+using System.Security.Cryptography.Asn1;
using System.Security.Cryptography.X509Certificates;
+using System.Security.Cryptography.X509Certificates.Asn1;
using Microsoft.Win32.SafeHandles;
namespace Internal.Cryptography.Pal
internal static string FindHttpAiaRecord(byte[] authorityInformationAccess, string recordTypeOid)
{
- DerSequenceReader reader = new DerSequenceReader(authorityInformationAccess);
+ AsnReader reader = new AsnReader(authorityInformationAccess, AsnEncodingRules.DER);
+ AsnReader sequenceReader = reader.ReadSequence();
+ reader.ThrowIfNotEmpty();
- while (reader.HasData)
+ while (sequenceReader.HasData)
{
- DerSequenceReader innerReader = reader.ReadSequence();
-
- // If the sequence's first element is a sequence, unwrap it.
- if (innerReader.PeekTag() == ConstructedSequenceTagId)
- {
- innerReader = innerReader.ReadSequence();
- }
-
- Oid oid = innerReader.ReadOid();
-
- if (StringComparer.Ordinal.Equals(oid.Value, recordTypeOid))
+ AccessDescriptionAsn.Decode(sequenceReader, out AccessDescriptionAsn description);
+ if (StringComparer.Ordinal.Equals(description.AccessMethod, recordTypeOid))
{
- string uri = innerReader.ReadIA5String();
-
- Uri parsedUri;
- if (!Uri.TryCreate(uri, UriKind.Absolute, out parsedUri))
+ GeneralNameAsn name = description.AccessLocation;
+ if (name.Uri != null &&
+ Uri.TryCreate(name.Uri, UriKind.Absolute, out Uri uri) &&
+ uri.Scheme == "http")
{
- continue;
- }
-
- if (!StringComparer.Ordinal.Equals(parsedUri.Scheme, "http"))
- {
- continue;
+ return name.Uri;
}
-
- return uri;
}
}
using System;
using System.Diagnostics;
using System.Security.Cryptography;
+using System.Security.Cryptography.Asn1;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Win32.SafeHandles;
private static DSA BuildDsaPublicKey(byte[] encodedKey, byte[] encodedParameters)
{
- // Dss-Parms ::= SEQUENCE {
- // p INTEGER,
- // q INTEGER,
- // g INTEGER
- // }
-
- // The encodedKey value is a DER INTEGER representing the Y value
-
- DerSequenceReader parametersReader = new DerSequenceReader(encodedParameters);
- DerSequenceReader keyReader = DerSequenceReader.CreateForPayload(encodedKey);
-
- DSAParameters parameters = new DSAParameters();
-
- // While this could use the object initializer, the read modifies the data stream, so
- // leaving these in flat call for clarity.
- parameters.P = parametersReader.ReadIntegerBytes();
- parameters.Q = parametersReader.ReadIntegerBytes();
- parameters.G = parametersReader.ReadIntegerBytes();
- parameters.Y = keyReader.ReadIntegerBytes();
-
- // Make the structure look like it would from Windows / .NET Framework
- TrimPaddingByte(ref parameters.P);
- TrimPaddingByte(ref parameters.Q);
-
- PadOrTrim(ref parameters.G, parameters.P.Length);
- PadOrTrim(ref parameters.Y, parameters.P.Length);
-
- DSA dsa = new DSAOpenSsl();
- dsa.ImportParameters(parameters);
- return dsa;
- }
-
- private static void TrimPaddingByte(ref byte[] data)
- {
- if (data.Length > 0 && data[0] == 0)
+ SubjectPublicKeyInfoAsn spki = new SubjectPublicKeyInfoAsn
{
- byte[] tmp = new byte[data.Length - 1];
- Buffer.BlockCopy(data, 1, tmp, 0, tmp.Length);
- data = tmp;
- }
- }
+ Algorithm = new AlgorithmIdentifierAsn { Algorithm = new Oid(Oids.Dsa), Parameters = encodedParameters },
+ SubjectPublicKey = encodedKey,
+ };
- private static void PadOrTrim(ref byte[] data, int dataLen)
- {
- if (data.Length == dataLen)
- return;
-
- if (data.Length < dataLen)
+ using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER))
{
- // Add leading 0s
- byte[] tmp = new byte[dataLen];
- Buffer.BlockCopy(data, 0, tmp, dataLen - data.Length, dataLen);
- data = tmp;
- return;
+ DSA dsa = new DSAOpenSsl();
+ spki.Encode(writer);
+ dsa.ImportSubjectPublicKeyInfo(writer.EncodeAsSpan(), out _);
+ return dsa;
}
-
- if (data.Length == dataLen + 1 && data[0] == 0)
- {
- byte[] tmp = new byte[dataLen];
- Buffer.BlockCopy(data, 1, tmp, 0, dataLen);
- data = tmp;
- return;
- }
-
- throw new CryptographicException();
}
}
}
{
AsnReader tavReader = rdnReader.ReadSequence();
string oid = tavReader.ReadObjectIdentifierAsString();
- string attributeValue = ReadString(tavReader);
+ string attributeValue = tavReader.ReadDirectoryOrIA5String();
tavReader.ThrowIfNotEmpty();
return decodedName.ToString();
}
-
- private static string ReadString(AsnReader tavReader)
- {
- Asn1Tag tag = tavReader.PeekTag();
-
- if (tag.TagClass != TagClass.Universal)
- {
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
- }
-
- switch ((UniversalTagNumber)tag.TagValue)
- {
- case UniversalTagNumber.BMPString:
- case UniversalTagNumber.IA5String:
- case UniversalTagNumber.PrintableString:
- case UniversalTagNumber.UTF8String:
- case UniversalTagNumber.T61String:
- return tavReader.GetCharacterString((UniversalTagNumber)tag.TagValue);
- default:
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
- }
- }
}
}
<data name="Cryptography_Asn_UnusedBitCountRange" xml:space="preserve">
<value>Unused bit count must be between 0 and 7, inclusive.</value>
</data>
- <data name="Cryptography_AsnSerializer_AmbiguousFieldType" xml:space="preserve">
- <value>Field '{0}' of type '{1}' has ambiguous type '{2}', an attribute derived from AsnTypeAttribute is required.</value>
- </data>
- <data name="Cryptography_AsnSerializer_Choice_AllowNullNonNullable" xml:space="preserve">
- <value>[Choice].AllowNull=true is not valid because type '{0}' cannot have a null value.</value>
- </data>
- <data name="Cryptography_AsnSerializer_Choice_ConflictingTagMapping" xml:space="preserve">
- <value>The tag ({0} {1}) for field '{2}' on type '{3}' already is associated in this context with field '{4}' on type '{5}'.</value>
- </data>
- <data name="Cryptography_AsnSerializer_Choice_DefaultValueDisallowed" xml:space="preserve">
- <value>Field '{0}' on [Choice] type '{1}' has a default value, which is not permitted.</value>
- </data>
- <data name="Cryptography_AsnSerializer_Choice_NoChoiceWasMade" xml:space="preserve">
- <value>An instance of [Choice] type '{0}' has no non-null fields.</value>
- </data>
- <data name="Cryptography_AsnSerializer_Choice_NonNullableField" xml:space="preserve">
- <value>Field '{0}' on [Choice] type '{1}' can not be assigned a null value.</value>
- </data>
- <data name="Cryptography_AsnSerializer_Choice_TooManyValues" xml:space="preserve">
- <value>Fields '{0}' and '{1}' on type '{2}' are both non-null when only one value is permitted.</value>
- </data>
- <data name="Cryptography_AsnSerializer_Choice_TypeCycle" xml:space="preserve">
- <value>Field '{0}' on [Choice] type '{1}' has introduced a type chain cycle.</value>
- </data>
- <data name="Cryptography_AsnSerializer_MultipleAsnTypeAttributes" xml:space="preserve">
- <value>Field '{0}' on type '{1}' has multiple attributes deriving from '{2}' when at most one is permitted.</value>
- </data>
- <data name="Cryptography_AsnSerializer_NoJaggedArrays" xml:space="preserve">
- <value>Type '{0}' cannot be serialized or deserialized because it is an array of arrays.</value>
- </data>
- <data name="Cryptography_AsnSerializer_NoMultiDimensionalArrays" xml:space="preserve">
- <value>Type '{0}' cannot be serialized or deserialized because it is a multi-dimensional array.</value>
- </data>
- <data name="Cryptography_AsnSerializer_NoOpenTypes" xml:space="preserve">
- <value>Type '{0}' cannot be serialized or deserialized because it is not sealed or has unbound generic parameters.</value>
- </data>
- <data name="Cryptography_AsnSerializer_Optional_NonNullableField" xml:space="preserve">
- <value>Field '{0}' on type '{1}' is declared [OptionalValue], but it can not be assigned a null value.</value>
- </data>
- <data name="Cryptography_AsnSerializer_SetValueException" xml:space="preserve">
- <value>Unable to set field {0} on type {1}.</value>
- </data>
- <data name="Cryptography_AsnSerializer_SpecificTagChoice" xml:space="preserve">
- <value>Field '{0}' on type '{1}' has specified an implicit tag value via [ExpectedTag] for [Choice] type '{2}'. ExplicitTag must be true, or the [ExpectedTag] attribute removed.</value>
- </data>
- <data name="Cryptography_AsnSerializer_UnexpectedTypeForAttribute" xml:space="preserve">
- <value>Field '{0}' of type '{1}' has an effective type of '{2}' when one of ({3}) was expected.</value>
- </data>
- <data name="Cryptography_AsnSerializer_UtcTimeTwoDigitYearMaxTooSmall" xml:space="preserve">
- <value>Field '{0}' on type '{1}' has a [UtcTime] TwoDigitYearMax value ({2}) smaller than the minimum (99).</value>
- </data>
- <data name="Cryptography_AsnSerializer_UnhandledType" xml:space="preserve">
- <value>Could not determine how to serialize or deserialize type '{0}'.</value>
- </data>
<data name="Cryptography_AsnWriter_EncodeUnbalancedStack" xml:space="preserve">
<value>Encode cannot be called while a Sequence or SetOf is still open.</value>
</data>
<Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeHandleCache.cs">
<Link>Common\Microsoft\Win32\SafeHandles\SafeHandleCache.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\System\Security\Cryptography\DerEncoder.cs">
- <Link>Common\System\Security\Cryptography\DerEncoder.cs</Link>
- </Compile>
- <Compile Include="$(CommonPath)\System\Security\Cryptography\DerSequenceReader.cs">
- <Link>Common\System\Security\Cryptography\DerSequenceReader.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1V2.cs">
<Link>Common\System\Security\Cryptography\Asn1V2.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1V2.Serializer.cs">
- <Link>Common\System\Security\Cryptography\Asn1V2.Serializer.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\System\Security\Cryptography\AsnReader.cs">
<Link>Common\System\Security\Cryptography\AsnReader.cs</Link>
</Compile>
<Link>Common\System\Security\Cryptography\Asn1\SubjectPublicKeyInfoAsn.xml.cs</Link>
<DependentUpon>Common\System\Security\Cryptography\Asn1\SubjectPublicKeyInfoAsn.xml</DependentUpon>
</Compile>
- <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\X509ExtensionAsn.cs">
- <Link>Common\System\Security\Cryptography\Asn1\X509ExtensionAsn.cs</Link>
+ <AsnXml Include="$(CommonPath)\System\Security\Cryptography\Asn1\X509ExtensionAsn.xml">
+ <Link>Common\System\Security\Cryptography\Asn1\X509ExtensionAsn.xml</Link>
+ </AsnXml>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\X509ExtensionAsn.xml.cs">
+ <Link>Common\System\Security\Cryptography\Asn1\X509ExtensionAsn.xml.cs</Link>
+ <DependentUpon>Common\System\Security\Cryptography\Asn1\X509ExtensionAsn.xml</DependentUpon>
+ </Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\X509ExtensionAsn.manual.cs">
+ <Link>Common\System\Security\Cryptography\Asn1\X509ExtensionAsn.manual.cs</Link>
+ <DependentUpon>Common\System\Security\Cryptography\Asn1\X509ExtensionAsn.xml</DependentUpon>
</Compile>
<Compile Include="$(CommonPath)\Internal\Cryptography\AsymmetricAlgorithmHelpers.Der.cs">
<Link>Common\Internal\Cryptography\AsymmetricAlgorithmHelpers.Der.cs</Link>
<DependentUpon>System\Security\Cryptography\X509Certificates\Asn1\X501AttributeAsn.xml</DependentUpon>
</Compile>
<Compile Include="System\Security\Cryptography\X509Certificates\Asn1\X501AttributeAsn.manual.cs" />
+ <AsnXml Include="System\Security\Cryptography\X509Certificates\Asn1\AccessDescriptionAsn.xml" />
+ <Compile Include="System\Security\Cryptography\X509Certificates\Asn1\AccessDescriptionAsn.xml.cs">
+ <DependentUpon>System\Security\Cryptography\X509Certificates\Asn1\AccessDescriptionAsn.xml</DependentUpon>
+ </Compile>
+ <AsnXml Include="System\Security\Cryptography\X509Certificates\Asn1\CertificatePolicyMappingAsn.xml" />
+ <Compile Include="System\Security\Cryptography\X509Certificates\Asn1\CertificatePolicyMappingAsn.xml.cs">
+ <DependentUpon>System\Security\Cryptography\X509Certificates\Asn1\CertificatePolicyMappingAsn.xml</DependentUpon>
+ </Compile>
+ <AsnXml Include="System\Security\Cryptography\X509Certificates\Asn1\CertificateTemplateAsn.xml" />
+ <Compile Include="System\Security\Cryptography\X509Certificates\Asn1\CertificateTemplateAsn.xml.cs">
+ <DependentUpon>System\Security\Cryptography\X509Certificates\Asn1\CertificateTemplateAsn.xml</DependentUpon>
+ </Compile>
+ <AsnXml Include="System\Security\Cryptography\X509Certificates\Asn1\PolicyConstraintsAsn.xml" />
+ <Compile Include="System\Security\Cryptography\X509Certificates\Asn1\PolicyConstraintsAsn.xml.cs">
+ <DependentUpon>System\Security\Cryptography\X509Certificates\Asn1\PolicyConstraintsAsn.xml</DependentUpon>
+ </Compile>
+ <AsnXml Include="System\Security\Cryptography\X509Certificates\Asn1\PolicyInformationAsn.xml" />
+ <Compile Include="System\Security\Cryptography\X509Certificates\Asn1\PolicyInformationAsn.xml.cs">
+ <DependentUpon>System\Security\Cryptography\X509Certificates\Asn1\PolicyInformationAsn.xml</DependentUpon>
+ </Compile>
</ItemGroup>
<ItemGroup Condition=" '$(TargetsWindows)' == 'true'">
<Compile Include="Internal\Cryptography\Pal.Windows\CertificatePal.cs" />
<Compile Include="Microsoft\Win32\SafeHandles\SafePasswordHandle.Unix.cs" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetsUnix)' == 'true' AND '$(TargetsOSX)' != 'true' ">
+ <AsnXml Include="System\Security\Cryptography\X509Certificates\Asn1\DistributionPointAsn.xml" />
+ <Compile Include="System\Security\Cryptography\X509Certificates\Asn1\DistributionPointAsn.xml.cs">
+ <DependentUpon>System\Security\Cryptography\X509Certificates\Asn1\DistributionPointAsn.xml</DependentUpon>
+ </Compile>
+ <AsnXml Include="System\Security\Cryptography\X509Certificates\Asn1\DistributionPointNameAsn.xml" />
+ <Compile Include="System\Security\Cryptography\X509Certificates\Asn1\DistributionPointNameAsn.xml.cs">
+ <DependentUpon>System\Security\Cryptography\X509Certificates\Asn1\DistributionPointNameAsn.xml</DependentUpon>
+ </Compile>
+ <Compile Include="System\Security\Cryptography\X509Certificates\Asn1\ReasonFlagsAsn.cs" />
<Compile Include="Internal\Cryptography\Pal.Unix\CertCollectionLoader.cs" />
<Compile Include="Internal\Cryptography\Pal.Unix\CertificateAssetDownloader.cs" />
<Compile Include="Internal\Cryptography\Pal.Unix\CertificatePal.cs" />
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<asn:Sequence
+ xmlns:asn="http://schemas.dot.net/asnxml/201808/"
+ name="AccessDescriptionAsn"
+ namespace="System.Security.Cryptography.X509Certificates.Asn1">
+
+ <!--
+ https://tools.ietf.org/html/rfc5280#section-4.2.2.1
+
+ AccessDescription ::= SEQUENCE {
+ accessMethod OBJECT IDENTIFIER,
+ accessLocation GeneralName
+ }
+ -->
+ <asn:ObjectIdentifier name="AccessMethod" backingType="string" />
+ <asn:AsnType name="AccessLocation" typeName="System.Security.Cryptography.Asn1.GeneralNameAsn" />
+</asn:Sequence>
\ No newline at end of file
--- /dev/null
+using System;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Asn1;
+
+namespace System.Security.Cryptography.X509Certificates.Asn1
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal partial struct AccessDescriptionAsn
+ {
+ internal string AccessMethod;
+ internal System.Security.Cryptography.Asn1.GeneralNameAsn AccessLocation;
+
+ internal void Encode(AsnWriter writer)
+ {
+ Encode(writer, Asn1Tag.Sequence);
+ }
+
+ internal void Encode(AsnWriter writer, Asn1Tag tag)
+ {
+ writer.PushSequence(tag);
+
+ writer.WriteObjectIdentifier(AccessMethod);
+ AccessLocation.Encode(writer);
+ writer.PopSequence(tag);
+ }
+
+ internal static AccessDescriptionAsn Decode(ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+ {
+ return Decode(Asn1Tag.Sequence, encoded, ruleSet);
+ }
+
+ internal static AccessDescriptionAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+ {
+ AsnReader reader = new AsnReader(encoded, ruleSet);
+
+ Decode(reader, expectedTag, out AccessDescriptionAsn decoded);
+ reader.ThrowIfNotEmpty();
+ return decoded;
+ }
+
+ internal static void Decode(AsnReader reader, out AccessDescriptionAsn decoded)
+ {
+ if (reader == null)
+ throw new ArgumentNullException(nameof(reader));
+
+ Decode(reader, Asn1Tag.Sequence, out decoded);
+ }
+
+ internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out AccessDescriptionAsn decoded)
+ {
+ if (reader == null)
+ throw new ArgumentNullException(nameof(reader));
+
+ decoded = default;
+ AsnReader sequenceReader = reader.ReadSequence(expectedTag);
+
+ decoded.AccessMethod = sequenceReader.ReadObjectIdentifierAsString();
+ System.Security.Cryptography.Asn1.GeneralNameAsn.Decode(sequenceReader, out decoded.AccessLocation);
+
+ sequenceReader.ThrowIfNotEmpty();
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<asn:Sequence
+ xmlns:asn="http://schemas.dot.net/asnxml/201808/"
+ name="CertificatePolicyMappingAsn"
+ namespace="System.Security.Cryptography.X509Certificates.Asn1">
+
+ <!--
+ https://tools.ietf.org/html/rfc5280#section-4.2.1.5
+
+ PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
+ issuerDomainPolicy CertPolicyId,
+ subjectDomainPolicy CertPolicyId
+ }
+
+ CertPolicyId ::= OBJECT IDENTIFIER
+ -->
+ <asn:ObjectIdentifier name="IssuerDomainPolicy" backingType="string" />
+ <asn:ObjectIdentifier name="SubjectDomainPolicy" backingType="string" />
+</asn:Sequence>
\ No newline at end of file
--- /dev/null
+using System;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Asn1;
+
+namespace System.Security.Cryptography.X509Certificates.Asn1
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal partial struct CertificatePolicyMappingAsn
+ {
+ internal string IssuerDomainPolicy;
+ internal string SubjectDomainPolicy;
+
+ internal void Encode(AsnWriter writer)
+ {
+ Encode(writer, Asn1Tag.Sequence);
+ }
+
+ internal void Encode(AsnWriter writer, Asn1Tag tag)
+ {
+ writer.PushSequence(tag);
+
+ writer.WriteObjectIdentifier(IssuerDomainPolicy);
+ writer.WriteObjectIdentifier(SubjectDomainPolicy);
+ writer.PopSequence(tag);
+ }
+
+ internal static CertificatePolicyMappingAsn Decode(ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+ {
+ return Decode(Asn1Tag.Sequence, encoded, ruleSet);
+ }
+
+ internal static CertificatePolicyMappingAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+ {
+ AsnReader reader = new AsnReader(encoded, ruleSet);
+
+ Decode(reader, expectedTag, out CertificatePolicyMappingAsn decoded);
+ reader.ThrowIfNotEmpty();
+ return decoded;
+ }
+
+ internal static void Decode(AsnReader reader, out CertificatePolicyMappingAsn decoded)
+ {
+ if (reader == null)
+ throw new ArgumentNullException(nameof(reader));
+
+ Decode(reader, Asn1Tag.Sequence, out decoded);
+ }
+
+ internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out CertificatePolicyMappingAsn decoded)
+ {
+ if (reader == null)
+ throw new ArgumentNullException(nameof(reader));
+
+ decoded = default;
+ AsnReader sequenceReader = reader.ReadSequence(expectedTag);
+
+ decoded.IssuerDomainPolicy = sequenceReader.ReadObjectIdentifierAsString();
+ decoded.SubjectDomainPolicy = sequenceReader.ReadObjectIdentifierAsString();
+
+ sequenceReader.ThrowIfNotEmpty();
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<asn:Sequence
+ xmlns:asn="http://schemas.dot.net/asnxml/201808/"
+ name="CertificateTemplateAsn"
+ namespace="System.Security.Cryptography.X509Certificates.Asn1">
+
+ <!--
+ Microsoft extension (1.3.6.1.4.1.311.21.7)
+
+ TemplateVersion ::= INTEGER
+
+ CertificateTemplate ::= SEQUENCE {
+ templateID OBJECT IDENTIFIER,
+ templateMajorVersion TemplateVersion,
+ templateMinorVersion TemplateVersion OPTIONAL
+ }
+ -->
+ <asn:ObjectIdentifier name="TemplateID" backingType="string" />
+ <asn:Integer name="TemplateMajorVersion" backingType="int" />
+ <asn:Integer name="TemplateMinorVersion" backingType="int" optional="true" />
+</asn:Sequence>
\ No newline at end of file
--- /dev/null
+using System;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Asn1;
+
+namespace System.Security.Cryptography.X509Certificates.Asn1
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal partial struct CertificateTemplateAsn
+ {
+ internal string TemplateID;
+ internal int TemplateMajorVersion;
+ internal int? TemplateMinorVersion;
+
+ internal void Encode(AsnWriter writer)
+ {
+ Encode(writer, Asn1Tag.Sequence);
+ }
+
+ internal void Encode(AsnWriter writer, Asn1Tag tag)
+ {
+ writer.PushSequence(tag);
+
+ writer.WriteObjectIdentifier(TemplateID);
+ writer.WriteInteger(TemplateMajorVersion);
+
+ if (TemplateMinorVersion.HasValue)
+ {
+ writer.WriteInteger(TemplateMinorVersion.Value);
+ }
+
+ writer.PopSequence(tag);
+ }
+
+ internal static CertificateTemplateAsn Decode(ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+ {
+ return Decode(Asn1Tag.Sequence, encoded, ruleSet);
+ }
+
+ internal static CertificateTemplateAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+ {
+ AsnReader reader = new AsnReader(encoded, ruleSet);
+
+ Decode(reader, expectedTag, out CertificateTemplateAsn decoded);
+ reader.ThrowIfNotEmpty();
+ return decoded;
+ }
+
+ internal static void Decode(AsnReader reader, out CertificateTemplateAsn decoded)
+ {
+ if (reader == null)
+ throw new ArgumentNullException(nameof(reader));
+
+ Decode(reader, Asn1Tag.Sequence, out decoded);
+ }
+
+ internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out CertificateTemplateAsn decoded)
+ {
+ if (reader == null)
+ throw new ArgumentNullException(nameof(reader));
+
+ decoded = default;
+ AsnReader sequenceReader = reader.ReadSequence(expectedTag);
+
+ decoded.TemplateID = sequenceReader.ReadObjectIdentifierAsString();
+
+ if (!sequenceReader.TryReadInt32(out decoded.TemplateMajorVersion))
+ {
+ sequenceReader.ThrowIfNotEmpty();
+ }
+
+
+ if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(Asn1Tag.Integer))
+ {
+
+ if (sequenceReader.TryReadInt32(out int tmpTemplateMinorVersion))
+ {
+ decoded.TemplateMinorVersion = tmpTemplateMinorVersion;
+ }
+ else
+ {
+ sequenceReader.ThrowIfNotEmpty();
+ }
+
+ }
+
+
+ sequenceReader.ThrowIfNotEmpty();
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<asn:Sequence
+ xmlns:asn="http://schemas.dot.net/asnxml/201808/"
+ name="DistributionPointAsn"
+ namespace="System.Security.Cryptography.X509Certificates.Asn1">
+
+ <!--
+ https://tools.ietf.org/html/rfc5280#section-4.2.1.13
+
+ DistributionPoint ::= SEQUENCE {
+ distributionPoint [0] DistributionPointName OPTIONAL,
+ reasons [1] ReasonFlags OPTIONAL,
+ cRLIssuer [2] GeneralNames OPTIONAL
+ }
+ -->
+ <asn:AsnType name="DistributionPoint" typeName="System.Security.Cryptography.X509Certificates.Asn1.DistributionPointNameAsn" explicitTag="0" optional="true" />
+ <asn:NamedBitList name="Reasons" backingType="System.Security.Cryptography.X509Certificates.Asn1.ReasonFlagsAsn" implicitTag="1" optional="true" />
+ <asn:SequenceOf name="CRLIssuer" implicitTag="2" optional="true">
+ <asn:AsnType typeName="System.Security.Cryptography.Asn1.GeneralNameAsn" />
+ </asn:SequenceOf>
+</asn:Sequence>
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Asn1;
+
+namespace System.Security.Cryptography.X509Certificates.Asn1
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal partial struct DistributionPointAsn
+ {
+ internal System.Security.Cryptography.X509Certificates.Asn1.DistributionPointNameAsn? DistributionPoint;
+ internal System.Security.Cryptography.X509Certificates.Asn1.ReasonFlagsAsn? Reasons;
+ internal System.Security.Cryptography.Asn1.GeneralNameAsn[] CRLIssuer;
+
+ internal void Encode(AsnWriter writer)
+ {
+ Encode(writer, Asn1Tag.Sequence);
+ }
+
+ internal void Encode(AsnWriter writer, Asn1Tag tag)
+ {
+ writer.PushSequence(tag);
+
+
+ if (DistributionPoint.HasValue)
+ {
+ writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0));
+ DistributionPoint.Value.Encode(writer);
+ writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0));
+ }
+
+
+ if (Reasons.HasValue)
+ {
+ writer.WriteNamedBitList(new Asn1Tag(TagClass.ContextSpecific, 1), Reasons.Value);
+ }
+
+
+ if (CRLIssuer != null)
+ {
+
+ writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2));
+ for (int i = 0; i < CRLIssuer.Length; i++)
+ {
+ CRLIssuer[i].Encode(writer);
+ }
+ writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2));
+
+ }
+
+ writer.PopSequence(tag);
+ }
+
+ internal static DistributionPointAsn Decode(ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+ {
+ return Decode(Asn1Tag.Sequence, encoded, ruleSet);
+ }
+
+ internal static DistributionPointAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+ {
+ AsnReader reader = new AsnReader(encoded, ruleSet);
+
+ Decode(reader, expectedTag, out DistributionPointAsn decoded);
+ reader.ThrowIfNotEmpty();
+ return decoded;
+ }
+
+ internal static void Decode(AsnReader reader, out DistributionPointAsn decoded)
+ {
+ if (reader == null)
+ throw new ArgumentNullException(nameof(reader));
+
+ Decode(reader, Asn1Tag.Sequence, out decoded);
+ }
+
+ internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out DistributionPointAsn decoded)
+ {
+ if (reader == null)
+ throw new ArgumentNullException(nameof(reader));
+
+ decoded = default;
+ AsnReader sequenceReader = reader.ReadSequence(expectedTag);
+ AsnReader explicitReader;
+ AsnReader collectionReader;
+
+
+ if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0)))
+ {
+ explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0));
+ System.Security.Cryptography.X509Certificates.Asn1.DistributionPointNameAsn tmpDistributionPoint;
+ System.Security.Cryptography.X509Certificates.Asn1.DistributionPointNameAsn.Decode(explicitReader, out tmpDistributionPoint);
+ decoded.DistributionPoint = tmpDistributionPoint;
+
+ explicitReader.ThrowIfNotEmpty();
+ }
+
+
+ if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1)))
+ {
+ decoded.Reasons = sequenceReader.GetNamedBitListValue<System.Security.Cryptography.X509Certificates.Asn1.ReasonFlagsAsn>(new Asn1Tag(TagClass.ContextSpecific, 1));
+ }
+
+
+ if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2)))
+ {
+
+ // Decode SEQUENCE OF for CRLIssuer
+ {
+ collectionReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2));
+ var tmpList = new List<System.Security.Cryptography.Asn1.GeneralNameAsn>();
+ System.Security.Cryptography.Asn1.GeneralNameAsn tmpItem;
+
+ while (collectionReader.HasData)
+ {
+ System.Security.Cryptography.Asn1.GeneralNameAsn.Decode(collectionReader, out tmpItem);
+ tmpList.Add(tmpItem);
+ }
+
+ decoded.CRLIssuer = tmpList.ToArray();
+ }
+
+ }
+
+
+ sequenceReader.ThrowIfNotEmpty();
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<asn:Choice
+ xmlns:asn="http://schemas.dot.net/asnxml/201808/"
+ name="DistributionPointNameAsn"
+ namespace="System.Security.Cryptography.X509Certificates.Asn1">
+
+ <!--
+ https://tools.ietf.org/html/rfc5280#section-4.2.1.13
+
+ DistributionPointName ::= CHOICE {
+ fullName [0] GeneralNames,
+ nameRelativeToCRLIssuer [1] RelativeDistinguishedName
+ }
+ -->
+ <asn:SequenceOf name="FullName" implicitTag="0">
+ <asn:AsnType typeName="System.Security.Cryptography.Asn1.GeneralNameAsn" />
+ </asn:SequenceOf>
+ <asn:AnyValue name="NameRelativeToCRLIssuer" implicitTag="1" />
+</asn:Choice>
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Asn1;
+
+namespace System.Security.Cryptography.X509Certificates.Asn1
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal partial struct DistributionPointNameAsn
+ {
+ internal System.Security.Cryptography.Asn1.GeneralNameAsn[] FullName;
+ internal ReadOnlyMemory<byte>? NameRelativeToCRLIssuer;
+
+#if DEBUG
+ static DistributionPointNameAsn()
+ {
+ var usedTags = new System.Collections.Generic.Dictionary<Asn1Tag, string>();
+ Action<Asn1Tag, string> ensureUniqueTag = (tag, fieldName) =>
+ {
+ if (usedTags.TryGetValue(tag, out string existing))
+ {
+ throw new InvalidOperationException($"Tag '{tag}' is in use by both '{existing}' and '{fieldName}'");
+ }
+
+ usedTags.Add(tag, fieldName);
+ };
+
+ ensureUniqueTag(new Asn1Tag(TagClass.ContextSpecific, 0), "FullName");
+ ensureUniqueTag(new Asn1Tag(TagClass.ContextSpecific, 1), "NameRelativeToCRLIssuer");
+ }
+#endif
+
+ internal void Encode(AsnWriter writer)
+ {
+ bool wroteValue = false;
+
+ if (FullName != null)
+ {
+ if (wroteValue)
+ throw new CryptographicException();
+
+
+ writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0));
+ for (int i = 0; i < FullName.Length; i++)
+ {
+ FullName[i].Encode(writer);
+ }
+ writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0));
+
+ wroteValue = true;
+ }
+
+ if (NameRelativeToCRLIssuer.HasValue)
+ {
+ if (wroteValue)
+ throw new CryptographicException();
+
+ // Validator for tag constraint for NameRelativeToCRLIssuer
+ {
+ if (!Asn1Tag.TryParse(NameRelativeToCRLIssuer.Value.Span, out Asn1Tag validateTag, out _) ||
+ !validateTag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1)))
+ {
+ throw new CryptographicException();
+ }
+ }
+
+ writer.WriteEncodedValue(NameRelativeToCRLIssuer.Value);
+ wroteValue = true;
+ }
+
+ if (!wroteValue)
+ {
+ throw new CryptographicException();
+ }
+ }
+
+ internal static DistributionPointNameAsn Decode(ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+ {
+ AsnReader reader = new AsnReader(encoded, ruleSet);
+
+ Decode(reader, out DistributionPointNameAsn decoded);
+ reader.ThrowIfNotEmpty();
+ return decoded;
+ }
+
+ internal static void Decode(AsnReader reader, out DistributionPointNameAsn decoded)
+ {
+ if (reader == null)
+ throw new ArgumentNullException(nameof(reader));
+
+ decoded = default;
+ Asn1Tag tag = reader.PeekTag();
+ AsnReader collectionReader;
+
+ if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0)))
+ {
+
+ // Decode SEQUENCE OF for FullName
+ {
+ collectionReader = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0));
+ var tmpList = new List<System.Security.Cryptography.Asn1.GeneralNameAsn>();
+ System.Security.Cryptography.Asn1.GeneralNameAsn tmpItem;
+
+ while (collectionReader.HasData)
+ {
+ System.Security.Cryptography.Asn1.GeneralNameAsn.Decode(collectionReader, out tmpItem);
+ tmpList.Add(tmpItem);
+ }
+
+ decoded.FullName = tmpList.ToArray();
+ }
+
+ }
+ else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1)))
+ {
+ decoded.NameRelativeToCRLIssuer = reader.GetEncodedValue();
+ }
+ else
+ {
+ throw new CryptographicException();
+ }
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<asn:Sequence
+ xmlns:asn="http://schemas.dot.net/asnxml/201808/"
+ name="PolicyConstraintsAsn"
+ namespace="System.Security.Cryptography.X509Certificates.Asn1">
+
+ <!--
+ https://tools.ietf.org/html/rfc5280#section-4.2.1.11
+
+ PolicyConstraints ::= SEQUENCE {
+ requireExplicitPolicy [0] SkipCerts OPTIONAL,
+ inhibitPolicyMapping [1] SkipCerts OPTIONAL
+ }
+
+ SkipCerts ::= INTEGER (0..MAX)
+ -->
+ <asn:Integer name="RequireExplicitPolicyDepth" implicitTag="0" backingType="int" optional="true" />
+ <asn:Integer name="InhibitMappingDepth" implicitTag="1" backingType="int" optional="true" />
+</asn:Sequence>
\ No newline at end of file
--- /dev/null
+using System;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Asn1;
+
+namespace System.Security.Cryptography.X509Certificates.Asn1
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal partial struct PolicyConstraintsAsn
+ {
+ internal int? RequireExplicitPolicyDepth;
+ internal int? InhibitMappingDepth;
+
+ internal void Encode(AsnWriter writer)
+ {
+ Encode(writer, Asn1Tag.Sequence);
+ }
+
+ internal void Encode(AsnWriter writer, Asn1Tag tag)
+ {
+ writer.PushSequence(tag);
+
+
+ if (RequireExplicitPolicyDepth.HasValue)
+ {
+ writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 0), RequireExplicitPolicyDepth.Value);
+ }
+
+
+ if (InhibitMappingDepth.HasValue)
+ {
+ writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 1), InhibitMappingDepth.Value);
+ }
+
+ writer.PopSequence(tag);
+ }
+
+ internal static PolicyConstraintsAsn Decode(ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+ {
+ return Decode(Asn1Tag.Sequence, encoded, ruleSet);
+ }
+
+ internal static PolicyConstraintsAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+ {
+ AsnReader reader = new AsnReader(encoded, ruleSet);
+
+ Decode(reader, expectedTag, out PolicyConstraintsAsn decoded);
+ reader.ThrowIfNotEmpty();
+ return decoded;
+ }
+
+ internal static void Decode(AsnReader reader, out PolicyConstraintsAsn decoded)
+ {
+ if (reader == null)
+ throw new ArgumentNullException(nameof(reader));
+
+ Decode(reader, Asn1Tag.Sequence, out decoded);
+ }
+
+ internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out PolicyConstraintsAsn decoded)
+ {
+ if (reader == null)
+ throw new ArgumentNullException(nameof(reader));
+
+ decoded = default;
+ AsnReader sequenceReader = reader.ReadSequence(expectedTag);
+
+
+ if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0)))
+ {
+
+ if (sequenceReader.TryReadInt32(new Asn1Tag(TagClass.ContextSpecific, 0), out int tmpRequireExplicitPolicyDepth))
+ {
+ decoded.RequireExplicitPolicyDepth = tmpRequireExplicitPolicyDepth;
+ }
+ else
+ {
+ sequenceReader.ThrowIfNotEmpty();
+ }
+
+ }
+
+
+ if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1)))
+ {
+
+ if (sequenceReader.TryReadInt32(new Asn1Tag(TagClass.ContextSpecific, 1), out int tmpInhibitMappingDepth))
+ {
+ decoded.InhibitMappingDepth = tmpInhibitMappingDepth;
+ }
+ else
+ {
+ sequenceReader.ThrowIfNotEmpty();
+ }
+
+ }
+
+
+ sequenceReader.ThrowIfNotEmpty();
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<asn:Sequence
+ xmlns:asn="http://schemas.dot.net/asnxml/201808/"
+ name="PolicyInformationAsn"
+ namespace="System.Security.Cryptography.X509Certificates.Asn1">
+
+ <!--
+ https://tools.ietf.org/html/rfc5280#section-4.2.1.4
+
+ PolicyInformation ::= SEQUENCE {
+ policyIdentifier CertPolicyId,
+ policyQualifiers SEQUENCE SIZE (1..MAX) OF
+ PolicyQualifierInfo OPTIONAL
+ }
+
+ CertPolicyId ::= OBJECT IDENTIFIER
+
+ PolicyQualifierInfo ::= SEQUENCE {
+ policyQualifierId PolicyQualifierId,
+ qualifier ANY DEFINED BY policyQualifierId
+ }
+ -->
+ <asn:ObjectIdentifier name="PolicyIdentifier" backingType="string" />
+ <asn:AnyValue name="PolicyQualifiers" optional="true" />
+</asn:Sequence>
\ No newline at end of file
--- /dev/null
+using System;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Asn1;
+
+namespace System.Security.Cryptography.X509Certificates.Asn1
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal partial struct PolicyInformationAsn
+ {
+ internal string PolicyIdentifier;
+ internal ReadOnlyMemory<byte>? PolicyQualifiers;
+
+ internal void Encode(AsnWriter writer)
+ {
+ Encode(writer, Asn1Tag.Sequence);
+ }
+
+ internal void Encode(AsnWriter writer, Asn1Tag tag)
+ {
+ writer.PushSequence(tag);
+
+ writer.WriteObjectIdentifier(PolicyIdentifier);
+
+ if (PolicyQualifiers.HasValue)
+ {
+ writer.WriteEncodedValue(PolicyQualifiers.Value);
+ }
+
+ writer.PopSequence(tag);
+ }
+
+ internal static PolicyInformationAsn Decode(ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+ {
+ return Decode(Asn1Tag.Sequence, encoded, ruleSet);
+ }
+
+ internal static PolicyInformationAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+ {
+ AsnReader reader = new AsnReader(encoded, ruleSet);
+
+ Decode(reader, expectedTag, out PolicyInformationAsn decoded);
+ reader.ThrowIfNotEmpty();
+ return decoded;
+ }
+
+ internal static void Decode(AsnReader reader, out PolicyInformationAsn decoded)
+ {
+ if (reader == null)
+ throw new ArgumentNullException(nameof(reader));
+
+ Decode(reader, Asn1Tag.Sequence, out decoded);
+ }
+
+ internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out PolicyInformationAsn decoded)
+ {
+ if (reader == null)
+ throw new ArgumentNullException(nameof(reader));
+
+ decoded = default;
+ AsnReader sequenceReader = reader.ReadSequence(expectedTag);
+
+ decoded.PolicyIdentifier = sequenceReader.ReadObjectIdentifierAsString();
+
+ if (sequenceReader.HasData)
+ {
+ decoded.PolicyQualifiers = sequenceReader.GetEncodedValue();
+ }
+
+
+ sequenceReader.ThrowIfNotEmpty();
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Security.Cryptography.X509Certificates.Asn1
+{
+ // https://tools.ietf.org/html/rfc5280#section-4.2.1.13
+ //
+ // ReasonFlags ::= BIT STRING {
+ // unused (0),
+ // keyCompromise (1),
+ // cACompromise (2),
+ // affiliationChanged (3),
+ // superseded (4),
+ // cessationOfOperation (5),
+ // certificateHold (6),
+ // privilegeWithdrawn (7),
+ // aACompromise (8)
+ // }
+ [Flags]
+ internal enum ReasonFlagsAsn
+ {
+ Unused = 1 << 0,
+ KeyCompromise = 1 << 1,
+ CACompromise = 1 << 2,
+ AffiliationChanged = 1 << 3,
+ Superseded = 1 << 4,
+ CessationOfOperation = 1 << 5,
+ CertificateHold = 1 << 6,
+ PrivilegeWithdrawn = 1 << 7,
+ AACompromise = 1 << 8,
+ }
+}
SR.Format(SR.Cryptography_CertReq_DuplicateExtension, extension.Oid.Value));
}
- extensionAsns.Add(new X509ExtensionAsn(extension, false));
+ extensionAsns.Add(new X509ExtensionAsn(extension));
}
tbsCertificate.Extensions = extensionAsns.ToArray();