1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-10 01:50:53 +09:00

Move 'Internal.Cryptography.Pal.CertificateData' into its own file. (dotnet/corefx#30707)

This moves all the managed pieces of Internal/Cryptography/Pal.OSX/CertificatePal.cs
which are not platform-specific into a separate file to allow them to be used in Mono.

Commit migrated from a69961ae69
This commit is contained in:
Martin Baulig 2018-06-28 12:28:24 -04:00 committed by Stephen Toub
parent c6fdf22f7a
commit 76a4991a94
3 changed files with 508 additions and 489 deletions

View file

@ -0,0 +1,506 @@
// 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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace Internal.Cryptography.Pal
{
internal enum GeneralNameType
{
OtherName = 0,
Rfc822Name = 1,
// RFC 822: Standard for the format of ARPA Internet Text Messages.
// That means "email", and an RFC 822 Name: "Email address"
Email = Rfc822Name,
DnsName = 2,
X400Address = 3,
DirectoryName = 4,
EdiPartyName = 5,
UniformResourceIdentifier = 6,
IPAddress = 7,
RegisteredId = 8,
}
internal struct CertificateData
{
internal struct AlgorithmIdentifier
{
internal string AlgorithmId;
internal byte[] Parameters;
}
internal byte[] RawData;
internal byte[] SubjectPublicKeyInfo;
internal int Version;
internal byte[] SerialNumber;
internal AlgorithmIdentifier TbsSignature;
internal X500DistinguishedName Issuer;
internal DateTime NotBefore;
internal DateTime NotAfter;
internal X500DistinguishedName Subject;
internal AlgorithmIdentifier PublicKeyAlgorithm;
internal byte[] PublicKey;
internal byte[] IssuerUniqueId;
internal byte[] SubjectUniqueId;
internal List<X509Extension> Extensions;
internal AlgorithmIdentifier SignatureAlgorithm;
internal byte[] SignatureValue;
internal CertificateData(byte[] rawData)
{
#if DEBUG
try
{
#endif
DerSequenceReader reader = new DerSequenceReader(rawData);
DerSequenceReader tbsCertificate = reader.ReadSequence();
if (tbsCertificate.PeekTag() == DerSequenceReader.ContextSpecificConstructedTag0)
{
DerSequenceReader version = tbsCertificate.ReadSequence();
Version = version.ReadInteger();
}
else if (tbsCertificate.PeekTag() != (byte)DerSequenceReader.DerTag.Integer)
{
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}
else
{
Version = 0;
}
if (Version < 0 || Version > 2)
throw new CryptographicException();
SerialNumber = tbsCertificate.ReadIntegerBytes();
DerSequenceReader tbsSignature = tbsCertificate.ReadSequence();
TbsSignature.AlgorithmId = tbsSignature.ReadOidAsString();
TbsSignature.Parameters = tbsSignature.HasData ? tbsSignature.ReadNextEncodedValue() : Array.Empty<byte>();
if (tbsSignature.HasData)
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
Issuer = new X500DistinguishedName(tbsCertificate.ReadNextEncodedValue());
DerSequenceReader validity = tbsCertificate.ReadSequence();
NotBefore = validity.ReadX509Date();
NotAfter = validity.ReadX509Date();
if (validity.HasData)
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
Subject = new X500DistinguishedName(tbsCertificate.ReadNextEncodedValue());
SubjectPublicKeyInfo = tbsCertificate.ReadNextEncodedValue();
DerSequenceReader subjectPublicKeyInfo = new DerSequenceReader(SubjectPublicKeyInfo);
DerSequenceReader subjectKeyAlgorithm = subjectPublicKeyInfo.ReadSequence();
PublicKeyAlgorithm.AlgorithmId = subjectKeyAlgorithm.ReadOidAsString();
PublicKeyAlgorithm.Parameters = subjectKeyAlgorithm.HasData ? subjectKeyAlgorithm.ReadNextEncodedValue() : Array.Empty<byte>();
if (subjectKeyAlgorithm.HasData)
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
PublicKey = subjectPublicKeyInfo.ReadBitString();
if (subjectPublicKeyInfo.HasData)
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
if (Version > 0 &&
tbsCertificate.HasData &&
tbsCertificate.PeekTag() == DerSequenceReader.ContextSpecificConstructedTag1)
{
IssuerUniqueId = tbsCertificate.ReadBitString();
}
else
{
IssuerUniqueId = null;
}
if (Version > 0 &&
tbsCertificate.HasData &&
tbsCertificate.PeekTag() == DerSequenceReader.ContextSpecificConstructedTag2)
{
SubjectUniqueId = tbsCertificate.ReadBitString();
}
else
{
SubjectUniqueId = null;
}
Extensions = new List<X509Extension>();
if (Version > 1 &&
tbsCertificate.HasData &&
tbsCertificate.PeekTag() == DerSequenceReader.ContextSpecificConstructedTag3)
{
DerSequenceReader extensions = tbsCertificate.ReadSequence();
extensions = extensions.ReadSequence();
while (extensions.HasData)
{
DerSequenceReader extensionReader = extensions.ReadSequence();
string oid = extensionReader.ReadOidAsString();
bool critical = false;
if (extensionReader.PeekTag() == (byte)DerSequenceReader.DerTag.Boolean)
{
critical = extensionReader.ReadBoolean();
}
byte[] extensionData = extensionReader.ReadOctetString();
Extensions.Add(new X509Extension(oid, extensionData, critical));
if (extensionReader.HasData)
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}
}
if (tbsCertificate.HasData)
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
DerSequenceReader signatureAlgorithm = reader.ReadSequence();
SignatureAlgorithm.AlgorithmId = signatureAlgorithm.ReadOidAsString();
SignatureAlgorithm.Parameters = signatureAlgorithm.HasData ? signatureAlgorithm.ReadNextEncodedValue() : Array.Empty<byte>();
if (signatureAlgorithm.HasData)
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
SignatureValue = reader.ReadBitString();
if (reader.HasData)
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
RawData = rawData;
#if DEBUG
}
catch (Exception e)
{
throw new CryptographicException(
$"Error in reading certificate:{Environment.NewLine}{PemPrintCert(rawData)}",
e);
}
#endif
}
public string GetNameInfo(X509NameType nameType, bool forIssuer)
{
// Algorithm behaviors (pseudocode). When forIssuer is true, replace "Subject" with "Issuer" and
// SAN (Subject Alternative Names) with IAN (Issuer Alternative Names).
//
// SimpleName: Subject[CN] ?? Subject[OU] ?? Subject[O] ?? Subject[E] ?? Subject.Rdns.FirstOrDefault() ??
// SAN.Entries.FirstOrDefault(type == GEN_EMAIL);
// EmailName: SAN.Entries.FirstOrDefault(type == GEN_EMAIL) ?? Subject[E];
// UpnName: SAN.Entries.FirsOrDefaultt(type == GEN_OTHER && entry.AsOther().OID == szOidUpn).AsOther().Value;
// DnsName: SAN.Entries.FirstOrDefault(type == GEN_DNS) ?? Subject[CN];
// DnsFromAlternativeName: SAN.Entries.FirstOrDefault(type == GEN_DNS);
// UrlName: SAN.Entries.FirstOrDefault(type == GEN_URI);
if (nameType == X509NameType.SimpleName)
{
X500DistinguishedName name = forIssuer ? Issuer : Subject;
string candidate = GetSimpleNameInfo(name);
if (candidate != null)
{
return candidate;
}
}
// Check the Subject Alternative Name (or Issuer Alternative Name) for the right value;
{
string extensionId = forIssuer ? Oids.IssuerAltName : Oids.SubjectAltName;
GeneralNameType? matchType = null;
string otherOid = null;
// Currently all X509NameType types have a path where they look at the SAN/IAN,
// but we need to figure out which kind they want.
switch (nameType)
{
case X509NameType.DnsName:
case X509NameType.DnsFromAlternativeName:
matchType = GeneralNameType.DnsName;
break;
case X509NameType.SimpleName:
case X509NameType.EmailName:
matchType = GeneralNameType.Email;
break;
case X509NameType.UpnName:
matchType = GeneralNameType.OtherName;
otherOid = Oids.UserPrincipalName;
break;
case X509NameType.UrlName:
matchType = GeneralNameType.UniformResourceIdentifier;
break;
}
if (matchType.HasValue)
{
foreach (X509Extension extension in Extensions)
{
if (extension.Oid.Value == extensionId)
{
string candidate = FindAltNameMatch(extension.RawData, matchType.Value, otherOid);
if (candidate != null)
{
return candidate;
}
}
}
}
else
{
Debug.Fail($"Unresolved matchType for X509NameType.{nameType}");
}
}
// Subject-based fallback
{
string expectedKey = null;
switch (nameType)
{
case X509NameType.EmailName:
expectedKey = Oids.EmailAddress;
break;
case X509NameType.DnsName:
// Note: This does not include DnsFromAlternativeName, since
// the subject (or issuer) is not the Alternative Name.
expectedKey = Oids.CommonName;
break;
}
if (expectedKey != null)
{
X500DistinguishedName name = forIssuer ? Issuer : Subject;
foreach (var kvp in ReadReverseRdns(name))
{
if (kvp.Key == expectedKey)
{
return kvp.Value;
}
}
}
}
return "";
}
private static string GetSimpleNameInfo(X500DistinguishedName name)
{
string ou = null;
string o = null;
string e = null;
string firstRdn = null;
foreach (var kvp in ReadReverseRdns(name))
{
string oid = kvp.Key;
string value = kvp.Value;
// TODO: Check this (and the OpenSSL-using version) if OU/etc are specified more than once.
// (Compare against Windows)
switch (oid)
{
case Oids.CommonName:
return value;
case Oids.OrganizationalUnit:
ou = value;
break;
case Oids.Organization:
o = value;
break;
case Oids.EmailAddress:
e = value;
break;
default:
if (firstRdn == null)
{
firstRdn = value;
}
break;
}
}
return ou ?? o ?? e ?? firstRdn;
}
private static string FindAltNameMatch(byte[] extensionBytes, GeneralNameType matchType, string otherOid)
{
// If Other, have OID, else, no OID.
Debug.Assert(
(otherOid == null) == (matchType != GeneralNameType.OtherName),
$"otherOid has incorrect nullarity for matchType {matchType}");
Debug.Assert(
matchType == GeneralNameType.UniformResourceIdentifier ||
matchType == GeneralNameType.DnsName ||
matchType == GeneralNameType.Email ||
matchType == GeneralNameType.OtherName,
$"matchType ({matchType}) is not currently supported");
Debug.Assert(
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);
if (matchType == GeneralNameType.OtherName)
{
expectedTag |= DerSequenceReader.ConstructedFlag;
}
DerSequenceReader altNameReader = new DerSequenceReader(extensionBytes);
while (altNameReader.HasData)
{
if (altNameReader.PeekTag() != expectedTag)
{
altNameReader.SkipValue();
continue;
}
switch (matchType)
{
case GeneralNameType.OtherName:
{
DerSequenceReader otherNameReader = altNameReader.ReadSequence();
string oid = otherNameReader.ReadOidAsString();
if (oid == 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();
}
// If the OtherName OID didn't match, move to the next entry.
continue;
}
case GeneralNameType.Rfc822Name:
case GeneralNameType.DnsName:
case GeneralNameType.UniformResourceIdentifier:
return altNameReader.ReadIA5String();
default:
altNameReader.SkipValue();
continue;
}
}
return null;
}
private static IEnumerable<KeyValuePair<string, string>> ReadReverseRdns(X500DistinguishedName name)
{
DerSequenceReader x500NameReader = new DerSequenceReader(name.RawData);
var rdnReaders = new Stack<DerSequenceReader>();
while (x500NameReader.HasData)
{
rdnReaders.Push(x500NameReader.ReadSet());
}
while (rdnReaders.Count > 0)
{
DerSequenceReader 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;
// Ignore anything we don't know how to read.
}
if (value != null)
{
yield return new KeyValuePair<string, string>(oid, value);
}
}
}
}
#if DEBUG
private static string PemPrintCert(byte[] rawData)
{
const string PemHeader = "-----BEGIN CERTIFICATE-----";
const string PemFooter = "-----END CERTIFICATE-----";
StringBuilder builder = new StringBuilder(PemHeader.Length + PemFooter.Length + rawData.Length * 2);
builder.Append(PemHeader);
builder.AppendLine();
builder.Append(Convert.ToBase64String(rawData, Base64FormattingOptions.InsertLineBreaks));
builder.AppendLine();
builder.Append(PemFooter);
builder.AppendLine();
return builder.ToString();
}
#endif
}
}

