File: Signing\Signatures\IssuerSerial.cs
Web Access
Project: src\src\nuget-client\src\NuGet.Core\NuGet.Packaging\NuGet.Packaging.csproj (NuGet.Packaging)
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using NuGet.Packaging.Signing.DerEncoding;

namespace NuGet.Packaging.Signing
{
    /*
        From RFC 2634 (https://tools.ietf.org/html/rfc2634#section-5.4.1):

            IssuerSerial ::= SEQUENCE {
                issuer                   GeneralNames,
                serialNumber             CertificateSerialNumber
            }

        From RFC 2634 (https://tools.ietf.org/html/rfc3280#section-4.2.1.7):

            GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName

        From RFC 3280 (https://tools.ietf.org/html/rfc3280#section-4.1):

            CertificateSerialNumber  ::=  INTEGER
    */
    /// <remarks>This is public only to facilitate testing.</remarks>
    public sealed class IssuerSerial
    {
        public IReadOnlyList<GeneralName> GeneralNames { get; }
        public byte[] SerialNumber { get; }

        private IssuerSerial(IReadOnlyList<GeneralName> generalNames, byte[] serialNumber)
        {
            GeneralNames = generalNames;
            SerialNumber = serialNumber;
        }

        public static IssuerSerial Create(X509Certificate2 certificate)
        {
            if (certificate == null)
            {
                throw new ArgumentNullException(nameof(certificate));
            }

            var generalNames = new[] { GeneralName.Create(certificate.IssuerName) };
            var serialNumber = certificate.GetSerialNumber();

            // Convert from little endian to big endian.
            Array.Reverse(serialNumber);

            return new IssuerSerial(generalNames, serialNumber);
        }

        public static IssuerSerial Read(byte[] bytes)
        {
            var reader = DerSequenceReader.CreateForPayload(bytes);

            return Read(reader);
        }

        internal static IssuerSerial Read(DerSequenceReader reader)
        {
            var sequenceReader = reader.ReadSequence();
            var generalNames = ReadGeneralNames(sequenceReader);
            var serialNumber = sequenceReader.ReadIntegerBytes();

            return new IssuerSerial(generalNames, serialNumber);
        }

        internal byte[][] Encode()
        {
            // Per RFC 5280 section 4.1.2.2 (https://tools.ietf.org/html/rfc5280#section-4.1.2.2)
            // serial number must be an unsigned integer.
            return DerEncoder.ConstructSegmentedSequence(
                DerEncoder.ConstructSegmentedSequence(GeneralNames.First().Encode()),
                DerEncoder.SegmentedEncodeUnsignedInteger(SerialNumber));
        }

        private static IReadOnlyList<GeneralName> ReadGeneralNames(DerSequenceReader reader)
        {
            var sequenceReader = reader.ReadSequence();
            var generalNames = new List<GeneralName>(capacity: 1);

            var generalName = GeneralName.Read(sequenceReader);

            if (generalName != null)
            {
                generalNames.Add(generalName);
            }

            if (sequenceReader.HasData)
            {
                throw new SignatureException(Strings.InvalidAsn1);
            }

            return generalNames.AsReadOnly();
        }
    }
}