Imported Upstream version 4.8.0.309

Former-commit-id: 5f9c6ae75f295e057a7d2971f3a6df4656fa8850
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2016-11-10 13:04:39 +00:00
parent ee1447783b
commit 94b2861243
4912 changed files with 390737 additions and 49310 deletions

View File

@ -5,12 +5,14 @@
namespace System.IdentityModel.Claims
{
using System.Collections.Generic;
using System.Diagnostics;
using System.IdentityModel.Policy;
using System.Net.Mail;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Principal;
using Globalization;
public class X509CertificateClaimSet : ClaimSet, IIdentityInfo, IDisposable
{
@ -172,24 +174,7 @@ namespace System.IdentityModel.Claims
if (!string.IsNullOrEmpty(value))
claims.Add(Claim.CreateX500DistinguishedNameClaim(this.certificate.SubjectName));
// App context switch for disabling support for multiple dns entries in a SAN certificate
if (LocalAppContextSwitches.DisableMultipleDNSEntriesInSANCertificate)
{
// old behavior, default for <= 4.6
value = this.certificate.GetNameInfo(X509NameType.DnsName, false);
if (!string.IsNullOrEmpty(value))
claims.Add(Claim.CreateDnsClaim(value));
}
else
{
// new behavior as this is the default long term behavior
// Since a SAN can have multiple DNS entries
string[] entries = GetDnsFromExtensions(this.certificate);
for (int i = 0; i < entries.Length; ++i)
{
claims.Add(Claim.CreateDnsClaim(entries[i]));
}
}
claims.AddRange(GetDnsClaims(this.certificate));
value = this.certificate.GetNameInfo(X509NameType.SimpleName, false);
if (!string.IsNullOrEmpty(value))
@ -258,25 +243,8 @@ namespace System.IdentityModel.Claims
{
if (right == null || Rights.PossessProperty.Equals(right))
{
// App context switch for disabling support for multiple dns entries in a SAN certificate
if (LocalAppContextSwitches.DisableMultipleDNSEntriesInSANCertificate)
{
// old behavior, default for <= 4.6
string value = this.certificate.GetNameInfo(X509NameType.DnsName, false);
if (!string.IsNullOrEmpty(value))
{
yield return Claim.CreateDnsClaim(value);
}
}
else
{
// new behavior since this is the default long term behavior
string[] entries = GetDnsFromExtensions(certificate);
for (int i = 0; i < entries.Length; ++i)
{
yield return Claim.CreateDnsClaim(entries[i]);
}
}
foreach (var claim in GetDnsClaims(certificate))
yield return claim;
}
}
else
@ -299,31 +267,44 @@ namespace System.IdentityModel.Claims
}
}
// Fixing Bug 795660: SAN having multiple DNS entries
private static string[] GetDnsFromExtensions(X509Certificate2 cert)
private static List<Claim> GetDnsClaims(X509Certificate2 cert)
{
foreach (X509Extension ext in cert.Extensions)
{
// Extension is SAN or SAN2
if (ext.Oid.Value == "2.5.29.7" || ext.Oid.Value == "2.5.29.17")
{
string asnString = ext.Format(true);
if (string.IsNullOrEmpty(asnString))
{
return new string[0];
}
List<Claim> dnsClaimEntries = new List<Claim>();
string[] rawDnsEntries = asnString.Split(new string[1] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
string[] dnsEntries = new string[rawDnsEntries.Length];
for (int i = 0; i < rawDnsEntries.Length; ++i)
// old behavior, default for <= 4.6
string value = cert.GetNameInfo(X509NameType.DnsName, false);
if (!string.IsNullOrEmpty(value))
dnsClaimEntries.Add(Claim.CreateDnsClaim(value));
// App context switch for disabling support for multiple dns entries in a SAN certificate
// If we can't dynamically parse the alt subject names, we will not add any dns claims ONLY for the alt subject names.
// In this way, if the X509NameType.DnsName was enough to succeed for the out-bound-message. We would have a success.
if (!LocalAppContextSwitches.DisableMultipleDNSEntriesInSANCertificate && X509SubjectAlternativeNameConstants.SuccessfullyInitialized)
{
foreach (X509Extension ext in cert.Extensions)
{
// Extension is SAN or SAN2
if (ext.Oid.Value == X509SubjectAlternativeNameConstants.SanOid || ext.Oid.Value == X509SubjectAlternativeNameConstants.San2Oid)
{
int equalSignIndex = rawDnsEntries[i].IndexOf('=');
dnsEntries[i] = rawDnsEntries[i].Substring(equalSignIndex + 1).Trim();
string asnString = ext.Format(false);
if (string.IsNullOrWhiteSpace(asnString))
break;
// SubjectAlternativeNames might contain something other than a dNSName,
// so we have to parse through and only use the dNSNames
// <identifier><delimiter><value><separator(s)>
string[] rawDnsEntries = asnString.Split(X509SubjectAlternativeNameConstants.SeparatorArray, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < rawDnsEntries.Length; i++)
{
string[] keyval = rawDnsEntries[i].Split(X509SubjectAlternativeNameConstants.Delimiter);
if (string.Equals(keyval[0], X509SubjectAlternativeNameConstants.Identifier))
dnsClaimEntries.Add(Claim.CreateDnsClaim(keyval[1]));
}
}
return dnsEntries;
}
}
return new string[0];
return dnsClaimEntries;
}
public override IEnumerator<Claim> GetEnumerator()
@ -367,6 +348,107 @@ namespace System.IdentityModel.Claims
get { return this.identity; }
}
}
// We don't have a strongly typed extension to parse Subject Alt Names, so we have to do a workaround
// to figure out what the identifier, delimiter, and separator is by using a well-known extension
private static class X509SubjectAlternativeNameConstants
{
public const string SanOid = "2.5.29.7";
public const string San2Oid = "2.5.29.17";
public static string Identifier
{
get;
private set;
}
public static char Delimiter
{
get;
private set;
}
public static string Separator
{
get;
private set;
}
public static string[] SeparatorArray
{
get;
private set;
}
public static bool SuccessfullyInitialized
{
get;
private set;
}
// static initializer will run before properties are accessed
static X509SubjectAlternativeNameConstants()
{
// Extracted a well-known X509Extension
byte[] x509ExtensionBytes = new byte[] {
48, 36, 130, 21, 110, 111, 116, 45, 114, 101, 97, 108, 45, 115, 117, 98, 106, 101, 99,
116, 45, 110, 97, 109, 101, 130, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109
};
const string subjectName = "not-real-subject-name";
string x509ExtensionFormattedString = string.Empty;
try
{
X509Extension x509Extension = new X509Extension(SanOid, x509ExtensionBytes, true);
x509ExtensionFormattedString = x509Extension.Format(false);
// Each OS has a different dNSName identifier and delimiter
// On Windows, dNSName == "DNS Name" (localizable), on Linux, dNSName == "DNS"
// e.g.,
// Windows: x509ExtensionFormattedString is: "DNS Name=not-real-subject-name, DNS Name=example.com"
// Linux: x509ExtensionFormattedString is: "DNS:not-real-subject-name, DNS:example.com"
// Parse: <identifier><delimiter><value><separator(s)>
int delimiterIndex = x509ExtensionFormattedString.IndexOf(subjectName) - 1;
Delimiter = x509ExtensionFormattedString[delimiterIndex];
// Make an assumption that all characters from the the start of string to the delimiter
// are part of the identifier
Identifier = x509ExtensionFormattedString.Substring(0, delimiterIndex);
int separatorFirstChar = delimiterIndex + subjectName.Length + 1;
int separatorLength = 1;
for (int i = separatorFirstChar + 1; i < x509ExtensionFormattedString.Length; i++)
{
// We advance until the first character of the identifier to determine what the
// separator is. This assumes that the identifier assumption above is correct
if (x509ExtensionFormattedString[i] == Identifier[0])
{
break;
}
separatorLength++;
}
Separator = x509ExtensionFormattedString.Substring(separatorFirstChar, separatorLength);
SeparatorArray = new string[1] { Separator };
SuccessfullyInitialized = true;
}
catch (Exception ex)
{
SuccessfullyInitialized = false;
DiagnosticUtility.TraceHandledException(
new FormatException(string.Format(CultureInfo.InvariantCulture,
"There was an error parsing the SubjectAlternativeNames: '{0}'. See inner exception for more details.{1}Detected values were: Identifier: '{2}'; Delimiter:'{3}'; Separator:'{4}'",
x509ExtensionFormattedString,
Environment.NewLine,
Identifier,
Delimiter,
Separator),
ex),
TraceEventType.Warning);
}
}
}
}
class X509Identity : GenericIdentity, IDisposable

