public override byte[] GetSignatureAlgorithmIdentifier(HashAlgorithmName hashAlgorithm) { // If we ever support options in PSS (like MGF-2, if such an MGF is ever invented) // Or, more reasonably, supporting a custom value for the salt size. if (_padding != RSASignaturePadding.Pss) { throw new CryptographicException(SR.Cryptography_InvalidPaddingMode); } uint cbSalt; string digestOid; if (hashAlgorithm == HashAlgorithmName.SHA256) { cbSalt = 256 / 8; digestOid = Oids.Sha256; } else if (hashAlgorithm == HashAlgorithmName.SHA384) { cbSalt = 384 / 8; digestOid = Oids.Sha384; } else if (hashAlgorithm == HashAlgorithmName.SHA512) { cbSalt = 512 / 8; digestOid = Oids.Sha512; } else { throw new ArgumentOutOfRangeException( nameof(hashAlgorithm), hashAlgorithm, SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithm.Name)); } // RFC 5754 says that the NULL for SHA2 (256/384/512) MUST be omitted // (https://tools.ietf.org/html/rfc5754#section-2) (and that you MUST // be able to read it even if someone wrote it down) // // Since we // * don't support SHA-1 in this class // * only support MGF-1 // * don't support the MGF PRF being different than hashAlgorithm // * use saltLength==hashLength // * don't allow custom trailer // we don't have to worry about any of the DEFAULTs. (specify, specify, specify, omit). PssParamsAsn parameters = new PssParamsAsn { HashAlgorithm = new AlgorithmIdentifierAsn { Algorithm = new Oid(digestOid) }, MaskGenAlgorithm = new AlgorithmIdentifierAsn { Algorithm = new Oid(Oids.Mgf1) }, SaltLength = cbSalt, TrailerField = 1, }; using (AsnWriter mgfParamWriter = new AsnWriter(AsnEncodingRules.DER)) { mgfParamWriter.PushSequence(); mgfParamWriter.WriteObjectIdentifier(digestOid); mgfParamWriter.PopSequence(); parameters.MaskGenAlgorithm.Parameters = mgfParamWriter.Encode(); } using (AsnWriter writer = AsnSerializer.Serialize(parameters, AsnEncodingRules.DER)) { AlgorithmIdentifierAsn identifier = new AlgorithmIdentifierAsn { Algorithm = new Oid(Oids.RsaPss), Parameters = writer.Encode(), }; using (AsnWriter identifierWriter = AsnSerializer.Serialize(identifier, AsnEncodingRules.DER)) { return(identifierWriter.Encode()); } } }
private static bool VerifyX509Signature( ReadOnlySpan <byte> toBeSigned, ReadOnlySpan <byte> signature, PublicKey publicKey, AlgorithmIdentifierAsn algorithmIdentifier) { RSA? rsa = publicKey.GetRSAPublicKey(); ECDsa?ecdsa = publicKey.GetECDsaPublicKey(); try { HashAlgorithmName hashAlg; if (algorithmIdentifier.Algorithm == Oids.RsaPss) { if (rsa is null || !algorithmIdentifier.Parameters.HasValue) { return(false); } PssParamsAsn pssParams = PssParamsAsn.Decode( algorithmIdentifier.Parameters.GetValueOrDefault(), AsnEncodingRules.DER); RSASignaturePadding padding = pssParams.GetSignaturePadding(); hashAlg = HashAlgorithmName.FromOid(pssParams.HashAlgorithm.Algorithm); return(rsa.VerifyData( toBeSigned, signature, hashAlg, padding)); } switch (algorithmIdentifier.Algorithm) { case Oids.RsaPkcs1Sha256: case Oids.ECDsaWithSha256: hashAlg = HashAlgorithmName.SHA256; break; case Oids.RsaPkcs1Sha384: case Oids.ECDsaWithSha384: hashAlg = HashAlgorithmName.SHA384; break; case Oids.RsaPkcs1Sha512: case Oids.ECDsaWithSha512: hashAlg = HashAlgorithmName.SHA512; break; case Oids.RsaPkcs1Sha1: case Oids.ECDsaWithSha1: hashAlg = HashAlgorithmName.SHA1; break; default: throw new NotSupportedException( SR.Format(SR.Cryptography_UnknownKeyAlgorithm, algorithmIdentifier.Algorithm)); } // All remaining supported algorithms have no defined parameters if (!algorithmIdentifier.HasNullEquivalentParameters()) { return(false); } switch (algorithmIdentifier.Algorithm) { case Oids.RsaPkcs1Sha256: case Oids.RsaPkcs1Sha384: case Oids.RsaPkcs1Sha512: case Oids.RsaPkcs1Sha1: if (rsa is null) { return(false); } return(rsa.VerifyData(toBeSigned, signature, hashAlg, RSASignaturePadding.Pkcs1)); case Oids.ECDsaWithSha256: case Oids.ECDsaWithSha384: case Oids.ECDsaWithSha512: case Oids.ECDsaWithSha1: if (ecdsa is null) { return(false); } return(ecdsa.VerifyData(toBeSigned, signature, hashAlg, DSASignatureFormat.Rfc3279DerSequence)); default: Debug.Fail( $"Algorithm ID {algorithmIdentifier.Algorithm} was in the first switch, but not the second"); return(false); } } catch (AsnContentException) { return(false); } catch (CryptographicException) { return(false); } finally { rsa?.Dispose(); ecdsa?.Dispose(); } }