public async Task VerifyGetCertificateCompletedSubsequently() { string certName = Recording.GenerateId(); await Client.StartCreateCertificateAsync(certName, DefaultPolicy); RegisterForCleanup(certName); // Pretend a separate process was started subsequently and we need to get the operation again. CertificateOperation operation = new CertificateOperation(Client, certName); // Need to call the real async wait method or the sync version of this test fails because it's using the instrumented Client directly. using CancellationTokenSource cts = new CancellationTokenSource(DefaultCertificateOperationTimeout); await operation.WaitForCompletionAsync(PollingInterval, cts.Token); Assert.IsTrue(operation.HasCompleted); Assert.IsTrue(operation.HasValue); KeyVaultCertificateWithPolicy certificateWithPolicy = operation.Value; Assert.NotNull(certificateWithPolicy); Assert.AreEqual(certName, certificateWithPolicy.Name); Assert.NotNull(certificateWithPolicy.Properties.Version); }
public async Task VerifyGetCertificateCompleted() { string certName = Recording.GenerateId(); CertificateOperation operation = await Client.StartCreateCertificateAsync(certName, DefaultPolicy); RegisterForCleanup(certName); await operation.WaitForCompletionAsync(DefaultCertificateOperationPollingInterval, default); KeyVaultCertificateWithPolicy certificateWithPolicy = await Client.GetCertificateAsync(certName); Assert.NotNull(certificateWithPolicy); Assert.AreEqual(certificateWithPolicy.Name, certName); Assert.NotNull(certificateWithPolicy.Properties.Version); KeyVaultCertificate certificate = await Client.GetCertificateVersionAsync(certName, certificateWithPolicy.Properties.Version); Assert.NotNull(certificate); Assert.AreEqual(certificate.Name, certName); }
public void ConfigureServices(IServiceCollection services) { services.AddSingleton <ILocalizationProvider, StorageLocalizationProvider>(); services.AddTextLocalization(options => { options.ReturnOnlyKeyIfNotFound = !_environment.IsDevelopment(); options.FallBackNeutralCulture = !_environment.IsDevelopment(); }).Configure <RequestLocalizationOptions>(options => { options.DefaultRequestCulture = new RequestCulture(Settings.SupportedCultures[0]); options.AddSupportedCultures(Settings.SupportedCultures); options.AddSupportedUICultures(Settings.SupportedCultures); }); services.AddScoped <LazyAssemblyLoader>(); var dataProtectionBuilder = services.AddDataProtection().SetApplicationName(projectName); services.RegisterStorage(Configuration); services.Configure <ApiBehaviorOptions>(options => { options.SuppressModelStateInvalidFilter = true; }); services.AddIdentity <ApplicationUser, ApplicationRole>() .AddRoles <ApplicationRole>() .AddEntityFrameworkStores <ApplicationDbContext>() .AddDefaultTokenProviders() .AddErrorDescriber <LocalizedIdentityErrorDescriber>(); services.AddScoped <IUserClaimsPrincipalFactory <ApplicationUser>, AdditionalUserClaimsPrincipalFactory>(); var authAuthority = Configuration[$"{projectName}:IS4ApplicationUrl"].TrimEnd('/'); // Adds IdentityServer https://identityserver4.readthedocs.io/en/latest/reference/options.html var identityServerBuilder = services.AddIdentityServer(options => { options.IssuerUri = authAuthority; options.Events.RaiseErrorEvents = true; options.Events.RaiseInformationEvents = true; options.Events.RaiseFailureEvents = true; options.Events.RaiseSuccessEvents = true; options.UserInteraction.ErrorUrl = "/identityserver/error"; }) .AddIdentityServerStores(Configuration) .AddAspNetIdentity <ApplicationUser>(); //https://identityserver4.readthedocs.io/en/latest/reference/aspnet_identity.html X509Certificate2 cert = null; var keysLocalFolder = Path.Combine(_environment.ContentRootPath, "Keys"); if (_environment.IsDevelopment()) { // The AddDeveloperSigningCredential extension creates temporary key material tempkey.jwk for signing tokens. // This might be useful to get started, but needs to be replaced by some persistent key material for production scenarios. // See http://docs.identityserver.io/en/release/topics/crypto.html#refcrypto for more information. // https://stackoverflow.com/questions/42351274/identityserver4-hosting-in-iis identityServerBuilder.AddDeveloperSigningCredential(); dataProtectionBuilder.PersistKeysToFileSystem(new DirectoryInfo(keysLocalFolder)); } else { // Running on Azure Web App service - read the setup doc at blazor-boilerplate.readthedocs.io // appsettings.json parameters used: // "RunsOnAzure": true, // "RunningAsAppService": true, // "RunningAsDocker": false, // not implemented yet // "AzureKeyVault": { // "UsingKeyVault": true, // "UseManagedAppIdentity": true, // "AppKey": "", // not implemented yet. // "AppSecret": "", // "KeyVaultURI": "https://YOURVAULTNAMEHERE.vault.azure.net/", // "CertificateIdentifier": "https://YOURVAULTNAMEHERE.vault.azure.net/certificates/BBAUTH/<HEX_VERSION_STRING_HERE>", // "CertificateName": "BBAUTH", // "StorageAccountBlobBaseUrl": "https://<YOUR_STORAGE_ACCOUNT_NAME_HERE>.blob.core.windows.net", // "ContainerName": "blazor-boilerplate-keys", // "KeysBlobName": "keys.xml" if (Convert.ToBoolean(Configuration["HostingOnAzure:RunsOnAzure"]) == true) { if (Convert.ToBoolean(Configuration["HostingOnAzure:AzureKeyVault:UsingKeyVault"]) == true) { if (Convert.ToBoolean(Configuration["HostingOnAzure:AzurekeyVault:UseManagedAppIdentity"]) == true) { //https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview // In production environment we have already configured Managed Identity (blazor-boilerplate) using Azure Portal for our app to access Key Vault and Blob Storage. // Set up TokenCredential options for production and development environments bool isDeployed = !_environment.IsDevelopment(); var credentialOptions = new DefaultAzureCredentialOptions { ExcludeEnvironmentCredential = isDeployed, ExcludeManagedIdentityCredential = false, // we only use this one in production ExcludeSharedTokenCacheCredential = isDeployed, ExcludeVisualStudioCredential = isDeployed, ExcludeVisualStudioCodeCredential = isDeployed, ExcludeAzureCliCredential = isDeployed, ExcludeInteractiveBrowserCredential = isDeployed, }; // In development environment DefaultAzureCredential() will use the shared token credential from the IDE. In Visual Studio this is under Options - Azure Service Authentication. if (_environment.IsDevelopment()) { // credentialOptions.SharedTokenCacheUsername = "******"; // specify user name to use if more than one Azure username is configured in IDE. // var defaultTenantId = "?????-????-????-????-????????"; // specify AAD tenant to authenticate against (from Azure Portal - AAD - Tenant ID) // credentialOptions.SharedTokenCacheTenantId = defaultTenantId; // credentialOptions.VisualStudioCodeTenantId = defaultTenantId; // credentialOptions.VisualStudioTenantId = defaultTenantId; } TokenCredential tokenCredential = new DefaultAzureCredential(credentialOptions); // The Azure Storage Container (blazor-boilerplate-keys) Access Control must grant blazor-boilerplate the following roles: // - Storage Blob Data Contributor var blobServiceClient = new BlobServiceClient( new Uri(Configuration["HostingOnAzure:AzureKeyVault:StorageAccountBlobBaseUrl"]), tokenCredential); BlobContainerClient blobContainerClient = blobServiceClient.GetBlobContainerClient(Configuration["HostingOnAzure:AzureKeyVault:ContainerName"]); BlobClient blobClient = blobContainerClient.GetBlobClient(Configuration["HostingOnAzure:AzureKeyVault:KeysBlobName"]); var certificateIdentifier = Configuration["HostingOnAzure:AzureKeyVault:CertificateIdentifier"]; dataProtectionBuilder.PersistKeysToAzureBlobStorage(blobClient); // 1. Remove the call to ProtectKeysWithAzureKeyVault below for the first run to create the keys.xml blob in place. // 2. Add the call to ProtectKeysWithAzureKeyVault for subsequent runs.so that keys.xml gets created - see the setup doc for more information dataProtectionBuilder.ProtectKeysWithAzureKeyVault(new Uri(certificateIdentifier), tokenCredential); // Azure Key Vault Access Policy must grant the following permissions to the blazor-boilerplate app: // - Secret Permissions: Get // - Certificate Permissions: Get // Retrieve the certificate and extract the secret so that we can build the new X509 certificate for later use by Identity Server var certificateClient = new CertificateClient(vaultUri: new Uri(Configuration["HostingOnAzure:AzureKeyVault:KeyVaultUri"]), credential: new DefaultAzureCredential()); var secretClient = new SecretClient(vaultUri: new Uri(Configuration["HostingOnAzure:AzureKeyVault:KeyVaultUri"]), credential: new DefaultAzureCredential()); KeyVaultCertificateWithPolicy certificateWithPolicy = certificateClient.GetCertificateAsync(Configuration["HostingOnAzure:AzureKeyVault:CertificateName"]).GetAwaiter().GetResult(); // retrieves latest version of certificate KeyVaultSecretIdentifier secretIdentifier = new KeyVaultSecretIdentifier(certificateWithPolicy.SecretId); KeyVaultSecret secret = secretClient.GetSecretAsync(secretIdentifier.Name, secretIdentifier.Version).GetAwaiter().GetResult(); byte[] privateKeyBytes = Convert.FromBase64String(secret.Value); cert = new X509Certificate2(privateKeyBytes, (string)null, X509KeyStorageFlags.MachineKeySet); } } else // if app id and app secret are used { throw new NotImplementedException(); } } else { dataProtectionBuilder.PersistKeysToFileSystem(new DirectoryInfo(keysLocalFolder)); } //TODO this implementation does not consider certificate expiration if (Convert.ToBoolean(Configuration[$"{projectName}:UseLocalCertStore"]) == true) { var certificateThumbprint = Configuration[$"{projectName}:CertificateThumbprint"]; var storeLocation = StoreLocation.LocalMachine; dynamic storeName = "WebHosting"; if (OperatingSystem.IsLinux()) { storeLocation = StoreLocation.CurrentUser; storeName = StoreName.My; } using X509Store store = new(storeName, storeLocation); store.Open(OpenFlags.ReadOnly); var certs = store.Certificates.Find(X509FindType.FindByThumbprint, certificateThumbprint, false); if (certs.Count > 0) { cert = certs[0]; } else { var certPath = Path.Combine(_environment.ContentRootPath, "AuthSample.pfx"); if (File.Exists(certPath)) { string certificatePassword = Configuration[$"{projectName}:CertificatePassword"] ?? "Admin123"; cert = new X509Certificate2(certPath, certificatePassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable); } } store.Close(); } // pass the resulting certificate to Identity Server if (cert != null) { identityServerBuilder.AddSigningCredential(cert); Log.Logger.Information($"Added certificate {cert.Subject} to Identity Server"); } else if (OperatingSystem.IsWindows()) { Log.Logger.Debug("Trying to use WebHosting Certificate for Identity Server"); identityServerBuilder.AddWebHostingCertificate(); } else { throw new Exception("Missing Certificate for Identity Server"); } } var authBuilder = services.AddAuthentication(options => { options.DefaultScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme; }) .AddIdentityServerAuthentication(options => { options.Authority = authAuthority; options.SupportedTokens = SupportedTokens.Jwt; options.RequireHttpsMetadata = _environment.IsProduction(); options.ApiName = IdentityServerConfig.LocalApiName; options.Events = new JwtBearerEvents { OnMessageReceived = context => { var accessToken = context.Request.Query["access_token"]; // If the request is for our hub... var path = context.HttpContext.Request.Path; if (!string.IsNullOrEmpty(accessToken) && (path.StartsWithSegments("/chathub"))) { // Read the token out of the query string context.Token = accessToken; } return(Task.CompletedTask); } }; }); #region ExternalAuthProviders //https://github.com/dotnet/aspnetcore/blob/master/src/Security/Authentication/samples/SocialSample/Startup.cs //https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/google-logins if (Convert.ToBoolean(Configuration["ExternalAuthProviders:Google:Enabled"] ?? "false")) { authBuilder.AddGoogle(options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.ClientId = Configuration["ExternalAuthProviders:Google:ClientId"]; options.ClientSecret = Configuration["ExternalAuthProviders:Google:ClientSecret"]; options.AuthorizationEndpoint += "?prompt=consent"; // Hack so we always get a refresh token, it only comes on the first authorization response options.AccessType = "offline"; options.SaveTokens = true; options.Events = new OAuthEvents() { OnRemoteFailure = HandleOnRemoteFailure }; options.ClaimActions.MapJsonSubKey("urn:google:image", "image", "url"); options.ClaimActions.Remove(ClaimTypes.GivenName); }); } if (Convert.ToBoolean(Configuration["ExternalAuthProviders:Facebook:Enabled"] ?? "false")) { // You must first create an app with Facebook and add its ID and Secret to your user-secrets. // https://developers.facebook.com/apps/ // https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#login authBuilder.AddFacebook(options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.AppId = Configuration["ExternalAuthProviders:Facebook:AppId"]; options.AppSecret = Configuration["ExternalAuthProviders:Facebook:AppSecret"]; options.Scope.Add("email"); options.Fields.Add("name"); options.Fields.Add("email"); options.SaveTokens = true; options.Events = new OAuthEvents() { OnRemoteFailure = HandleOnRemoteFailure }; }); } if (Convert.ToBoolean(Configuration["ExternalAuthProviders:Twitter:Enabled"] ?? "false")) { // You must first create an app with Twitter and add its key and Secret to your user-secrets. // https://apps.twitter.com/ // https://developer.twitter.com/en/docs/basics/authentication/api-reference/access_token authBuilder.AddTwitter(options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.ConsumerKey = Configuration["ExternalAuthProviders:Twitter:ConsumerKey"]; options.ConsumerSecret = Configuration["ExternalAuthProviders:Twitter:ConsumerSecret"]; // http://stackoverflow.com/questions/22627083/can-we-get-email-id-from-twitter-oauth-api/32852370#32852370 // http://stackoverflow.com/questions/36330675/get-users-email-from-twitter-api-for-external-login-authentication-asp-net-mvc?lq=1 options.RetrieveUserDetails = true; options.SaveTokens = true; options.ClaimActions.MapJsonKey("urn:twitter:profilepicture", "profile_image_url", ClaimTypes.Uri); options.Events = new TwitterEvents() { OnRemoteFailure = HandleOnRemoteFailure }; }); } //https://github.com/xamarin/Essentials/blob/master/Samples/Sample.Server.WebAuthenticator/Startup.cs if (Convert.ToBoolean(Configuration["ExternalAuthProviders:Apple:Enabled"] ?? "false")) { authBuilder.AddApple(options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.ClientId = Configuration["ExternalAuthProviders:Apple:ClientId"]; options.KeyId = Configuration["ExternalAuthProviders:Apple:KeyId"]; options.TeamId = Configuration["ExternalAuthProviders:Apple:TeamId"]; options.UsePrivateKey(keyId => _environment.ContentRootFileProvider.GetFileInfo($"AuthKey_{keyId}.p8")); options.SaveTokens = true; }); } // You must first create an app with Microsoft Account and add its ID and Secret to your user-secrets. // https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app if (Convert.ToBoolean(Configuration["ExternalAuthProviders:Microsoft:Enabled"] ?? "false")) { authBuilder.AddMicrosoftAccount(options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.ClientId = Configuration["ExternalAuthProviders:Microsoft:ClientId"]; options.ClientSecret = Configuration["ExternalAuthProviders:Microsoft:ClientSecret"]; options.SaveTokens = true; options.Scope.Add("offline_access"); options.Events = new OAuthEvents() { OnRemoteFailure = HandleOnRemoteFailure }; }); } #endregion #region Authorization //Add Policies / Claims / Authorization - https://identityserver4.readthedocs.io/en/latest/topics/add_apis.html#advanced services.AddScoped <EntityPermissions>(); services.AddSingleton <IAuthorizationPolicyProvider, AuthorizationPolicyProvider>(); services.AddTransient <IAuthorizationHandler, DomainRequirementHandler>(); services.AddTransient <IAuthorizationHandler, PermissionRequirementHandler>(); #endregion services.Configure <IdentityOptions>(options => { // Password settings options.Password.RequireDigit = RequireDigit; options.Password.RequiredLength = RequiredLength; options.Password.RequireNonAlphanumeric = RequireNonAlphanumeric; options.Password.RequireUppercase = RequireUppercase; options.Password.RequireLowercase = RequireLowercase; //options.Password.RequiredUniqueChars = 6; // Lockout settings options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30); options.Lockout.MaxFailedAccessAttempts = 10; options.Lockout.AllowedForNewUsers = true; // Require Confirmed Email User settings if (Convert.ToBoolean(Configuration[$"{projectName}:RequireConfirmedEmail"] ?? "false")) { options.User.RequireUniqueEmail = true; options.SignIn.RequireConfirmedEmail = true; } }); #region Cookies // cookie policy to deal with temporary browser incompatibilities services.AddSameSiteCookiePolicy(); //https://docs.microsoft.com/en-us/aspnet/core/security/gdpr services.Configure <CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential // cookies is needed for a given request. options.CheckConsentNeeded = context => false; //consent not required // requires using Microsoft.AspNetCore.Http; //options.MinimumSameSitePolicy = SameSiteMode.None; }); //services.ConfigureExternalCookie(options => // { // macOS login fix //options.Cookie.SameSite = SameSiteMode.None; //}); services.ConfigureApplicationCookie(options => { options.Cookie.IsEssential = true; options.Cookie.HttpOnly = true; options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest; options.ExpireTimeSpan = TimeSpan.FromDays(Convert.ToDouble(Configuration[$"{projectName}:CookieExpireTimeSpanDays"] ?? "30")); options.LoginPath = Constants.Settings.LoginPath; //options.AccessDeniedPath = "/Identity/Account/AccessDenied"; // ReturnUrlParameter requires //using Microsoft.AspNetCore.Authentication.Cookies; options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter; options.SlidingExpiration = true; // Suppress redirect on API URLs in ASP.NET Core -> https://stackoverflow.com/a/56384729/54159 options.Events = new CookieAuthenticationEvents() { OnRedirectToAccessDenied = context => { if (context.Request.Path.StartsWithSegments("/api")) { context.Response.StatusCode = Status403Forbidden; } return(Task.CompletedTask); }, OnRedirectToLogin = context => { context.Response.StatusCode = Status401Unauthorized; return(Task.CompletedTask); } }; }); #endregion services.AddMvc().AddNewtonsoftJson(opt => { // Set Breeze defaults for entity serialization var ss = JsonSerializationFns.UpdateWithDefaults(opt.SerializerSettings); if (ss.ContractResolver is DefaultContractResolver resolver) { resolver.NamingStrategy = null; // remove json camelCasing; names are converted on the client. } if (_environment.IsDevelopment()) { ss.Formatting = Newtonsoft.Json.Formatting.Indented; // format JSON for debugging } }) // Add Breeze exception filter to send errors back to the client .AddMvcOptions(o => { o.Filters.Add(new GlobalExceptionFilter()); }) .AddViewLocalization().AddDataAnnotationsLocalization(options => { options.DataAnnotationLocalizerProvider = (type, factory) => { return(factory.Create(typeof(Global))); }; }).AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining <LocalizationRecordValidator>()); services.AddServerSideBlazor().AddCircuitOptions(o => { if (_environment.IsDevelopment()) { o.DetailedErrors = true; } }).AddHubOptions(o => { o.MaximumReceiveMessageSize = 131072; }); services.AddSignalR(); if (_enableAPIDoc) { services.AddOpenApiDocument(document => { document.Title = "BlazorBoilerplate API"; document.Version = typeof(Startup).GetTypeInfo().Assembly.GetName().Version.ToString(); document.AddSecurity("bearer", Enumerable.Empty <string>(), new OpenApiSecurityScheme { Type = OpenApiSecuritySchemeType.OAuth2, Description = "Local Identity Server", OpenIdConnectUrl = $"{authAuthority}/.well-known/openid-configuration", //not working Flow = OpenApiOAuth2Flow.AccessCode, Flows = new OpenApiOAuthFlows() { AuthorizationCode = new OpenApiOAuthFlow() { Scopes = new Dictionary <string, string> { { LocalApi.ScopeName, IdentityServerConfig.LocalApiName } }, AuthorizationUrl = $"{authAuthority}/connect/authorize", TokenUrl = $"{authAuthority}/connect/token" }, } });; document.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("bearer")); // new OperationSecurityScopeProcessor("bearer")); }); } services.AddScoped <IUserSession, UserSession>(); services.AddSingleton <IHttpContextAccessor, HttpContextAccessor>(); services.Add(ServiceDescriptor.Scoped(typeof(ITenantSettings <>), typeof(TenantSettingsManager <>))); services.AddTransient <IEmailFactory, EmailFactory>(); services.AddTransient <IAccountManager, AccountManager>(); services.AddTransient <IAdminManager, AdminManager>(); services.AddTransient <IEmailManager, EmailManager>(); services.AddTransient <IExternalAuthManager, ExternalAuthManager>(); #region Automapper //Automapper to map DTO to Models https://www.c-sharpcorner.com/UploadFile/1492b1/crud-operations-using-automapper-in-mvc-application/ var automapperConfig = new MapperConfiguration(configuration => { configuration.AddProfile(new MappingProfile()); }); var autoMapper = automapperConfig.CreateMapper(); services.AddSingleton(autoMapper); #endregion /* ServerSideBlazor */ services.AddScoped <IAccountApiClient, AccountApiClient>(); services.AddScoped <AppState>(); // setup HttpClient for server side in a client side compatible fashion ( with auth cookie ) // if (!services.Any(x => x.ServiceType == typeof(HttpClient))) // { services.AddScoped(s => { // creating the URI helper needs to wait until the JS Runtime is initialized, so defer it. var navigationManager = s.GetRequiredService <NavigationManager>(); var httpContextAccessor = s.GetRequiredService <IHttpContextAccessor>(); var cookies = httpContextAccessor.HttpContext.Request.Cookies; var httpClientHandler = new HttpClientHandler() { UseCookies = false }; if (_environment.IsDevelopment()) { // Return 'true' to allow certificates that are untrusted/invalid httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return(true); }; } var client = new HttpClient(httpClientHandler); if (cookies.Any()) { var cks = new List <string>(); foreach (var cookie in cookies) { cks.Add($"{cookie.Key}={cookie.Value}"); } client.DefaultRequestHeaders.Add("Cookie", string.Join(';', cks)); } client.BaseAddress = new Uri(navigationManager.BaseUri); return(client); }); // } services.AddScoped <ILocalizationApiClient, LocalizationApiClient>(); services.AddScoped <IApiClient, ApiClient>(); // Authentication providers Log.Logger.Debug("Removing AuthenticationStateProvider..."); var serviceDescriptor = services.FirstOrDefault(descriptor => descriptor.ServiceType == typeof(AuthenticationStateProvider)); if (serviceDescriptor != null) { services.Remove(serviceDescriptor); } Log.Logger.Debug("Adding AuthenticationStateProvider..."); services.AddScoped <AuthenticationStateProvider, IdentityAuthenticationStateProvider>(); /**********************/ services.AddModules(); if (Log.Logger.IsEnabled(Serilog.Events.LogEventLevel.Debug)) { Log.Logger.Debug($"Total Services Registered: {services.Count}"); foreach (var service in services) { Log.Logger.Debug($"\n\tService: {service.ServiceType.FullName}\n\tLifetime: {service.Lifetime}\n\tInstance: {service.ImplementationType?.FullName}"); } } }
public async Task ValidateMergeCertificate() { string serverCertificateName = Recording.GenerateId(); // Generate the request. CertificatePolicy policy = new CertificatePolicy(WellKnownIssuerNames.Unknown, "CN=Azure SDK") { CertificateTransparency = false, ContentType = CertificateContentType.Pkcs12, }; CertificateOperation operation = await Client.StartCreateCertificateAsync(serverCertificateName, policy); RegisterForCleanup(serverCertificateName); await using IAsyncDisposable disposableOperation = EnsureDeleted(operation); // Read the CA. byte[] caCertificateBytes = Convert.FromBase64String(CaPublicKeyBase64); X509Certificate2 caCertificate = new X509Certificate2(caCertificateBytes); // Read CA private key since getting it from caCertificate above throws. AsymmetricCipherKeyPair caPrivateKey; using (StringReader caPrivateKeyReader = new StringReader(CaPrivateKeyPem)) { Org.BouncyCastle.OpenSsl.PemReader reader = new Org.BouncyCastle.OpenSsl.PemReader(caPrivateKeyReader); caPrivateKey = (AsymmetricCipherKeyPair)reader.ReadObject(); } // Read the CSR. Pkcs10CertificationRequest csr = new Pkcs10CertificationRequest(operation.Properties.Csr); CertificationRequestInfo csrInfo = csr.GetCertificationRequestInfo(); // Parse the issuer subject name. Hashtable oidLookup = new Hashtable(X509Name.DefaultLookup) { { "s", new DerObjectIdentifier("2.5.4.8") }, }; X509Name issuerName = new X509Name(true, oidLookup, caCertificate.Subject); // Sign the request. X509V3CertificateGenerator generator = new X509V3CertificateGenerator(); generator.SetIssuerDN(issuerName); generator.SetSerialNumber(BigInteger.One); generator.SetNotBefore(DateTime.Now); generator.SetNotAfter(DateTime.Now.AddDays(1)); generator.SetSubjectDN(csrInfo.Subject); generator.SetPublicKey(csr.GetPublicKey()); Asn1SignatureFactory signatureFactory = new Asn1SignatureFactory("SHA256WITHRSA", caPrivateKey.Private); X509Certificate serverSignedPublicKey = generator.Generate(signatureFactory); // Merge the certificate chain. MergeCertificateOptions options = new MergeCertificateOptions(serverCertificateName, new[] { serverSignedPublicKey.GetEncoded(), caCertificateBytes }); KeyVaultCertificateWithPolicy mergedServerCertificate = await Client.MergeCertificateAsync(options); X509Certificate2 serverCertificate = new X509Certificate2(mergedServerCertificate.Cer); Assert.AreEqual(csrInfo.Subject.ToString(), serverCertificate.Subject); Assert.AreEqual(serverCertificateName, mergedServerCertificate.Name); KeyVaultCertificateWithPolicy completedServerCertificate = await operation.WaitForCompletionAsync(DefaultCertificateOperationPollingInterval, default); Assert.AreEqual(mergedServerCertificate.Name, completedServerCertificate.Name); CollectionAssert.AreEqual(mergedServerCertificate.Cer, completedServerCertificate.Cer); }
public async Task VerifyCertificateOperationError() { string issuerName = Recording.GenerateId(); string certName = Recording.GenerateId(); CertificateIssuer certIssuer = new CertificateIssuer(issuerName, "DigiCert") { AccountId = "test", Password = "******", OrganizationId = "test", }; await Client.CreateIssuerAsync(certIssuer); CertificateOperation operation = null; try { CertificatePolicy certificatePolicy = DefaultPolicy; certificatePolicy.IssuerName = issuerName; operation = await Client.StartCreateCertificateAsync(certName, certificatePolicy); operation = InstrumentOperation(operation); RegisterForCleanup(certName); using CancellationTokenSource cts = new CancellationTokenSource(DefaultCertificateOperationTimeout); while (!operation.HasCompleted) { await Task.Delay(PollingInterval, cts.Token); await operation.UpdateStatusAsync(cts.Token); } InvalidOperationException ex = Assert.Throws <InvalidOperationException>(() => { KeyVaultCertificateWithPolicy cert = operation.Value; }); StringAssert.StartsWith("The certificate operation failed: ", ex.Message); Assert.IsTrue(operation.HasCompleted); Assert.IsFalse(operation.HasValue); Assert.AreEqual(200, operation.GetRawResponse().Status); Assert.AreEqual("failed", operation.Properties.Status); } catch (TaskCanceledException) when(operation != null) { Assert.Inconclusive("Timed out while waiting for operation {0}", operation.Id); } finally { await Client.DeleteIssuerAsync(issuerName); } }
public async Task VerifyImportCertificatePem() { const string pem = "-----BEGIN CERTIFICATE-----\n" + "MIIDqzCCApMCFC+MROpib4t03Wqzgkcod1lad6JtMA0GCSqGSIb3DQEBCwUAMIGR\n" + "MQswCQYDVQQGEwJVUzELMAkGA1UECAwCV0ExEDAOBgNVBAcMB1JlZG1vbmQxEjAQ\n" + "BgNVBAoMCU1pY3Jvc29mdDESMBAGA1UECwwJQXp1cmUgU0RLMRIwEAYDVQQDDAlB\n" + "enVyZSBTREsxJzAlBgkqhkiG9w0BCQEWGG9wZW5zb3VyY2VAbWljcm9zb2Z0LmNv\n" + "bTAeFw0yMDAyMTQyMzE3MTZaFw0yNTAyMTIyMzE3MTZaMIGRMQswCQYDVQQGEwJV\n" + "UzELMAkGA1UECAwCV0ExEDAOBgNVBAcMB1JlZG1vbmQxEjAQBgNVBAoMCU1pY3Jv\n" + "c29mdDESMBAGA1UECwwJQXp1cmUgU0RLMRIwEAYDVQQDDAlBenVyZSBTREsxJzAl\n" + "BgkqhkiG9w0BCQEWGG9wZW5zb3VyY2VAbWljcm9zb2Z0LmNvbTCCASIwDQYJKoZI\n" + "hvcNAQEBBQADggEPADCCAQoCggEBANwCTuK0OnFc8UytzzCIB5pUWqWCMZA8kWO1\n" + "Es84wOVupPTZHNDWKI57prj0CB5JP2yU8BkIFjhkV/9wc2KLjKwu7xaJTwBZF/i0\n" + "t8dPBbgiEUmK6xdbJsLXoef/XZ5AmvCKb0mimEMvL8KgeF5OHuZJuYO0zCiRNVtp\n" + "ZYSx2R73qhgy5klDHh346qQd5T+KbsdK3DArilT86QO1GrpBWl1GPvHJ3VZ1OO33\n" + "iFWfyEVgwdAtMAkWXH8Eh1/MpPE8WQk5X5pdVEu+RJLLrVbgr+cnlVzfirSVLRar\n" + "KZROAB3e2x8JdSqylnar/WWK11NERdiKaZr3WxAkceuVkTsKmRkCAwEAATANBgkq\n" + "hkiG9w0BAQsFAAOCAQEAYLfk2dBcW1mJbkVYx80ogDUy/xX3d+uuop2gZwUXuzWY\n" + "I4uXzSEsY37/+NKzOX6PtET3X6xENDW7AuJhTuWmTGZtPB1AjiVKLIgRwugV3Ovr\n" + "1DoPBIvS7iCHGGcsr7tAgYxiVATlIcczCxQG1KPhrrLSUDxkbiyUHpyroExHGBeC\n" + "UflT2BIO+TZ+44aYfO7vuwpu0ajfB6Rs0s/DM+uUTWCfsVvyPenObHz5HF2vxf75\n" + "y8pr3fYKuUvpJ45T0ZjiXyRpkBTDudU3vuYuyAP3PwO6F/ic7Rm9D1uzEI38Va+o\n" + "6CUh4NJnpIZIBs7T+rPwhKrUuM7BEO0CL7VTh37UzA==\n" + "-----END CERTIFICATE-----\n" + "-----BEGIN PRIVATE KEY-----\n" + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDcAk7itDpxXPFM\n" + "rc8wiAeaVFqlgjGQPJFjtRLPOMDlbqT02RzQ1iiOe6a49AgeST9slPAZCBY4ZFf/\n" + "cHNii4ysLu8WiU8AWRf4tLfHTwW4IhFJiusXWybC16Hn/12eQJrwim9JophDLy/C\n" + "oHheTh7mSbmDtMwokTVbaWWEsdke96oYMuZJQx4d+OqkHeU/im7HStwwK4pU/OkD\n" + "tRq6QVpdRj7xyd1WdTjt94hVn8hFYMHQLTAJFlx/BIdfzKTxPFkJOV+aXVRLvkSS\n" + "y61W4K/nJ5Vc34q0lS0WqymUTgAd3tsfCXUqspZ2q/1litdTREXYimma91sQJHHr\n" + "lZE7CpkZAgMBAAECggEAMRfSwoO1BtbWgWXHdezkxWtNTuFebfEWAEnHiLYBVTD7\n" + "XieUZoVjR2gQK/VIWnm9zVzutqc3Th4WBMny9WpuWX2fnEfHeSxoTPcGi1L207/G\n" + "W8LD8tJEM/YqCrrRCR8hc8twSd4eW9+LqMJmGaUVAA4zd1BAvkyou10pahLFgEMZ\n" + "nlYxOzz0KrniNIdQxhwfaXZYUzX5ooJYtgY74vnSOHQhepRt5HY9B7iZ6jm/3ulA\n" + "aJnfNbQ8YDYTS0R+OGv8RXU/jLCm5+TPwx0XFwZ6vRtWwWUUxhLV77Re9GP1xIx9\n" + "VnYm9W3RyOm/KD9keQMTWKT0bLGB8fC6kj2mvbjgAQKBgQDzh5sy7q9RA+GqprC8\n" + "8aUmkaTMXNahPPPJoLOflJ/+QlOt6YZUIn55vmicVsvFzr9hbxdTW7aQS91iAu05\n" + "swEyltsR0my7FXsHZnN4SBct2FimAzMLTWQr10vLLRoSR5CNpUdoXGWFOAa3LKrZ\n" + "aPJEM1hA3h2XDfZ7Gtxjg4ypIQKBgQDnRl9pGwd83MkoxT4CiZvNbvdBg4lXlHcA\n" + "JoZ9OfoOey+7WRsOFsMvQapXf+JlvixP0ldECXZyxifswvfmiR2oqYTeRbITderg\n" + "mwjDjN571Ui0ls5HwCBE+/iZoNmQI5INAPqsQMXwW0rx4YNXHblsJ0qT+3yFNWOF\n" + "m6STMH8Y+QKBgFai8JivB1nICrleMdQWF43gFIPLp2OXPpeFf0GPa1fWGtTtFifK\n" + "WbpP/gFYc4f8pGMyVVcHcqxlAO5EYka7ovpvZqIxfRMVcj5QuVWaN/zMUcVFsBwe\n" + "PTvHjSRL+FF2ejuaCAxdipRZOTJjRqivyDhxF72EB3zcr8pd5PfWLe1hAoGASJRO\n" + "JvcDj4zeWDwmLLewvHTBhb7Y4DJIcjSk6jHCpr7ECQB6vB4qnO73nUQV8aYP0/EH\n" + "z+NEV9qV9vhswd1wAFlKyFKJAxBzaI9e3becrrINghb9n4jM17lXmCbhgBmZoRkY\n" + "kew18itERspl5HYAlc9y2SQIPOm3VNu2dza1/EkCgYEAlTMyL6arbtJJsygzVn8l\n" + "gKHuURwp1cxf6hUuXKJ56xI/I1OZjMidZM0bYSznmK9SGNxlfNbIV8vNhQfiwR6t\n" + "HyGypSRP+h9MS9E66boXyINaOClZqiCn0pI9aiIpl3D6EbT6e7+zKljT0XmZJduK\n" + "BkRGMfUngiT8oVyaMtZWYPM=\n" + "-----END PRIVATE KEY-----\n"; string caCertificateName = Recording.GenerateId(); byte[] caCertificateBytes = Encoding.ASCII.GetBytes(pem); ImportCertificateOptions options = new ImportCertificateOptions(caCertificateName, caCertificateBytes) { Policy = new CertificatePolicy(WellKnownIssuerNames.Self, "CN=Azure SDK") { ContentType = CertificateContentType.Pem, }, }; KeyVaultCertificateWithPolicy cert = await Client.ImportCertificateAsync(options); RegisterForCleanup(caCertificateName); byte[] pubBytes = Convert.FromBase64String(CaPublicKeyBase64); CollectionAssert.AreEqual(pubBytes, cert.Cer); }
static async Task <int> Main() { // Environment variable with the Key Vault endpoint. string keyVaultUrl = Environment.GetEnvironmentVariable("AZURE_KEYVAULT_URL"); // Instantiate a certificate client that will be used to call the service. Notice that the client is using // default Azure credentials. To make default credentials work, ensure that environment variables 'AZURE_CLIENT_ID', // 'AZURE_CLIENT_KEY' and 'AZURE_TENANT_ID' are set with the service principal credentials. CertificateClient client = new CertificateClient(new Uri(keyVaultUrl), new DefaultAzureCredential()); int repeat = 0; const int total = 3; while (++repeat <= total) { Console.WriteLine("Repeat #{0}...", repeat); try { // Let's create a self-signed certificate using the default policy. If the certificate // already exists in the Key Vault, then a new version of the key is created. string certName = $"defaultCert-{Guid.NewGuid()}"; CertificateOperation certOp = await client.StartCreateCertificateAsync(certName, CertificatePolicy.Default); // Next, let's wait on the certificate operation to complete. Note that certificate creation can last an indeterministic // amount of time, so applications should only wait on the operation to complete in the case the issuance time is well // known and within the scope of the application lifetime. In this case we are creating a self-signed certificate which // should be issued in a relatively short amount of time. Response <KeyVaultCertificateWithPolicy> certificateResponse = await certOp.WaitForCompletionAsync(); KeyVaultCertificateWithPolicy certificate = certificateResponse.Value; // At some time later we could get the created certificate along with its policy from the Key Vault. certificate = await client.GetCertificateAsync(certName); Console.WriteLine($"Certificate was returned with name {certificate.Name} which expires {certificate.Properties.ExpiresOn}"); // We find that the certificate has been compromised and we want to disable it so applications will no longer be able // to access the compromised version of the certificate. CertificateProperties certificateProperties = certificate.Properties; certificateProperties.Enabled = false; Response <KeyVaultCertificate> updatedCertResponse = await client.UpdateCertificatePropertiesAsync(certificateProperties); Console.WriteLine($"Certificate enabled set to '{updatedCertResponse.Value.Properties.Enabled}'"); // We need to create a new version of the certificate that applications can use to replace the compromised certificate. // Creating a certificate with the same name and policy as the compromised certificate will create another version of the // certificate with similar properties to the original certificate CertificateOperation newCertOp = await client.StartCreateCertificateAsync(certificate.Name, certificate.Policy); KeyVaultCertificateWithPolicy newCert = await newCertOp.WaitForCompletionAsync(); // The certificate is no longer needed, need to delete it from the Key Vault. DeleteCertificateOperation operation = await client.StartDeleteCertificateAsync(certName); // You only need to wait for completion if you want to purge or recover the certificate. await operation.WaitForCompletionAsync(); // If the keyvault is soft-delete enabled, then for permanent deletion, the deleted key needs to be purged. await client.PurgeDeletedCertificateAsync(certName); } catch (RequestFailedException ex) { Console.WriteLine($"Request failed! {ex.Message} {ex.StackTrace}"); return(-1); } catch (Exception ex) { Console.WriteLine($"Unexpected exception! {ex.Message} {ex.StackTrace}"); return(-1); } } Console.WriteLine("Success!"); return(0); }
private async Task MigrationGuide() { #region Snippet:Azure_Security_KeyVault_Certificates_Snippets_MigrationGuide_Create CertificateClient client = new CertificateClient( new Uri("https://myvault.vault.azure.net"), new DefaultAzureCredential()); #endregion Snippet:Azure_Security_KeyVault_Certificates_Snippets_MigrationGuide_Create #region Snippet:Azure_Security_KeyVault_Certificates_Snippets_MigrationGuide_CreateWithOptions using (HttpClient httpClient = new HttpClient()) { CertificateClientOptions options = new CertificateClientOptions { Transport = new HttpClientTransport(httpClient) }; //@@CertificateClient client = new CertificateClient( /*@@*/ CertificateClient _ = new CertificateClient( new Uri("https://myvault.vault.azure.net"), new DefaultAzureCredential(), options); } #endregion Snippet:Azure_Security_KeyVault_Certificates_Snippets_MigrationGuide_CreateWithOptions #region Snippet:Azure_Security_KeyVault_Certificates_Snippets_MigrationGuide_CreateCustomPolicy CertificatePolicy policy = new CertificatePolicy("issuer-name", "CN=customdomain.com") { ContentType = CertificateContentType.Pkcs12, KeyType = CertificateKeyType.Rsa, ReuseKey = true, KeyUsage = { CertificateKeyUsage.CrlSign, CertificateKeyUsage.DataEncipherment, CertificateKeyUsage.DigitalSignature, CertificateKeyUsage.KeyEncipherment, CertificateKeyUsage.KeyAgreement, CertificateKeyUsage.KeyCertSign }, ValidityInMonths = 12, LifetimeActions = { new LifetimeAction(CertificatePolicyAction.AutoRenew) { DaysBeforeExpiry = 90, } } }; #endregion Snippet:Azure_Security_KeyVault_Certificates_Snippets_MigrationGuide_CreateSelfSignedPolicy #region Snippet:Azure_Security_KeyVault_Certificates_Snippets_MigrationGuide_CreateSelfSignedPolicy //@@CertificatePolicy policy = CertificatePolicy.Default; /*@@*/ policy = CertificatePolicy.Default; #endregion Snippet:Azure_Security_KeyVault_Certificates_Snippets_MigrationGuide_CreateSelfSignedPolicy { #region Snippet:Azure_Security_KeyVault_Certificates_Snippets_MigrationGuide_CreateCertificate // Start certificate creation. // Depending on the policy and your business process, this could even take days for manual signing. CertificateOperation createOperation = await client.StartCreateCertificateAsync("certificate-name", policy); KeyVaultCertificateWithPolicy certificate = await createOperation.WaitForCompletionAsync(TimeSpan.FromSeconds(20), CancellationToken.None); // If you need to restart the application you can recreate the operation and continue awaiting. createOperation = new CertificateOperation(client, "certificate-name"); certificate = await createOperation.WaitForCompletionAsync(TimeSpan.FromSeconds(20), CancellationToken.None); #endregion Snippet:Azure_Security_KeyVault_Certificates_Snippets_MigrationGuide_CreateCertificate } { #region Snippet:Azure_Security_KeyVault_Certificates_Snippets_MigrationGuide_ImportCertificate byte[] cer = File.ReadAllBytes("certificate.pfx"); ImportCertificateOptions importCertificateOptions = new ImportCertificateOptions("certificate-name", cer) { Policy = policy }; KeyVaultCertificateWithPolicy certificate = await client.ImportCertificateAsync(importCertificateOptions); #endregion Snippet:Azure_Security_KeyVault_Certificates_Snippets_MigrationGuide_ImportCertificate } { #region Snippet:Azure_Security_KeyVault_Certificates_Snippets_MigrationGuide_ListCertificates // List all certificates asynchronously. await foreach (CertificateProperties item in client.GetPropertiesOfCertificatesAsync()) { KeyVaultCertificateWithPolicy certificate = await client.GetCertificateAsync(item.Name); } // List all certificates synchronously. foreach (CertificateProperties item in client.GetPropertiesOfCertificates()) { KeyVaultCertificateWithPolicy certificate = client.GetCertificate(item.Name); } #endregion Snippet:Azure_Security_KeyVault_Certificates_Snippets_MigrationGuide_ListCertificates } { #region Snippet:Azure_Security_KeyVault_Certificates_Snippets_MigrationGuide_DeleteCertificate // Delete the certificate. DeleteCertificateOperation deleteOperation = await client.StartDeleteCertificateAsync("certificate-name"); // Purge or recover the deleted certificate if soft delete is enabled. if (deleteOperation.Value.RecoveryId != null) { // Deleting a certificate does not happen immediately. Wait for the certificate to be deleted. DeletedCertificate deletedCertificate = await deleteOperation.WaitForCompletionAsync(); // Purge the deleted certificate. await client.PurgeDeletedCertificateAsync(deletedCertificate.Name); // You can also recover the deleted certificate using StartRecoverDeletedCertificateAsync, // which returns RecoverDeletedCertificateOperation you can await like DeleteCertificateOperation above. } #endregion Snippet:Azure_Security_KeyVault_Certificates_Snippets_MigrationGuide_DeleteCertificate } }
public void HelloWorldSync() { // Environment variable with the Key Vault endpoint. string keyVaultUrl = TestEnvironment.KeyVaultUrl; #region Snippet:CertificatesSample1CertificateClient var client = new CertificateClient(new Uri(keyVaultUrl), new DefaultAzureCredential()); #endregion #region Snippet:CertificatesSample1CreateCertificate string certName = $"defaultCert-{Guid.NewGuid()}"; CertificateOperation certOp = client.StartCreateCertificate(certName, CertificatePolicy.Default); while (!certOp.HasCompleted) { certOp.UpdateStatus(); Thread.Sleep(TimeSpan.FromSeconds(1)); } #endregion #region Snippet:CertificatesSample1GetCertificateWithPolicy KeyVaultCertificateWithPolicy certificate = client.GetCertificate(certName); Debug.WriteLine($"Certificate was returned with name {certificate.Name} which expires {certificate.Properties.ExpiresOn}"); #endregion #region Snippet:CertificatesSample1UpdateCertificate CertificateProperties certificateProperties = certificate.Properties; certificateProperties.Enabled = false; KeyVaultCertificate updatedCert = client.UpdateCertificateProperties(certificateProperties); Debug.WriteLine($"Certificate enabled set to '{updatedCert.Properties.Enabled}'"); #endregion #region Snippet:CertificatesSample1CreateCertificateWithNewVersion CertificateOperation newCertOp = client.StartCreateCertificate(certificate.Name, certificate.Policy); while (!newCertOp.HasCompleted) { newCertOp.UpdateStatus(); Thread.Sleep(TimeSpan.FromSeconds(1)); } #endregion #region Snippet:CertificatesSample1DeleteCertificate DeleteCertificateOperation operation = client.StartDeleteCertificate(certName); // You only need to wait for completion if you want to purge or recover the certificate. while (!operation.HasCompleted) { Thread.Sleep(2000); operation.UpdateStatus(); } #endregion // If the keyvault is soft-delete enabled, then for permanent deletion, the deleted certificate needs to be purged. client.PurgeDeletedCertificate(certName); }
public bool IsExpired(KeyVaultCertificateWithPolicy cert, TimeSpan buffer) => IsExpired(cert.Properties.ExpiresOn, buffer);
/// <summary> /// Проверка Expiration Date сертификата относительно текущей даты с запасом 20 дней /// (настраивается в конфигурации) /// </summary> public bool IsExpired(KeyVaultCertificateWithPolicy cert) => IsExpired(cert, TimeSpan.FromDays(settings.RenewalBeforeExpireDays));
public async Task <byte[]> GetCertificateBytesAsync(string certName) { KeyVaultCertificateWithPolicy certificateWithPolicy = await _certClient.GetCertificateAsync(certName); return(certificateWithPolicy.Cer); }
public CertificateInfo(KeyVaultCertificateWithPolicy certificate, ICertificateStore store) { _certificate = certificate ?? throw new ArgumentNullException(nameof(certificate)); Store = store ?? throw new ArgumentNullException(nameof(store)); }
/// <summary> /// Load a certificate from Key Vault, including the private key. /// </summary> /// <param name="keyVaultUrl">URL of Key Vault.</param> /// <param name="certificateName">Name of the certificate.</param> /// <param name="x509KeyStorageFlags">Defines where and how to import the private key of an X.509 certificate.</param> /// <returns>An <see cref="X509Certificate2"/> certificate.</returns> /// <remarks>This code is inspired by Heath Stewart's code in: /// https://github.com/heaths/azsdk-sample-getcert/blob/master/Program.cs#L46-L82. /// </remarks> private static X509Certificate2?LoadFromKeyVault( string keyVaultUrl, string certificateName, X509KeyStorageFlags x509KeyStorageFlags) { Uri keyVaultUri = new Uri(keyVaultUrl); DefaultAzureCredentialOptions options = new DefaultAzureCredentialOptions { ManagedIdentityClientId = UserAssignedManagedIdentityClientId, }; DefaultAzureCredential credential = new DefaultAzureCredential(options); CertificateClient certificateClient = new CertificateClient(keyVaultUri, credential); SecretClient secretClient = new SecretClient(keyVaultUri, credential); KeyVaultCertificateWithPolicy certificate = certificateClient.GetCertificate(certificateName); if (certificate.Properties.NotBefore == null || certificate.Properties.ExpiresOn == null) { return(null); } if (DateTimeOffset.UtcNow < certificate.Properties.NotBefore || DateTimeOffset.UtcNow > certificate.Properties.ExpiresOn) { return(null); } // Return a certificate with only the public key if the private key is not exportable. if (certificate.Policy?.Exportable != true) { return(new X509Certificate2( certificate.Cer, (string?)null, x509KeyStorageFlags)); } // Parse the secret ID and version to retrieve the private key. string[] segments = certificate.SecretId.AbsolutePath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); if (segments.Length != 3) { throw new InvalidOperationException(string.Format( CultureInfo.InvariantCulture, CertificateErrorMessage.IncorrectNumberOfUriSegments, segments.Length, certificate.SecretId)); } string secretName = segments[1]; string secretVersion = segments[2]; KeyVaultSecret secret = secretClient.GetSecret(secretName, secretVersion); // For PEM, you'll need to extract the base64-encoded message body. // .NET 5.0 preview introduces the System.Security.Cryptography.PemEncoding class to make this easier. if (CertificateConstants.MediaTypePksc12.Equals(secret.Properties.ContentType, StringComparison.OrdinalIgnoreCase)) { return(LoadFromBase64Encoded(secret.Value, x509KeyStorageFlags)); } throw new NotSupportedException( string.Format( CultureInfo.InvariantCulture, CertificateErrorMessage.OnlyPkcs12IsSupported, secret.Properties.ContentType)); }
/// <summary> /// Initializes a new instance of the type /// </summary> /// <param name="certificate">The key vault certificate</param> /// <param name="httpStatus">The http status code returned</param> /// <param name="elapsedMilliseconds">The number of milliseconds that elapsed executing the query</param> internal KeyVaultCertificateResponse(KeyVaultCertificateWithPolicy certificate, int httpStatus, long elapsedMilliseconds) : this((KeyVaultCertificate)certificate, httpStatus, elapsedMilliseconds) { Policy = certificate.Policy; }
public void HelloWorldSync() { // Environment variable with the Key Vault endpoint. string keyVaultUrl = Environment.GetEnvironmentVariable("AZURE_KEYVAULT_URL"); // Instantiate a certificate client that will be used to call the service. Notice that the client is using // default Azure credentials. To make default credentials work, ensure that environment variables 'AZURE_CLIENT_ID', // 'AZURE_CLIENT_KEY' and 'AZURE_TENANT_ID' are set with the service principal credentials. var client = new CertificateClient(new Uri(keyVaultUrl), new DefaultAzureCredential()); // Let's create a self signed certifiate using the default policy. If the certificiate // already exists in the Key Vault, then a new version of the key is created. string certName = $"defaultCert-{Guid.NewGuid()}"; CertificateOperation certOp = client.StartCreateCertificate(certName, CertificatePolicy.Default); // Next let's wait on the certificate operation to complete. Note that certificate creation can last an indeterministic // amount of time, so applications should only wait on the operation to complete in the case the issuance time is well // known and within the scope of the application lifetime. In this case we are creating a self-signed certificate which // should be issued in a relatively short amount of time. while (!certOp.HasCompleted) { certOp.UpdateStatus(); Thread.Sleep(TimeSpan.FromSeconds(1)); } // Let's get the created certificate along with it's policy from the Key Vault. KeyVaultCertificateWithPolicy certificate = client.GetCertificate(certName); Debug.WriteLine($"Certificate was returned with name {certificate.Name} which expires {certificate.Properties.ExpiresOn}"); // We find that the certificate has been compromised and we want to disable it so applications will no longer be able // to access the compromised version of the certificate. CertificateProperties certificateProperties = certificate.Properties; certificateProperties.Enabled = false; KeyVaultCertificate updatedCert = client.UpdateCertificateProperties(certificateProperties); Debug.WriteLine($"Certificate enabled set to '{updatedCert.Properties.Enabled}'"); // We need to create a new version of the certificate that applications can use to replace the compromised certificate. // Creating a certificate with the same name and policy as the compromised certificate will create another version of the // certificate with similar properties to the original certificate CertificateOperation newCertOp = client.StartCreateCertificate(certificate.Name, certificate.Policy); while (!newCertOp.HasCompleted) { newCertOp.UpdateStatus(); Thread.Sleep(TimeSpan.FromSeconds(1)); } // The certificate is no longer needed, need to delete it from the Key Vault. client.DeleteCertificate(certName); // To ensure certificate is deleted on server side. Assert.IsTrue(WaitForDeletedCertificate(client, certName)); // If the keyvault is soft-delete enabled, then for permanent deletion, deleted certificate needs to be purged. client.PurgeDeletedCertificate(certName); }