View File

@ -15,9 +15,11 @@ namespace System.IdentityModel
{
private const string EnableCachedEmptyDefaultAuthorizationContextString = "Switch.System.IdentityModel.EnableCachedEmptyDefaultAuthorizationContext";
private const string DisableMultipleDNSEntriesInSANCertificateString = "Switch.System.IdentityModel.DisableMultipleDNSEntriesInSANCertificate";
private const string DisableUpdatingRsaProviderTypeString = "Switch.System.IdentityModel.DisableUpdatingRsaProviderType";
private static int enableCachedEmptyDefaultAuthorizationContext;
private static int disableMultipleDNSEntriesInSANCertificate;
private static int disableUpdatingRsaProviderType;
public static bool EnableCachedEmptyDefaultAuthorizationContext
{
@ -37,16 +39,29 @@ namespace System.IdentityModel
}
}
public static bool DisableUpdatingRsaProviderType
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return LocalAppContext.GetCachedSwitchValue(DisableUpdatingRsaProviderTypeString, ref disableUpdatingRsaProviderType);
}
}
public static void SetDefaultsLessOrEqual_452()
{
#pragma warning disable BCL0012
// Define the switches that should be true for 4.5.2 or less, false for 4.6+.
LocalAppContext.DefineSwitchDefault(EnableCachedEmptyDefaultAuthorizationContextString, true);
#pragma warning restore BCL0012
}
public static void SetDefaultsLessOrEqual_46()
{
#pragma warning disable BCL0012
// Define the switches that should be true for 4.6 or less, false for 4.6.1+.
LocalAppContext.DefineSwitchDefault(DisableMultipleDNSEntriesInSANCertificateString, true);
#pragma warning restore BCL0012
}
}
}

