File: Signing\Signatures\SigningCertificateV2.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.Security.Cryptography.X509Certificates;
using NuGet.Common;
using NuGet.Packaging.Signing.DerEncoding;

namespace NuGet.Packaging.Signing
{
    /*
        From RFC 5035 (https://tools.ietf.org/html/rfc5035):

            SigningCertificateV2 ::= SEQUENCE {
                certs        SEQUENCE OF ESSCertIDv2,
                policies     SEQUENCE OF PolicyInformation OPTIONAL
            }
    */
    /// <remarks>This is public only to facilitate testing.</remarks>
    public sealed class SigningCertificateV2
    {
        public IReadOnlyList<EssCertIdV2> Certificates { get; }
        public IReadOnlyList<PolicyInformation>? Policies { get; }

        private SigningCertificateV2(
            IReadOnlyList<EssCertIdV2> certificates,
            IReadOnlyList<PolicyInformation>? policies)
        {
            Certificates = certificates;
            Policies = policies;
        }

        public static SigningCertificateV2 Create(X509Certificate2 certificate, HashAlgorithmName hashAlgorithmName)
        {
            if (certificate == null)
            {
                throw new ArgumentNullException(nameof(certificate));
            }

            var essCertIdV2 = EssCertIdV2.Create(certificate, hashAlgorithmName);

            return new SigningCertificateV2(new[] { essCertIdV2 }, policies: null);
        }

        public static SigningCertificateV2 Read(byte[] bytes)
        {
            var reader = new DerSequenceReader(bytes);

            return Read(reader);
        }

        internal static SigningCertificateV2 Read(DerSequenceReader reader)
        {
            var essCertIdV2Reader = reader.ReadSequence();
            var certificates = ReadCertificates(essCertIdV2Reader);
            IReadOnlyList<PolicyInformation>? policies = null;

            if (reader.HasData)
            {
                var policiesReader = reader.ReadSequence();
                policies = ReadPolicies(policiesReader);

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

            return new SigningCertificateV2(certificates, policies);
        }

        public byte[] Encode()
        {
            var entries = new List<byte[][]>(Certificates.Count);

            foreach (var essCertIdV2 in Certificates)
            {
                entries.Add(essCertIdV2.Encode());
            }

            return DerEncoder.ConstructSequence(DerEncoder.ConstructSegmentedSequence(entries));
        }

        private static IReadOnlyList<EssCertIdV2> ReadCertificates(DerSequenceReader reader)
        {
            var certificates = new List<EssCertIdV2>();

            while (reader.HasData)
            {
                var certificate = EssCertIdV2.Read(reader);

                certificates.Add(certificate);
            }

            return certificates.AsReadOnly();
        }

        private static IReadOnlyList<PolicyInformation> ReadPolicies(DerSequenceReader reader)
        {
            var policies = new List<PolicyInformation>();

            while (reader.HasData)
            {
                var policy = PolicyInformation.Read(reader);

                policies.Add(policy);
            }

            return policies.AsReadOnly();
        }
    }
}