internal static bool TryGetStepOne(
            X509 currentCerts,
            X509 targetCerts,
            CertificateClusterUpgradeStep previousStep,
            out CertificateClusterUpgradeStep step)
        {
            // step 1 adds all newly added certs and issuers (if any) to the white list
            step = null;
            List <string> currentThumbprints               = CertificateClusterUpgradeFlow.GetThumbprints(currentCerts.ClusterCertificate);
            List <string> addedThumbprints                 = CertificateClusterUpgradeFlow.GetAddedThumbprints(currentCerts.ClusterCertificate, targetCerts.ClusterCertificate);
            Dictionary <string, string> currentCns         = CertificateClusterUpgradeFlow.GetCns(currentCerts.ClusterCertificateCommonNames);
            Dictionary <string, string> addedCnsAndIssuers = CertificateClusterUpgradeFlow.GetAddedCnsAndIssuers(currentCerts.ClusterCertificateCommonNames, targetCerts.ClusterCertificateCommonNames);

            if (addedThumbprints.Any() || addedCnsAndIssuers.Any())
            {
                step = new CertificateClusterUpgradeStep(
                    thumbprintWhiteList: currentThumbprints.Concat(addedThumbprints).ToList(),
                    thumbprintLoadList: currentCerts.ClusterCertificate,
                    thumbprintFileStoreSvcList: currentCerts.ClusterCertificate,
                    commonNameWhiteList: CertificateClusterUpgradeFlow.MergeCnsAndIssuers(currentCns, addedCnsAndIssuers),
                    commonNameLoadList: currentCerts.ClusterCertificateCommonNames,
                    commonNameFileStoreSvcList: currentCerts.ClusterCertificateCommonNames);
            }

            return(true);
        }
        internal void ValidateCertificates()
        {
            if (this.clusterProperties.Security == null || this.clusterProperties.Security.CertificateInformation == null)
            {
                return;
            }

            X509 certInfo = this.clusterProperties.Security.CertificateInformation;
            List <ClientCertificateThumbprint> clientCertificateThumbprints = certInfo.ClientCertificateThumbprints;
            List <ClientCertificateCommonName> clientCertificateCommonNames = certInfo.ClientCertificateCommonNames;

            bool serverCertificatePresent    = certInfo.ServerCertificate != null || (certInfo.ServerCertificateCommonNames != null && certInfo.ServerCertificateCommonNames.Any());
            bool clientCertificatInfoPresent = (clientCertificateCommonNames != null && clientCertificateCommonNames.Count > 0) ||
                                               (clientCertificateThumbprints != null && clientCertificateThumbprints.Count > 0);

            if (clientCertificatInfoPresent && !serverCertificatePresent)
            {
                throw new ValidationException(ClusterManagementErrorCode.ClientCertDefinedWithoutServerCert);
            }

            SettingsValidator.ValidateThumbprints(certInfo);
            SettingsValidator.ValidateCommonNames(certInfo);
            SettingsValidator.ValidateCommonNamesAgainstThumbprints(certInfo);
            SettingsValidator.ValidateIssuerCertStore(certInfo);
        }
        private static void ValidateIssuerPinningAndIssuerStoresCannotExistTogether(X509 certInfo)
        {
            if (certInfo.ClusterCertificateCommonNames != null &&
                certInfo.ClusterCertificateCommonNames.CommonNames != null &&
                certInfo.ClusterCertificateCommonNames.CommonNames.Any(commonName => !string.IsNullOrEmpty(commonName.CertificateIssuerThumbprint)) &&
                certInfo.ClusterCertificateIssuerStores != null &&
                certInfo.ClusterCertificateIssuerStores.Count() != 0)
            {
                throw new ValidationException(ClusterManagementErrorCode.IssuerCertStoreAndIssuerPinningCannotBeTogether);
            }

            if (certInfo.ServerCertificateCommonNames != null &&
                certInfo.ServerCertificateCommonNames.CommonNames != null &&
                certInfo.ServerCertificateCommonNames.CommonNames.Any(commonName => !string.IsNullOrEmpty(commonName.CertificateIssuerThumbprint)) &&
                certInfo.ServerCertificateIssuerStores != null &&
                certInfo.ServerCertificateIssuerStores.Count() != 0)
            {
                throw new ValidationException(ClusterManagementErrorCode.IssuerCertStoreAndIssuerPinningCannotBeTogether);
            }

            if (certInfo.ClientCertificateCommonNames != null &&
                certInfo.ClientCertificateCommonNames.Any(clientCert => !string.IsNullOrEmpty(clientCert.CertificateIssuerThumbprint)) &&
                certInfo.ClientCertificateIssuerStores != null &&
                certInfo.ClientCertificateIssuerStores.Count() != 0)
            {
                throw new ValidationException(ClusterManagementErrorCode.IssuerCertStoreAndIssuerPinningCannotBeTogether);
            }
        }
        internal static void ValidateThumbprints(X509 certInfo)
        {
            List <ClientCertificateThumbprint> clientCertificateThumbprints = certInfo.ClientCertificateThumbprints;
            Dictionary <List <string>, ClusterManagementErrorCode> thumbprintListsToValidate = new Dictionary <List <string>, ClusterManagementErrorCode>();

            if (clientCertificateThumbprints != null)
            {
                thumbprintListsToValidate.Add(clientCertificateThumbprints.Select(ct => ct.CertificateThumbprint).ToList(), ClusterManagementErrorCode.InvalidClientCertificateThumbprint);
            }

            if (certInfo.ServerCertificate != null)
            {
                thumbprintListsToValidate.Add(certInfo.ServerCertificate.ToThumbprintList(), ClusterManagementErrorCode.InvalidServerCertificateThumbprint);
            }

            if (certInfo.ClusterCertificate != null)
            {
                thumbprintListsToValidate.Add(certInfo.ClusterCertificate.ToThumbprintList(), ClusterManagementErrorCode.InvalidClusterCertificateThumbprint);
            }

            if (certInfo.ReverseProxyCertificate != null)
            {
                thumbprintListsToValidate.Add(certInfo.ReverseProxyCertificate.ToThumbprintList(), ClusterManagementErrorCode.InvalidReverseProxyCertificateThumbprint);
            }

            foreach (KeyValuePair <List <string>, ClusterManagementErrorCode> entry in thumbprintListsToValidate)
            {
                if (!IsValidThumbprintList(entry.Key))
                {
                    throw new ValidationException(entry.Value);
                }
            }
        }
 internal static IEnumerable <CertificateCommonNameBase> GetAllCommonNames(X509 certInfo)
 {
     return(new List <IEnumerable <CertificateCommonNameBase> >
     {
         certInfo.ClientCertificateCommonNames,
         certInfo.ClusterCertificateCommonNames != null ? certInfo.ClusterCertificateCommonNames.CommonNames : null,
         certInfo.ServerCertificateCommonNames != null ? certInfo.ServerCertificateCommonNames.CommonNames : null,
         certInfo.ReverseProxyCertificateCommonNames != null ? certInfo.ReverseProxyCertificateCommonNames.CommonNames : null,
     }.Where(p => p != null).SelectMany(q => q));
 }
        internal static void ValidateCommonNames(X509 certInfo)
        {
            // CNs must not be null
            IEnumerable <CertificateCommonNameBase> allCns = SettingsValidator.GetAllCommonNames(certInfo);

            if (allCns.Any(cn => string.IsNullOrWhiteSpace(cn.CertificateCommonName)))
            {
                throw new ValidationException(ClusterManagementErrorCode.InvalidCommonName);
            }

            IEnumerable <CertificateCommonNameBase> allCnsWithoutItSupport = SettingsValidator.GetAllCommonNames(certInfo, false);

            if (allCnsWithoutItSupport.Any(cn => !string.IsNullOrWhiteSpace(cn.CertificateIssuerThumbprint)))
            {
                throw new ValidationException(ClusterManagementErrorCode.UnsupportedIssuerThumbprintPair);
            }

            // Issuer thumbprints must be separated by comma, and should not dupe under the same cn
            IEnumerable <CertificateCommonNameBase> allCnsWithItSupport = SettingsValidator.GetAllCommonNames(certInfo, true);

            foreach (CertificateCommonNameBase cn in allCnsWithItSupport)
            {
                if (!string.IsNullOrWhiteSpace(cn.CertificateIssuerThumbprint) &&
                    !SettingsValidator.AreValidIssuerThumbprints(cn.CertificateIssuerThumbprint))
                {
                    throw new ValidationException(ClusterManagementErrorCode.InvalidCertificateIssuerThumbprints, cn.CertificateIssuerThumbprint);
                }
            }

            // Up to 2 CNs are supported, except client CN
            List <ServerCertificateCommonNames> cnsWithCountCap = new List <ServerCertificateCommonNames>()
            {
                certInfo.ClusterCertificateCommonNames,
                certInfo.ReverseProxyCertificateCommonNames,
                certInfo.ServerCertificateCommonNames
            };

            if (cnsWithCountCap.Any(cns => cns != null && cns.CommonNames != null && cns.CommonNames.Count > 2))
            {
                throw new ValidationException(ClusterManagementErrorCode.InvalidCommonNameCount);
            }

            // no dupe CN is allowed for the same cert type, except client CN
            foreach (List <CertificateCommonNameBase> cns in cnsWithCountCap
                     .Where(p => p != null && p.CommonNames != null)
                     .Select(p => p.CommonNames))
            {
                if (cns.Any(current =>
                            cns.Any(other => current != other && current.CertificateCommonName == other.CertificateCommonName)))
                {
                    throw new ValidationException(ClusterManagementErrorCode.DupeCommonNameNotAllowedForClusterCert);
                }
            }
        }
 internal static void GetFileStoreSvcListForCertTypeChange(
     X509 currentCerts,
     X509 targetCerts,
     out CertificateDescription thumbprintFileStoreSvcCerts,
     out ServerCertificateCommonNames commonNameFileStoreSvcCerts)
 {
     if (targetCerts.ClusterCertificate == null)
     {
         thumbprintFileStoreSvcCerts = currentCerts.ClusterCertificate;
         commonNameFileStoreSvcCerts = targetCerts.ClusterCertificateCommonNames;
     }
     else
     {
         thumbprintFileStoreSvcCerts = targetCerts.ClusterCertificate;
         commonNameFileStoreSvcCerts = currentCerts.ClusterCertificateCommonNames;
     }
 }
        public static List <CertificateClusterUpgradeStep> GetUpgradeFlow(X509 currentCerts, X509 targetCerts)
        {
            List <CertificateClusterUpgradeStep> result       = new List <CertificateClusterUpgradeStep>();
            CertificateClusterUpgradeStep        previousStep = null;
            bool shouldContinue = true;

            for (int i = 0; i < CertificateClusterUpgradeFlow.upgradeStepDelegateTable.Length && shouldContinue; i++)
            {
                CertificateClusterUpgradeStep step;
                shouldContinue = CertificateClusterUpgradeFlow.upgradeStepDelegateTable[i](currentCerts, targetCerts, previousStep, out step);
                previousStep   = step;
                if (step != null)
                {
                    result.Add(step);
                }
            }

            return(result);
        }
        internal static bool TryGetStepThree(
            X509 currentCerts,
            X509 targetCerts,
            CertificateClusterUpgradeStep previousStep,
            out CertificateClusterUpgradeStep step)
        {
            Dictionary <string, string> commonNameWhiteList = targetCerts.ClusterCertificateCommonNames == null || !targetCerts.ClusterCertificateCommonNames.Any() ?
                                                              null : targetCerts.ClusterCertificateCommonNames.CommonNames.ToDictionary(p => p.CertificateCommonName, p => p.CertificateIssuerThumbprint);

            step = new CertificateClusterUpgradeStep(
                thumbprintWhiteList: targetCerts.ClusterCertificate == null ? null : targetCerts.ClusterCertificate.ToThumbprintList(),
                thumbprintLoadList: targetCerts.ClusterCertificate,
                thumbprintFileStoreSvcList: targetCerts.ClusterCertificate,
                commonNameWhiteList: commonNameWhiteList,
                commonNameLoadList: targetCerts.ClusterCertificateCommonNames,
                commonNameFileStoreSvcList: targetCerts.ClusterCertificateCommonNames);

            return(true);
        }
