~~NOTOC~~ ====== Scenarij 9.3: SSO integracija ====== **Kategorija:** [[.:start|Overitev]] \\ **Kompleksnost:** 4/5 (Visoka) \\ **Predpogoji:** Identity Provider, certifikati odjemalcev \\ **Ocenjeni čas:** 30-45 minut ---- ===== Opis ===== Ta scenarij opisuje **integracijo overitve na osnovi certifikatov v sisteme enotne prijave (SSO)**. Certifikati se lahko uporabljajo kot primarni ali dodaten faktor overitve. **Podprti protokoli:** * **SAML 2.0** - X.509 Certificate Holder Assertion * **OIDC** - mTLS Client Credentials * **WS-Federation** - X.509 Token * **Kerberos PKINIT** - Certificate-to-Kerberos ---- ===== Potek dela: OIDC z mTLS ===== sequenceDiagram participant User as Uporabnik participant App as Aplikacija participant IdP as Identity Provider participant API User->>App: Dostop App->>IdP: Authorization Request IdP->>User: Zahteva certifikat odjemalca User->>IdP: Predložitev certifikata IdP->>IdP: Validacija certifikata IdP->>App: ID Token + Access Token App->>API: Zahteva + Access Token API->>App: Odgovor ---- ===== Primer kode: OIDC z overitvijo s certifikatom ===== using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Authentication.Certificate; var builder = WebApplication.CreateBuilder(args); // Overitev s certifikatom za komunikacijo z IdP builder.Services.AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie("Cookies") .AddOpenIdConnect(options => { options.Authority = "https://idp.example.com"; options.ClientId = "my-app"; options.ClientSecret = "secret"; // Ali certifikat odjemalca // mTLS za Token Endpoint options.BackchannelHttpHandler = CreateMtlsHandler(); options.ResponseType = "code"; options.SaveTokens = true; // Preslikava claims certifikata options.ClaimActions.MapJsonKey("certificate_thumbprint", "x5t"); options.ClaimActions.MapJsonKey("certificate_subject", "x5t#S256"); options.Events = new OpenIdConnectEvents { OnTokenValidated = context => { // Dodatna validacija var cert = context.HttpContext.Connection.ClientCertificate; if (cert != null) { // Povezava certifikata z žetonom var identity = context.Principal.Identity as ClaimsIdentity; identity?.AddClaim(new Claim("client_cert_subject", cert.Subject)); } return Task.CompletedTask; } }; }); static HttpClientHandler CreateMtlsHandler() { var handler = new HttpClientHandler(); handler.ClientCertificates.Add( new X509Certificate2("client.pfx", "Password!") ); return handler; } ---- ===== Primer kode: SAML z X.509 Holder-of-Key ===== using ComponentSpace.Saml2; public class SamlCertificateAuthService { private readonly ISamlServiceProvider _samlServiceProvider; public async Task AuthenticateWithCertificate( HttpContext httpContext, X509Certificate2 clientCert) { // Certifikat kot Holder-of-Key Subject Confirmation var samlResponse = await _samlServiceProvider.ReceiveSSOAsync(); // Preverjanje Subject Confirmation Method if (samlResponse.SubjectConfirmationMethod != "urn:oasis:names:tc:SAML:2.0:cm:holder-of-key") { throw new SecurityException("Zahtevana Holder-of-Key Confirmation"); } // Primerjava certifikata iz SAML-Assertion s predloženim var assertionCert = ExtractCertificateFromAssertion(samlResponse); if (assertionCert.Thumbprint != clientCert.Thumbprint) { throw new SecurityException("Certifikat se ne ujema z Assertion"); } return samlResponse.ClaimsPrincipal; } private X509Certificate2 ExtractCertificateFromAssertion(SamlResponse response) { // Ekstrakcija X.509 certifikata iz SubjectConfirmationData var keyInfoXml = response.SubjectConfirmationDataKeyInfo; // Razčlenjevanje X509Data iz KeyInfo // ... return null; } } ---- ===== Active Directory preslikava certifikatov ===== public class AdCertificateMappingService { private readonly string _ldapPath; public AdCertificateMappingService(string ldapPath) { _ldapPath = ldapPath; // npr. "LDAP://dc.example.com" } public DirectoryEntry FindUserByCertificate(X509Certificate2 cert) { using var searcher = new DirectorySearcher(new DirectoryEntry(_ldapPath)); // Možnost 1: UPN iz SAN var upn = GetUpnFromCertificate(cert); if (upn != null) { searcher.Filter = $"(userPrincipalName={upn})"; return searcher.FindOne()?.GetDirectoryEntry(); } // Možnost 2: altSecurityIdentities (Eksplicitna preslikava) var issuerSerial = $"X509:{cert.Issuer}{cert.SerialNumber}"; searcher.Filter = $"(altSecurityIdentities={issuerSerial})"; var result = searcher.FindOne(); if (result != null) return result.GetDirectoryEntry(); // Možnost 3: Subject + Issuer var subjectIssuer = $"X509:{cert.Subject}{cert.Issuer}"; searcher.Filter = $"(altSecurityIdentities={subjectIssuer})"; return searcher.FindOne()?.GetDirectoryEntry(); } public void MapCertificateToUser(string userDn, X509Certificate2 cert, CertMappingType type) { using var user = new DirectoryEntry($"{_ldapPath}/{userDn}"); string mapping = type switch { CertMappingType.IssuerSerial => $"X509:{cert.Issuer}{cert.SerialNumber}", CertMappingType.SubjectIssuer => $"X509:{cert.Subject}{cert.Issuer}", CertMappingType.SubjectOnly => $"X509:{cert.Subject}", CertMappingType.IssuerSubjectHash => $"X509:{cert.GetCertHashString()}", _ => throw new ArgumentException("Neznan tip preslikave") }; var altSecIds = user.Properties["altSecurityIdentities"]; altSecIds.Add(mapping); user.CommitChanges(); Console.WriteLine($"Preslikava ustvarjena: {mapping}"); } private string GetUpnFromCertificate(X509Certificate2 cert) { // UPN OID: 1.3.6.1.4.1.311.20.2.3 var sanExt = cert.Extensions["2.5.29.17"]; if (sanExt == null) return null; // Razčlenjevanje SAN za UPN... return null; } } public enum CertMappingType { IssuerSerial, SubjectIssuer, SubjectOnly, IssuerSubjectHash } ---- ===== Azure AD overitev na osnovi certifikatov ===== // Azure AD App Registration za CBA public class AzureAdCbaService { private readonly IConfidentialClientApplication _app; public AzureAdCbaService(string tenantId, string clientId, X509Certificate2 clientCert) { _app = ConfidentialClientApplicationBuilder .Create(clientId) .WithAuthority($"https://login.microsoftonline.com/{tenantId}") .WithCertificate(clientCert) // Certifikat odjemalca namesto skrivnosti .Build(); } public async Task AcquireTokenForClient(string[] scopes) { return await _app.AcquireTokenForClient(scopes).ExecuteAsync(); } public async Task AcquireTokenOnBehalfOf( string[] scopes, string userToken) { var userAssertion = new UserAssertion(userToken); return await _app.AcquireTokenOnBehalfOf(scopes, userAssertion).ExecuteAsync(); } } // Azure AD B2C z overitvijo s certifikatom builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApi(builder.Configuration) .EnableTokenAcquisitionToCallDownstreamApi() .AddInMemoryTokenCaches(); ---- ===== Keycloak X.509 Authenticator ===== { "alias": "x509-auth", "providerId": "auth-x509-client-username-form", "enabled": true, "config": { "x509-cert-auth.mapping-source-selection": "Subject's Common Name", "x509-cert-auth.regular-expression": "CN=(.*?)(?:,|$)", "x509-cert-auth.user-mapping-method": "Username or Email", "x509-cert-auth.crl-checking-enabled": "true", "x509-cert-auth.ocsp-checking-enabled": "true", "x509-cert-auth.ocsp-responder-uri": "http://ocsp.example.com", "x509-cert-auth.timestamp-validation-enabled": "true", "x509-cert-auth.keyusage": "digitalSignature", "x509-cert-auth.extendedkeyusage": "id-kp-clientAuth" } } ---- ===== Panožno-specifični SSO scenariji ===== ^ Panoga ^ IdP ^ Protokol ^ Zahteva za certifikat ^ | **Podjetje** | Azure AD / Okta | OIDC | UPN v SAN | | **Vlada** | ADFS / PIV | SAML HoK | PIV Auth Cert | | **Zdravstvo** | gematik IdP | OIDC | HBA/SMC-B | | **Finance** | Lastna PKI | SAML | Kvalificiran | ---- ===== Povezani scenariji ===== ^ Povezava ^ Scenarij ^ Opis ^ | **Predpogoj** | [[.:mtls_client_auth|9.1 mTLS Client-Auth]] | TLS-raven | | **Povezano** | [[.:smartcard_login|9.2 Prijava s pametno kartico]] | Strojna overitev | | **Povezano** | [[sl:int:pqcrypt:szenarien:zertifikate:client_cert|3.2 Certifikat odjemalca]] | Ustvarjanje certifikata | ---- << [[.:smartcard_login|9.2 Prijava s pametno kartico]] | [[.:start|Pregled overitve]] | [[sl:int:pqcrypt:szenarien:start|Vsi scenariji]] >> {{tag>scenarij overitev sso saml oidc azure-ad}} ---- //Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//