diff --git a/.github/images/AzureApp-advanced-store-type-dialog.png b/.github/images/AzureApp-advanced-store-type-dialog.png deleted file mode 100644 index c35554a..0000000 Binary files a/.github/images/AzureApp-advanced-store-type-dialog.png and /dev/null differ diff --git a/.github/images/AzureApp-basic-store-type-dialog.png b/.github/images/AzureApp-basic-store-type-dialog.png deleted file mode 100644 index 6fabcfc..0000000 Binary files a/.github/images/AzureApp-basic-store-type-dialog.png and /dev/null differ diff --git a/.github/images/AzureApp-custom-fields-store-type-dialog.png b/.github/images/AzureApp-custom-fields-store-type-dialog.png deleted file mode 100644 index b6fed6f..0000000 Binary files a/.github/images/AzureApp-custom-fields-store-type-dialog.png and /dev/null differ diff --git a/.github/images/AzureSP-advanced-store-type-dialog.png b/.github/images/AzureSP-advanced-store-type-dialog.png deleted file mode 100644 index 83c9024..0000000 Binary files a/.github/images/AzureSP-advanced-store-type-dialog.png and /dev/null differ diff --git a/.github/images/AzureSP-basic-store-type-dialog.png b/.github/images/AzureSP-basic-store-type-dialog.png deleted file mode 100644 index 7a58676..0000000 Binary files a/.github/images/AzureSP-basic-store-type-dialog.png and /dev/null differ diff --git a/.github/images/AzureSP-custom-fields-store-type-dialog.png b/.github/images/AzureSP-custom-fields-store-type-dialog.png deleted file mode 100644 index b6fed6f..0000000 Binary files a/.github/images/AzureSP-custom-fields-store-type-dialog.png and /dev/null differ diff --git a/AzureEnterpriseApplicationOrchestrator.Tests/AzureEnterpriseApplicationOrchestrator.Tests.csproj b/AzureEnterpriseApplicationOrchestrator.Tests/AzureEnterpriseApplicationOrchestrator.Tests.csproj index 26164ed..ff06551 100644 --- a/AzureEnterpriseApplicationOrchestrator.Tests/AzureEnterpriseApplicationOrchestrator.Tests.csproj +++ b/AzureEnterpriseApplicationOrchestrator.Tests/AzureEnterpriseApplicationOrchestrator.Tests.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 enable enable diff --git a/AzureEnterpriseApplicationOrchestrator.Tests/Client.cs b/AzureEnterpriseApplicationOrchestrator.Tests/Client.cs index e2fb0f8..18db671 100644 --- a/AzureEnterpriseApplicationOrchestrator.Tests/Client.cs +++ b/AzureEnterpriseApplicationOrchestrator.Tests/Client.cs @@ -28,8 +28,10 @@ public AzureEnterpriseApplicationOrchestrator_Client() ConfigureLogging(); } - [IntegrationTestingFact] - public void GraphClient_Application_AddGetRemove_ReturnSuccess() + [IntegrationTestingTheory] + [InlineData("clientcert")] + [InlineData("clientsecret")] + public void GraphClient_Application_AddGetRemove_ReturnSuccess(string testAuthMethod) { // Arrange string certName = "AppTest" + Guid.NewGuid().ToString()[..6]; @@ -37,13 +39,22 @@ public void GraphClient_Application_AddGetRemove_ReturnSuccess() string b64Cert = Convert.ToBase64String(ssCert.Export(X509ContentType.Cert)); IntegrationTestingFact env = new(); - - IAzureGraphClient client = new GraphClient.Builder() + IAzureGraphClientBuilder clientBuilder = new GraphClient.Builder() .WithTenantId(env.TenantId) .WithApplicationId(env.ApplicationId) - .WithClientSecret(env.ClientSecret) - .WithTargetApplicationId(env.TargetApplicationId) - .Build(); + .WithTargetApplicationId(env.TargetApplicationId); + + if (testAuthMethod == "clientcert") + { + clientBuilder.WithClientSecret(env.ClientSecret); + } + else + { + var cert = X509Certificate2.CreateFromPemFile(env.ClientCertificatePath); + clientBuilder.WithClientCertificate(cert); + } + + IAzureGraphClient client = clientBuilder.Build(); // Step 1 - Add the certificate to the Application @@ -89,8 +100,10 @@ public void GraphClient_Application_AddGetRemove_ReturnSuccess() Assert.False(exists); } - [IntegrationTestingFact] - public void GraphClient_ServicePrincipal_AddGetRemove_ReturnSuccess() + [IntegrationTestingTheory] + [InlineData("clientcert")] + [InlineData("clientsecret")] + public void GraphClient_ServicePrincipal_AddGetRemove_ReturnSuccess(string testAuthMethod) { // Arrange const string password = "passwordpasswordpassword"; @@ -99,13 +112,22 @@ public void GraphClient_ServicePrincipal_AddGetRemove_ReturnSuccess() string b64PfxSslCert = Convert.ToBase64String(ssCert.Export(X509ContentType.Pfx, password)); IntegrationTestingFact env = new(); - - IAzureGraphClient client = new GraphClient.Builder() + IAzureGraphClientBuilder clientBuilder = new GraphClient.Builder() .WithTenantId(env.TenantId) .WithApplicationId(env.ApplicationId) - .WithClientSecret(env.ClientSecret) - .WithTargetApplicationId(env.TargetApplicationId) - .Build(); + .WithTargetApplicationId(env.TargetApplicationId); + + if (testAuthMethod == "clientcert") + { + clientBuilder.WithClientSecret(env.ClientSecret); + } + else + { + var cert = X509Certificate2.CreateFromPemFile(env.ClientCertificatePath); + clientBuilder.WithClientCertificate(cert); + } + + IAzureGraphClient client = clientBuilder.Build(); // Step 1 - Add the certificate to the Service Principal (and set it as the preferred SAML signing certificate) @@ -152,8 +174,10 @@ public void GraphClient_ServicePrincipal_AddGetRemove_ReturnSuccess() Assert.False(exists); } - [IntegrationTestingFact] - public void GraphClient_DiscoverApplicationIds_ReturnSuccess() + [IntegrationTestingTheory] + [InlineData("clientcert")] + [InlineData("clientsecret")] + public void GraphClient_DiscoverApplicationIds_ReturnSuccess(string testAuthMethod) { // Arrange const string password = "passwordpasswordpassword"; @@ -162,13 +186,22 @@ public void GraphClient_DiscoverApplicationIds_ReturnSuccess() string b64PfxSslCert = Convert.ToBase64String(ssCert.Export(X509ContentType.Pfx, password)); IntegrationTestingFact env = new(); - - IAzureGraphClient client = new GraphClient.Builder() + IAzureGraphClientBuilder clientBuilder = new GraphClient.Builder() .WithTenantId(env.TenantId) .WithApplicationId(env.ApplicationId) - .WithClientSecret(env.ClientSecret) - .WithTargetApplicationId(env.TargetApplicationId) - .Build(); + .WithTargetApplicationId(env.TargetApplicationId); + + if (testAuthMethod == "clientcert") + { + clientBuilder.WithClientSecret(env.ClientSecret); + } + else + { + var cert = X509Certificate2.CreateFromPemFile(env.ClientCertificatePath); + clientBuilder.WithClientCertificate(cert); + } + + IAzureGraphClient client = clientBuilder.Build(); // Act OperationResult> operationResult = client.DiscoverApplicationIds(); diff --git a/AzureEnterpriseApplicationOrchestrator.Tests/FakeClient.cs b/AzureEnterpriseApplicationOrchestrator.Tests/FakeClient.cs index ec28ed3..e8be1d1 100644 --- a/AzureEnterpriseApplicationOrchestrator.Tests/FakeClient.cs +++ b/AzureEnterpriseApplicationOrchestrator.Tests/FakeClient.cs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System.Security.Cryptography.X509Certificates; using AzureEnterpriseApplicationOrchestrator.Client; using Keyfactor.Logging; using Keyfactor.Orchestrators.Common.Enums; @@ -31,6 +32,7 @@ public class FakeBuilder : IAzureGraphClientBuilder public string? _targetApplicationId { get; set; } public string? _applicationId { get; set; } public string? _clientSecret { get; set; } + public X509Certificate2? _clientCertificate { get; set; } public string? _azureCloudEndpoint { get; set; } public IAzureGraphClientBuilder WithTenantId(string tenantId) @@ -57,6 +59,12 @@ public IAzureGraphClientBuilder WithClientSecret(string clientSecret) return this; } + public IAzureGraphClientBuilder WithClientCertificate(X509Certificate2 clientCertificate) + { + _clientCertificate = clientCertificate; + return this; + } + public IAzureGraphClientBuilder WithAzureCloud(string azureCloud) { _azureCloudEndpoint = azureCloud; diff --git a/AzureEnterpriseApplicationOrchestrator.Tests/IntegrationTestingFact.cs b/AzureEnterpriseApplicationOrchestrator.Tests/IntegrationTestingFact.cs index 6bdd65d..ee6e960 100644 --- a/AzureEnterpriseApplicationOrchestrator.Tests/IntegrationTestingFact.cs +++ b/AzureEnterpriseApplicationOrchestrator.Tests/IntegrationTestingFact.cs @@ -19,6 +19,7 @@ public sealed class IntegrationTestingFact : FactAttribute public string TenantId { get; private set; } public string ApplicationId { get; private set; } public string ClientSecret { get; private set; } + public string ClientCertificatePath { get; private set; } public string TargetApplicationId { get; private set; } @@ -27,6 +28,7 @@ public IntegrationTestingFact() TenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID") ?? string.Empty; ApplicationId = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID") ?? string.Empty; ClientSecret = Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET") ?? string.Empty; + ClientCertificatePath = Environment.GetEnvironmentVariable("AZURE_PATH_TO_CLIENT_CERTIFICATE") ?? string.Empty; TargetApplicationId = Environment.GetEnvironmentVariable("AZURE_TARGET_APPLICATION_ID") ?? string.Empty; @@ -37,3 +39,27 @@ public IntegrationTestingFact() } } +public sealed class IntegrationTestingTheory : TheoryAttribute +{ + public string TenantId { get; private set; } + public string ApplicationId { get; private set; } + public string ClientSecret { get; private set; } + public string ClientCertificatePath { get; private set; } + + public string TargetApplicationId { get; private set; } + + public IntegrationTestingTheory() + { + TenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID") ?? string.Empty; + ApplicationId = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID") ?? string.Empty; + ClientSecret = Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET") ?? string.Empty; + ClientCertificatePath = Environment.GetEnvironmentVariable("AZURE_PATH_TO_CLIENT_CERTIFICATE") ?? string.Empty; + + TargetApplicationId = Environment.GetEnvironmentVariable("AZURE_TARGET_APPLICATION_ID") ?? string.Empty; + + if (string.IsNullOrEmpty(TenantId) || string.IsNullOrEmpty(ApplicationId) || string.IsNullOrEmpty(ClientSecret) || string.IsNullOrEmpty(TargetApplicationId)) + { + Skip = "Integration testing environment variables are not set - Skipping test. Please run `make setup` to set the environment variables."; + } + } +} diff --git a/AzureEnterpriseApplicationOrchestrator.Tests/JobClientBuilder.cs b/AzureEnterpriseApplicationOrchestrator.Tests/JobClientBuilder.cs index 2f6eb88..cad3f39 100644 --- a/AzureEnterpriseApplicationOrchestrator.Tests/JobClientBuilder.cs +++ b/AzureEnterpriseApplicationOrchestrator.Tests/JobClientBuilder.cs @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; using AzureEnterpriseApplicationOrchestrator; using AzureEnterpriseApplicationOrchestrator.Client; using AzureEnterpriseApplicationOrchestrator.Tests; @@ -32,7 +35,7 @@ public AzureEnterpriseApplicationOrchestrator_JobClientBuilder() } [Fact] - public void GraphJobClientBuilder_ValidCertificateStoreConfig_BuildValidClient() + public void GraphJobClientBuilder_ValidCertificateStoreConfigWithClientSecret_BuildValidClient() { // Verify that the GraphJobClientBuilder uses the certificate store configuration // provided by Keyfactor Command/the Universal Orchestrator correctly as required @@ -68,6 +71,93 @@ public void GraphJobClientBuilder_ValidCertificateStoreConfig_BuildValidClient() _logger.LogInformation("GraphJobClientBuilder_ValidCertificateStoreConfig_BuildValidClient - Success"); } + + [IntegrationTestingTheory] + [InlineData("pkcs12")] + [InlineData("pem")] + [InlineData("encryptedPem")] + public void GraphJobClientBuilder_ValidCertificateStoreConfigWithClientCertificate_BuildValidClient(string certificateFormat) + { + // Verify that the GraphJobClientBuilder uses the certificate store configuration + // provided by Keyfactor Command/the Universal Orchestrator correctly as required + // by the IAzureGraphClientBuilder interface. + + // Arrange + GraphJobClientBuilder jobClientBuilderWithFakeBuilder = new(); + + string password = "passwordpasswordpassword"; + string certName = "SPTest" + Guid.NewGuid().ToString()[..6]; + X509Certificate2 ssCert = GetSelfSignedCert(certName); + + string b64ClientCertificate; + if (certificateFormat == "pkcs12") + { + b64ClientCertificate = Convert.ToBase64String(ssCert.Export(X509ContentType.Pfx, password)); + } + else if (certificateFormat == "pem") + { + string pemCert = ssCert.ExportCertificatePem(); + string keyPem = ssCert.GetRSAPrivateKey()!.ExportPkcs8PrivateKeyPem(); + b64ClientCertificate = Convert.ToBase64String(Encoding.UTF8.GetBytes(keyPem + '\n' + pemCert)); + password = ""; + } + else + { + PbeParameters pbeParameters = new PbeParameters( + PbeEncryptionAlgorithm.Aes256Cbc, + HashAlgorithmName.SHA384, + 300_000); + string pemCert = ssCert.ExportCertificatePem(); + string keyPem = ssCert.GetRSAPrivateKey()!.ExportEncryptedPkcs8PrivateKeyPem(password.ToCharArray(), pbeParameters); + b64ClientCertificate = Convert.ToBase64String(Encoding.UTF8.GetBytes(keyPem + '\n' + pemCert)); + } + + // Set up the certificate store with names that correspond to how we expect them to be interpreted by + // the builder + CertificateStore fakeCertificateStoreDetails = new() + { + ClientMachine = "fake-tenant-id", + StorePath = "fake-azure-target-application-id", + Properties = $@"{{""ServerUsername"": ""fake-azure-application-id"",""ServerPassword"": ""{password}"",""ClientCertificate"": ""{b64ClientCertificate}"",""AzureCloud"": ""fake-azure-cloud""}}" + }; + + // Act + IAzureGraphClient fakeAppGatewayClient = jobClientBuilderWithFakeBuilder + .WithCertificateStoreDetails(fakeCertificateStoreDetails) + .Build(); + + // Assert + + // IAzureGraphClient doesn't require any of the properties set by the builder to be exposed + // since the production Build() method creates an Azure Resource Manager client. + // But, our builder is fake and exposes the properties we need to test (via the FakeBuilder class). + Assert.Equal("fake-tenant-id", jobClientBuilderWithFakeBuilder._builder._tenantId); + Assert.Equal("fake-azure-target-application-id", jobClientBuilderWithFakeBuilder._builder._targetApplicationId); + Assert.Equal("fake-azure-application-id", jobClientBuilderWithFakeBuilder._builder._applicationId); + Assert.Equal("fake-azure-cloud", jobClientBuilderWithFakeBuilder._builder._azureCloudEndpoint); + Assert.Equal(ssCert.GetCertHash(), jobClientBuilderWithFakeBuilder._builder._clientCertificate!.GetCertHash()); + Assert.NotNull(jobClientBuilderWithFakeBuilder._builder._clientCertificate!.GetRSAPrivateKey()); + Assert.Equal(jobClientBuilderWithFakeBuilder._builder._clientCertificate!.GetRSAPrivateKey()!.ExportRSAPrivateKeyPem(), ssCert.GetRSAPrivateKey()!.ExportRSAPrivateKeyPem()); + + _logger.LogInformation("GraphJobClientBuilder_ValidCertificateStoreConfig_BuildValidClient - Success"); + } + + public static X509Certificate2 GetSelfSignedCert(string hostname) + { + RSA rsa = RSA.Create(2048); + CertificateRequest req = new CertificateRequest($"CN={hostname}", rsa, HashAlgorithmName.SHA256, + RSASignaturePadding.Pkcs1); + + SubjectAlternativeNameBuilder subjectAlternativeNameBuilder = new SubjectAlternativeNameBuilder(); + subjectAlternativeNameBuilder.AddDnsName(hostname); + req.CertificateExtensions.Add(subjectAlternativeNameBuilder.Build()); + req.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false)); + req.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("2.5.29.32.0"), new Oid("1.3.6.1.5.5.7.3.1") }, false)); + + X509Certificate2 selfSignedCert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(5)); + Console.Write($"Created self-signed certificate for \"{hostname}\" with thumbprint {selfSignedCert.Thumbprint}\n"); + return selfSignedCert; + } static void ConfigureLogging() { diff --git a/AzureEnterpriseApplicationOrchestrator/AzureEnterpriseApplicationOrchestrator.csproj b/AzureEnterpriseApplicationOrchestrator/AzureEnterpriseApplicationOrchestrator.csproj index be0d3c2..9ffa756 100644 --- a/AzureEnterpriseApplicationOrchestrator/AzureEnterpriseApplicationOrchestrator.csproj +++ b/AzureEnterpriseApplicationOrchestrator/AzureEnterpriseApplicationOrchestrator.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 disable false false @@ -10,16 +10,16 @@ - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/AzureEnterpriseApplicationOrchestrator/Client/GraphClient.cs b/AzureEnterpriseApplicationOrchestrator/Client/GraphClient.cs index 310ead8..73b3b78 100644 --- a/AzureEnterpriseApplicationOrchestrator/Client/GraphClient.cs +++ b/AzureEnterpriseApplicationOrchestrator/Client/GraphClient.cs @@ -64,6 +64,7 @@ public class Builder : IAzureGraphClientBuilder private string _tenantId { get; set; } private string _applicationId { get; set; } private string _clientSecret { get; set; } + private X509Certificate2 _clientCertificate { get; set; } private string _targetApplicationId { get; set; } private Uri _azureCloudEndpoint { get; set; } @@ -91,6 +92,12 @@ public IAzureGraphClientBuilder WithClientSecret(string clientSecret) return this; } + public IAzureGraphClientBuilder WithClientCertificate(X509Certificate2 clientCertificate) + { + _clientCertificate = clientCertificate; + return this; + } + public IAzureGraphClientBuilder WithAzureCloud(string azureCloud) { if (string.IsNullOrWhiteSpace(azureCloud)) @@ -129,9 +136,24 @@ public IAzureGraphClient Build() AdditionallyAllowedTenants = { "*" } }; - TokenCredential credential = new ClientSecretCredential( - _tenantId, _applicationId, _clientSecret, credentialOptions - ); + TokenCredential credential; + if (!string.IsNullOrWhiteSpace(_clientSecret)) + { + credential = new ClientSecretCredential( + _tenantId, _applicationId, _clientSecret, credentialOptions + ); + } + else if (_clientCertificate != null) + { + credential = new ClientCertificateCredential( + _tenantId, _applicationId, _clientCertificate, credentialOptions + ); + } + else + { + throw new Exception("Client secret or client certificate must be provided."); + } + string[] scopes = { "https://graph.microsoft.com/.default" }; diff --git a/AzureEnterpriseApplicationOrchestrator/Client/IAzureGraphClient.cs b/AzureEnterpriseApplicationOrchestrator/Client/IAzureGraphClient.cs index 6b73c03..2043c67 100644 --- a/AzureEnterpriseApplicationOrchestrator/Client/IAzureGraphClient.cs +++ b/AzureEnterpriseApplicationOrchestrator/Client/IAzureGraphClient.cs @@ -13,6 +13,7 @@ // limitations under the License. using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; using Keyfactor.Orchestrators.Extensions; namespace AzureEnterpriseApplicationOrchestrator.Client; @@ -23,6 +24,7 @@ public interface IAzureGraphClientBuilder public IAzureGraphClientBuilder WithTargetApplicationId(string applicationId); public IAzureGraphClientBuilder WithApplicationId(string applicationId); public IAzureGraphClientBuilder WithClientSecret(string clientSecret); + public IAzureGraphClientBuilder WithClientCertificate(X509Certificate2 clientCertificate); public IAzureGraphClientBuilder WithAzureCloud(string azureCloud); public IAzureGraphClient Build(); } diff --git a/AzureEnterpriseApplicationOrchestrator/GraphJobClientBuilder.cs b/AzureEnterpriseApplicationOrchestrator/GraphJobClientBuilder.cs index dbe2e39..3474155 100644 --- a/AzureEnterpriseApplicationOrchestrator/GraphJobClientBuilder.cs +++ b/AzureEnterpriseApplicationOrchestrator/GraphJobClientBuilder.cs @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; using AzureEnterpriseApplicationOrchestrator.Client; using Keyfactor.Logging; using Keyfactor.Orchestrators.Extensions; @@ -25,11 +29,12 @@ namespace AzureEnterpriseApplicationOrchestrator; public TBuilder _builder = new TBuilder(); private ILogger _logger = LogHandler.GetClassLogger>(); - public class CertificateStoreProperties + public record CertificateStoreProperties { - public string ServerUsername { get; set; } - public string ServerPassword { get; set; } - public string AzureCloud { get; set; } + public string ServerUsername { get; init; } + public string ServerPassword { get; init; } + public string ClientCertificate { get; init; } + public string AzureCloud { get; init; } } public GraphJobClientBuilder WithCertificateStoreDetails(CertificateStore details) @@ -41,16 +46,29 @@ public GraphJobClientBuilder WithCertificateStoreDetails(CertificateSt _logger.LogTrace($"Builder - ClientMachine => TenantId: {details.ClientMachine}"); _logger.LogTrace($"Builder - StorePath => TargetApplicationId: {details.StorePath}"); _logger.LogTrace($"Builder - ServerUsername => ApplicationId: {properties.ServerUsername}"); - _logger.LogTrace($"Builder - ServerPassword => ClientSecret: {properties.ServerPassword}"); _logger.LogTrace($"Builder - AzureCloud => AzureCloud: {properties.AzureCloud}"); - + _builder .WithTenantId(details.ClientMachine) .WithApplicationId(properties.ServerUsername) - .WithClientSecret(properties.ServerPassword) .WithTargetApplicationId(details.StorePath) .WithAzureCloud(properties.AzureCloud); + if (string.IsNullOrWhiteSpace(properties.ClientCertificate)) + { + _logger.LogDebug("Client certificate not present - Using Client Secret authentication"); + _logger.LogTrace($"Builder - ServerPassword => ClientSecret: {properties.ServerPassword}"); + _builder.WithClientSecret(properties.ServerPassword); + } + else + { + _logger.LogDebug("Client certificate present - Using Client Certificate authentication"); + _logger.LogTrace($"Builder - ServerPassword => ClientCertificateKeyPassword: {properties.ServerPassword}"); + X509Certificate2 clientCert = SerializeClientCertificate(properties.ClientCertificate, properties.ServerPassword); + _builder.WithClientCertificate(clientCert); + } + + return this; } @@ -72,4 +90,98 @@ public IAzureGraphClient Build() { return _builder.Build(); } + + private X509Certificate2 SerializeClientCertificate(string clientCertificate, string password) + { + // clientCertificate is a Base64 encoded certificate that's either PEM or PKCS#12 encoded. + // We expect that it includes a private key compatible with the dotnet standard crypto libraries. + + byte[] rawCertBytes = Convert.FromBase64String(clientCertificate); + X509Certificate2 serializedCertificate = null; + + // Try to serialize the certificate without any special handling + try + { + serializedCertificate = new X509Certificate2(rawCertBytes, password, X509KeyStorageFlags.Exportable); + if (serializedCertificate.HasPrivateKey) { + _logger.LogTrace("Successfully serialized certificate using standard X509Certificate2"); + return serializedCertificate; + } + } + catch (CryptographicException e) + { + _logger.LogDebug($"Couldn't serialize certificate using X509Certificate2: {e.Message} - trying to serialize from PEM"); + } + + try + { + return SerializePemCertificateAndKey(clientCertificate, password); + } + catch (Exception e) + { + string message = $"Couldn't serialize certificate as PEM: {e.Message} - please ensure that the certificate is valid."; + _logger.LogError(message); + throw new CryptographicException(message); + } + } + + private X509Certificate2 SerializePemCertificateAndKey(string clientCertificate, string password) + { + _logger.LogDebug($"Attempting to serialize client certificate and private key from PEM encoding"); + ReadOnlySpan utf8Cert = Encoding.UTF8.GetChars(Convert.FromBase64String(clientCertificate)); + + _logger.LogTrace("Finding all PEM objects in ClientCertificate"); + + ReadOnlySpan certificate = new char[0]; + ReadOnlySpan key = new char[0]; + + int numberOfPemObjects = 0; + + while (PemEncoding.TryFind(utf8Cert, out PemFields field)) + { + numberOfPemObjects++; + string label = utf8Cert[field.Label].ToString(); + _logger.LogTrace($"Found PEM object with label {label} at location {field.Location}"); + + if (label == "CERTIFICATE") + { + _logger.LogTrace($"Storing {label} as certificate for serialization"); + certificate = utf8Cert[field.Location]; + } + else + { + _logger.LogTrace($"Storing {label} as private key for serialization"); + key = utf8Cert[field.Location]; + } + + // Reconstruct utf8Cert without the PEM object + Range objectRange = field.Location; + int start = objectRange.Start.Value; + int end = objectRange.End.Value; + char[] newUtf8Cert = new char[utf8Cert.Length - (end - start)]; + + _logger.LogTrace($"Trimming range {field.Location} [{end - start} bytes]"); + // Copy over the slice before the start of the range + utf8Cert.Slice(0, start).CopyTo(newUtf8Cert); + // Copy over the slice after the end of the range + utf8Cert.Slice(end).CopyTo(newUtf8Cert.AsSpan(start)); + + utf8Cert = newUtf8Cert; + } + + if (numberOfPemObjects != 2) + { + throw new CryptographicException($"Expected 2 PEM objects in ClientCertificate, found {numberOfPemObjects}"); + } + + _logger.LogDebug("Successfully extracted certificate and private key from PEM encoding - serializing certificate"); + if (string.IsNullOrEmpty(password)) + { + return X509Certificate2.CreateFromPem(certificate, key); + } + else + { + return X509Certificate2.CreateFromEncryptedPem(certificate, key, password); + } + } } diff --git a/CHANGELOG.md b/CHANGELOG.md index dd0d904..a9d0ac1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,3 +4,9 @@ - 2.0.0 - Properly report Private Key existence in Inventory job for both AzureSP and AzureApp. - Remove Private Key handling from AzureApp Certificate Store Type + +- 3.0.0 + - Implement client certificate authentication as a secondary authentication method to Microsoft Graph API + +- 3.1.0 + - fix(deps): Revert main Azure App Registration and Enterprise Application Orchestrator extension .NET project to .NET 6 from .NET 8. diff --git a/README.md b/README.md index d2c8e49..fdb22b2 100644 --- a/README.md +++ b/README.md @@ -48,34 +48,246 @@ The Keyfactor Universal Orchestrator may be installed on either Windows or Linux --- +

+ Azure App Registration and Enterprise Application Universal Orchestrator Extension +

+ +

+ +Integration Status: production +Release +Issues +GitHub Downloads (all assets, all releases) +

+ +

+ + + Support + + · + + Installation + + · + + License + + · + + Related Integrations + +

+ + ## Overview + The Azure App Registration and Enterprise Application Orchestrator extension remotely manages both Azure [App Registration/Application](https://learn.microsoft.com/en-us/entra/identity-platform/certificate-credentials) certificates and [Enterprise Application/Service Principal](https://docs.microsoft.com/en-us/azure/active-directory/develop/enterprise-apps-certificate-credentials) certificates. Application certificates are typically public key only and used for client certificate authentication, while Service Principal certificates are commonly used for [SAML Assertion signing](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/tutorial-manage-certificates-for-federated-single-sign-on). The extension implements the Inventory, Management Add, Management Remove, and Discovery job types. -Certificates used for client authentication by Applications (configured in App Registrations) are represented by the [`AzureApp` store type](docs/azureapp.md), and certificates used for SSO/SAML assertion signing are represented by the [`AzureSP` store type](docs/azuresp.md). Both store types are managed by the same extension. The extension is configured with a single Azure Service Principal that is used to authenticate to the [Microsoft Graph API](https://learn.microsoft.com/en-us/graph/use-the-api). The Azure App Registration and Enterprise Application Orchestrator extension manages certificates for Azure App Registrations (Applications) and Enterprise Applications (Service Principals) differently. +Certificates used for client authentication by Applications (configured in App Registrations) are represented by the [`AzureApp` store type](docs/azureapp.md), and certificates used for SSO/SAML assertion signing are represented by the [`AzureSP` store type](docs/azuresp.md). Both store types are managed by the same extension. The extension is configured with a single Azure Service Principal that is used to authenticate to the [Microsoft Graph API](https://learn.microsoft.com/en-us/graph/use-the-api). The Azure App Registration and Enterprise Application Orchestrator extension manages certificates for Azure App Registrations (Applications) and Enterprise Applications (Service Principals) differently. ## Installation -Before installing the Azure App Registration and Enterprise Application Orchestrator extension, it's recommended to install [kfutil](https://github.com/Keyfactor/kfutil). Kfutil is a command-line tool that simplifies the process of creating store types, installing extensions, and instantiating certificate stores in Keyfactor Command. +Before installing the Azure App Registration and Enterprise Application Universal Orchestrator extension, it's recommended to install [kfutil](https://github.com/Keyfactor/kfutil). Kfutil is a command-line tool that simplifies the process of creating store types, installing extensions, and instantiating certificate stores in Keyfactor Command. + +The Azure App Registration and Enterprise Application Universal Orchestrator extension implements 2 Certificate Store Types. Depending on your use case, you may elect to install one, or all of these Certificate Store Types. An overview for each type is linked below: +* [Azure App Registration (Application)](docs/azureapp.md) +* [Azure Enterprise Application (Service Principal)](docs/azuresp.md) + +
Azure App Registration (Application) + + +1. Follow the [requirements section](docs/azureapp.md#requirements) to configure a Service Account and grant necessary API permissions. + +
Requirements + + ### Azure Service Principal (Graph API Authentication) + + The Azure App Registration and Enterprise Application Orchestrator extension uses an [Azure Service Principal](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals?tabs=browser) for authentication. Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal) to create a service principal. Currently, Client Secret authentication is supported. The Service Principal must have the following API Permission: + - **_Microsoft Graph Application Permissions_**: + - `Application.ReadWrite.All` (_not_ Delegated; Admin Consent) - Allows the app to create, read, update and delete applications and service principals without a signed-in user. + + > For more information on Admin Consent for App-only access (also called "Application Permissions"), see the [primer on application-only access](https://learn.microsoft.com/en-us/azure/active-directory/develop/app-only-access-primer). + + Alternatively, the Service Principal can be granted the `Application.ReadWrite.OwnedBy` permission if the Service Principal is only intended to manage its own App Registration/Application. + + #### Client Certificate or Client Secret + + Beginning in version 3.0.0, the Azure App Registration and Enterprise Application Orchestrator extension supports both [client certificate authentication](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) and [client secret](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-2-add-a-client-secret) authentication. + + * **Client Secret** - Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-2-add-a-client-secret) to create a Client Secret. This secret will be used as the **Server Password** field in the [Certificate Store Configuration](#certificate-store-configuration) section. + * **Client Certificate** - Create a client certificate key pair with the Client Authentication extended key usage. The client certificate will be used in the ClientCertificate field in the [Certificate Store Configuration](#certificate-store-configuration) section. If you have access to Keyfactor Command, the instructions in this section walk you through enrolling a certificate and ensuring that it's in the correct format. Once enrolled, follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) to add the _public key_ certificate (no private key) to the service principal used for authentication. + + The certificate can be in either of the following formats: + * Base64-encoded PKCS#12 (PFX) with a matching private key. + * Base64-encoded PEM-encoded certificate _and_ PEM-encoded PKCS8 private key. Make sure that the certificate and private key are separated with a newline. The order doesn't matter - the extension will determine which is which. + + If the private key is encrypted, the encryption password will replace the **Server Password** field in the [Certificate Store Configuration](#certificate-store-configuration) section. -1. Follow the Requirements section to configure a Service Account and grant the necessary API permissions. + > **Creating and Formatting a Client Certificate using Keyfactor Command** + > + > To get started quickly, you can follow the instructions below to create and properly format a client certificate to authenticate to the Microsoft Graph API. + > + > 1. In Keyfactor Command, hover over **Enrollment** and select **PFX Enrollment**. + > 2. Select a **Template** that supports Client Authentication as an extended key usage. + > 3. Populate the certificate subject as appropriate for the Template. It may be sufficient to only populate the Common Name, but consult your IT policy to ensure that this certificate is compliant. + > 4. At the bottom of the page, uncheck the box for **Include Chain**, and select either **PFX** or **PEM** as the certificate Format. + > 5. Make a note of the password on the next page - it won't be shown again. + > 6. Prepare the certificate and private key for Azure and the Orchestrator extension: + > * If you downloaded the certificate in PEM format, use the commands below: + > + > ```shell + > # Verify that the certificate downloaded from Command contains the certificate and private key. They should be in the same file + > cat + > + > # Separate the certificate from the private key + > openssl x509 -in -out pubkeycert.pem + > + > # Base64 encode the certificate and private key + > cat | base64 > clientcertkeypair.pem.base64 + > ``` + > + > * If you downloaded the certificate in PFX format, use the commands below: + > + > ```shell + > # Export the certificate from the PFX file + > openssl pkcs12 -in -clcerts -nokeys -out pubkeycert.pem + > + > # Base64 encode the PFX file + > cat | base64 > clientcert.pfx.base64 + > ``` + > 7. Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) to add the public key certificate to the service principal used for authentication. + > + > You will use `clientcert.[pem|pfx].base64` as the **ClientCertificate** field in the [Certificate Store Configuration](#certificate-store-configuration) section. - * [Azure App Registration/Application](docs/azureapp.md#requirements) - * [Azure Enterprise Application/Service Principal](docs/azuresp.md#requirements) + ### Azure App Registration (Application) + + #### Application Certificates + + Application certificates are used for client authentication and are typically public key only. No additional configuration in Azure is necessary to manage Application certificates since all App Registrations can contain any number of [Certificates and Secrets](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app#add-credentials). Unless the Discovery job is used, you should collect the Application IDs for each App Registration that contains certificates to be managed. + + + +
2. Create Certificate Store Types for the Azure App Registration and Enterprise Application Orchestrator extension. * **Using kfutil**: ```shell + # Azure App Registration (Application) kfutil store-types create AzureApp - kfutil store-types create AzureSP ``` * **Manually**: + * [Azure App Registration (Application)](docs/azureapp.md#certificate-store-type-configuration) + +3. Install the Azure App Registration and Enterprise Application Universal Orchestrator extension. + + * **Using kfutil**: On the server that that hosts the Universal Orchestrator, run the following command: + + ```shell + # Windows Server + kfutil orchestrator extension -e azure-application-orchestrator@latest --out "C:\Program Files\Keyfactor\Keyfactor Orchestrator\extensions" + + # Linux + kfutil orchestrator extension -e azure-application-orchestrator@latest --out "/opt/keyfactor/orchestrator/extensions" + ``` - * [Azure App Registration/Application](docs/azureapp.md#certificate-store-type-configuration) - * [Azure Enterprise Application/Service Principal](docs/azuresp.md#certificate-store-type-configuration) + * **Manually**: Follow the [official Command documentation](https://software.keyfactor.com/Core-OnPrem/Current/Content/InstallingAgents/NetCoreOrchestrator/CustomExtensions.htm?Highlight=extensions) to install the latest [Azure App Registration and Enterprise Application Universal Orchestrator extension](https://github.com/Keyfactor/azure-application-orchestrator/releases/latest). -3. Install the Azure App Registration and Enterprise Application Orchestrator extension. +4. Create new certificate stores in Keyfactor Command for the Sample Universal Orchestrator extension. + * [Azure App Registration (Application)](docs/azureapp.md#certificate-store-configuration) +
+ +
Azure Enterprise Application (Service Principal) + + +1. Follow the [requirements section](docs/azuresp.md#requirements) to configure a Service Account and grant necessary API permissions. + +
Requirements + + ### Azure Service Principal (Graph API Authentication) + + The Azure App Registration and Enterprise Application Orchestrator extension uses an [Azure Service Principal](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals?tabs=browser) for authentication. Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal) to create a service principal. Currently, Client Secret authentication is supported. The Service Principal must have the following API Permission: + - **_Microsoft Graph Application Permissions_**: + - `Application.ReadWrite.All` (_not_ Delegated; Admin Consent) - Allows the app to create, read, update and delete applications and service principals without a signed-in user. + + > For more information on Admin Consent for App-only access (also called "Application Permissions"), see the [primer on application-only access](https://learn.microsoft.com/en-us/azure/active-directory/develop/app-only-access-primer). + + Alternatively, the Service Principal can be granted the `Application.ReadWrite.OwnedBy` permission if the Service Principal is only intended to manage its own App Registration/Application. + + #### Client Certificate or Client Secret + + Beginning in version 3.0.0, the Azure App Registration and Enterprise Application Orchestrator extension supports both [client certificate authentication](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) and [client secret](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-2-add-a-client-secret) authentication. + + * **Client Secret** - Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-2-add-a-client-secret) to create a Client Secret. This secret will be used as the **Server Password** field in the [Certificate Store Configuration](#certificate-store-configuration) section. + * **Client Certificate** - Create a client certificate key pair with the Client Authentication extended key usage. The client certificate will be used in the ClientCertificate field in the [Certificate Store Configuration](#certificate-store-configuration) section. If you have access to Keyfactor Command, the instructions in this section walk you through enrolling a certificate and ensuring that it's in the correct format. Once enrolled, follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) to add the _public key_ certificate (no private key) to the service principal used for authentication. + + The certificate can be in either of the following formats: + * Base64-encoded PKCS#12 (PFX) with a matching private key. + * Base64-encoded PEM-encoded certificate _and_ PEM-encoded PKCS8 private key. Make sure that the certificate and private key are separated with a newline. The order doesn't matter - the extension will determine which is which. + + If the private key is encrypted, the encryption password will replace the **Server Password** field in the [Certificate Store Configuration](#certificate-store-configuration) section. + + > **Creating and Formatting a Client Certificate using Keyfactor Command** + > + > To get started quickly, you can follow the instructions below to create and properly format a client certificate to authenticate to the Microsoft Graph API. + > + > 1. In Keyfactor Command, hover over **Enrollment** and select **PFX Enrollment**. + > 2. Select a **Template** that supports Client Authentication as an extended key usage. + > 3. Populate the certificate subject as appropriate for the Template. It may be sufficient to only populate the Common Name, but consult your IT policy to ensure that this certificate is compliant. + > 4. At the bottom of the page, uncheck the box for **Include Chain**, and select either **PFX** or **PEM** as the certificate Format. + > 5. Make a note of the password on the next page - it won't be shown again. + > 6. Prepare the certificate and private key for Azure and the Orchestrator extension: + > * If you downloaded the certificate in PEM format, use the commands below: + > + > ```shell + > # Verify that the certificate downloaded from Command contains the certificate and private key. They should be in the same file + > cat + > + > # Separate the certificate from the private key + > openssl x509 -in -out pubkeycert.pem + > + > # Base64 encode the certificate and private key + > cat | base64 > clientcertkeypair.pem.base64 + > ``` + > + > * If you downloaded the certificate in PFX format, use the commands below: + > + > ```shell + > # Export the certificate from the PFX file + > openssl pkcs12 -in -clcerts -nokeys -out pubkeycert.pem + > + > # Base64 encode the PFX file + > cat | base64 > clientcert.pfx.base64 + > ``` + > 7. Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) to add the public key certificate to the service principal used for authentication. + > + > You will use `clientcert.[pem|pfx].base64` as the **ClientCertificate** field in the [Certificate Store Configuration](#certificate-store-configuration) section. + + ### Enterprise Application (Service Principal) + + #### Service Principal Certificates + + Service Principal certificates are typically used for SAML Token signing. Service Principals are created from Enterprise Applications, and will mostly be configured with a variation of Microsoft's [SAML-based single sign-on](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/add-application-portal) documentation. For more information on the mechanics of the Service Principal certificate management capabilities of this extension, please see the [mechanics](#extension-mechanics) section. + + + +
+ +2. Create Certificate Store Types for the Azure App Registration and Enterprise Application Orchestrator extension. + + * **Using kfutil**: + + ```shell + # Azure Enterprise Application (Service Principal) + kfutil store-types create AzureSP + ``` + + * **Manually**: + * [Azure Enterprise Application (Service Principal)](docs/azuresp.md#certificate-store-type-configuration) + +3. Install the Azure App Registration and Enterprise Application Universal Orchestrator extension. * **Using kfutil**: On the server that that hosts the Universal Orchestrator, run the following command: @@ -87,13 +299,20 @@ Before installing the Azure App Registration and Enterprise Application Orchestr kfutil orchestrator extension -e azure-application-orchestrator@latest --out "/opt/keyfactor/orchestrator/extensions" ``` - * **Manually**: Follow the [official Command documentation](https://software.keyfactor.com/Core-OnPrem/Current/Content/InstallingAgents/NetCoreOrchestrator/CustomExtensions.htm?Highlight=extensions) to install the latest [Azure App Registration and Enterprise Application Orchestrator extension](https://github.com/Keyfactor/azure-application-orchestrator/releases/latest). + * **Manually**: Follow the [official Command documentation](https://software.keyfactor.com/Core-OnPrem/Current/Content/InstallingAgents/NetCoreOrchestrator/CustomExtensions.htm?Highlight=extensions) to install the latest [Azure App Registration and Enterprise Application Universal Orchestrator extension](https://github.com/Keyfactor/azure-application-orchestrator/releases/latest). + +4. Create new certificate stores in Keyfactor Command for the Sample Universal Orchestrator extension. + * [Azure Enterprise Application (Service Principal)](docs/azuresp.md#certificate-store-configuration) +
+ + +## License -4. Create new certificate stores in Keyfactor Command for the Azure App Registration and Enterprise Application Orchestrator extension. +Apache License 2.0, see [LICENSE](LICENSE). - * [Azure App Registration/Application](docs/azureapp.md#certificate-store-configuration) - * [Azure Enterprise Application/Service Principal](docs/azuresp.md#certificate-store-configuration) +## Related Integrations +See all [Keyfactor Universal Orchestrator extensions](https://github.com/orgs/Keyfactor/repositories?q=orchestrator). When creating cert store type manually, that store property names and entry parameter names are case sensitive diff --git a/docs/azureapp.md b/docs/azureapp.md index 93a1deb..9380654 100644 --- a/docs/azureapp.md +++ b/docs/azureapp.md @@ -1,7 +1,20 @@ -## Azure App Registration/Application +## Azure App Registration (Application) Azure [App Registration/Application certificates](https://learn.microsoft.com/en-us/entra/identity-platform/certificate-credentials) are typically used for client authentication by applications and are typically public key only in Azure. The general model by which these credentials are consumed is that the certificate and private key are accessible by the Application using the App Registration, and are passed to the service that is authenticating the Application. The Azure App Registration and Enterprise Application Orchestrator extension implements the Inventory, Management Add, Management Remove, and Discovery job types for managing these certificates. + + +### Supported Job Types + +| Job Name | Supported | +| -------- | --------- | +| Inventory | ✅ | +| Management Add | ✅ | +| Management Remove | ✅ | +| Discovery | ✅ | +| Create | | +| Reenrollment | | + ## Requirements ### Azure Service Principal (Graph API Authentication) @@ -14,12 +27,63 @@ The Azure App Registration and Enterprise Application Orchestrator extension use Alternatively, the Service Principal can be granted the `Application.ReadWrite.OwnedBy` permission if the Service Principal is only intended to manage its own App Registration/Application. +#### Client Certificate or Client Secret + +Beginning in version 3.0.0, the Azure App Registration and Enterprise Application Orchestrator extension supports both [client certificate authentication](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) and [client secret](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-2-add-a-client-secret) authentication. + +* **Client Secret** - Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-2-add-a-client-secret) to create a Client Secret. This secret will be used as the **Server Password** field in the [Certificate Store Configuration](#certificate-store-configuration) section. +* **Client Certificate** - Create a client certificate key pair with the Client Authentication extended key usage. The client certificate will be used in the ClientCertificate field in the [Certificate Store Configuration](#certificate-store-configuration) section. If you have access to Keyfactor Command, the instructions in this section walk you through enrolling a certificate and ensuring that it's in the correct format. Once enrolled, follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) to add the _public key_ certificate (no private key) to the service principal used for authentication. + + The certificate can be in either of the following formats: + * Base64-encoded PKCS#12 (PFX) with a matching private key. + * Base64-encoded PEM-encoded certificate _and_ PEM-encoded PKCS8 private key. Make sure that the certificate and private key are separated with a newline. The order doesn't matter - the extension will determine which is which. + + If the private key is encrypted, the encryption password will replace the **Server Password** field in the [Certificate Store Configuration](#certificate-store-configuration) section. + +> **Creating and Formatting a Client Certificate using Keyfactor Command** +> +> To get started quickly, you can follow the instructions below to create and properly format a client certificate to authenticate to the Microsoft Graph API. +> +> 1. In Keyfactor Command, hover over **Enrollment** and select **PFX Enrollment**. +> 2. Select a **Template** that supports Client Authentication as an extended key usage. +> 3. Populate the certificate subject as appropriate for the Template. It may be sufficient to only populate the Common Name, but consult your IT policy to ensure that this certificate is compliant. +> 4. At the bottom of the page, uncheck the box for **Include Chain**, and select either **PFX** or **PEM** as the certificate Format. +> 5. Make a note of the password on the next page - it won't be shown again. +> 6. Prepare the certificate and private key for Azure and the Orchestrator extension: +> * If you downloaded the certificate in PEM format, use the commands below: +> +> ```shell +> # Verify that the certificate downloaded from Command contains the certificate and private key. They should be in the same file +> cat +> +> # Separate the certificate from the private key +> openssl x509 -in -out pubkeycert.pem +> +> # Base64 encode the certificate and private key +> cat | base64 > clientcertkeypair.pem.base64 +> ``` +> +> * If you downloaded the certificate in PFX format, use the commands below: +> +> ```shell +> # Export the certificate from the PFX file +> openssl pkcs12 -in -clcerts -nokeys -out pubkeycert.pem +> +> # Base64 encode the PFX file +> cat | base64 > clientcert.pfx.base64 +> ``` +> 7. Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) to add the public key certificate to the service principal used for authentication. +> +> You will use `clientcert.[pem|pfx].base64` as the **ClientCertificate** field in the [Certificate Store Configuration](#certificate-store-configuration) section. + ### Azure App Registration (Application) #### Application Certificates Application certificates are used for client authentication and are typically public key only. No additional configuration in Azure is necessary to manage Application certificates since all App Registrations can contain any number of [Certificates and Secrets](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app#add-credentials). Unless the Discovery job is used, you should collect the Application IDs for each App Registration that contains certificates to be managed. + + ## Extension Mechanics The Azure App Registration and Enterprise Application Orchestrator extension uses the [Microsoft Dotnet Graph SDK](https://learn.microsoft.com/en-us/graph/sdks/sdks-overview) to interact with the Microsoft Graph API. The extension uses the following Graph API endpoints to manage Application certificates: @@ -36,16 +100,18 @@ The Discovery operation uses the "Directories to search" field, and accepts inpu - `*` - If the asterisk symbol `*` is used, the extension will search for all Azure App Registrations that the Service Principal has access to, but only in the tenant that the discovery job was configured for as specified by the "Client Machine" field in the certificate store configuration. - `,,...` - If a comma-separated list of tenant IDs is used, the extension will search for all Azure App Registrations available in each tenant specified in the list. The tenant IDs should be the GUIDs associated with each tenant, and it's the user's responsibility to ensure that the service principal has access to the specified tenants. +> The Discovery Job only supports Client Secret authentication. + + + ## Certificate Store Type Configuration -The recommended method for creating the `AzureApp` Certificate Store Type is to use [kfutil](https://github.com/Keyfactor/kfutil). After installing, use the following command to create the `AzureApp` Certificate Store Type: +The recommended method for creating the `AzureApp` Certificate Store Type is to use [kfutil](https://github.com/Keyfactor/kfutil). After installing, use the following command to create the `` Certificate Store Type: ```shell kfutil store-types create AzureApp ``` -Alternatively, you can create the `AzureApp` Certificate Store Type manually by following the following steps: -
AzureApp Create a store type called `AzureApp` with the attributes in the tables below: @@ -53,32 +119,35 @@ Create a store type called `AzureApp` with the attributes in the tables below: ### Basic Tab | Attribute | Value | Description | | --------- | ----- | ----- | -| Name | Azure Application (Auth) | Display name for the store type (may be customized) | +| Name | Azure App Registration (Application) | Display name for the store type (may be customized) | | Short Name | AzureApp | Short display name for the store type | | Capability | AzureApp | Store type name orchestrator will register with. Check the box to allow entry of value | | Supported Job Types (check the box for each) | Add, Discovery, Remove | Job types the extension supports | -| Needs Server | ✓ | Determines if a target server name is required when creating store | +| Supports Add | ✅ | Check the box. Indicates that the Store Type supports Management Add | +| Supports Remove | ✅ | Check the box. Indicates that the Store Type supports Management Remove | +| Supports Discovery | ✅ | Check the box. Indicates that the Store Type supports Discovery | +| Supports Reenrollment | | Indicates that the Store Type supports Reenrollment | +| Supports Create | | Indicates that the Store Type supports store creation | +| Needs Server | ✅ | Determines if a target server name is required when creating store | | Blueprint Allowed | | Determines if store type may be included in an Orchestrator blueprint | | Uses PowerShell | | Determines if underlying implementation is PowerShell | | Requires Store Password | | Determines if a store password is required when configuring an individual store. | | Supports Entry Password | | Determines if an individual entry within a store can have a password. | - The Basic tab should look like this: -![AzureApp Basic Tab](../.github/images/AzureApp-basic-store-type-dialog.png) +![AzureApp Basic Tab](../docsource/images/AzureApp-basic-store-type-dialog.png) ### Advanced Tab | Attribute | Value | Description | | --------- | ----- | ----- | | Supports Custom Alias | Required | Determines if an individual entry within a store can have a custom Alias. | -| Private Key Handling | Forbidden | This determines if Keyfactor can send the private key associated with a certificate to the store. Required because IIS certificates without private keys would be invalid. | +| Private Key Handling | Required | This determines if Keyfactor can send the private key associated with a certificate to the store. Required because IIS certificates without private keys would be invalid. | | PFX Password Style | Default | 'Default' - PFX password is randomly generated, 'Custom' - PFX password may be specified when the enrollment job is created (Requires the Allow Custom Password application setting to be enabled.) | - The Advanced tab should look like this: -![AzureApp Advanced Tab](../.github/images/AzureApp-advanced-store-type-dialog.png) +![AzureApp Advanced Tab](../docsource/images/AzureApp-advanced-store-type-dialog.png) ### Custom Fields Tab Custom fields operate at the certificate store level and are used to control how the orchestrator connects to the remote target server containing the certificate store to be managed. The following custom fields should be added to the store type: @@ -86,32 +155,38 @@ Custom fields operate at the certificate store level and are used to control how | Name | Display Name | Type | Default Value/Options | Required | Description | | ---- | ------------ | ---- | --------------------- | -------- | ----------- | | ServerUsername | Server Username | Secret | | | The Application ID of the Service Principal used to authenticate with Microsoft Graph for managing Application/Service Principal certificates. | -| ServerPassword | Server Password | Secret | | | A Client Secret that the extension will use to authenticate with Microsoft Graph for managing Application/Service Principal certificates. | -| ServerUseSsl | Use SSL | Bool | true | ✓ | Specifies whether SSL should be used for communication with the server. Set to 'true' to enable SSL, and 'false' to disable it. | +| ServerPassword | Server Password | Secret | | | A Client Secret that the extension will use to authenticate with Microsoft Graph for managing Application/Service Principal certificates, OR the password that encrypts the private key in ClientCertificate | +| ClientCertificate | Client Certificate | Secret | | | The client certificate used to authenticate with Microsoft Graph for managing Application/Service Principal certificates. See the [requirements](#client-certificate-or-client-secret) for more information. | +| AzureCloud | Azure Global Cloud Authority Host | MultipleChoice | public,china,germany,government | | Specifies the Azure Cloud instance used by the organization. | +| ServerUseSsl | Use SSL | Bool | true | ✅ | Specifies whether SSL should be used for communication with the server. Set to 'true' to enable SSL, and 'false' to disable it. | The Custom Fields tab should look like this: -![AzureApp Custom Fields Tab](../.github/images/AzureApp-custom-fields-store-type-dialog.png) +![AzureApp Custom Fields Tab](../docsource/images/AzureApp-custom-fields-store-type-dialog.png) + +
## Certificate Store Configuration -After creating the `AzureApp` Certificate Store Type and installing the Azure App Registration and Enterprise Application Orchestrator extension, you can create new [Certificate Stores](https://software.keyfactor.com/Core-OnPrem/Current/Content/ReferenceGuide/Certificate%20Stores.htm?Highlight=certificate%20store) to manage certificates in the remote platform. +After creating the `AzureApp` Certificate Store Type and installing the Azure App Registration and Enterprise Application Universal Orchestrator extension, you can create new [Certificate Stores](https://software.keyfactor.com/Core-OnPrem/Current/Content/ReferenceGuide/Certificate%20Stores.htm?Highlight=certificate%20store) to manage certificates in the remote platform. The following table describes the required and optional fields for the `AzureApp` certificate store type. | Attribute | Description | | --------- | ----------- | -| Category | Select Azure Application (Auth) or the customized certificate store name from the previous step. | +| Category | Select "Azure App Registration (Application)" or the customized certificate store name from the previous step. | | Container | Optional container to associate certificate store with. | | Client Machine | The Azure Tenant (directory) ID that owns the Service Principal. | | Store Path | The Application ID of the target Application/Service Principal that will be managed by the Azure App Registration and Enterprise Application Orchestrator extension. | -| Orchestrator | Select an approved orchestrator capable of managing AzureApp certificates. Specifically, one with the AzureApp capability. | -| Server Username | The Application ID of the Service Principal used to authenticate with Microsoft Graph for managing Application/Service Principal certificates. | -| Server Password | A Client Secret that the extension will use to authenticate with Microsoft Graph for managing Application/Service Principal certificates. | -| Use SSL | Specifies whether SSL should be used for communication with the server. Set to 'true' to enable SSL, and 'false' to disable it. | +| Orchestrator | Select an approved orchestrator capable of managing `AzureApp` certificates. Specifically, one with the `AzureApp` capability. | +| ServerUsername | The Application ID of the Service Principal used to authenticate with Microsoft Graph for managing Application/Service Principal certificates. | +| ServerPassword | A Client Secret that the extension will use to authenticate with Microsoft Graph for managing Application/Service Principal certificates, OR the password that encrypts the private key in ClientCertificate | +| ClientCertificate | The client certificate used to authenticate with Microsoft Graph for managing Application/Service Principal certificates. See the [requirements](#client-certificate-or-client-secret) for more information. | +| AzureCloud | Specifies the Azure Cloud instance used by the organization. | +| ServerUseSsl | Specifies whether SSL should be used for communication with the server. Set to 'true' to enable SSL, and 'false' to disable it. | * **Using kfutil** @@ -125,6 +200,4 @@ The following table describes the required and optional fields for the `AzureApp kfutil stores import csv --store-type-name AzureApp --file AzureApp.csv ``` -* **Manually with the Command UI**: In Keyfactor Command, navigate to Certificate Stores from the Locations Menu. Click the Add button to create a new Certificate Store using the attributes in the table above. - - +* **Manually with the Command UI**: In Keyfactor Command, navigate to Certificate Stores from the Locations Menu. Click the Add button to create a new Certificate Store using the attributes in the table above. \ No newline at end of file diff --git a/docs/azuresp.md b/docs/azuresp.md index 2e48382..5335d60 100644 --- a/docs/azuresp.md +++ b/docs/azuresp.md @@ -1,8 +1,20 @@ -### Azure Enterprise Application/Service Principal +## Azure Enterprise Application (Service Principal) The Azure Enterprise Application/Service Principal certificate operations are implemented by the `AzureSP` store type, and supports the management of a single certificate for use in SSO/SAML assertion signing. The Management Add operation is only supported with the certificate replacement option, since adding a new certificate will replace the existing certificate. The Add operation will also set newly added certificates as the active certificate for SSO/SAML usage. The Management Remove operation removes the certificate from the Enterprise Application/Service Principal, which is the same as removing the SSO/SAML signing certificate. The Discovery operation discovers all Enterprise Applications/Service Principals in the tenant. + +### Supported Job Types + +| Job Name | Supported | +| -------- | --------- | +| Inventory | ✅ | +| Management Add | ✅ | +| Management Remove | ✅ | +| Discovery | ✅ | +| Create | | +| Reenrollment | | + ## Requirements ### Azure Service Principal (Graph API Authentication) @@ -15,12 +27,63 @@ The Azure App Registration and Enterprise Application Orchestrator extension use Alternatively, the Service Principal can be granted the `Application.ReadWrite.OwnedBy` permission if the Service Principal is only intended to manage its own App Registration/Application. +#### Client Certificate or Client Secret + +Beginning in version 3.0.0, the Azure App Registration and Enterprise Application Orchestrator extension supports both [client certificate authentication](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) and [client secret](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-2-add-a-client-secret) authentication. + +* **Client Secret** - Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-2-add-a-client-secret) to create a Client Secret. This secret will be used as the **Server Password** field in the [Certificate Store Configuration](#certificate-store-configuration) section. +* **Client Certificate** - Create a client certificate key pair with the Client Authentication extended key usage. The client certificate will be used in the ClientCertificate field in the [Certificate Store Configuration](#certificate-store-configuration) section. If you have access to Keyfactor Command, the instructions in this section walk you through enrolling a certificate and ensuring that it's in the correct format. Once enrolled, follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) to add the _public key_ certificate (no private key) to the service principal used for authentication. + + The certificate can be in either of the following formats: + * Base64-encoded PKCS#12 (PFX) with a matching private key. + * Base64-encoded PEM-encoded certificate _and_ PEM-encoded PKCS8 private key. Make sure that the certificate and private key are separated with a newline. The order doesn't matter - the extension will determine which is which. + + If the private key is encrypted, the encryption password will replace the **Server Password** field in the [Certificate Store Configuration](#certificate-store-configuration) section. + +> **Creating and Formatting a Client Certificate using Keyfactor Command** +> +> To get started quickly, you can follow the instructions below to create and properly format a client certificate to authenticate to the Microsoft Graph API. +> +> 1. In Keyfactor Command, hover over **Enrollment** and select **PFX Enrollment**. +> 2. Select a **Template** that supports Client Authentication as an extended key usage. +> 3. Populate the certificate subject as appropriate for the Template. It may be sufficient to only populate the Common Name, but consult your IT policy to ensure that this certificate is compliant. +> 4. At the bottom of the page, uncheck the box for **Include Chain**, and select either **PFX** or **PEM** as the certificate Format. +> 5. Make a note of the password on the next page - it won't be shown again. +> 6. Prepare the certificate and private key for Azure and the Orchestrator extension: +> * If you downloaded the certificate in PEM format, use the commands below: +> +> ```shell +> # Verify that the certificate downloaded from Command contains the certificate and private key. They should be in the same file +> cat +> +> # Separate the certificate from the private key +> openssl x509 -in -out pubkeycert.pem +> +> # Base64 encode the certificate and private key +> cat | base64 > clientcertkeypair.pem.base64 +> ``` +> +> * If you downloaded the certificate in PFX format, use the commands below: +> +> ```shell +> # Export the certificate from the PFX file +> openssl pkcs12 -in -clcerts -nokeys -out pubkeycert.pem +> +> # Base64 encode the PFX file +> cat | base64 > clientcert.pfx.base64 +> ``` +> 7. Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) to add the public key certificate to the service principal used for authentication. +> +> You will use `clientcert.[pem|pfx].base64` as the **ClientCertificate** field in the [Certificate Store Configuration](#certificate-store-configuration) section. + ### Enterprise Application (Service Principal) #### Service Principal Certificates Service Principal certificates are typically used for SAML Token signing. Service Principals are created from Enterprise Applications, and will mostly be configured with a variation of Microsoft's [SAML-based single sign-on](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/add-application-portal) documentation. For more information on the mechanics of the Service Principal certificate management capabilities of this extension, please see the [mechanics](#extension-mechanics) section. + + ## Extension Mechanics The Azure App Registration and Enterprise Application Orchestrator extension uses the [Microsoft Dotnet Graph SDK](https://learn.microsoft.com/en-us/graph/sdks/sdks-overview) to interact with the Microsoft Graph API. The extension uses the following Graph API endpoints to manage Service Principal certificates: @@ -37,16 +100,18 @@ The Discovery operation uses the "Directories to search" field, and accepts inpu - `*` - If the asterisk symbol `*` is used, the extension will search for all Azure Enterprise Applications that the Service Principal has access to, but only in the tenant that the discovery job was configured for as specified by the "Client Machine" field in the certificate store configuration. - `,,...` - If a comma-separated list of tenant IDs is used, the extension will search for all Azure Enterprise Applications available in each tenant specified in the list. The tenant IDs should be the GUIDs associated with each tenant, and it's the user's responsibility to ensure that the service principal has access to the specified tenants. +> The Discovery Job only supports Client Secret authentication. + + + ## Certificate Store Type Configuration -The recommended method for creating the `AzureSP` Certificate Store Type is to use [kfutil](https://github.com/Keyfactor/kfutil). After installing, use the following command to create the `AzureSP` Certificate Store Type: +The recommended method for creating the `AzureSP` Certificate Store Type is to use [kfutil](https://github.com/Keyfactor/kfutil). After installing, use the following command to create the `` Certificate Store Type: ```shell kfutil store-types create AzureSP ``` -Alternatively, you can create the `AzureSP` Certificate Store Type manually by following the following steps: -
AzureSP Create a store type called `AzureSP` with the attributes in the tables below: @@ -54,20 +119,24 @@ Create a store type called `AzureSP` with the attributes in the tables below: ### Basic Tab | Attribute | Value | Description | | --------- | ----- | ----- | -| Name | Azure Service Principal (SSO/SAML) | Display name for the store type (may be customized) | +| Name | Azure Enterprise Application (Service Principal) | Display name for the store type (may be customized) | | Short Name | AzureSP | Short display name for the store type | | Capability | AzureSP | Store type name orchestrator will register with. Check the box to allow entry of value | | Supported Job Types (check the box for each) | Add, Discovery, Remove | Job types the extension supports | -| Needs Server | ✓ | Determines if a target server name is required when creating store | +| Supports Add | ✅ | Check the box. Indicates that the Store Type supports Management Add | +| Supports Remove | ✅ | Check the box. Indicates that the Store Type supports Management Remove | +| Supports Discovery | ✅ | Check the box. Indicates that the Store Type supports Discovery | +| Supports Reenrollment | | Indicates that the Store Type supports Reenrollment | +| Supports Create | | Indicates that the Store Type supports store creation | +| Needs Server | ✅ | Determines if a target server name is required when creating store | | Blueprint Allowed | | Determines if store type may be included in an Orchestrator blueprint | | Uses PowerShell | | Determines if underlying implementation is PowerShell | | Requires Store Password | | Determines if a store password is required when configuring an individual store. | | Supports Entry Password | | Determines if an individual entry within a store can have a password. | - The Basic tab should look like this: -![AzureSP Basic Tab](../.github/images/AzureSP-basic-store-type-dialog.png) +![AzureSP Basic Tab](../docsource/images/AzureSP-basic-store-type-dialog.png) ### Advanced Tab | Attribute | Value | Description | @@ -76,10 +145,9 @@ The Basic tab should look like this: | Private Key Handling | Required | This determines if Keyfactor can send the private key associated with a certificate to the store. Required because IIS certificates without private keys would be invalid. | | PFX Password Style | Default | 'Default' - PFX password is randomly generated, 'Custom' - PFX password may be specified when the enrollment job is created (Requires the Allow Custom Password application setting to be enabled.) | - The Advanced tab should look like this: -![AzureSP Advanced Tab](../.github/images/AzureSP-advanced-store-type-dialog.png) +![AzureSP Advanced Tab](../docsource/images/AzureSP-advanced-store-type-dialog.png) ### Custom Fields Tab Custom fields operate at the certificate store level and are used to control how the orchestrator connects to the remote target server containing the certificate store to be managed. The following custom fields should be added to the store type: @@ -87,38 +155,43 @@ Custom fields operate at the certificate store level and are used to control how | Name | Display Name | Type | Default Value/Options | Required | Description | | ---- | ------------ | ---- | --------------------- | -------- | ----------- | | ServerUsername | Server Username | Secret | | | The Application ID of the Service Principal used to authenticate with Microsoft Graph for managing Application/Service Principal certificates. | -| ServerPassword | Server Password | Secret | | | A Client Secret that the extension will use to authenticate with Microsoft Graph for managing Application/Service Principal certificates. | -| ServerUseSsl | Use SSL | Bool | true | ✓ | Specifies whether SSL should be used for communication with the server. Set to 'true' to enable SSL, and 'false' to disable it. | +| ServerPassword | Server Password | Secret | | | A Client Secret that the extension will use to authenticate with Microsoft Graph for managing Application/Service Principal certificates, OR the password that encrypts the private key in ClientCertificate | +| ClientCertificate | Client Certificate | Secret | | | The client certificate used to authenticate with Microsoft Graph for managing Application/Service Principal certificates. See the [requirements](#client-certificate-or-client-secret) for more information. | +| AzureCloud | Azure Global Cloud Authority Host | MultipleChoice | public,china,germany,government | | Specifies the Azure Cloud instance used by the organization. | +| ServerUseSsl | Use SSL | Bool | true | ✅ | Specifies whether SSL should be used for communication with the server. Set to 'true' to enable SSL, and 'false' to disable it. | The Custom Fields tab should look like this: -![AzureSP Custom Fields Tab](../.github/images/AzureSP-custom-fields-store-type-dialog.png) +![AzureSP Custom Fields Tab](../docsource/images/AzureSP-custom-fields-store-type-dialog.png) + -
+ ## Certificate Store Configuration -After creating the `AzureSP` Certificate Store Type and installing the Azure App Registration and Enterprise Application Orchestrator extension, you can create new [Certificate Stores](https://software.keyfactor.com/Core-OnPrem/Current/Content/ReferenceGuide/Certificate%20Stores.htm?Highlight=certificate%20store) to manage certificates in the remote platform. +After creating the `AzureSP` Certificate Store Type and installing the Azure App Registration and Enterprise Application Universal Orchestrator extension, you can create new [Certificate Stores](https://software.keyfactor.com/Core-OnPrem/Current/Content/ReferenceGuide/Certificate%20Stores.htm?Highlight=certificate%20store) to manage certificates in the remote platform. The following table describes the required and optional fields for the `AzureSP` certificate store type. | Attribute | Description | | --------- | ----------- | -| Category | Select Azure Service Principal (SSO/SAML) or the customized certificate store name from the previous step. | +| Category | Select "Azure Enterprise Application (Service Principal)" or the customized certificate store name from the previous step. | | Container | Optional container to associate certificate store with. | | Client Machine | The Azure Tenant (directory) ID that owns the Service Principal. | | Store Path | The Application ID of the target Application/Service Principal that will be managed by the Azure App Registration and Enterprise Application Orchestrator extension. | -| Orchestrator | Select an approved orchestrator capable of managing AzureSP certificates. Specifically, one with the AzureSP capability. | -| Server Username | The Application ID of the Service Principal used to authenticate with Microsoft Graph for managing Application/Service Principal certificates. | -| Server Password | A Client Secret that the extension will use to authenticate with Microsoft Graph for managing Application/Service Principal certificates. | -| Use SSL | Specifies whether SSL should be used for communication with the server. Set to 'true' to enable SSL, and 'false' to disable it. | +| Orchestrator | Select an approved orchestrator capable of managing `AzureSP` certificates. Specifically, one with the `AzureSP` capability. | +| ServerUsername | The Application ID of the Service Principal used to authenticate with Microsoft Graph for managing Application/Service Principal certificates. | +| ServerPassword | A Client Secret that the extension will use to authenticate with Microsoft Graph for managing Application/Service Principal certificates, OR the password that encrypts the private key in ClientCertificate | +| ClientCertificate | The client certificate used to authenticate with Microsoft Graph for managing Application/Service Principal certificates. See the [requirements](#client-certificate-or-client-secret) for more information. | +| AzureCloud | Specifies the Azure Cloud instance used by the organization. | +| ServerUseSsl | Specifies whether SSL should be used for communication with the server. Set to 'true' to enable SSL, and 'false' to disable it. | * **Using kfutil** ```shell - # Generate a CSV template for the AzureSP certificate store + # Generate a CSV template for the AzureApp certificate store kfutil stores import generate-template --store-type-name AzureSP --outpath AzureSP.csv # Open the CSV file and fill in the required fields for each certificate store. @@ -127,5 +200,4 @@ The following table describes the required and optional fields for the `AzureSP` kfutil stores import csv --store-type-name AzureSP --file AzureSP.csv ``` -* **Manually with the Command UI**: In Keyfactor Command, navigate to Certificate Stores from the Locations Menu. Click the Add button to create a new Certificate Store using the attributes in the table above. - +* **Manually with the Command UI**: In Keyfactor Command, navigate to Certificate Stores from the Locations Menu. Click the Add button to create a new Certificate Store using the attributes in the table above. \ No newline at end of file diff --git a/docsource/azureapp.md b/docsource/azureapp.md new file mode 100644 index 0000000..457bb74 --- /dev/null +++ b/docsource/azureapp.md @@ -0,0 +1,89 @@ +# Overview + +Azure [App Registration/Application certificates](https://learn.microsoft.com/en-us/entra/identity-platform/certificate-credentials) are typically used for client authentication by applications and are typically public key only in Azure. The general model by which these credentials are consumed is that the certificate and private key are accessible by the Application using the App Registration, and are passed to the service that is authenticating the Application. The Azure App Registration and Enterprise Application Orchestrator extension implements the Inventory, Management Add, Management Remove, and Discovery job types for managing these certificates. + +# Requirements + +### Azure Service Principal (Graph API Authentication) + +The Azure App Registration and Enterprise Application Orchestrator extension uses an [Azure Service Principal](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals?tabs=browser) for authentication. Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal) to create a service principal. Currently, Client Secret authentication is supported. The Service Principal must have the following API Permission: +- **_Microsoft Graph Application Permissions_**: + - `Application.ReadWrite.All` (_not_ Delegated; Admin Consent) - Allows the app to create, read, update and delete applications and service principals without a signed-in user. + +> For more information on Admin Consent for App-only access (also called "Application Permissions"), see the [primer on application-only access](https://learn.microsoft.com/en-us/azure/active-directory/develop/app-only-access-primer). + +Alternatively, the Service Principal can be granted the `Application.ReadWrite.OwnedBy` permission if the Service Principal is only intended to manage its own App Registration/Application. + +#### Client Certificate or Client Secret + +Beginning in version 3.0.0, the Azure App Registration and Enterprise Application Orchestrator extension supports both [client certificate authentication](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) and [client secret](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-2-add-a-client-secret) authentication. + +* **Client Secret** - Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-2-add-a-client-secret) to create a Client Secret. This secret will be used as the **Server Password** field in the [Certificate Store Configuration](#certificate-store-configuration) section. +* **Client Certificate** - Create a client certificate key pair with the Client Authentication extended key usage. The client certificate will be used in the ClientCertificate field in the [Certificate Store Configuration](#certificate-store-configuration) section. If you have access to Keyfactor Command, the instructions in this section walk you through enrolling a certificate and ensuring that it's in the correct format. Once enrolled, follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) to add the _public key_ certificate (no private key) to the service principal used for authentication. + + The certificate can be in either of the following formats: + * Base64-encoded PKCS#12 (PFX) with a matching private key. + * Base64-encoded PEM-encoded certificate _and_ PEM-encoded PKCS8 private key. Make sure that the certificate and private key are separated with a newline. The order doesn't matter - the extension will determine which is which. + + If the private key is encrypted, the encryption password will replace the **Server Password** field in the [Certificate Store Configuration](#certificate-store-configuration) section. + +> **Creating and Formatting a Client Certificate using Keyfactor Command** +> +> To get started quickly, you can follow the instructions below to create and properly format a client certificate to authenticate to the Microsoft Graph API. +> +> 1. In Keyfactor Command, hover over **Enrollment** and select **PFX Enrollment**. +> 2. Select a **Template** that supports Client Authentication as an extended key usage. +> 3. Populate the certificate subject as appropriate for the Template. It may be sufficient to only populate the Common Name, but consult your IT policy to ensure that this certificate is compliant. +> 4. At the bottom of the page, uncheck the box for **Include Chain**, and select either **PFX** or **PEM** as the certificate Format. +> 5. Make a note of the password on the next page - it won't be shown again. +> 6. Prepare the certificate and private key for Azure and the Orchestrator extension: +> * If you downloaded the certificate in PEM format, use the commands below: +> +> ```shell +> # Verify that the certificate downloaded from Command contains the certificate and private key. They should be in the same file +> cat +> +> # Separate the certificate from the private key +> openssl x509 -in -out pubkeycert.pem +> +> # Base64 encode the certificate and private key +> cat | base64 > clientcertkeypair.pem.base64 +> ``` +> +> * If you downloaded the certificate in PFX format, use the commands below: +> +> ```shell +> # Export the certificate from the PFX file +> openssl pkcs12 -in -clcerts -nokeys -out pubkeycert.pem +> +> # Base64 encode the PFX file +> cat | base64 > clientcert.pfx.base64 +> ``` +> 7. Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) to add the public key certificate to the service principal used for authentication. +> +> You will use `clientcert.[pem|pfx].base64` as the **ClientCertificate** field in the [Certificate Store Configuration](#certificate-store-configuration) section. + + +### Azure App Registration (Application) + +#### Application Certificates + +Application certificates are used for client authentication and are typically public key only. No additional configuration in Azure is necessary to manage Application certificates since all App Registrations can contain any number of [Certificates and Secrets](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app#add-credentials). Unless the Discovery job is used, you should collect the Application IDs for each App Registration that contains certificates to be managed. + +# Extension Mechanics + +The Azure App Registration and Enterprise Application Orchestrator extension uses the [Microsoft Dotnet Graph SDK](https://learn.microsoft.com/en-us/graph/sdks/sdks-overview) to interact with the Microsoft Graph API. The extension uses the following Graph API endpoints to manage Application certificates: + +* [Get Application](https://learn.microsoft.com/en-us/graph/api/application-get?view=graph-rest-1.0&tabs=http) - Used to obtain the Object ID of the App Registration, and to download the certificates owned by the App Registration. +* [Update Application](https://learn.microsoft.com/en-us/graph/api/application-update?view=graph-rest-1.0&tabs=http) - Used to modify the App Registration to add or remove certificates. + * Specifically, the extension manipulates the [`keyCredentials` resource](https://learn.microsoft.com/en-us/graph/api/resources/keycredential?view=graph-rest-1.0) of the Application object. + +### Discovery Job + +The Discovery operation discovers all Azure App Registrations that the Service Principal has access to. The discovered App Registrations (specifically, their Application IDs) are reported back to Command and can be easily added as certificate stores from the Locations tab. + +The Discovery operation uses the "Directories to search" field, and accepts input in one of the following formats: +- `*` - If the asterisk symbol `*` is used, the extension will search for all Azure App Registrations that the Service Principal has access to, but only in the tenant that the discovery job was configured for as specified by the "Client Machine" field in the certificate store configuration. +- `,,...` - If a comma-separated list of tenant IDs is used, the extension will search for all Azure App Registrations available in each tenant specified in the list. The tenant IDs should be the GUIDs associated with each tenant, and it's the user's responsibility to ensure that the service principal has access to the specified tenants. + +> The Discovery Job only supports Client Secret authentication. diff --git a/docsource/azuresp.md b/docsource/azuresp.md new file mode 100644 index 0000000..864b84a --- /dev/null +++ b/docsource/azuresp.md @@ -0,0 +1,88 @@ +# Overview + +The Azure Enterprise Application/Service Principal certificate operations are implemented by the `AzureSP` store type, and supports the management of a single certificate for use in SSO/SAML assertion signing. The Management Add operation is only supported with the certificate replacement option, since adding a new certificate will replace the existing certificate. The Add operation will also set newly added certificates as the active certificate for SSO/SAML usage. The Management Remove operation removes the certificate from the Enterprise Application/Service Principal, which is the same as removing the SSO/SAML signing certificate. The Discovery operation discovers all Enterprise Applications/Service Principals in the tenant. + +# Requirements + +### Azure Service Principal (Graph API Authentication) + +The Azure App Registration and Enterprise Application Orchestrator extension uses an [Azure Service Principal](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals?tabs=browser) for authentication. Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal) to create a service principal. Currently, Client Secret authentication is supported. The Service Principal must have the following API Permission: +- **_Microsoft Graph Application Permissions_**: + - `Application.ReadWrite.All` (_not_ Delegated; Admin Consent) - Allows the app to create, read, update and delete applications and service principals without a signed-in user. + +> For more information on Admin Consent for App-only access (also called "Application Permissions"), see the [primer on application-only access](https://learn.microsoft.com/en-us/azure/active-directory/develop/app-only-access-primer). + +Alternatively, the Service Principal can be granted the `Application.ReadWrite.OwnedBy` permission if the Service Principal is only intended to manage its own App Registration/Application. + +#### Client Certificate or Client Secret + +Beginning in version 3.0.0, the Azure App Registration and Enterprise Application Orchestrator extension supports both [client certificate authentication](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) and [client secret](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-2-add-a-client-secret) authentication. + +* **Client Secret** - Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-2-add-a-client-secret) to create a Client Secret. This secret will be used as the **Server Password** field in the [Certificate Store Configuration](#certificate-store-configuration) section. +* **Client Certificate** - Create a client certificate key pair with the Client Authentication extended key usage. The client certificate will be used in the ClientCertificate field in the [Certificate Store Configuration](#certificate-store-configuration) section. If you have access to Keyfactor Command, the instructions in this section walk you through enrolling a certificate and ensuring that it's in the correct format. Once enrolled, follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) to add the _public key_ certificate (no private key) to the service principal used for authentication. + + The certificate can be in either of the following formats: + * Base64-encoded PKCS#12 (PFX) with a matching private key. + * Base64-encoded PEM-encoded certificate _and_ PEM-encoded PKCS8 private key. Make sure that the certificate and private key are separated with a newline. The order doesn't matter - the extension will determine which is which. + + If the private key is encrypted, the encryption password will replace the **Server Password** field in the [Certificate Store Configuration](#certificate-store-configuration) section. + +> **Creating and Formatting a Client Certificate using Keyfactor Command** +> +> To get started quickly, you can follow the instructions below to create and properly format a client certificate to authenticate to the Microsoft Graph API. +> +> 1. In Keyfactor Command, hover over **Enrollment** and select **PFX Enrollment**. +> 2. Select a **Template** that supports Client Authentication as an extended key usage. +> 3. Populate the certificate subject as appropriate for the Template. It may be sufficient to only populate the Common Name, but consult your IT policy to ensure that this certificate is compliant. +> 4. At the bottom of the page, uncheck the box for **Include Chain**, and select either **PFX** or **PEM** as the certificate Format. +> 5. Make a note of the password on the next page - it won't be shown again. +> 6. Prepare the certificate and private key for Azure and the Orchestrator extension: +> * If you downloaded the certificate in PEM format, use the commands below: +> +> ```shell +> # Verify that the certificate downloaded from Command contains the certificate and private key. They should be in the same file +> cat +> +> # Separate the certificate from the private key +> openssl x509 -in -out pubkeycert.pem +> +> # Base64 encode the certificate and private key +> cat | base64 > clientcertkeypair.pem.base64 +> ``` +> +> * If you downloaded the certificate in PFX format, use the commands below: +> +> ```shell +> # Export the certificate from the PFX file +> openssl pkcs12 -in -clcerts -nokeys -out pubkeycert.pem +> +> # Base64 encode the PFX file +> cat | base64 > clientcert.pfx.base64 +> ``` +> 7. Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) to add the public key certificate to the service principal used for authentication. +> +> You will use `clientcert.[pem|pfx].base64` as the **ClientCertificate** field in the [Certificate Store Configuration](#certificate-store-configuration) section. + +### Enterprise Application (Service Principal) + +#### Service Principal Certificates + +Service Principal certificates are typically used for SAML Token signing. Service Principals are created from Enterprise Applications, and will mostly be configured with a variation of Microsoft's [SAML-based single sign-on](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/add-application-portal) documentation. For more information on the mechanics of the Service Principal certificate management capabilities of this extension, please see the [mechanics](#extension-mechanics) section. + +# Extension Mechanics + +The Azure App Registration and Enterprise Application Orchestrator extension uses the [Microsoft Dotnet Graph SDK](https://learn.microsoft.com/en-us/graph/sdks/sdks-overview) to interact with the Microsoft Graph API. The extension uses the following Graph API endpoints to manage Service Principal certificates: + +* [Get Service Principal](https://learn.microsoft.com/en-us/graph/api/serviceprincipal-get?view=graph-rest-1.0&tabs=http) - Used to obtain the Object ID of the Enterprise Application, and to download the certificates owned by the Service Principal. +* [Update Service Principal](https://learn.microsoft.com/en-us/graph/api/serviceprincipal-update?view=graph-rest-1.0&tabs=http) - Used to modify the Enterprise Application to add or remove certificates. + * Specifically, the extension manipulates the [`keyCredentials` resource](https://learn.microsoft.com/en-us/graph/api/resources/keycredential?view=graph-rest-1.0) of the Service Principal object. + +### Discovery Job + +The Discovery operation discovers all Azure Enterprise Applications that the Service Principal has access to. The discovered Enterprise Applications (specifically, their Application IDs) are reported back to Command and can be easily added as certificate stores from the Locations tab. + +The Discovery operation uses the "Directories to search" field, and accepts input in one of the following formats: +- `*` - If the asterisk symbol `*` is used, the extension will search for all Azure Enterprise Applications that the Service Principal has access to, but only in the tenant that the discovery job was configured for as specified by the "Client Machine" field in the certificate store configuration. +- `,,...` - If a comma-separated list of tenant IDs is used, the extension will search for all Azure Enterprise Applications available in each tenant specified in the list. The tenant IDs should be the GUIDs associated with each tenant, and it's the user's responsibility to ensure that the service principal has access to the specified tenants. + +> The Discovery Job only supports Client Secret authentication. diff --git a/docsource/images/AzureApp-advanced-store-type-dialog.png b/docsource/images/AzureApp-advanced-store-type-dialog.png new file mode 100644 index 0000000..2b71e8c Binary files /dev/null and b/docsource/images/AzureApp-advanced-store-type-dialog.png differ diff --git a/docsource/images/AzureApp-basic-store-type-dialog.png b/docsource/images/AzureApp-basic-store-type-dialog.png new file mode 100644 index 0000000..dc2fa78 Binary files /dev/null and b/docsource/images/AzureApp-basic-store-type-dialog.png differ diff --git a/docsource/images/AzureApp-custom-fields-store-type-dialog.png b/docsource/images/AzureApp-custom-fields-store-type-dialog.png new file mode 100644 index 0000000..296cd70 Binary files /dev/null and b/docsource/images/AzureApp-custom-fields-store-type-dialog.png differ diff --git a/docsource/images/AzureSP-advanced-store-type-dialog.png b/docsource/images/AzureSP-advanced-store-type-dialog.png new file mode 100644 index 0000000..2b71e8c Binary files /dev/null and b/docsource/images/AzureSP-advanced-store-type-dialog.png differ diff --git a/docsource/images/AzureSP-basic-store-type-dialog.png b/docsource/images/AzureSP-basic-store-type-dialog.png new file mode 100644 index 0000000..ea8bd33 Binary files /dev/null and b/docsource/images/AzureSP-basic-store-type-dialog.png differ diff --git a/docsource/images/AzureSP-custom-fields-store-type-dialog.png b/docsource/images/AzureSP-custom-fields-store-type-dialog.png new file mode 100644 index 0000000..296cd70 Binary files /dev/null and b/docsource/images/AzureSP-custom-fields-store-type-dialog.png differ diff --git a/docsource/overview.md b/docsource/overview.md new file mode 100644 index 0000000..6daa83c --- /dev/null +++ b/docsource/overview.md @@ -0,0 +1,6 @@ +## Overview + +The Azure App Registration and Enterprise Application Orchestrator extension remotely manages both Azure [App Registration/Application](https://learn.microsoft.com/en-us/entra/identity-platform/certificate-credentials) certificates and [Enterprise Application/Service Principal](https://docs.microsoft.com/en-us/azure/active-directory/develop/enterprise-apps-certificate-credentials) certificates. Application certificates are typically public key only and used for client certificate authentication, while Service Principal certificates are commonly used for [SAML Assertion signing](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/tutorial-manage-certificates-for-federated-single-sign-on). The extension implements the Inventory, Management Add, Management Remove, and Discovery job types. + +Certificates used for client authentication by Applications (configured in App Registrations) are represented by the [`AzureApp` store type](docs/azureapp.md), and certificates used for SSO/SAML assertion signing are represented by the [`AzureSP` store type](docs/azuresp.md). Both store types are managed by the same extension. The extension is configured with a single Azure Service Principal that is used to authenticate to the [Microsoft Graph API](https://learn.microsoft.com/en-us/graph/use-the-api). The Azure App Registration and Enterprise Application Orchestrator extension manages certificates for Azure App Registrations (Applications) and Enterprise Applications (Service Principals) differently. + diff --git a/integration-manifest.json b/integration-manifest.json index 59235ff..7c7f817 100644 --- a/integration-manifest.json +++ b/integration-manifest.json @@ -34,6 +34,8 @@ "ShortName": "AzureApp", "Capability": "AzureApp", "LocalStore": false, + "ClientMachineDescription": "The Azure Tenant (directory) ID that owns the Service Principal.", + "StorePathDescription": "The Application ID of the target Application/Service Principal that will be managed by the Azure App Registration and Enterprise Application Orchestrator extension.", "SupportedOperations": { "Add": true, "Remove": true, @@ -46,12 +48,29 @@ "Name": "ServerUsername", "DisplayName": "Server Username", "Type": "Secret", + "Description": "The Application ID of the Service Principal used to authenticate with Microsoft Graph for managing Application/Service Principal certificates.", "Required": false }, { "Name": "ServerPassword", "DisplayName": "Server Password", "Type": "Secret", + "Description": "A Client Secret that the extension will use to authenticate with Microsoft Graph for managing Application/Service Principal certificates, OR the password that encrypts the private key in ClientCertificate", + "Required": false + }, + { + "Name": "ClientCertificate", + "DisplayName": "Client Certificate", + "Type": "Secret", + "Description": "The client certificate used to authenticate with Microsoft Graph for managing Application/Service Principal certificates. See the [requirements](#client-certificate-or-client-secret) for more information.", + "Required": false + }, + { + "Name": "AzureCloud", + "DisplayName": "Azure Global Cloud Authority Host", + "Type": "MultipleChoice", + "DefaultValue": "public,china,germany,government", + "Description": "Specifies the Azure Cloud instance used by the organization.", "Required": false }, { @@ -59,6 +78,7 @@ "DisplayName": "Use SSL", "Type": "Bool", "DefaultValue": "true", + "Description": "Specifies whether SSL should be used for communication with the server. Set to 'true' to enable SSL, and 'false' to disable it.", "Required": true } ], @@ -78,6 +98,8 @@ "ShortName": "AzureSP", "Capability": "AzureSP", "LocalStore": false, + "ClientMachineDescription": "The Azure Tenant (directory) ID that owns the Service Principal.", + "StorePathDescription": "The Application ID of the target Application/Service Principal that will be managed by the Azure App Registration and Enterprise Application Orchestrator extension.", "SupportedOperations": { "Add": true, "Remove": true, @@ -90,12 +112,29 @@ "Name": "ServerUsername", "DisplayName": "Server Username", "Type": "Secret", + "Description": "The Application ID of the Service Principal used to authenticate with Microsoft Graph for managing Application/Service Principal certificates.", "Required": false }, { "Name": "ServerPassword", "DisplayName": "Server Password", "Type": "Secret", + "Description": "A Client Secret that the extension will use to authenticate with Microsoft Graph for managing Application/Service Principal certificates, OR the password that encrypts the private key in ClientCertificate", + "Required": false + }, + { + "Name": "ClientCertificate", + "DisplayName": "Client Certificate", + "Type": "Secret", + "Description": "The client certificate used to authenticate with Microsoft Graph for managing Application/Service Principal certificates. See the [requirements](#client-certificate-or-client-secret) for more information.", + "Required": false + }, + { + "Name": "AzureCloud", + "DisplayName": "Azure Global Cloud Authority Host", + "Type": "MultipleChoice", + "DefaultValue": "public,china,germany,government", + "Description": "Specifies the Azure Cloud instance used by the organization.", "Required": false }, { @@ -103,6 +142,7 @@ "DisplayName": "Use SSL", "Type": "Bool", "DefaultValue": "true", + "Description": "Specifies whether SSL should be used for communication with the server. Set to 'true' to enable SSL, and 'false' to disable it.", "Required": true } ], @@ -117,29 +157,7 @@ "BlueprintAllowed": false, "CustomAliasAllowed": "Required" } - ], - "store_types_metadata": { - "AzureApp": { - "ClientMachine": "The Azure Tenant (directory) ID that owns the Service Principal.", - "StorePath": "The Application ID of the target Application/Service Principal that will be managed by the Azure App Registration and Enterprise Application Orchestrator extension.", - "Properties": { - "ServerUsername": "The Application ID of the Service Principal used to authenticate with Microsoft Graph for managing Application/Service Principal certificates.", - "ServerPassword": "A Client Secret that the extension will use to authenticate with Microsoft Graph for managing Application/Service Principal certificates.", - "ServerUseSsl": "Specifies whether SSL should be used for communication with the server. Set to 'true' to enable SSL, and 'false' to disable it." - }, - "EntryParameters": {} - }, - "AzureSP": { - "ClientMachine": "The Azure Tenant (directory) ID that owns the Service Principal.", - "StorePath": "The Application ID of the target Application/Service Principal that will be managed by the Azure App Registration and Enterprise Application Orchestrator extension.", - "Properties": { - "ServerUsername": "The Application ID of the Service Principal used to authenticate with Microsoft Graph for managing Application/Service Principal certificates.", - "ServerPassword": "A Client Secret that the extension will use to authenticate with Microsoft Graph for managing Application/Service Principal certificates.", - "ServerUseSsl": "Specifies whether SSL should be used for communication with the server. Set to 'true' to enable SSL, and 'false' to disable it." - }, - "EntryParameters": {} - } - } + ] }, "pam": {} } diff --git a/readme_source.md b/readme_source.md index 4442e7e..36ccab5 100644 --- a/readme_source.md +++ b/readme_source.md @@ -1,31 +1,243 @@ +

+ Azure App Registration and Enterprise Application Universal Orchestrator Extension +

+ +

+ +Integration Status: production +Release +Issues +GitHub Downloads (all assets, all releases) +

+ +

+ + + Support + + · + + Installation + + · + + License + + · + + Related Integrations + +

+ + ## Overview + The Azure App Registration and Enterprise Application Orchestrator extension remotely manages both Azure [App Registration/Application](https://learn.microsoft.com/en-us/entra/identity-platform/certificate-credentials) certificates and [Enterprise Application/Service Principal](https://docs.microsoft.com/en-us/azure/active-directory/develop/enterprise-apps-certificate-credentials) certificates. Application certificates are typically public key only and used for client certificate authentication, while Service Principal certificates are commonly used for [SAML Assertion signing](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/tutorial-manage-certificates-for-federated-single-sign-on). The extension implements the Inventory, Management Add, Management Remove, and Discovery job types. -Certificates used for client authentication by Applications (configured in App Registrations) are represented by the [`AzureApp` store type](docs/azureapp.md), and certificates used for SSO/SAML assertion signing are represented by the [`AzureSP` store type](docs/azuresp.md). Both store types are managed by the same extension. The extension is configured with a single Azure Service Principal that is used to authenticate to the [Microsoft Graph API](https://learn.microsoft.com/en-us/graph/use-the-api). The Azure App Registration and Enterprise Application Orchestrator extension manages certificates for Azure App Registrations (Applications) and Enterprise Applications (Service Principals) differently. +Certificates used for client authentication by Applications (configured in App Registrations) are represented by the [`AzureApp` store type](docs/azureapp.md), and certificates used for SSO/SAML assertion signing are represented by the [`AzureSP` store type](docs/azuresp.md). Both store types are managed by the same extension. The extension is configured with a single Azure Service Principal that is used to authenticate to the [Microsoft Graph API](https://learn.microsoft.com/en-us/graph/use-the-api). The Azure App Registration and Enterprise Application Orchestrator extension manages certificates for Azure App Registrations (Applications) and Enterprise Applications (Service Principals) differently. ## Installation -Before installing the Azure App Registration and Enterprise Application Orchestrator extension, it's recommended to install [kfutil](https://github.com/Keyfactor/kfutil). Kfutil is a command-line tool that simplifies the process of creating store types, installing extensions, and instantiating certificate stores in Keyfactor Command. +Before installing the Azure App Registration and Enterprise Application Universal Orchestrator extension, it's recommended to install [kfutil](https://github.com/Keyfactor/kfutil). Kfutil is a command-line tool that simplifies the process of creating store types, installing extensions, and instantiating certificate stores in Keyfactor Command. + +The Azure App Registration and Enterprise Application Universal Orchestrator extension implements 2 Certificate Store Types. Depending on your use case, you may elect to install one, or all of these Certificate Store Types. An overview for each type is linked below: +* [Azure App Registration (Application)](docs/azureapp.md) +* [Azure Enterprise Application (Service Principal)](docs/azuresp.md) + +
Azure App Registration (Application) + + +1. Follow the [requirements section](docs/azureapp.md#requirements) to configure a Service Account and grant necessary API permissions. + +
Requirements + + ### Azure Service Principal (Graph API Authentication) + + The Azure App Registration and Enterprise Application Orchestrator extension uses an [Azure Service Principal](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals?tabs=browser) for authentication. Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal) to create a service principal. Currently, Client Secret authentication is supported. The Service Principal must have the following API Permission: + - **_Microsoft Graph Application Permissions_**: + - `Application.ReadWrite.All` (_not_ Delegated; Admin Consent) - Allows the app to create, read, update and delete applications and service principals without a signed-in user. + + > For more information on Admin Consent for App-only access (also called "Application Permissions"), see the [primer on application-only access](https://learn.microsoft.com/en-us/azure/active-directory/develop/app-only-access-primer). + + Alternatively, the Service Principal can be granted the `Application.ReadWrite.OwnedBy` permission if the Service Principal is only intended to manage its own App Registration/Application. + + #### Client Certificate or Client Secret + + Beginning in version 3.0.0, the Azure App Registration and Enterprise Application Orchestrator extension supports both [client certificate authentication](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) and [client secret](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-2-add-a-client-secret) authentication. + + * **Client Secret** - Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-2-add-a-client-secret) to create a Client Secret. This secret will be used as the **Server Password** field in the [Certificate Store Configuration](#certificate-store-configuration) section. + * **Client Certificate** - Create a client certificate key pair with the Client Authentication extended key usage. The client certificate will be used in the ClientCertificate field in the [Certificate Store Configuration](#certificate-store-configuration) section. If you have access to Keyfactor Command, the instructions in this section walk you through enrolling a certificate and ensuring that it's in the correct format. Once enrolled, follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) to add the _public key_ certificate (no private key) to the service principal used for authentication. + + The certificate can be in either of the following formats: + * Base64-encoded PKCS#12 (PFX) with a matching private key. + * Base64-encoded PEM-encoded certificate _and_ PEM-encoded PKCS8 private key. Make sure that the certificate and private key are separated with a newline. The order doesn't matter - the extension will determine which is which. -1. Follow the Requirements section to configure a Service Account and grant the necessary API permissions. + If the private key is encrypted, the encryption password will replace the **Server Password** field in the [Certificate Store Configuration](#certificate-store-configuration) section. - * [Azure App Registration/Application](docs/azureapp.md#requirements) - * [Azure Enterprise Application/Service Principal](docs/azuresp.md#requirements) + > **Creating and Formatting a Client Certificate using Keyfactor Command** + > + > To get started quickly, you can follow the instructions below to create and properly format a client certificate to authenticate to the Microsoft Graph API. + > + > 1. In Keyfactor Command, hover over **Enrollment** and select **PFX Enrollment**. + > 2. Select a **Template** that supports Client Authentication as an extended key usage. + > 3. Populate the certificate subject as appropriate for the Template. It may be sufficient to only populate the Common Name, but consult your IT policy to ensure that this certificate is compliant. + > 4. At the bottom of the page, uncheck the box for **Include Chain**, and select either **PFX** or **PEM** as the certificate Format. + > 5. Make a note of the password on the next page - it won't be shown again. + > 6. Prepare the certificate and private key for Azure and the Orchestrator extension: + > * If you downloaded the certificate in PEM format, use the commands below: + > + > ```shell + > # Verify that the certificate downloaded from Command contains the certificate and private key. They should be in the same file + > cat + > + > # Separate the certificate from the private key + > openssl x509 -in -out pubkeycert.pem + > + > # Base64 encode the certificate and private key + > cat | base64 > clientcertkeypair.pem.base64 + > ``` + > + > * If you downloaded the certificate in PFX format, use the commands below: + > + > ```shell + > # Export the certificate from the PFX file + > openssl pkcs12 -in -clcerts -nokeys -out pubkeycert.pem + > + > # Base64 encode the PFX file + > cat | base64 > clientcert.pfx.base64 + > ``` + > 7. Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) to add the public key certificate to the service principal used for authentication. + > + > You will use `clientcert.[pem|pfx].base64` as the **ClientCertificate** field in the [Certificate Store Configuration](#certificate-store-configuration) section. + + ### Azure App Registration (Application) + + #### Application Certificates + + Application certificates are used for client authentication and are typically public key only. No additional configuration in Azure is necessary to manage Application certificates since all App Registrations can contain any number of [Certificates and Secrets](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app#add-credentials). Unless the Discovery job is used, you should collect the Application IDs for each App Registration that contains certificates to be managed. + + + +
2. Create Certificate Store Types for the Azure App Registration and Enterprise Application Orchestrator extension. * **Using kfutil**: ```shell + # Azure App Registration (Application) kfutil store-types create AzureApp - kfutil store-types create AzureSP ``` * **Manually**: + * [Azure App Registration (Application)](docs/azureapp.md#certificate-store-type-configuration) + +3. Install the Azure App Registration and Enterprise Application Universal Orchestrator extension. + + * **Using kfutil**: On the server that that hosts the Universal Orchestrator, run the following command: + + ```shell + # Windows Server + kfutil orchestrator extension -e azure-application-orchestrator@latest --out "C:\Program Files\Keyfactor\Keyfactor Orchestrator\extensions" + + # Linux + kfutil orchestrator extension -e azure-application-orchestrator@latest --out "/opt/keyfactor/orchestrator/extensions" + ``` + + * **Manually**: Follow the [official Command documentation](https://software.keyfactor.com/Core-OnPrem/Current/Content/InstallingAgents/NetCoreOrchestrator/CustomExtensions.htm?Highlight=extensions) to install the latest [Azure App Registration and Enterprise Application Universal Orchestrator extension](https://github.com/Keyfactor/azure-application-orchestrator/releases/latest). + +4. Create new certificate stores in Keyfactor Command for the Sample Universal Orchestrator extension. + * [Azure App Registration (Application)](docs/azureapp.md#certificate-store-configuration) +
+ +
Azure Enterprise Application (Service Principal) + + +1. Follow the [requirements section](docs/azuresp.md#requirements) to configure a Service Account and grant necessary API permissions. + +
Requirements + + ### Azure Service Principal (Graph API Authentication) + + The Azure App Registration and Enterprise Application Orchestrator extension uses an [Azure Service Principal](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals?tabs=browser) for authentication. Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal) to create a service principal. Currently, Client Secret authentication is supported. The Service Principal must have the following API Permission: + - **_Microsoft Graph Application Permissions_**: + - `Application.ReadWrite.All` (_not_ Delegated; Admin Consent) - Allows the app to create, read, update and delete applications and service principals without a signed-in user. + + > For more information on Admin Consent for App-only access (also called "Application Permissions"), see the [primer on application-only access](https://learn.microsoft.com/en-us/azure/active-directory/develop/app-only-access-primer). + + Alternatively, the Service Principal can be granted the `Application.ReadWrite.OwnedBy` permission if the Service Principal is only intended to manage its own App Registration/Application. + + #### Client Certificate or Client Secret + + Beginning in version 3.0.0, the Azure App Registration and Enterprise Application Orchestrator extension supports both [client certificate authentication](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) and [client secret](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-2-add-a-client-secret) authentication. + + * **Client Secret** - Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-2-add-a-client-secret) to create a Client Secret. This secret will be used as the **Server Password** field in the [Certificate Store Configuration](#certificate-store-configuration) section. + * **Client Certificate** - Create a client certificate key pair with the Client Authentication extended key usage. The client certificate will be used in the ClientCertificate field in the [Certificate Store Configuration](#certificate-store-configuration) section. If you have access to Keyfactor Command, the instructions in this section walk you through enrolling a certificate and ensuring that it's in the correct format. Once enrolled, follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) to add the _public key_ certificate (no private key) to the service principal used for authentication. + + The certificate can be in either of the following formats: + * Base64-encoded PKCS#12 (PFX) with a matching private key. + * Base64-encoded PEM-encoded certificate _and_ PEM-encoded PKCS8 private key. Make sure that the certificate and private key are separated with a newline. The order doesn't matter - the extension will determine which is which. + + If the private key is encrypted, the encryption password will replace the **Server Password** field in the [Certificate Store Configuration](#certificate-store-configuration) section. + + > **Creating and Formatting a Client Certificate using Keyfactor Command** + > + > To get started quickly, you can follow the instructions below to create and properly format a client certificate to authenticate to the Microsoft Graph API. + > + > 1. In Keyfactor Command, hover over **Enrollment** and select **PFX Enrollment**. + > 2. Select a **Template** that supports Client Authentication as an extended key usage. + > 3. Populate the certificate subject as appropriate for the Template. It may be sufficient to only populate the Common Name, but consult your IT policy to ensure that this certificate is compliant. + > 4. At the bottom of the page, uncheck the box for **Include Chain**, and select either **PFX** or **PEM** as the certificate Format. + > 5. Make a note of the password on the next page - it won't be shown again. + > 6. Prepare the certificate and private key for Azure and the Orchestrator extension: + > * If you downloaded the certificate in PEM format, use the commands below: + > + > ```shell + > # Verify that the certificate downloaded from Command contains the certificate and private key. They should be in the same file + > cat + > + > # Separate the certificate from the private key + > openssl x509 -in -out pubkeycert.pem + > + > # Base64 encode the certificate and private key + > cat | base64 > clientcertkeypair.pem.base64 + > ``` + > + > * If you downloaded the certificate in PFX format, use the commands below: + > + > ```shell + > # Export the certificate from the PFX file + > openssl pkcs12 -in -clcerts -nokeys -out pubkeycert.pem + > + > # Base64 encode the PFX file + > cat | base64 > clientcert.pfx.base64 + > ``` + > 7. Follow [Microsoft's documentation](https://learn.microsoft.com/en-us/graph/auth-register-app-v2#option-1-add-a-certificate) to add the public key certificate to the service principal used for authentication. + > + > You will use `clientcert.[pem|pfx].base64` as the **ClientCertificate** field in the [Certificate Store Configuration](#certificate-store-configuration) section. + + ### Enterprise Application (Service Principal) - * [Azure App Registration/Application](docs/azureapp.md#certificate-store-type-configuration) - * [Azure Enterprise Application/Service Principal](docs/azuresp.md#certificate-store-type-configuration) + #### Service Principal Certificates -3. Install the Azure App Registration and Enterprise Application Orchestrator extension. + Service Principal certificates are typically used for SAML Token signing. Service Principals are created from Enterprise Applications, and will mostly be configured with a variation of Microsoft's [SAML-based single sign-on](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/add-application-portal) documentation. For more information on the mechanics of the Service Principal certificate management capabilities of this extension, please see the [mechanics](#extension-mechanics) section. + + + +
+ +2. Create Certificate Store Types for the Azure App Registration and Enterprise Application Orchestrator extension. + + * **Using kfutil**: + + ```shell + # Azure Enterprise Application (Service Principal) + kfutil store-types create AzureSP + ``` + + * **Manually**: + * [Azure Enterprise Application (Service Principal)](docs/azuresp.md#certificate-store-type-configuration) + +3. Install the Azure App Registration and Enterprise Application Universal Orchestrator extension. * **Using kfutil**: On the server that that hosts the Universal Orchestrator, run the following command: @@ -37,10 +249,17 @@ Before installing the Azure App Registration and Enterprise Application Orchestr kfutil orchestrator extension -e azure-application-orchestrator@latest --out "/opt/keyfactor/orchestrator/extensions" ``` - * **Manually**: Follow the [official Command documentation](https://software.keyfactor.com/Core-OnPrem/Current/Content/InstallingAgents/NetCoreOrchestrator/CustomExtensions.htm?Highlight=extensions) to install the latest [Azure App Registration and Enterprise Application Orchestrator extension](https://github.com/Keyfactor/azure-application-orchestrator/releases/latest). + * **Manually**: Follow the [official Command documentation](https://software.keyfactor.com/Core-OnPrem/Current/Content/InstallingAgents/NetCoreOrchestrator/CustomExtensions.htm?Highlight=extensions) to install the latest [Azure App Registration and Enterprise Application Universal Orchestrator extension](https://github.com/Keyfactor/azure-application-orchestrator/releases/latest). + +4. Create new certificate stores in Keyfactor Command for the Sample Universal Orchestrator extension. + * [Azure Enterprise Application (Service Principal)](docs/azuresp.md#certificate-store-configuration) +
+ + +## License -4. Create new certificate stores in Keyfactor Command for the Azure App Registration and Enterprise Application Orchestrator extension. +Apache License 2.0, see [LICENSE](LICENSE). - * [Azure App Registration/Application](docs/azureapp.md#certificate-store-configuration) - * [Azure Enterprise Application/Service Principal](docs/azuresp.md#certificate-store-configuration) +## Related Integrations +See all [Keyfactor Universal Orchestrator extensions](https://github.com/orgs/Keyfactor/repositories?q=orchestrator).