Example #10
0
 internal static IEnumerable <CertificateCommonNameBase> GetAllCommonNames(X509 certInfo, bool isIssuerThumbprintSupported)
 {
     if (isIssuerThumbprintSupported)
     {
         return(new List <IEnumerable <CertificateCommonNameBase> >
         {
             certInfo.ClientCertificateCommonNames,
             certInfo.ClusterCertificateCommonNames != null ? certInfo.ClusterCertificateCommonNames.CommonNames : null,
             certInfo.ServerCertificateCommonNames != null ? certInfo.ServerCertificateCommonNames.CommonNames : null,
         }.Where(p => p != null).SelectMany(q => q));
     }
     else
     {
         return(new List <IEnumerable <CertificateCommonNameBase> >
         {
             certInfo.ReverseProxyCertificateCommonNames != null ? certInfo.ReverseProxyCertificateCommonNames.CommonNames : null,
         }.Where(p => p != null).SelectMany(q => q));
     }
 }
Example #11
0
        internal static void ValidateCommonNamesAgainstThumbprints(X509 certInfo)
        {
            // for any cert except client cert, CN and thumbprint are not allowed to be configured together
            KeyValuePair <ServerCertificateCommonNames, CertificateDescription>[] pairs = new KeyValuePair <ServerCertificateCommonNames, CertificateDescription>[]
            {
                new KeyValuePair <ServerCertificateCommonNames, CertificateDescription>(certInfo.ClusterCertificateCommonNames, certInfo.ClusterCertificate),
                new KeyValuePair <ServerCertificateCommonNames, CertificateDescription>(certInfo.ServerCertificateCommonNames, certInfo.ServerCertificate),
                new KeyValuePair <ServerCertificateCommonNames, CertificateDescription>(certInfo.ReverseProxyCertificateCommonNames, certInfo.ReverseProxyCertificate),
            };

            foreach (var pair in pairs)
            {
                ServerCertificateCommonNames cns         = pair.Key;
                CertificateDescription       thumbprints = pair.Value;
                if (cns != null && thumbprints != null)
                {
                    throw new ValidationException(ClusterManagementErrorCode.InvalidCommonNameThumbprintPair);
                }
            }
        }
