Kit.Core/LibExternal/Minio/Credentials/CertificateIdentityProvider.cs

137 lines
4.7 KiB
C#

/*
* 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<AccessCredentials> 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<CertificateResponse>(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;
}
}