View File

@ -309,22 +309,26 @@ namespace System.IdentityModel.Tokens
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SR.GetString(SR.EmptyOrNullArgumentString, "algorithm"));
}
// We support one of the two algoritms, but not both.
// We support:
// XmlDsigDSAUrl = "http://www.w3.org/2000/09/xmldsig#dsa-sha1";
// XmlDsigRSASHA1Url = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
// RsaSha256Signature = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
AsymmetricAlgorithm privateKey = LevelUpRsa(this.PrivateKey, algorithm);
object algorithmObject = CryptoHelper.GetAlgorithmFromConfig(algorithm);
if (algorithmObject != null)
{
SignatureDescription description = algorithmObject as SignatureDescription;
if (description != null)
return description.CreateFormatter(this.PrivateKey);
return description.CreateFormatter(privateKey);
try
{
AsymmetricSignatureFormatter asymmetricSignatureFormatter = algorithmObject as AsymmetricSignatureFormatter;
if (asymmetricSignatureFormatter != null)
{
asymmetricSignatureFormatter.SetKey(this.PrivateKey);
asymmetricSignatureFormatter.SetKey(privateKey);
return asymmetricSignatureFormatter;
}
}
@ -356,19 +360,10 @@ namespace System.IdentityModel.Tokens
case SecurityAlgorithms.RsaSha256Signature:
// Ensure that we have an RSA algorithm object.
RSACryptoServiceProvider rsa_prov_full = (this.PrivateKey as RSACryptoServiceProvider);
if (rsa_prov_full == null)
RSA rsaSha256 = (privateKey as RSA);
if (rsaSha256 == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PrivateKeyNotRSA)));
CspParameters csp = new CspParameters();
csp.ProviderType = 24;
csp.KeyContainerName = rsa_prov_full.CspKeyContainerInfo.KeyContainerName;
csp.KeyNumber = (int)rsa_prov_full.CspKeyContainerInfo.KeyNumber;
if (rsa_prov_full.CspKeyContainerInfo.MachineKeyStore)
csp.Flags = CspProviderFlags.UseMachineKeyStore;
csp.Flags |= CspProviderFlags.UseExistingKey;
return new RSAPKCS1SignatureFormatter(new RSACryptoServiceProvider(csp));
return new RSAPKCS1SignatureFormatter(rsaSha256);
default:
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.UnsupportedCryptoAlgorithm, algorithm)));
@ -376,6 +371,45 @@ namespace System.IdentityModel.Tokens
}
private static AsymmetricAlgorithm LevelUpRsa(AsymmetricAlgorithm asymmetricAlgorithm, string algorithm)
{
// If user turned off leveling up at app level, return
if (LocalAppContextSwitches.DisableUpdatingRsaProviderType)
return asymmetricAlgorithm;
if (asymmetricAlgorithm == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("asymmetricAlgorithm"));
if (string.IsNullOrEmpty(algorithm))
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SR.GetString(SR.EmptyOrNullArgumentString, "algorithm"));
// only level up if alg is sha256
if (!string.Equals(algorithm, SecurityAlgorithms.RsaSha256Signature))
return asymmetricAlgorithm;
RSACryptoServiceProvider rsaCsp = asymmetricAlgorithm as RSACryptoServiceProvider;
if (rsaCsp == null)
return asymmetricAlgorithm;
// ProviderType == 1(PROV_RSA_FULL) and providerType == 12(PROV_RSA_SCHANNEL) are provider types that only support SHA1. Change them to PROV_RSA_AES=24 that supports SHA2 also.
// Only levels up if the associated key is not a hardware key.
// Another provider type related to rsa, PROV_RSA_SIG == 2 that only supports Sha1 is no longer supported
if ((rsaCsp.CspKeyContainerInfo.ProviderType == 1 || rsaCsp.CspKeyContainerInfo.ProviderType == 12) && !rsaCsp.CspKeyContainerInfo.HardwareDevice)
{
CspParameters csp = new CspParameters();
csp.ProviderType = 24;
csp.KeyContainerName = rsaCsp.CspKeyContainerInfo.KeyContainerName;
csp.KeyNumber = (int)rsaCsp.CspKeyContainerInfo.KeyNumber;
if (rsaCsp.CspKeyContainerInfo.MachineKeyStore)
csp.Flags = CspProviderFlags.UseMachineKeyStore;
csp.Flags |= CspProviderFlags.UseExistingKey;
return new RSACryptoServiceProvider(csp);
}
return rsaCsp;
}
public override bool HasPrivateKey()
{
return (this.PrivateKey != null);