/// <summary> /// Returns the issuers for the certificate. /// </summary> /// <param name="certificate">The certificate.</param> /// <param name="issuers">The issuers.</param> /// <returns></returns> public async Task <bool> GetIssuers(X509Certificate2 certificate, List <CertificateIdentifier> issuers) { bool isTrusted = false; CertificateIdentifier issuer = null; do { issuer = await GetIssuer(certificate, m_trustedCertificateList, m_trustedCertificateStore, true); if (issuer == null) { issuer = await GetIssuer(certificate, m_issuerCertificateList, m_issuerCertificateStore, true); } else { isTrusted = true; } if (issuer != null) { issuers.Add(issuer); certificate = await issuer.Find(false); // check for root. if (Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer)) { break; } } }while (issuer != null); return(isTrusted); }
/// <summary> /// Updates the object from another object (usage is not updated). /// </summary> /// <param name="certificate">The certificate.</param> private void Paste(CertificateIdentifier certificate) { this.SubjectName = certificate.SubjectName; this.Thumbprint = certificate.Thumbprint; this.RawData = certificate.RawData; this.ValidationOptions = certificate.ValidationOptions; this.Certificate = certificate.Certificate; }
/// <summary> /// Initializes the object with an X509 certificate identifier /// </summary> public UserIdentity(CertificateIdentifier certificateId) { if (certificateId == null) throw new ArgumentNullException("certificateId"); Task<X509Certificate2> task = certificateId.Find(); task.Wait(); X509Certificate2 certificate = task.Result; if (certificate != null) { Initialize(certificate); } }
/// <summary> /// Remove from certificate store /// </summary> /// <param name="identifier"></param> /// <param name="certificate"></param> public static void RemoveFromStore(this CertificateIdentifier identifier, X509Certificate2 certificate) { if (certificate == null) { throw new ArgumentNullException(nameof(certificate)); } using (var store = CertificateStoreIdentifier.CreateStore(identifier.StoreType)) { store.Open(identifier.StorePath); store.Remove(certificate.YieldReturn()); } }
/// <summary> /// Add to certificate store /// </summary> /// <param name="identifier"></param> /// <param name="certificate"></param> /// <param name="noCopy"></param> /// <returns></returns> public static void AddToStore(this CertificateIdentifier identifier, X509Certificate2 certificate, bool noCopy = false) { if (certificate == null) { throw new ArgumentNullException(nameof(certificate)); } using (var store = CertificateStoreIdentifier.CreateStore(identifier.StoreType)) { store.Open(identifier.StorePath); store.Add(certificate.YieldReturn(), noCopy); } }
/// <summary> /// Initializes the object with an X509 certificate identifier /// </summary> private void Initialize(CertificateIdentifier certificateId) { if (certificateId == null) { throw new ArgumentNullException("certificateId"); } X509Certificate2 certificate = certificateId.Find(); if (certificate != null) { Initialize(new X509SecurityToken(certificate)); } }
/// <summary> /// Initializes the object with an X509 certificate identifier /// </summary> public UserIdentity(CertificateIdentifier certificateId) { if (certificateId == null) { throw new ArgumentNullException("certificateId"); } X509Certificate2 certificate = certificateId.Find().Result; if (certificate != null) { Initialize(certificate); } }
/// <summary> /// Returns the issuers for the certificates. /// </summary> public async Task <bool> GetIssuers(X509Certificate2Collection certificates, List <CertificateIdentifier> issuers) { bool isTrusted = false; CertificateIdentifier issuer = null; X509Certificate2 certificate = certificates[0]; CertificateIdentifierCollection collection = new CertificateIdentifierCollection(); for (int ii = 1; ii < certificates.Count; ii++) { collection.Add(new CertificateIdentifier(certificates[ii])); } do { issuer = await GetIssuer(certificate, m_trustedCertificateList, m_trustedCertificateStore, true); if (issuer == null) { issuer = await GetIssuer(certificate, m_issuerCertificateList, m_issuerCertificateStore, true); if (issuer == null) { issuer = await GetIssuer(certificate, collection, null, true); } } if (issuer != null) { isTrusted = true; issuers.Add(issuer); certificate = await issuer.Find(false); // check for root. if (Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer)) { break; } } else { isTrusted = false; } }while (issuer != null); return(isTrusted); }
/// <summary> /// Initializes the object with an X509 certificate identifier /// </summary> public UserIdentity(CertificateIdentifier certificateId) { if (certificateId == null) { throw new ArgumentNullException("certificateId"); } Task <X509Certificate2> task = certificateId.Find(); task.Wait(); X509Certificate2 certificate = task.Result; if (certificate != null) { Initialize(certificate); } }
/// <summary> /// Initializes the object with an X509 certificate identifier /// </summary> public UserIdentity(CertificateIdentifier certificateId) { if (certificateId == null) { throw new ArgumentNullException("certificateId"); } Task <X509Certificate2> task = certificateId.Find(); task.Wait(); X509Certificate2 certificate = task.Result; if (certificate != null) { X509IdentityToken token = new X509IdentityToken(); token.CertificateData = certificate.RawData; token.Certificate = certificate; Initialize(token); } }
/// <summary> /// Returns true if the objects are equal. /// </summary> public override bool Equals(object obj) { if (Object.ReferenceEquals(this, obj)) { return(true); } CertificateIdentifier id = obj as CertificateIdentifier; if (id == null) { return(false); } if (m_certificate != null && id.m_certificate != null) { return(m_certificate.Thumbprint == id.m_certificate.Thumbprint); } if (Thumbprint == id.Thumbprint) { return(true); } if (m_storeLocation != id.m_storeLocation) { return(false); } if (m_storeName != id.m_storeName) { return(false); } if (SubjectName != id.SubjectName) { return(false); } return(true); }
/// <summary> /// Throws an exception if validation fails. /// </summary> /// <param name="certificates">The certificates to be checked.</param> /// <exception cref="ServiceResultException">If certificate[0] cannot be accepted</exception> protected virtual async Task InternalValidate(X509Certificate2Collection certificates) { X509Certificate2 certificate = certificates[0]; // check for previously validated certificate. X509Certificate2 certificate2 = null; if (m_validatedCertificates.TryGetValue(certificate.Thumbprint, out certificate2)) { if (Utils.IsEqual(certificate2.RawData, certificate.RawData)) { return; } } CertificateIdentifier trustedCertificate = await GetTrustedCertificate(certificate); // get the issuers (checks the revocation lists if using directory stores). List <CertificateIdentifier> issuers = new List <CertificateIdentifier>(); bool isIssuerTrusted = await GetIssuers(certificates, issuers); // setup policy chain X509ChainPolicy policy = new X509ChainPolicy(); policy.RevocationFlag = X509RevocationFlag.EntireChain; policy.RevocationMode = X509RevocationMode.NoCheck; policy.VerificationFlags = X509VerificationFlags.NoFlag; foreach (CertificateIdentifier issuer in issuers) { if ((issuer.ValidationOptions & CertificateValidationOptions.SuppressRevocationStatusUnknown) != 0) { policy.VerificationFlags |= X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreCtlSignerRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreEndRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreRootRevocationUnknown; } // we did the revocation check in the GetIssuers call. No need here. policy.RevocationMode = X509RevocationMode.NoCheck; policy.ExtraStore.Add(issuer.Certificate); } // build chain. X509Chain chain = new X509Chain(); chain.ChainPolicy = policy; chain.Build(certificate); // check the chain results. CertificateIdentifier target = trustedCertificate; if (target == null) { target = new CertificateIdentifier(certificate); } for (int ii = 0; ii < chain.ChainElements.Count; ii++) { X509ChainElement element = chain.ChainElements[ii]; CertificateIdentifier issuer = null; if (ii < issuers.Count) { issuer = issuers[ii]; } // check for chain status errors. foreach (X509ChainStatus status in element.ChainElementStatus) { ServiceResult result = CheckChainStatus(status, target, issuer, (ii != 0)); if (ServiceResult.IsBad(result)) { // check untrusted certificates. if (trustedCertificate == null) { throw new ServiceResultException(StatusCodes.BadSecurityChecksFailed); } throw new ServiceResultException(result); } } if (issuer != null) { target = issuer; } } bool issuedByCA = !Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer); // check if certificate issuer is trusted. if (issuedByCA && !isIssuerTrusted) { if (m_applicationCertificate == null || !Utils.IsEqual(m_applicationCertificate.RawData, certificate.RawData)) { throw ServiceResultException.Create( StatusCodes.BadCertificateUntrusted, "Certificate issuer is not trusted.\r\nSubjectName: {0}\r\nIssuerName: {1}", certificate.SubjectName.Name, certificate.IssuerName.Name); } } // check if certificate is trusted. if (trustedCertificate == null && !isIssuerTrusted) { if (m_applicationCertificate == null || !Utils.IsEqual(m_applicationCertificate.RawData, certificate.RawData)) { throw ServiceResultException.Create( StatusCodes.BadCertificateUntrusted, "Certificate is not trusted.\r\nSubjectName: {0}\r\nIssuerName: {1}", certificate.SubjectName.Name, certificate.IssuerName.Name); } } }
/// <summary> /// Throws an exception if validation fails. /// </summary> /// <param name="certificate">The certificate to be checked.</param> /// <exception cref="ServiceResultException">If certificate cannot be accepted</exception> protected virtual void InternalValidate(X509Certificate2 certificate) { lock (m_lock) { // check for previously validated certificate. X509Certificate2 certificate2 = null; if (m_validatedCertificates.TryGetValue(certificate.Thumbprint, out certificate2)) { if (Utils.IsEqual(certificate2.RawData, certificate.RawData)) { return; } } CertificateIdentifier trustedCertificate = GetTrustedCertificate(certificate); // get the issuers (checks the revocation lists if using directory stores). List<CertificateIdentifier> issuers = new List<CertificateIdentifier>(); bool isIssuerTrusted = GetIssuers(certificate, issuers); // setup policy chain X509ChainPolicy policy = new X509ChainPolicy(); policy.RevocationFlag = X509RevocationFlag.EntireChain; policy.RevocationMode = X509RevocationMode.NoCheck; policy.VerificationFlags = X509VerificationFlags.NoFlag; foreach (CertificateIdentifier issuer in issuers) { if ((issuer.ValidationOptions & CertificateValidationOptions.SuppressRevocationStatusUnknown) != 0) { policy.VerificationFlags |= X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreCtlSignerRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreEndRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreRootRevocationUnknown; } // we did the revocation check in the GetIssuers call. No need here. policy.RevocationMode = X509RevocationMode.NoCheck; policy.ExtraStore.Add(issuer.Certificate); } // build chain. X509Chain chain = new X509Chain(); chain.ChainPolicy = policy; chain.Build(certificate); // check the chain results. CertificateIdentifier target = trustedCertificate; if (target == null) { target = new CertificateIdentifier(certificate); } for (int ii = 0; ii < chain.ChainElements.Count; ii++) { X509ChainElement element = chain.ChainElements[ii]; CertificateIdentifier issuer = null; if (ii < issuers.Count) { issuer = issuers[ii]; } // check for chain status errors. foreach (X509ChainStatus status in element.ChainElementStatus) { ServiceResult result = CheckChainStatus(status, target, issuer, (ii != 0)); if (ServiceResult.IsBad(result)) { throw new ServiceResultException(result); } } if (issuer != null) { target = issuer; } } // check if certificate is trusted. if (trustedCertificate == null && !isIssuerTrusted) { if (m_applicationCertificate == null || !Utils.IsEqual(m_applicationCertificate.RawData, certificate.RawData)) { throw ServiceResultException.Create( StatusCodes.BadCertificateUntrusted, "Certificate is not trusted.\r\nSubjectName: {0}\r\nIssuerName: {1}", certificate.SubjectName.Name, certificate.IssuerName.Name); } } } }
private static ServiceResult CheckChainStatus(X509ChainStatus status, CertificateIdentifier id, CertificateIdentifier issuer, bool isIssuer) { switch (status.Status) { case X509ChainStatusFlags.NotValidForUsage: { return ServiceResult.Create( (isIssuer) ? StatusCodes.BadCertificateUseNotAllowed : StatusCodes.BadCertificateIssuerUseNotAllowed, "Certificate may not be used as an application instance certificate. {0}: {1}", status.Status, status.StatusInformation); } case X509ChainStatusFlags.NoError: case X509ChainStatusFlags.OfflineRevocation: case X509ChainStatusFlags.InvalidBasicConstraints: case X509ChainStatusFlags.PartialChain: { break; } case X509ChainStatusFlags.UntrustedRoot: { // ignore this error because the root check is done // by looking the certificate up in the trusted issuer stores passed to the validator. // the ChainStatus uses the Windows trusted issuer stores. break; } case X509ChainStatusFlags.RevocationStatusUnknown: { if (issuer != null) { if ((issuer.ValidationOptions & CertificateValidationOptions.SuppressRevocationStatusUnknown) != 0) { break; } } // check for meaning less errors for self-signed certificates. if (id.Certificate != null && Utils.CompareDistinguishedName(id.Certificate.Subject, id.Certificate.Subject)) { break; } return ServiceResult.Create( (isIssuer) ? StatusCodes.BadCertificateIssuerRevocationUnknown : StatusCodes.BadCertificateRevocationUnknown, "Certificate revocation status cannot be verified. {0}: {1}", status.Status, status.StatusInformation); } case X509ChainStatusFlags.Revoked: { return ServiceResult.Create( (isIssuer) ? StatusCodes.BadCertificateIssuerRevoked : StatusCodes.BadCertificateRevoked, "Certificate has been revoked. {0}: {1}", status.Status, status.StatusInformation); } case X509ChainStatusFlags.NotTimeNested: { if (id != null && ((id.ValidationOptions & CertificateValidationOptions.SuppressCertificateExpired) != 0)) { break; } return ServiceResult.Create( StatusCodes.BadCertificateIssuerTimeInvalid, "Certificate issuer validatity time does not overhas is expired or not yet valid. {0}: {1}", status.Status, status.StatusInformation); } case X509ChainStatusFlags.NotTimeValid: { if (id != null && ((id.ValidationOptions & CertificateValidationOptions.SuppressCertificateExpired) != 0)) { break; } return ServiceResult.Create( (isIssuer) ? StatusCodes.BadCertificateIssuerTimeInvalid : StatusCodes.BadCertificateTimeInvalid, "Certificate has is expired or not yet valid. {0}: {1}", status.Status, status.StatusInformation); } default: { return ServiceResult.Create( StatusCodes.BadCertificateInvalid, "Certificate validation failed. {0}: {1}", status.Status, status.StatusInformation); } } return null; }
/// <summary> /// Throws an exception if validation fails. /// </summary> /// <param name="certificates">The certificates to be checked.</param> /// <exception cref="ServiceResultException">If certificate[0] cannot be accepted</exception> protected virtual async Task InternalValidate(X509Certificate2Collection certificates) { X509Certificate2 certificate = certificates[0]; // check for previously validated certificate. X509Certificate2 certificate2 = null; if (m_validatedCertificates.TryGetValue(certificate.Thumbprint, out certificate2)) { if (Utils.IsEqual(certificate2.RawData, certificate.RawData)) { return; } } CertificateIdentifier trustedCertificate = await GetTrustedCertificate(certificate); // get the issuers (checks the revocation lists if using directory stores). List <CertificateIdentifier> issuers = new List <CertificateIdentifier>(); bool isIssuerTrusted = await GetIssuers(certificates, issuers); // setup policy chain X509ChainPolicy policy = new X509ChainPolicy(); policy.RevocationFlag = X509RevocationFlag.EntireChain; policy.RevocationMode = X509RevocationMode.NoCheck; policy.VerificationFlags = X509VerificationFlags.NoFlag; foreach (CertificateIdentifier issuer in issuers) { if ((issuer.ValidationOptions & CertificateValidationOptions.SuppressRevocationStatusUnknown) != 0) { policy.VerificationFlags |= X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreCtlSignerRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreEndRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreRootRevocationUnknown; } // we did the revocation check in the GetIssuers call. No need here. policy.RevocationMode = X509RevocationMode.NoCheck; policy.ExtraStore.Add(issuer.Certificate); } // build chain. bool chainStatusChecked = false; X509Chain chain = new X509Chain(); chain.ChainPolicy = policy; chain.Build(certificate); // check the chain results. CertificateIdentifier target = trustedCertificate; if (target == null) { target = new CertificateIdentifier(certificate); } for (int ii = 0; ii < chain.ChainElements.Count; ii++) { X509ChainElement element = chain.ChainElements[ii]; CertificateIdentifier issuer = null; if (ii < issuers.Count) { issuer = issuers[ii]; } // check for chain status errors. foreach (X509ChainStatus status in element.ChainElementStatus) { ServiceResult result = CheckChainStatus(status, target, issuer, (ii != 0)); if (ServiceResult.IsBad(result)) { // check untrusted certificates. if (trustedCertificate == null) { throw new ServiceResultException(StatusCodes.BadSecurityChecksFailed); } throw new ServiceResultException(result); } chainStatusChecked = true; } if (issuer != null) { target = issuer; } } // check whether the chain is complete (if there is a chain) bool issuedByCA = !Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer); bool chainIncomplete = false; if (issuers.Count > 0) { var rootCertificate = issuers[issuers.Count - 1].Certificate; if (!Utils.CompareDistinguishedName(rootCertificate.Subject, rootCertificate.Issuer)) { chainIncomplete = true; } } else { if (issuedByCA) { // no issuer found at all chainIncomplete = true; } } if (issuedByCA && (!chainStatusChecked || chainIncomplete)) { throw ServiceResultException.Create( StatusCodes.BadCertificateChainIncomplete, "Certificate chain validation incomplete.\r\nSubjectName: {0}\r\nIssuerName: {1}", certificate.SubjectName.Name, certificate.IssuerName.Name); } // check if certificate issuer is trusted. if (issuedByCA && !isIssuerTrusted && trustedCertificate == null) { if (m_applicationCertificate == null || !Utils.IsEqual(m_applicationCertificate.RawData, certificate.RawData)) { throw ServiceResultException.Create( StatusCodes.BadCertificateUntrusted, "Certificate issuer is not trusted.\r\nSubjectName: {0}\r\nIssuerName: {1}", certificate.SubjectName.Name, certificate.IssuerName.Name); } } // check if certificate is trusted. if (trustedCertificate == null && !isIssuerTrusted) { if (m_applicationCertificate == null || !Utils.IsEqual(m_applicationCertificate.RawData, certificate.RawData)) { throw ServiceResultException.Create( StatusCodes.BadCertificateUntrusted, "Certificate is not trusted.\r\nSubjectName: {0}\r\nIssuerName: {1}", certificate.SubjectName.Name, certificate.IssuerName.Name); } } // check if certificate is valid for use as app/sw or user cert X509KeyUsageFlags certificateKeyUsage = CertificateFactory.GetKeyUsage(certificate); if ((certificateKeyUsage & X509KeyUsageFlags.DataEncipherment) == 0) { throw new ServiceResultException(StatusCodes.BadCertificateUseNotAllowed, "Usage of certificate is not allowed."); } // check if minimum requirements are met if (m_rejectSHA1SignedCertificates && IsSHA1SignatureAlgorithm(certificate.SignatureAlgorithm)) { throw new ServiceResultException(StatusCodes.BadCertificatePolicyCheckFailed, "SHA1 signed certificates are not trusted"); } if (certificate.GetRSAPublicKey().KeySize < m_minimumCertificateKeySize) { throw new ServiceResultException(StatusCodes.BadCertificatePolicyCheckFailed, "Certificate doesn't meet minimum key length requirement"); } }
/// <summary> /// Creates a SAML token for the specified email address. /// </summary> public static UserIdentity CreateSAMLToken(string emailAddress) { // Normally this would be done by a server that is capable of verifying that // the user is a legimate holder of email address. Using a local certficate to // signed the SAML token is a short cut that would never be done in a real system. CertificateIdentifier userid = new CertificateIdentifier(); userid.StoreType = CertificateStoreType.Windows; userid.StorePath = "LocalMachine\\My"; userid.SubjectName = "UA Sample Client"; X509Certificate2 certificate = userid.Find(); X509SecurityToken signingToken = new X509SecurityToken(certificate); // Create list of confirmation strings List<string> confirmations = new List<string>(); // Add holder-of-key string to list of confirmation strings confirmations.Add("urn:oasis:names:tc:SAML:1.0:cm:bearer"); // Create SAML subject statement based on issuer member variable, confirmation string collection // local variable and proof key identifier parameter SamlSubject subject = new SamlSubject("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", null, emailAddress); // Create a list of SAML attributes List<SamlAttribute> attributes = new List<SamlAttribute>(); Claim claim = Claim.CreateNameClaim(emailAddress); attributes.Add(new SamlAttribute(claim)); // Create list of SAML statements List<SamlStatement> statements = new List<SamlStatement>(); // Add a SAML attribute statement to the list of statements. Attribute statement is based on // subject statement and SAML attributes resulting from claims statements.Add(new SamlAttributeStatement(subject, attributes)); // Create a valid from/until condition DateTime validFrom = DateTime.UtcNow; DateTime validTo = DateTime.UtcNow.AddHours(12); SamlConditions conditions = new SamlConditions(validFrom, validTo); // Create the SAML assertion SamlAssertion assertion = new SamlAssertion( "_" + Guid.NewGuid().ToString(), signingToken.Certificate.Subject, validFrom, conditions, null, statements); SecurityKey signingKey = new System.IdentityModel.Tokens.RsaSecurityKey((RSA)signingToken.Certificate.PrivateKey); // Set the signing credentials for the SAML assertion assertion.SigningCredentials = new SigningCredentials( signingKey, System.IdentityModel.Tokens.SecurityAlgorithms.RsaSha1Signature, System.IdentityModel.Tokens.SecurityAlgorithms.Sha1Digest, new SecurityKeyIdentifier(signingToken.CreateKeyIdentifierClause<X509ThumbprintKeyIdentifierClause>())); return new UserIdentity(new SamlSecurityToken(assertion)); }
/// <summary> /// Initializes the validator from the configuration for a token policy. /// </summary> /// <param name="issuerCertificate">The issuer certificate.</param> private SecurityTokenResolver CreateSecurityTokenResolver(CertificateIdentifier issuerCertificate) { if (issuerCertificate == null) { throw new ArgumentNullException("issuerCertificate"); } // find the certificate. X509Certificate2 certificate = issuerCertificate.Find(false); if (certificate == null) { throw ServiceResultException.Create( StatusCodes.BadCertificateInvalid, "Could not find issuer certificate: {0}", issuerCertificate); } // create a security token representing the certificate. List<SecurityToken> tokens = new List<SecurityToken>(); tokens.Add(new X509SecurityToken(certificate)); // create issued token resolver. SecurityTokenResolver tokenResolver = SecurityTokenResolver.CreateDefaultSecurityTokenResolver( new System.Collections.ObjectModel.ReadOnlyCollection<SecurityToken>(tokens), false); return tokenResolver; }
/// <summary> /// Initializes the object with an X509 certificate identifier /// </summary> private void Initialize(CertificateIdentifier certificateId) { if (certificateId == null) throw new ArgumentNullException("certificateId"); X509Certificate2 certificate = certificateId.Find(); if (certificate != null) { Initialize(new X509SecurityToken(certificate)); } }
/// <summary> /// Initializes the object with an X509 certificate identifier /// </summary> /// <param name="certificateId">The certificate identifier.</param> public UserIdentity(CertificateIdentifier certificateId) { Initialize(certificateId); }
/// <summary> /// Synchronous helper implementation of CheckApplicationInstanceCertificate for C++ Proxy /// </summary> public static void CheckApplicationInstanceCertificate(ApplicationConfiguration configuration) { // create a default certificate id none specified. CertificateIdentifier id = configuration.SecurityConfiguration.ApplicationCertificate; if (id == null) { id = new CertificateIdentifier(); id.StoreType = Utils.DefaultStoreType; id.StorePath = Utils.DefaultStorePath; id.SubjectName = configuration.ApplicationName; } // check for certificate with a private key. X509Certificate2 certificate = null; Task.Run(async () => certificate = await id.Find(true)).Wait(); if (certificate != null) { return; } // construct the subject name from the List<string> hostNames = new List<string>(); hostNames.Add(Utils.GetHostName()); string commonName = Utils.Format("CN={0}", configuration.ApplicationName); string domainName = Utils.Format("DC={0}", hostNames[0]); string subjectName = Utils.Format("{0}, {1}", commonName, domainName); // create a new certificate with a new public key pair. certificate = CertificateFactory.CreateCertificate( id.StoreType, id.StorePath, configuration.ApplicationUri, configuration.ApplicationName, subjectName, hostNames, 2048, 120, 256); // update and save the configuration file. id.Certificate = certificate; configuration.SaveToFile(configuration.SourceFilePath); // add certificate to the trusted peer store so other applications will trust it. ICertificateStore store = configuration.SecurityConfiguration.TrustedPeerCertificates.OpenStore(); try { Task.Run(async () => { X509Certificate2Collection certificateCollection = await store.FindByThumbprint(certificate.Thumbprint); if (certificateCollection != null) { await store.Add(certificateCollection[0]); } } ).Wait(); } finally { store.Close(); } // tell the certificate validator about the new certificate. configuration.CertificateValidator.Update(configuration.SecurityConfiguration); }
/// <summary> /// Throws an exception if validation fails. /// </summary> /// <param name="certificates">The certificates to be checked.</param> /// <exception cref="ServiceResultException">If certificate[0] cannot be accepted</exception> protected virtual void InternalValidateWithChainSupportEnabled(X509Certificate2Collection certificates) { lock (m_lock) { X509Certificate2 certificate = certificates[0]; // check for previously validated certificate. X509Certificate2 certificate2 = null; if (m_validatedCertificates.TryGetValue(certificate.Thumbprint, out certificate2)) { if (Utils.IsEqual(certificate2.RawData, certificate.RawData)) { return; } } CertificateIdentifier trustedCertificate = GetTrustedCertificate(certificate); // get the issuers (checks the revocation lists if using directory stores). List <CertificateIdentifier> issuers = new List <CertificateIdentifier>(); bool isIssuerTrusted = GetIssuersWithChainSupportEnabled(certificates, issuers); // setup policy chain X509ChainPolicy policy = new X509ChainPolicy(); policy.RevocationFlag = X509RevocationFlag.EntireChain; policy.RevocationMode = X509RevocationMode.NoCheck; policy.VerificationFlags = X509VerificationFlags.NoFlag; foreach (CertificateIdentifier issuer in issuers) { if ((issuer.ValidationOptions & CertificateValidationOptions.SuppressRevocationStatusUnknown) != 0) { policy.VerificationFlags |= X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreCtlSignerRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreEndRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreRootRevocationUnknown; } // we did the revocation check in the GetIssuers call. No need here. policy.RevocationMode = X509RevocationMode.NoCheck; policy.ExtraStore.Add(issuer.Certificate); } // build chain. X509Chain chain = new X509Chain(); chain.ChainPolicy = policy; chain.Build(certificate); // check the chain results. CertificateIdentifier target = trustedCertificate; if (target == null) { target = new CertificateIdentifier(certificate); } for (int ii = 0; ii < chain.ChainElements.Count; ii++) { X509ChainElement element = chain.ChainElements[ii]; CertificateIdentifier issuer = null; if (ii < issuers.Count) { issuer = issuers[ii]; } // check for chain status errors. foreach (X509ChainStatus status in element.ChainElementStatus) { ServiceResult result = CheckChainStatus(status, target, issuer, (ii != 0)); if (ServiceResult.IsBad(result)) { throw new ServiceResultException(result); } } if (issuer != null) { target = issuer; } } } }
/// <summary> /// Checks for a valid application instance certificate. /// </summary> /// <param name="silent">if set to <c>true</c> no dialogs will be displayed.</param> /// <param name="minimumKeySize">Minimum size of the key.</param> public void CheckApplicationInstanceCertificate( bool silent, ushort minimumKeySize) { Utils.Trace(Utils.TraceMasks.Information, "Checking application instance certificate."); ApplicationConfiguration configuration = null; if (m_applicationConfiguration == null) { LoadApplicationConfiguration(silent); } configuration = m_applicationConfiguration; bool createNewCertificate = true; // find the existing certificate. CertificateIdentifier id = configuration.SecurityConfiguration.ApplicationCertificate; if (id == null) { throw ServiceResultException.Create(StatusCodes.BadConfigurationError, "Configuration file does not specify a certificate."); } X509Certificate2 certificate = id.Find(true); // check that it is ok. if (certificate != null) { createNewCertificate = !CheckApplicationInstanceCertificate(configuration, certificate, silent, minimumKeySize); } else { // check for missing private key. certificate = id.Find(false); if (certificate != null) { throw ServiceResultException.Create(StatusCodes.BadConfigurationError, "Cannot access certificate private key. Subject={0}", certificate.Subject); } // check for missing thumbprint. if (!String.IsNullOrEmpty(id.Thumbprint)) { if (!String.IsNullOrEmpty(id.SubjectName)) { CertificateIdentifier id2 = new CertificateIdentifier(); id2.StoreType = id.StoreType; id2.StorePath = id.StorePath; id2.SubjectName = id.SubjectName; certificate = id2.Find(true); } if (certificate != null) { string message = Utils.Format( "Thumbprint was explicitly specified in the configuration." + "\r\nAnother certificate with the same subject name was found." + "\r\nUse it instead?\r\n" + "\r\nRequested: {0}" + "\r\nFound: {1}", id.SubjectName, certificate.Subject); throw ServiceResultException.Create(StatusCodes.BadConfigurationError, message); } else { string message = Utils.Format("Thumbprint was explicitly specified in the configuration. Cannot generate a new certificate."); throw ServiceResultException.Create(StatusCodes.BadConfigurationError, message); } } else { if (String.IsNullOrEmpty(id.SubjectName)) { string message = Utils.Format("Both SubjectName and Thumbprint are not specified in the configuration. Cannot generate a new certificate."); throw ServiceResultException.Create(StatusCodes.BadConfigurationError, message); } } } // create a new certificate. if (createNewCertificate) { certificate = CreateApplicationInstanceCertificate(configuration, minimumKeySize, 600); } // ensure it is trusted. else { AddToTrustedStore(configuration, certificate); } // add to discovery server. if (configuration.ApplicationType == ApplicationType.Server || configuration.ApplicationType == ApplicationType.ClientAndServer) { try { AddToDiscoveryServerTrustList(certificate, null, null, configuration.SecurityConfiguration.TrustedPeerCertificates); } catch (Exception e) { Utils.Trace(e, "Could not add certificate to LDS trust list."); } } }
/// <summary> /// Creates a minimal endpoint description which allows a client to connect to a server. /// </summary> /// <remarks> /// In most cases the client will use the server's discovery endpoint to fetch the information /// constained in this structure. /// </remarks> public static EndpointDescription CreateEndpointDescription() { // create the endpoint description. EndpointDescription endpointDescription = new EndpointDescription(); endpointDescription.EndpointUrl = Utils.Format("http://{0}:61211/UA/SampleClient", System.Net.Dns.GetHostName()); // endpointDescription.EndpointUrl = Utils.Format("opc.tcp://{0}:51210/UA/SampleServer", System.Net.Dns.GetHostName()); // endpointDescription.EndpointUrl = Utils.Format("http://{0}:51211/UA/SampleServer/None", System.Net.Dns.GetHostName()); // endpointDescription.EndpointUrl = Utils.Format("http://{0}:51211/UA/SampleServer", System.Net.Dns.GetHostName()); // specify the security policy to use. // endpointDescription.SecurityPolicyUri = SecurityPolicies.None; // endpointDescription.SecurityMode = MessageSecurityMode.None;; endpointDescription.SecurityPolicyUri = SecurityPolicies.Basic128Rsa15; endpointDescription.SecurityMode = MessageSecurityMode.SignAndEncrypt; // specify the transport profile. endpointDescription.TransportProfileUri = Profiles.WsHttpXmlOrBinaryTransport; // endpointDescription.TransportProfileUri = Profiles.WsHttpXmlTransport; // endpointDescription.TransportProfileUri = Profiles.UaTcpTransport; endpointDescription.Server.DiscoveryUrls.Add(Utils.Format("http://{0}:61211/UA/SampleClient/discovery", System.Net.Dns.GetHostName())); // load the the server certificate from the local certificate store. CertificateIdentifier certificateIdentifier = new CertificateIdentifier(); certificateIdentifier.StoreType = CertificateStoreType.Windows; certificateIdentifier.StorePath = "LocalMachine\\My"; certificateIdentifier.SubjectName = "UA Sample Client"; X509Certificate2 serverCertificate = certificateIdentifier.Find(); if (serverCertificate == null) { throw ServiceResultException.Create(StatusCodes.BadCertificateInvalid, "Could not find server certificate: {0}", certificateIdentifier.SubjectName); } endpointDescription.ServerCertificate = serverCertificate.RawData; return endpointDescription; }
private static ServiceResult CheckChainStatus(X509ChainStatus status, CertificateIdentifier id, CertificateIdentifier issuer, bool isIssuer) { switch (status.Status) { case X509ChainStatusFlags.NotValidForUsage: { return(ServiceResult.Create( (isIssuer) ? StatusCodes.BadCertificateUseNotAllowed : StatusCodes.BadCertificateIssuerUseNotAllowed, "Certificate may not be used as an application instance certificate. {0}: {1}", status.Status, status.StatusInformation)); } case X509ChainStatusFlags.NoError: case X509ChainStatusFlags.OfflineRevocation: case X509ChainStatusFlags.InvalidBasicConstraints: case X509ChainStatusFlags.PartialChain: { break; } case X509ChainStatusFlags.UntrustedRoot: { // ignore this error because the root check is done // by looking the certificate up in the trusted issuer stores passed to the validator. // the ChainStatus uses the Windows trusted issuer stores. break; } case X509ChainStatusFlags.RevocationStatusUnknown: { if (issuer != null) { if ((issuer.ValidationOptions & CertificateValidationOptions.SuppressRevocationStatusUnknown) != 0) { break; } } // check for meaning less errors for self-signed certificates. if (id.Certificate != null && Utils.CompareDistinguishedName(id.Certificate.Subject, id.Certificate.Subject)) { break; } return(ServiceResult.Create( (isIssuer) ? StatusCodes.BadCertificateIssuerRevocationUnknown : StatusCodes.BadCertificateRevocationUnknown, "Certificate revocation status cannot be verified. {0}: {1}", status.Status, status.StatusInformation)); } case X509ChainStatusFlags.Revoked: { return(ServiceResult.Create( (isIssuer) ? StatusCodes.BadCertificateIssuerRevoked : StatusCodes.BadCertificateRevoked, "Certificate has been revoked. {0}: {1}", status.Status, status.StatusInformation)); } case X509ChainStatusFlags.NotTimeNested: { if (id != null && ((id.ValidationOptions & CertificateValidationOptions.SuppressCertificateExpired) != 0)) { break; } return(ServiceResult.Create( StatusCodes.BadCertificateIssuerTimeInvalid, "Certificate issuer validatity time does not overhas is expired or not yet valid. {0}: {1}", status.Status, status.StatusInformation)); } case X509ChainStatusFlags.NotTimeValid: { if (id != null && ((id.ValidationOptions & CertificateValidationOptions.SuppressCertificateExpired) != 0)) { break; } return(ServiceResult.Create( (isIssuer) ? StatusCodes.BadCertificateIssuerTimeInvalid : StatusCodes.BadCertificateTimeInvalid, "Certificate has is expired or not yet valid. {0}: {1}", status.Status, status.StatusInformation)); } default: { return(ServiceResult.Create( StatusCodes.BadCertificateInvalid, "Certificate validation failed. {0}: {1}", status.Status, status.StatusInformation)); } } return(null); }
/// <summary> /// Returns the issuers for the certificates. /// </summary> public bool GetIssuersWithChainSupportEnabled(X509Certificate2Collection certificates, List <CertificateIdentifier> issuers) { bool isTrusted = false; bool isChainComplete = false; CertificateIdentifier issuer = null; X509Certificate2 certificate = certificates[0]; // application certificate is trusted CertificateIdentifier trustedCertificate = GetTrustedCertificate(certificate); if (trustedCertificate != null) { isTrusted = true; } if (Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer)) { if (!isTrusted) { throw ServiceResultException.Create( StatusCodes.BadCertificateUntrusted, "Self Signed Certificate is not trusted.\r\nIssuerName: {0}", certificate.IssuerName.Name); } return(isTrusted); } CertificateIdentifierCollection collection = new CertificateIdentifierCollection(); for (int ii = 1; ii < certificates.Count; ii++) { collection.Add(new CertificateIdentifier(certificates[ii])); } do { issuer = GetIssuer(certificate, m_trustedCertificateList, m_trustedCertificateStore, true); if (issuer != null) { isTrusted = true; } if (issuer == null) { issuer = GetIssuer(certificate, m_issuerCertificateList, m_issuerCertificateStore, true); if (issuer == null) { issuer = GetIssuer(certificate, collection, null, true); } } if (issuer != null) { //isTrusted = true; issuers.Add(issuer); certificate = issuer.Find(false); // check for root. if (Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer)) { isChainComplete = true; break; } } else { isTrusted = false; } } while (issuer != null); if (!isChainComplete) { throw ServiceResultException.Create( StatusCodes.BadSecurityChecksFailed, "Certificate chain not complete.\r\nSubjectName: {0}\r\nIssuerName: {1}", certificates[0].SubjectName.Name, certificates[0].IssuerName.Name); } if (!isTrusted) { throw ServiceResultException.Create( StatusCodes.BadCertificateUntrusted, "Certificate issuer is not trusted.\r\nSubjectName: {0}\r\nIssuerName: {1}", certificates[0].SubjectName.Name, certificates[0].IssuerName.Name); } return(isTrusted); }