View file

@ -526,246 +526,8 @@ namespace Internal.Cryptography.Pal
public string GetNameInfo(X509NameType nameType, bool forIssuer)
{
// Algorithm behaviors (pseudocode). When forIssuer is true, replace "Subject" with "Issuer" and
// SAN (Subject Alternative Names) with IAN (Issuer Alternative Names).
//
// SimpleName: Subject[CN] ?? Subject[OU] ?? Subject[O] ?? Subject[E] ?? Subject.Rdns.FirstOrDefault() ??
// SAN.Entries.FirstOrDefault(type == GEN_EMAIL);
// EmailName: SAN.Entries.FirstOrDefault(type == GEN_EMAIL) ?? Subject[E];
// UpnName: SAN.Entries.FirsOrDefaultt(type == GEN_OTHER && entry.AsOther().OID == szOidUpn).AsOther().Value;
// DnsName: SAN.Entries.FirstOrDefault(type == GEN_DNS) ?? Subject[CN];
// DnsFromAlternativeName: SAN.Entries.FirstOrDefault(type == GEN_DNS);
// UrlName: SAN.Entries.FirstOrDefault(type == GEN_URI);
EnsureCertData();
if (nameType == X509NameType.SimpleName)
{
X500DistinguishedName name = forIssuer ? _certData.Issuer : _certData.Subject;
string candidate = GetSimpleNameInfo(name);
if (candidate != null)
{
return candidate;
}
}
// Check the Subject Alternative Name (or Issuer Alternative Name) for the right value;
{
string extensionId = forIssuer ? Oids.IssuerAltName : Oids.SubjectAltName;
GeneralNameType? matchType = null;
string otherOid = null;
// Currently all X509NameType types have a path where they look at the SAN/IAN,
// but we need to figure out which kind they want.
switch (nameType)
{
case X509NameType.DnsName:
case X509NameType.DnsFromAlternativeName:
matchType = GeneralNameType.DnsName;
break;
case X509NameType.SimpleName:
case X509NameType.EmailName:
matchType = GeneralNameType.Email;
break;
case X509NameType.UpnName:
matchType = GeneralNameType.OtherName;
otherOid = Oids.UserPrincipalName;
break;
case X509NameType.UrlName:
matchType = GeneralNameType.UniformResourceIdentifier;
break;
}
if (matchType.HasValue)
{
foreach (X509Extension extension in _certData.Extensions)
{
if (extension.Oid.Value == extensionId)
{
string candidate = FindAltNameMatch(extension.RawData, matchType.Value, otherOid);
if (candidate != null)
{
return candidate;
}
}
}
}
else
{
Debug.Fail($"Unresolved matchType for X509NameType.{nameType}");
}
}
// Subject-based fallback
{
string expectedKey = null;
switch (nameType)
{
case X509NameType.EmailName:
expectedKey = Oids.EmailAddress;
break;
case X509NameType.DnsName:
// Note: This does not include DnsFromAlternativeName, since
// the subject (or issuer) is not the Alternative Name.
expectedKey = Oids.CommonName;
break;
}
if (expectedKey != null)
{
X500DistinguishedName name = forIssuer ? _certData.Issuer : _certData.Subject;
foreach (var kvp in ReadReverseRdns(name))
{
if (kvp.Key == expectedKey)
{
return kvp.Value;
}
}
}
}
return "";
}
private static string GetSimpleNameInfo(X500DistinguishedName name)
{
string ou = null;
string o = null;
string e = null;
string firstRdn = null;
foreach (var kvp in ReadReverseRdns(name))
{
string oid = kvp.Key;
string value = kvp.Value;
// TODO: Check this (and the OpenSSL-using version) if OU/etc are specified more than once.
// (Compare against Windows)
switch (oid)
{
case Oids.CommonName:
return value;
case Oids.OrganizationalUnit:
ou = value;
break;
case Oids.Organization:
o = value;
break;
case Oids.EmailAddress:
e = value;
break;
default:
if (firstRdn == null)
{
firstRdn = value;
}
break;
}
}
return ou ?? o ?? e ?? firstRdn;
}
private static string FindAltNameMatch(byte[] extensionBytes, GeneralNameType matchType, string otherOid)
{
// If Other, have OID, else, no OID.
Debug.Assert(
(otherOid == null) == (matchType != GeneralNameType.OtherName),
$"otherOid has incorrect nullarity for matchType {matchType}");
Debug.Assert(
matchType == GeneralNameType.UniformResourceIdentifier ||
matchType == GeneralNameType.DnsName ||
matchType == GeneralNameType.Email ||
matchType == GeneralNameType.OtherName,
$"matchType ({matchType}) is not currently supported");
Debug.Assert(
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);
if (matchType == GeneralNameType.OtherName)
{
expectedTag |= DerSequenceReader.ConstructedFlag;
}
DerSequenceReader altNameReader = new DerSequenceReader(extensionBytes);
while (altNameReader.HasData)
{
if (altNameReader.PeekTag() != expectedTag)
{
altNameReader.SkipValue();
continue;
}
switch (matchType)
{
case GeneralNameType.OtherName:
{
DerSequenceReader otherNameReader = altNameReader.ReadSequence();
string oid = otherNameReader.ReadOidAsString();
if (oid == 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();
}
// If the OtherName OID didn't match, move to the next entry.
continue;
}
case GeneralNameType.Rfc822Name:
case GeneralNameType.DnsName:
case GeneralNameType.UniformResourceIdentifier:
return altNameReader.ReadIA5String();
default:
altNameReader.SkipValue();
continue;
}
}
return null;
return _certData.GetNameInfo(nameType, forIssuer);
}
public void AppendPrivateKeyInfo(StringBuilder sb)
@ -791,255 +553,5 @@ namespace Internal.Cryptography.Pal
_readCertData = true;
}
private static IEnumerable<KeyValuePair<string, string>> ReadReverseRdns(X500DistinguishedName name)
{
DerSequenceReader x500NameReader = new DerSequenceReader(name.RawData);
var rdnReaders = new Stack<DerSequenceReader>();
while (x500NameReader.HasData)
{
rdnReaders.Push(x500NameReader.ReadSet());
}
while (rdnReaders.Count > 0)
{
DerSequenceReader 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;
// Ignore anything we don't know how to read.
}
if (value != null)
{
yield return new KeyValuePair<string, string>(oid, value);
}
}
}
}
}
internal enum GeneralNameType
{
OtherName = 0,
Rfc822Name = 1,
// RFC 822: Standard for the format of ARPA Internet Text Messages.
// That means "email", and an RFC 822 Name: "Email address"
Email = Rfc822Name,
DnsName = 2,
X400Address = 3,
DirectoryName = 4,
EdiPartyName = 5,
UniformResourceIdentifier = 6,
IPAddress = 7,
RegisteredId = 8,
}
internal struct CertificateData
{
internal struct AlgorithmIdentifier
{
internal string AlgorithmId;
internal byte[] Parameters;
}
internal byte[] RawData;
internal byte[] SubjectPublicKeyInfo;
internal int Version;
internal byte[] SerialNumber;
internal AlgorithmIdentifier TbsSignature;
internal X500DistinguishedName Issuer;
internal DateTime NotBefore;
internal DateTime NotAfter;
internal X500DistinguishedName Subject;
internal AlgorithmIdentifier PublicKeyAlgorithm;
internal byte[] PublicKey;
internal byte[] IssuerUniqueId;
internal byte[] SubjectUniqueId;
internal List<X509Extension> Extensions;
internal AlgorithmIdentifier SignatureAlgorithm;
internal byte[] SignatureValue;
internal CertificateData(byte[] rawData)
{
#if DEBUG
try
{
#endif
DerSequenceReader reader = new DerSequenceReader(rawData);
DerSequenceReader tbsCertificate = reader.ReadSequence();
if (tbsCertificate.PeekTag() == DerSequenceReader.ContextSpecificConstructedTag0)
{
DerSequenceReader version = tbsCertificate.ReadSequence();
Version = version.ReadInteger();
}
else if (tbsCertificate.PeekTag() != (byte)DerSequenceReader.DerTag.Integer)
{
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}
else
{
Version = 0;
}
if (Version < 0 || Version > 2)
throw new CryptographicException();
SerialNumber = tbsCertificate.ReadIntegerBytes();
DerSequenceReader tbsSignature = tbsCertificate.ReadSequence();
TbsSignature.AlgorithmId = tbsSignature.ReadOidAsString();
TbsSignature.Parameters = tbsSignature.HasData ? tbsSignature.ReadNextEncodedValue() : Array.Empty<byte>();
if (tbsSignature.HasData)
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
Issuer = new X500DistinguishedName(tbsCertificate.ReadNextEncodedValue());
DerSequenceReader validity = tbsCertificate.ReadSequence();
NotBefore = validity.ReadX509Date();
NotAfter = validity.ReadX509Date();
if (validity.HasData)
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
Subject = new X500DistinguishedName(tbsCertificate.ReadNextEncodedValue());
SubjectPublicKeyInfo = tbsCertificate.ReadNextEncodedValue();
DerSequenceReader subjectPublicKeyInfo = new DerSequenceReader(SubjectPublicKeyInfo);
DerSequenceReader subjectKeyAlgorithm = subjectPublicKeyInfo.ReadSequence();
PublicKeyAlgorithm.AlgorithmId = subjectKeyAlgorithm.ReadOidAsString();
PublicKeyAlgorithm.Parameters = subjectKeyAlgorithm.HasData ? subjectKeyAlgorithm.ReadNextEncodedValue() : Array.Empty<byte>();
if (subjectKeyAlgorithm.HasData)
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
PublicKey = subjectPublicKeyInfo.ReadBitString();
if (subjectPublicKeyInfo.HasData)
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
if (Version > 0 &&
tbsCertificate.HasData &&
tbsCertificate.PeekTag() == DerSequenceReader.ContextSpecificConstructedTag1)
{
IssuerUniqueId = tbsCertificate.ReadBitString();
}
else
{
IssuerUniqueId = null;
}
if (Version > 0 &&
tbsCertificate.HasData &&
tbsCertificate.PeekTag() == DerSequenceReader.ContextSpecificConstructedTag2)
{
SubjectUniqueId = tbsCertificate.ReadBitString();
}
else
{
SubjectUniqueId = null;
}
Extensions = new List<X509Extension>();
if (Version > 1 &&
tbsCertificate.HasData &&
tbsCertificate.PeekTag() == DerSequenceReader.ContextSpecificConstructedTag3)
{
DerSequenceReader extensions = tbsCertificate.ReadSequence();
extensions = extensions.ReadSequence();
while (extensions.HasData)
{
DerSequenceReader extensionReader = extensions.ReadSequence();
string oid = extensionReader.ReadOidAsString();
bool critical = false;
if (extensionReader.PeekTag() == (byte)DerSequenceReader.DerTag.Boolean)
{
critical = extensionReader.ReadBoolean();
}
byte[] extensionData = extensionReader.ReadOctetString();
Extensions.Add(new X509Extension(oid, extensionData, critical));
if (extensionReader.HasData)
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}
}
if (tbsCertificate.HasData)
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
DerSequenceReader signatureAlgorithm = reader.ReadSequence();
SignatureAlgorithm.AlgorithmId = signatureAlgorithm.ReadOidAsString();
SignatureAlgorithm.Parameters = signatureAlgorithm.HasData ? signatureAlgorithm.ReadNextEncodedValue() : Array.Empty<byte>();
if (signatureAlgorithm.HasData)
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
SignatureValue = reader.ReadBitString();
if (reader.HasData)
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
RawData = rawData;
#if DEBUG
}
catch (Exception e)
{
throw new CryptographicException(
$"Error in reading certificate:{Environment.NewLine}{PemPrintCert(rawData)}",
e);
}
#endif
}
#if DEBUG
private static string PemPrintCert(byte[] rawData)
{
const string PemHeader = "-----BEGIN CERTIFICATE-----";
const string PemFooter = "-----END CERTIFICATE-----";
StringBuilder builder = new StringBuilder(PemHeader.Length + PemFooter.Length + rawData.Length * 2);
builder.Append(PemHeader);
builder.AppendLine();
builder.Append(Convert.ToBase64String(rawData, Base64FormattingOptions.InsertLineBreaks));
builder.AppendLine();
builder.Append(PemFooter);
builder.AppendLine();
return builder.ToString();
}
#endif
}
}

View file

@ -375,6 +375,7 @@
<Link>Common\System\Security\Cryptography\SecKeyPair.cs</Link>
</Compile>
<Compile Include="Internal\Cryptography\Pal.OSX\CertificatePal.cs" />
<Compile Include="Internal\Cryptography\Pal.OSX\CertificateData.cs" />
<Compile Include="Internal\Cryptography\Pal.OSX\ChainPal.cs" />
<Compile Include="Internal\Cryptography\Pal.OSX\FindPal.cs" />
<Compile Include="Internal\Cryptography\Pal.OSX\StorePal.cs" />