Example #12
0
        internal static void ValidateIssuerCertStore(X509 certInfo)
        {
            // Issuercertstore must not exist if corresponding cn does not exist for cert
            if (certInfo.ClusterCertificateCommonNames == null && certInfo.ClusterCertificateIssuerStores != null && certInfo.ClusterCertificateIssuerStores.Count() != 0)
            {
                throw new ValidationException(ClusterManagementErrorCode.IssuerCertStoreSpecifiedWithoutCommonNameCertificate);
            }

            if (certInfo.ServerCertificateCommonNames == null && certInfo.ServerCertificateIssuerStores != null && certInfo.ServerCertificateIssuerStores.Count() != 0)
            {
                throw new ValidationException(ClusterManagementErrorCode.IssuerCertStoreSpecifiedWithoutCommonNameCertificate);
            }

            if (certInfo.ClientCertificateCommonNames == null && certInfo.ClientCertificateIssuerStores != null && certInfo.ClientCertificateIssuerStores.Count() != 0)
            {
                throw new ValidationException(ClusterManagementErrorCode.IssuerCertStoreSpecifiedWithoutCommonNameCertificate);
            }

            // Issuer thumbprint pinning and issuer store cannot be specified together
            SettingsValidator.ValidateIssuerPinningAndIssuerStoresCannotExistTogether(certInfo);

            if (certInfo.ClusterCertificateIssuerStores != null && certInfo.ClusterCertificateIssuerStores.Count != 0)
            {
                SettingsValidator.ValidateClusterServerIssuerCertStoreProperties(certInfo.ClusterCertificateIssuerStores);
            }

            if (certInfo.ServerCertificateIssuerStores != null && certInfo.ServerCertificateIssuerStores.Count != 0)
            {
                SettingsValidator.ValidateClusterServerIssuerCertStoreProperties(certInfo.ServerCertificateIssuerStores);
            }

            if (certInfo.ClientCertificateIssuerStores != null && certInfo.ClientCertificateIssuerStores.Count != 0)
            {
                SettingsValidator.ValidateClientIssuerCertStoreProperties(certInfo.ClientCertificateIssuerStores);
            }
        }
        internal static bool TryGetStepTwo(
            X509 currentCerts,
            X509 targetCerts,
            CertificateClusterUpgradeStep previousStep,
            out CertificateClusterUpgradeStep step)
        {
            // step 2:
            // white list: inherit from the previous step
            // load list: replace all current certs with target certs
            // fss list: change if necessary
            int changedThumbprintCount = CertificateClusterUpgradeFlow.GetChangedThumbprintCount(currentCerts.ClusterCertificate, targetCerts.ClusterCertificate);
            int changedCnCount         = CertificateClusterUpgradeFlow.GetChangedCnCount(currentCerts.ClusterCertificateCommonNames, targetCerts.ClusterCertificateCommonNames);

            List <string> removedThumbprints = CertificateClusterUpgradeFlow.GetAddedThumbprints(targetCerts.ClusterCertificate, currentCerts.ClusterCertificate);
            List <string> removedCns         = CertificateClusterUpgradeFlow.GetAddedCns(targetCerts.ClusterCertificateCommonNames, currentCerts.ClusterCertificateCommonNames);

            CertificateDescription       thumbprintFileStoreSvcCerts = null;
            ServerCertificateCommonNames commonNameFileStoreSvcCerts = null;
            bool shouldContinue = true;

            switch (changedThumbprintCount + changedCnCount)
            {
            case 1:
            {
                if (removedThumbprints.Any() || removedCns.Any())
                {
                    // cert removal
                    thumbprintFileStoreSvcCerts = currentCerts.ClusterCertificate;
                    commonNameFileStoreSvcCerts = currentCerts.ClusterCertificateCommonNames;
                }
                else
                {
                    // cert add
                    thumbprintFileStoreSvcCerts = targetCerts.ClusterCertificate;
                    commonNameFileStoreSvcCerts = targetCerts.ClusterCertificateCommonNames;
                    shouldContinue = false;
                }

                break;
            }

            case 2:
            {
                if (CertificateClusterUpgradeFlow.IsSwap(currentCerts.ClusterCertificate, targetCerts.ClusterCertificate))
                {
                    thumbprintFileStoreSvcCerts = new CertificateDescription()
                    {
                        Thumbprint          = currentCerts.ClusterCertificate.Thumbprint,
                        ThumbprintSecondary = currentCerts.ClusterCertificate.Thumbprint,
                        X509StoreName       = currentCerts.ClusterCertificate.X509StoreName,
                    };
                }
                else
                {
                    if (changedThumbprintCount == 2)
                    {
                        // thumbprint replace
                        thumbprintFileStoreSvcCerts = new CertificateDescription()
                        {
                            Thumbprint          = currentCerts.ClusterCertificate.Thumbprint,
                            ThumbprintSecondary = targetCerts.ClusterCertificate.Thumbprint,
                            X509StoreName       = currentCerts.ClusterCertificate.X509StoreName,
                        };
                    }
                    else if (changedCnCount == 2)
                    {
                        // cn replace
                        commonNameFileStoreSvcCerts = new ServerCertificateCommonNames()
                        {
                            CommonNames = new List <CertificateCommonNameBase>()
                            {
                                currentCerts.ClusterCertificateCommonNames.CommonNames[0],
                                targetCerts.ClusterCertificateCommonNames.CommonNames[0]
                            },
                            X509StoreName = currentCerts.ClusterCertificateCommonNames.X509StoreName
                        };
                    }
                    else
                    {
                        // 1 thumbprint <-> 1 cn
                        CertificateClusterUpgradeFlow.GetFileStoreSvcListForCertTypeChange(
                            currentCerts,
                            targetCerts,
                            out thumbprintFileStoreSvcCerts,
                            out commonNameFileStoreSvcCerts);
                    }
                }

                break;
            }

            case 3:
            case 4:
            {
                // 1 thumbprints <-> 2 cns, or 2 thumbprints <-> 1 cns, or 2 thumbprints <-> 2 cns
                CertificateClusterUpgradeFlow.GetFileStoreSvcListForCertTypeChange(
                    currentCerts,
                    targetCerts,
                    out thumbprintFileStoreSvcCerts,
                    out commonNameFileStoreSvcCerts);
                break;
            }

            default:
                throw new NotSupportedException(string.Format("It's not supported that {0} certificate thumbprints and {1} certificate common names have changed", changedThumbprintCount, changedCnCount));
            }

            step = new CertificateClusterUpgradeStep(
                thumbprintWhiteList: previousStep != null ? previousStep.ThumbprintWhiteList : CertificateClusterUpgradeFlow.GetThumbprints(currentCerts.ClusterCertificate),
                thumbprintLoadList: targetCerts.ClusterCertificate,
                thumbprintFileStoreSvcList: thumbprintFileStoreSvcCerts,
                commonNameWhiteList: previousStep != null ? previousStep.CommonNameWhiteList : CertificateClusterUpgradeFlow.GetCns(currentCerts.ClusterCertificateCommonNames),
                commonNameLoadList: targetCerts.ClusterCertificateCommonNames,
                commonNameFileStoreSvcList: commonNameFileStoreSvcCerts);

            return(shouldContinue);
        }