private static unsafe CertificateRequest LoadSigningRequest(
ReadOnlySpan <byte> pkcs10,
bool permitTrailingData,
HashAlgorithmName signerHashAlgorithm,
out int bytesConsumed,
CertificateRequestLoadOptions options,
RSASignaturePadding?signerSignaturePadding)
{
ArgumentException.ThrowIfNullOrEmpty(signerHashAlgorithm.Name, nameof(signerHashAlgorithm));
if ((options & ~AllOptions) != 0)
{
throw new ArgumentOutOfRangeException(nameof(options), options, SR.Argument_InvalidFlag);
}
bool skipSignatureValidation =
(options & CertificateRequestLoadOptions.SkipSignatureValidation) != 0;
bool unsafeLoadCertificateExtensions =
(options & CertificateRequestLoadOptions.UnsafeLoadCertificateExtensions) != 0;
try
{
AsnValueReader outer = new AsnValueReader(pkcs10, AsnEncodingRules.DER);
int encodedLength = outer.PeekEncodedValue().Length;
AsnValueReader pkcs10Asn = outer.ReadSequence();
CertificateRequest req;
if (!permitTrailingData)
{
outer.ThrowIfNotEmpty();
}
fixed(byte *p10ptr = pkcs10)
{
using (PointerMemoryManager <byte> manager = new PointerMemoryManager <byte>(p10ptr, encodedLength))
{
ReadOnlyMemory <byte> rebind = manager.Memory;
ReadOnlySpan <byte> encodedRequestInfo = pkcs10Asn.PeekEncodedValue();
CertificationRequestInfoAsn requestInfo;
AlgorithmIdentifierAsn algorithmIdentifier;
ReadOnlySpan <byte> signature;
int signatureUnusedBitCount;
CertificationRequestInfoAsn.Decode(ref pkcs10Asn, rebind, out requestInfo);
AlgorithmIdentifierAsn.Decode(ref pkcs10Asn, rebind, out algorithmIdentifier);
if (!pkcs10Asn.TryReadPrimitiveBitString(out signatureUnusedBitCount, out signature))
{
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}
pkcs10Asn.ThrowIfNotEmpty();
if (requestInfo.Version < 0)
{
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}
// They haven't bumped from v0 to v1 as of 2022.
const int MaxSupportedVersion = 0;
if (requestInfo.Version != MaxSupportedVersion)
{
throw new CryptographicException(
SR.Format(
SR.Cryptography_CertReq_Load_VersionTooNew,
requestInfo.Version,
MaxSupportedVersion));
}
PublicKey publicKey = PublicKey.DecodeSubjectPublicKeyInfo(ref requestInfo.SubjectPublicKeyInfo);
if (!skipSignatureValidation)
{
// None of the supported signature algorithms support signatures that are not full bytes.
// So, shortcut the verification on the bit length
if (signatureUnusedBitCount != 0 ||
!VerifyX509Signature(encodedRequestInfo, signature, publicKey, algorithmIdentifier))
{
throw new CryptographicException(SR.Cryptography_CertReq_SignatureVerificationFailed);
}
}
X500DistinguishedName subject = new X500DistinguishedName(requestInfo.Subject.Span);
req = new CertificateRequest(
subject,
publicKey,
signerHashAlgorithm,
signerSignaturePadding);
if (requestInfo.Attributes is not null)
{
bool foundCertExt = false;
foreach (AttributeAsn attr in requestInfo.Attributes)
{
if (attr.AttrType == Oids.Pkcs9ExtensionRequest)
{
if (foundCertExt)
{
throw new CryptographicException(
SR.Cryptography_CertReq_Load_DuplicateExtensionRequests);
}
foundCertExt = true;
if (attr.AttrValues.Length != 1)
{
throw new CryptographicException(
SR.Cryptography_CertReq_Load_DuplicateExtensionRequests);
}
AsnValueReader extsReader = new AsnValueReader(
attr.AttrValues[0].Span,
AsnEncodingRules.DER);
AsnValueReader exts = extsReader.ReadSequence();
extsReader.ThrowIfNotEmpty();
// Minimum length is 1, so do..while
do
{
X509ExtensionAsn.Decode(ref exts, rebind, out X509ExtensionAsn extAsn);
if (unsafeLoadCertificateExtensions)
{
X509Extension ext = new X509Extension(
extAsn.ExtnId,
extAsn.ExtnValue.Span,
extAsn.Critical);
X509Extension?rich =
X509Certificate2.CreateCustomExtensionIfAny(extAsn.ExtnId);
if (rich is not null)
{
rich.CopyFrom(ext);
req.CertificateExtensions.Add(rich);
}
else
{
req.CertificateExtensions.Add(ext);
}
}
} while (exts.HasData);
}
else
{
if (attr.AttrValues.Length == 0)
{
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}
foreach (ReadOnlyMemory <byte> val in attr.AttrValues)
{
req.OtherRequestAttributes.Add(
new AsnEncodedData(attr.AttrType, val.Span));
}
}
}
}
}
}
bytesConsumed = encodedLength;
return(req);
}
catch (AsnContentException e)
{
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e);
}
}