/* * MinIO .NET Library for Amazon S3 Compatible Cloud Storage, * (C) 2022 MinIO, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System.Globalization; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Web; using Minio.DataModel; using Minio.Exceptions; using Minio.Helper; #if (NET472_OR_GREATER || NET6_0_OR_GREATER) using System.Security.Authentication; #else using System.Net; #endif /* * Certificate Identity Credential provider. * This is a MinIO Extension to AssumeRole STS APIs on * AWS, purely based on client certificates mTLS authentication. */ namespace Minio.Credentials; public class CertificateIdentityProvider : IClientProvider { private readonly int defaultDurationInSeconds = 3600; public CertificateIdentityProvider() { DurationInSeconds = defaultDurationInSeconds; } internal string StsEndpoint { get; set; } internal int DurationInSeconds { get; set; } internal X509Certificate2 ClientCertificate { get; set; } internal Uri PostEndpoint { get; set; } internal HttpClient HttpClient { get; set; } internal AccessCredentials Credentials { get; set; } public AccessCredentials GetCredentials() { return GetCredentialsAsync().AsTask().ConfigureAwait(false).GetAwaiter().GetResult(); } public async ValueTask GetCredentialsAsync() { if (Credentials?.AreExpired() == false) return Credentials; if (HttpClient is null) throw new InvalidOperationException(nameof(HttpClient) + " cannot be null or empty"); if (ClientCertificate is null) throw new InvalidOperationException(nameof(ClientCertificate) + " cannot be null or empty"); using var response = await HttpClient.PostAsync(PostEndpoint, null).ConfigureAwait(false); var certResponse = new CertificateResponse(); if (response.IsSuccessStatusCode) { var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); using var stream = new MemoryStream(Encoding.UTF8.GetBytes(content).AsMemory().ToArray()); certResponse = Utils.DeserializeXml(stream); } if (Credentials is null && certResponse?.Cr is not null) Credentials = certResponse.Cr.Credentials; return Credentials; } public CertificateIdentityProvider WithStsEndpoint(string stsEndpoint) { if (string.IsNullOrEmpty(stsEndpoint)) throw new InvalidEndpointException("Missing mandatory argument: stsEndpoint"); if (!stsEndpoint.StartsWith("https", StringComparison.OrdinalIgnoreCase)) throw new InvalidEndpointException($"stsEndpoint {stsEndpoint} is invalid." + " The scheme must be https"); StsEndpoint = stsEndpoint; return this; } public CertificateIdentityProvider WithHttpClient(HttpClient httpClient = null) { HttpClient = httpClient; return this; } public CertificateIdentityProvider WithCertificate(X509Certificate2 cert = null) { ClientCertificate = cert; return this; } public CertificateIdentityProvider Build() { if (string.IsNullOrEmpty(DurationInSeconds.ToString(CultureInfo.InvariantCulture))) DurationInSeconds = defaultDurationInSeconds; var builder = new UriBuilder(StsEndpoint); var query = HttpUtility.ParseQueryString(builder.Query); query["Action"] = "AssumeRoleWithCertificate"; query["Version"] = "2011-06-15"; query["DurationInSeconds"] = DurationInSeconds.ToString(CultureInfo.InvariantCulture); builder.Query = query.ToString(); PostEndpoint = builder.Uri; var handler = new HttpClientHandler { ClientCertificateOptions = ClientCertificateOption.Manual }; #if (NET472_OR_GREATER || NET6_0_OR_GREATER) handler.SslProtocols = SslProtocols.Tls12; #else ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11; #endif _ = handler.ClientCertificates.Add(ClientCertificate); HttpClient ??= new HttpClient(handler) { BaseAddress = new Uri(StsEndpoint) }; Credentials = GetCredentials(); return this; } }