From 76d39d096d9483f0ce7864eb38fb083406dfe9d6 Mon Sep 17 00:00:00 2001 From: Joseph VanWanzeele Date: Tue, 19 Nov 2024 14:58:54 -0500 Subject: [PATCH 1/5] added PKI Role Models --- .../V1/SecretsEngines/PKI/PKIRole.cs | 316 ++++++++++++++++++ .../V1/SecretsEngines/PKI/PKIRoleNames.cs | 20 ++ 2 files changed, 336 insertions(+) create mode 100644 src/VaultSharp/V1/SecretsEngines/PKI/PKIRole.cs create mode 100644 src/VaultSharp/V1/SecretsEngines/PKI/PKIRoleNames.cs diff --git a/src/VaultSharp/V1/SecretsEngines/PKI/PKIRole.cs b/src/VaultSharp/V1/SecretsEngines/PKI/PKIRole.cs new file mode 100644 index 00000000..3320354b --- /dev/null +++ b/src/VaultSharp/V1/SecretsEngines/PKI/PKIRole.cs @@ -0,0 +1,316 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace VaultSharp.V1.SecretsEngines.PKI +{ + public class PKIRole + { + /// + /// If set, clients can request certificates for any domain, regardless of allowed_domains restrictions. See the documentation for more information. + /// + [JsonPropertyName("allow_any_name")] + public bool AllowAnyName { get; set; } + + /// + /// If set, clients can request certificates for the base domains themselves, e.g. "example.com" of domains listed in allowed_domains. + /// This is a separate option as in some cases this can be considered a security threat. See the documentation for more information. + /// + [JsonPropertyName("allow_bare_domains")] + public bool AllowBareDomains { get; set; } + + /// + /// If set, domains specified in allowed_domains can include shell-style glob patterns, e.g. "ftp*.example.com". + /// See the documentation for more information. + /// + [JsonPropertyName("allow_glob_domains")] + public bool AllowGlobDomains { get; set; } + + /// + /// If set, IP Subject Alternative Names are allowed. Any valid IP is accepted and No authorization checking is performed. + /// + [JsonPropertyName("allow_ip_sans")] + public bool AllowIpSans { get; set; } + + /// + /// Whether to allow "localhost" and "localdomain" as a valid common name in a request, independent of allowed_domains value. + /// + [JsonPropertyName("allow_localhost")] + public bool AllowLocalhost { get; set; } + + /// + /// If set, clients can request certificates for subdomains of domains listed in allowed_domains, including wildcard subdomains. + /// See the documentation for more information. + /// + [JsonPropertyName("allow_subdomains")] + public bool AllowSubdomains { get; set; } + + /// + /// + /// + [JsonPropertyName("allow_token_displayname")] + public bool AllowTokenDisplayname { get; set; } + + /// + /// If set, allows certificates with wildcards in the common name to be issued, conforming to RFC 6125's Section 6.4.3; e.g., + /// ".example.net" or "bz.example.net". See the documentation for more information. + /// + [JsonPropertyName("allow_wildcard_certificates")] + public bool AllowWildcardIdentifiers { get; set; } + + /// + /// Specifies the domains this role is allowed to issue certificates for. This is used with the allow_bare_domains, allow_subdomains, + /// and allow_glob_domains to determine matches for the common name, DNS-typed SAN entries, and Email-typed SAN entries of certificates. + /// See the documentation for more information. This parameter accepts a comma-separated string or list of domains. + /// + [JsonPropertyName("allowed_domains")] + public List AllowedDomains { get; set; } + + /// + /// If set, Allowed domains can be specified using identity template policies. Non-templated domains are also permitted. + /// + [JsonPropertyName("allowed_domains_template")] + public bool AllowedDomainsTemplate { get; set; } + + /// + /// If set, an array of allowed serial numbers to put in Subject. These values support globbing. + /// + [JsonPropertyName("allowed_serial_numbers")] + public List AllowedSerialNumbers { get; set; } + + /// + /// If set, an array of allowed URIs for URI Subject Alternative Names. Any valid URI is accepted, these values support globbing. + /// + [JsonPropertyName("allowed_uri_sans")] + public List AllowedUriSans { get; set; } + + /// + /// If set, Allowed URI SANs can be specified using identity template policies. Non-templated URI SANs are also permitted. + /// + [JsonPropertyName("allowed_uri_sans_template")] + public bool AllowedUriSansTemplate { get; set; } + + /// + /// If set, an array of allowed user-ids to put in user system login name specified here: https://www.rfc-editor.org/rfc/rfc1274#section-9.3.1 + /// + [JsonPropertyName("allowed_user_ids")] + public List AllowedUserIds { get; set; } + + /// + /// Mark Basic Constraints valid when issuing non-CA certificates. + /// + [JsonPropertyName("basic_constraints_valid_for_non_ca")] + public bool BasicConstraintsValidForNonCA { get; set; } + + /// + /// If set, certificates are flagged for client auth use. Defaults to true. See also RFC 5280 Section 4.2.1.12. + /// + [JsonPropertyName("client_flag")] + public bool ClientFlag { get; set; } + + /// + /// List of allowed validations to run against the Common Name field. Values can include 'email' to validate the CN is a email address, + /// 'hostname' to validate the CN is a valid hostname (potentially including wildcards). When multiple validations are specified, + /// these take OR semantics (either email OR hostname are allowed). The special value 'disabled' allows disabling all CN name validations, + /// allowing for arbitrary non-Hostname, non-Email address CNs. + /// + [JsonPropertyName("cn_validations")] + public List CNVAlidations { get; set; } + + /// + /// If set, certificates are flagged for code signing use. Defaults to false. See also RFC 5280 Section 4.2.1.12. + /// + [JsonPropertyName("code_signing_flag")] + public bool CodeSigningFlag { get; set; } + + /// + /// If set, Country will be set to this value in certificates issued by this role. + /// + [JsonPropertyName("country")] + public string Country { get; set; } + + /// + /// If set, certificates are flagged for email protection use. Defaults to false. See also RFC 5280 Section 4.2.1.12. + /// + [JsonPropertyName("email_protection_flag")] + public bool EmailProtectionFlag { get; set; } + + /// + /// If set, only valid host names are allowed for CN and DNS SANs, and the host part of email addresses. Defaults to true. + /// + [JsonPropertyName("enforce_hostnames")] + public bool EnforceHostnames { get; set; } + + /// + /// A comma-separated string or list of extended key usages. Valid values can be found at https://golang.org/pkg/crypto/x509/#ExtKeyUsage -- + /// simply drop the "ExtKeyUsage" part of the name. To remove all key usages from being set, set this value to an empty list. + /// See also RFC 5280 Section 4.2.1.12. + /// + [JsonPropertyName("ext_key_usage")] + public List ExtKeyUsage { get; set; } + + /// + /// If set, certificates issued/signed against this role will have Vault leases attached to them. Defaults to "false". + /// Certificates can be added to the CRL by "vault revoke " when certificates are associated with leases. + /// It can also be done using the "pki/revoke" endpoint. However, when lease generation is disabled, invoking "pki/revoke" would be the only + /// way to add the certificates to the CRL. When large number of certificates are generated with long lifetimes, it is recommended that lease generation be disabled, + /// as large amount of leases adversely affect the startup time of Vault. + /// + [JsonPropertyName("generate_lease")] + public bool GenerateLease { get; set; } + + /// + /// Reference to the issuer used to sign requests serviced by this role. + /// + [JsonPropertyName("issuer_ref")] + public string IssuerRef { get; set; } + + /// + /// The number of bits to use. Allowed values are: + /// 0 (universal default); + /// with rsa key_type: 2048 (default), 3072, or 4096; + /// with ec key_type: 224, 256 (default), 384, or 521; + /// ignored with ed25519. + /// + [JsonPropertyName("key_bits")] + public int KeyBits { get; set; } + + /// + /// The type of key to use; defaults to RSA. "rsa" "ec", "ed25519" and "any" are the only valid values. + /// + [JsonPropertyName("key_type")] + public string KeyType { get; set; } + + /// + /// A comma-separated string or list of key usages (not extended key usages). + /// Valid values can be found at https://golang.org/pkg/crypto/x509/#KeyUsage -- simply drop the "KeyUsage" part of the name. + /// To remove all key usages from being set, set this value to an empty list. See also RFC 5280 Section 4.2.1.3. + /// + [JsonPropertyName("key_usage")] + public List KeyUsage { get; set; } + + /// + /// If set, Locality will be set to this value in certificates issued by this role. + /// + [JsonPropertyName("locality")] + public List Locality { get; set; } + + /// + /// The maximum allowed lease duration. If not set, defaults to the system maximum lease TTL. + /// + [JsonPropertyName("max_ttl")] + public long MaxTTL { get; set; } + + /// + /// If set, certificates issued/signed against this role will not be stored in the storage backend. + /// This can improve performance when issuing large numbers of certificates. However, certificates issued in this way cannot be enumerated or revoked, + /// so this option is recommended only for certificates that are non-sensitive, or extremely short-lived. + /// This option implies a value of "false" for "generate_lease". + /// + [JsonPropertyName("no_store")] + public bool NoStore { get; set; } + + /// + /// If set, if a client attempts to issue or sign a certificate with attached cert_metadata to store, the issuance / signing instead fails. + /// + [JsonPropertyName("no_store_metadata")] + public bool NoStoreMetadata { get; set; } + + /// + /// Set the not after field of the certificate with specified date value. The value format should be given in UTC format YYYY-MM-ddTHH:MM:SSZ. + /// + [JsonPropertyName("not_after")] + public string NotAfter { get; set; } + + /// + /// The duration in seconds before now which the certificate needs to be backdated by. + /// + [JsonPropertyName("not_before_duration")] + public long NotBeforeDuration { get; set; } + + /// + /// If set, O (Organization) will be set to this value in certificates issued by this role. + /// + [JsonPropertyName("organization")] + public List Organization { get; set; } + + /// + /// If set, OU (OrganizationalUnit) will be set to this value in certificates issued by this role. + /// + [JsonPropertyName("ou")] + public List OrganizationalUnit { get; set; } + + /// + /// A comma-separated string or list of policy OIDs, or a JSON list of qualified policy information, + /// which must include an oid, and may include a notice and/or cps url, + /// using the form [{"oid"="1.3.6.1.4.1.7.8","notice"="I am a user Notice"}, {"oid"="1.3.6.1.4.1.44947.1.2.4 ","cps"="https://example.com"}]. + /// + [JsonPropertyName("policy_identifiers")] + public List PolicyIdentifiers { get; set; } + + /// + /// If set, Postal Code will be set to this value in certificates issued by this role. + /// + [JsonPropertyName("postal_code")] + public List PostalCode { get; set; } + + /// + /// If set, Province will be set to this value in certificates issued by this role. + /// + [JsonPropertyName("province")] + public List Province { get; set; } + + /// + /// If set to false, makes the 'common_name' field optional while generating a certificate. + /// + [JsonPropertyName("require_cn")] + public bool RequireCN { get; set; } + + /// + /// If set, certificates are flagged for server auth use. Defaults to true. See also RFC 5280 Section 4.2.1.12. + /// + [JsonPropertyName("server_flag")] + public bool ServerFlag { get; set; } + + /// + /// The number of bits to use in the signature algorithm; accepts 256 for SHA-2-256, 384 for SHA-2-384, and 512 for SHA-2-512. + /// Defaults to 0 to automatically detect based on key length (SHA-2-256 for RSA keys, and matching the curve size for NIST P-Curves). + /// + [JsonPropertyName("signature_bits")] + public int SignatureBits { get; set; } + + /// + /// If set, Street Address will be set to this value in certificates issued by this role. + /// + [JsonPropertyName("street_address")] + public List StreetAddress { get; set; } + + /// + /// The lease duration (validity period of the certificate) if no specific lease duration is requested. + /// The lease duration controls the expiration of certificates issued by this backend. Defaults to the system default value or the value of max_ttl, + /// whichever is shorter. + /// + [JsonPropertyName("ttl")] + public long TTL { get; set; } + + /// + /// If set, when used with a signing profile, the common name in the CSR will be used. This does not include any requested Subject Alternative Names; + /// use use_csr_sans for that. + /// Defaults to true. + /// + [JsonPropertyName("use_csr_common_name")] + public bool UseCsrCommonName { get; set; } + + /// + /// If set, when used with a signing profile, the SANs in the CSR will be used. This does not include the Common Name (cn); use use_csr_common_name for that. + /// Defaults to true. + /// + [JsonPropertyName("use_csr_sans")] + public bool UseCsrSans { get; set; } + + /// + /// Whether or not to use PSS signatures when using a RSA key-type issuer. Defaults to false. + /// + [JsonPropertyName("use_pss")] + public bool UsePss { get; set; } + } +} diff --git a/src/VaultSharp/V1/SecretsEngines/PKI/PKIRoleNames.cs b/src/VaultSharp/V1/SecretsEngines/PKI/PKIRoleNames.cs new file mode 100644 index 00000000..709f716d --- /dev/null +++ b/src/VaultSharp/V1/SecretsEngines/PKI/PKIRoleNames.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace VaultSharp.V1.SecretsEngines.PKI +{ + public class PKIRoleNames + /// + /// Vault Response Model containing list of role keys (role names) + /// + { + /// + /// Gets or sets list of certificate keys (serial numbers) + /// + /// + /// List of certificate keys (serial numbers) + /// + [JsonPropertyName("keys")] + public List Keys { get; set; } + } +} From 2cd88161b923aa04e356dce2c07015c9e26da9b5 Mon Sep 17 00:00:00 2001 From: Joseph VanWanzeele Date: Tue, 19 Nov 2024 17:13:07 -0500 Subject: [PATCH 2/5] Added method placeholder definitions for role management --- .../SecretsEngines/PKI/IPKISecretsEngine.cs | 68 +++++++++++++++++++ .../PKI/PKISecretsEngineProvider.cs | 20 ++++++ 2 files changed, 88 insertions(+) diff --git a/src/VaultSharp/V1/SecretsEngines/PKI/IPKISecretsEngine.cs b/src/VaultSharp/V1/SecretsEngines/PKI/IPKISecretsEngine.cs index 0a3fca95..c743b505 100644 --- a/src/VaultSharp/V1/SecretsEngines/PKI/IPKISecretsEngine.cs +++ b/src/VaultSharp/V1/SecretsEngines/PKI/IPKISecretsEngine.cs @@ -200,5 +200,73 @@ public interface IPKISecretsEngine /// The secret with the certificate chain data /// Task> ReadDefaultIssuerCertificateChainAsync(CertificateFormat certificateFormat, string pkiBackendMountPoint = null); + + /// + /// Retrieves a list of all PKI role names. + /// + /// + /// The mount point for the PKI backend. Defaults to + /// Provide a value only if you have customized the PKI mount point. + /// + /// + /// The secret with the list of role names + /// + Task> ListRolesAsync(string pkiBackendMountPoint = null); + + /// + /// Retrieves details for a role by name. + /// + /// + /// The name of the role to be retrieved. + /// + /// + /// The mount point for the PKI backend. Defaults to + /// Provide a value only if you have customized the PKI mount point. + /// + /// + /// The secret with the role data + /// + Task> ReadRoleAsync(string pkiRoleName, string pkiBackendMountPoint = null); + + /// + /// Writes a role definition with the provided pkiRoleName. + /// if the role already exists, it will overwrite it with all values (including elided parameters) + /// To update a role with only the provided parameters, use 'PatchRoleAsync' + /// + /// + /// The name of the role to be retrieved. + /// + /// + /// The PKIRole object containing the values. + /// + /// + /// The mount point for the PKI backend. Defaults to + /// Provide a value only if you have customized the PKI mount point. + /// + /// + /// The secret with the role data + /// + Task> WriteRoleAsync(string pkiRoleName, PKIRole pkiRoleDetails, string pkiBackendMountPoint = null); + + /// + /// Patches an existing role definition with the provided pkiRoleName. + /// Use this method for updating (not replacing) a role definition with the non-null values provided in the pkiRoleDetails object. + /// This method requires the role definition to already exist, and will update it with any non-null values set in pkiRoleDetails. + /// To replace a role definition, use the 'WriteRoleAsync' method. + /// + /// + /// The name of the role to be retrieved. + /// + /// + /// The PKIRole object containing the values. + /// + /// + /// The mount point for the PKI backend. Defaults to + /// Provide a value only if you have customized the PKI mount point. + /// + /// + /// The secret with the role data + /// + Task> PatchRoleAsync(string pkiRoleName, PKIRole pkiRoleDetails, string pkiBackendMountPoint = null); } } \ No newline at end of file diff --git a/src/VaultSharp/V1/SecretsEngines/PKI/PKISecretsEngineProvider.cs b/src/VaultSharp/V1/SecretsEngines/PKI/PKISecretsEngineProvider.cs index 2e89fca9..3273bf5a 100644 --- a/src/VaultSharp/V1/SecretsEngines/PKI/PKISecretsEngineProvider.cs +++ b/src/VaultSharp/V1/SecretsEngines/PKI/PKISecretsEngineProvider.cs @@ -126,5 +126,25 @@ public async Task> ReadDefaultIssuerCertificateChainAsyn var certificateDataSecret = await _polymath.MakeVaultApiRequest>(pkiBackendMountPoint ?? _polymath.VaultClientSettings.SecretsEngineMountPoints.PKI, path, HttpMethod.Get, unauthenticated: true).ConfigureAwait(_polymath.VaultClientSettings.ContinueAsyncTasksOnCapturedContext); return certificateDataSecret; } + + public Task> ListRolesAsync(string pkiBackendMountPoint = null) + { + throw new NotImplementedException(); + } + + public Task> ReadRoleAsync(string pkiRoleName, string pkiBackendMountPoint = null) + { + throw new NotImplementedException(); + } + + public Task> WriteRoleAsync(string pkiRoleName, PKIRole pkiRoleDetails, string pkiBackendMountPoint = null) + { + throw new NotImplementedException(); + } + + public Task> PatchRoleAsync(string pkiRoleName, PKIRole pkiRoleDetails, string pkiBackendMountPoint = null) + { + throw new NotImplementedException(); + } } } \ No newline at end of file From 1a39d582753a1879b3356a3e77498001836476d2 Mon Sep 17 00:00:00 2001 From: Joseph VanWanzeele Date: Wed, 20 Nov 2024 14:07:24 -0500 Subject: [PATCH 3/5] Added methods for generating a new root CA for the PKI Secrets Engine. Added PKI Samples. --- .../SecretsEngines/PKI/GenerateRootRequest.cs | 207 ++++++++++++++++++ .../PKI/GenerateRootResponse.cs | 61 ++++++ .../SecretsEngines/PKI/IPKISecretsEngine.cs | 17 ++ .../PKI/PKISecretsEngineProvider.cs | 40 +++- .../Secrets/PKISecretsBackendSamples.cs | 79 +++++++ .../Backends/SecretsBackendSamples.cs | 3 + 6 files changed, 395 insertions(+), 12 deletions(-) create mode 100644 src/VaultSharp/V1/SecretsEngines/PKI/GenerateRootRequest.cs create mode 100644 src/VaultSharp/V1/SecretsEngines/PKI/GenerateRootResponse.cs create mode 100644 test/VaultSharp.Samples/Backends/Secrets/PKISecretsBackendSamples.cs diff --git a/src/VaultSharp/V1/SecretsEngines/PKI/GenerateRootRequest.cs b/src/VaultSharp/V1/SecretsEngines/PKI/GenerateRootRequest.cs new file mode 100644 index 00000000..660b0b0b --- /dev/null +++ b/src/VaultSharp/V1/SecretsEngines/PKI/GenerateRootRequest.cs @@ -0,0 +1,207 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace VaultSharp.V1.SecretsEngines.PKI +{ + public class GenerateRootRequest + { + /// + /// The requested Subject Alternative Names, if any, in a comma-delimited list. May contain both DNS names and email addresses. + /// + [JsonPropertyName("alt_names")] + public string AltNames { get; set; } + + /// + /// The requested common name; if you want more than one, specify the alternative names in the alt_names map. If not specified when signing, + /// the common name will be taken from the CSR; other names must still be specified in alt_names or ip_sans. + /// + [JsonPropertyName("common_name")] + public string CommonName { get; set; } + + /// + /// If set, Country will be set to this value. + /// + [JsonPropertyName("country")] + public List Country { get; set; } + + /// + /// If true, the Common Name will not be included in DNS or Email Subject Alternate Names. Defaults to false (CN is included). + /// + [JsonPropertyName("exclude_cn_from_sans")] + public bool ExcludeCNFromSans { get; set; } + + /// + /// Format for returned data. Can be "pem", "der", or "pem_bundle". If "pem_bundle", any private key and issuing cert will be appended to the certificate pem. + /// If "der", the value will be base64 encoded. Defaults to "pem". + /// + [JsonPropertyName("format")] + public string Format { get; set; } + + /// + /// The requested IP SANs, if any + /// + [JsonPropertyName("ip_sans")] + public List IpSans { get; set; } + + /// + /// Provide a name to the generated or existing issuer, the name must be unique across all issuers and not be the reserved value 'default' + /// + [JsonPropertyName("issuer_name")] + public string IssuerName { get; set; } + + /// + /// The number of bits to use. Allowed values are 0 (universal default); + /// with rsa key_type: 2048 (default), 3072, 4096 or 8192; + /// with ec key_type: 224, 256 (default), 384, or 521; + /// ignored with ed25519. + /// + [JsonPropertyName("key_bits")] + public int KeyBits { get; set; } + + /// + /// Provide a name to the generated or existing key, the name must be unique across all keys and not be the reserved value 'default' + /// + [JsonPropertyName("key_name")] + public string KeyName { get; set; } + + /// + /// Reference to a existing key; either "default" for the configured default key, an identifier or the name assigned to the key. + /// + [JsonPropertyName("key_ref")] + public string KeyRef { get; set; } + + /// + /// The type of key to use; defaults to RSA. "rsa" "ec" and "ed25519" are the only valid values. + /// + [JsonPropertyName("key_type")] + public string KeyType { get; set; } + + /// + /// If set, Locality will be set to this value. + /// + [JsonPropertyName("locality")] + public List Locality { get; set; } + + /// + /// The name of the managed key to use when the exported type is kms. When kms type is the key type, this field or managed_key_name is required. + /// Ignored for other types. + /// + [JsonPropertyName("managed_key_id")] + public string ManagedKeyId { get; set; } + + /// + /// The name of the managed key to use when the exported type is kms. When kms type is the key type, this field or managed_key_id is required. + /// Ignored for other types. + /// + [JsonPropertyName("managed_key_name")] + public string ManagedKeyName { get; set; } + + /// + /// The maximum allowable path length + /// + [JsonPropertyName("max_path_length")] + public int MaxPathLength { get; set; } + + /// + /// Set the not after field of the certificate with specified date value. + /// The value format should be given in UTC format YYYY-MM-ddTHH:MM:SSZ + /// + [JsonPropertyName("not_after")] + public string NotAfter { get; set; } + + /// + /// The duration before now which the certificate needs to be backdated by. + /// + [JsonPropertyName("not_before_duration")] + public string NotBeforeDuration { get; set; } + + /// + /// If set, O (Organization) will be set to this value. + /// + [JsonPropertyName("organization")] + public List Organization { get; set; } + + /// + /// Requested other SANs, in an array with the format ;UTF8: for each entry. + /// + [JsonPropertyName("other_sans")] + public List OtherSans { get; set; } + + /// + /// If set, OU (OrganizationalUnit) will be set to this value. + /// + [JsonPropertyName("ou")] + public List OrganizationalUnit { get; set; } + + /// + /// Domains for which this certificate is allowed to sign or issue child certificates. + /// If set, all DNS names (subject and alt) on child certs must be exact matches or subsets of the given domains + /// (see https://tools.ietf.org/html/rfc5280#section-4.2.1.10). + /// + [JsonPropertyName("permitted_dns_domains")] + public List PermittedDnsDomains { get; set; } + + /// + /// If set, Postal Code will be set to this value. + /// + [JsonPropertyName("postal_code")] + public List PostalCode { get; set; } + + /// + /// Format for the returned private key. + /// Generally the default will be controlled by the "format" parameter as either base64-encoded DER or PEM-encoded DER. + /// However, this can be set to "pkcs8" to have the returned private key contain base64-encoded pkcs8 or PEM-encoded pkcs8 instead. + /// Defaults to "der". + /// + [JsonPropertyName("private_key_format")] + public string PrivateKeyFormat { get; set; } + + /// + /// If set, Province will be set to this value. + /// + [JsonPropertyName("province")] + public List Province { get; set; } + + /// + /// The Subject's requested serial number, if any. See RFC 4519 Section 2.31 'serialNumber' for a description of this field. + /// If you want more than one, specify alternative names in the alt_names map using OID 2.5.4.5. + /// This has no impact on the final certificate's Serial Number field. + /// + [JsonPropertyName("serial_number")] + public string SerialNumber { get; set; } + + /// + /// The number of bits to use in the signature algorithm; accepts 256 for SHA-2-256, 384 for SHA-2-384, and 512 for SHA-2-512. + /// Defaults to 0 to automatically detect based on key length (SHA-2-256 for RSA keys, and matching the curve size for NIST P-Curves). + /// + [JsonPropertyName("signature_bits")] + public int SignatureBits { get; set; } + + /// + /// If set, Street Address will be set to this value. + /// + [JsonPropertyName("street_address")] + public List StreetAddress { get; set; } + + /// + /// The requested Time To Live for the certificate; sets the expiration date. + /// If not specified the role default, backend default, or system default TTL is used, in that order. Cannot be larger than the mount max TTL. + /// Note: this only has an effect when generating a CA cert or signing a CA cert, not when generating a CSR for an intermediate CA. + /// + [JsonPropertyName("ttl")] + public string TTL { get; set; } + + /// + /// The requested URI SANs, if any, in a comma-delimited list. + /// + [JsonPropertyName("uri_sans")] + public List UriSans { get; set; } + + /// + /// Whether or not to use PSS signatures when using a RSA key-type issuer. Defaults to false. + /// + [JsonPropertyName("use_pss")] + public bool UsePss { get; set; } + + } +} diff --git a/src/VaultSharp/V1/SecretsEngines/PKI/GenerateRootResponse.cs b/src/VaultSharp/V1/SecretsEngines/PKI/GenerateRootResponse.cs new file mode 100644 index 00000000..16c7af9e --- /dev/null +++ b/src/VaultSharp/V1/SecretsEngines/PKI/GenerateRootResponse.cs @@ -0,0 +1,61 @@ +using System.Text.Json.Serialization; + +namespace VaultSharp.V1.SecretsEngines.PKI +{ + public class GenerateRootResponse + { + /// + /// The generated self-signed CA certificate. + /// + [JsonPropertyName("certificate")] + public string Certificate { get; set; } + + /// + /// The expiration of the given issuer. + /// + [JsonPropertyName("expiration")] + public long Expiration { get; set; } + + /// + /// The ID of the issuer + /// + [JsonPropertyName("issuer_id")] + public string IssuerId { get; set; } + + /// + /// The name of the issuer. + /// + [JsonPropertyName("issuer_name")] + public string IssuerName { get; set; } + + /// + /// The issuing certificate authority. + /// + [JsonPropertyName("issuing_ca")] + public string IssuingCA { get; set; } + + /// + /// The ID of the key. + /// + [JsonPropertyName("key_id")] + public string KeyId { get; set; } + + /// + /// The key name if given. + /// + [JsonPropertyName("key_name")] + public string KeyName { get; set; } + + /// + /// The private key if exported was specified + /// + [JsonPropertyName("private_key")] + public string PrivateKey { get; set; } + + /// + /// The requested Subject's named serial number. + /// + [JsonPropertyName("serial_number")] + public string SerialNumber { get; set; } + } +} diff --git a/src/VaultSharp/V1/SecretsEngines/PKI/IPKISecretsEngine.cs b/src/VaultSharp/V1/SecretsEngines/PKI/IPKISecretsEngine.cs index c743b505..0ae8e894 100644 --- a/src/VaultSharp/V1/SecretsEngines/PKI/IPKISecretsEngine.cs +++ b/src/VaultSharp/V1/SecretsEngines/PKI/IPKISecretsEngine.cs @@ -201,6 +201,23 @@ public interface IPKISecretsEngine /// Task> ReadDefaultIssuerCertificateChainAsync(CertificateFormat certificateFormat, string pkiBackendMountPoint = null); + /// + /// Generates a new self-signed CA certificate and private key. If the path ends with exported, the private key will be returned in the response; + /// if it is internal the private key will not be returned and cannot be retrieved later; + /// if it is existing, the key specified by key_ref will be reused for this root; + /// if it is kms, a managed key will be used. + /// + /// valid values are "exported", "internal", and "existing" + /// The configuration values to use for the newly generated root certificate + /// + /// The mount point for the PKI backend. Defaults to + /// Provide a value only if you have customized the PKI mount point. + /// + /// The secret with the generated root response + /// if the type was "exported", the private key will be included. + /// + Task> GenerateRootAsync(string type, GenerateRootRequest generateRootRequest, string pkiBackendMountPoint = null); + /// /// Retrieves a list of all PKI role names. /// diff --git a/src/VaultSharp/V1/SecretsEngines/PKI/PKISecretsEngineProvider.cs b/src/VaultSharp/V1/SecretsEngines/PKI/PKISecretsEngineProvider.cs index 3273bf5a..b19e0778 100644 --- a/src/VaultSharp/V1/SecretsEngines/PKI/PKISecretsEngineProvider.cs +++ b/src/VaultSharp/V1/SecretsEngines/PKI/PKISecretsEngineProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Net.Http; using System.Threading.Tasks; using VaultSharp.Core; @@ -26,7 +27,7 @@ public async Task> GetCredentialsAsync(string pki return result; } - + public async Task> SignCertificateAsync(string pkiRoleName, SignCertificatesRequestOptions signCertificatesRequestOptions, string pkiBackendMountPoint = null, string wrapTimeToLive = null) { Checker.NotNull(pkiRoleName, "pkiRoleName"); @@ -77,7 +78,7 @@ public async Task ReadCACertificateAsync(CertificateFormat c : CertificateFormat.der; var result = await _polymath.MakeVaultApiRequest(pkiBackendMountPoint ?? _polymath.VaultClientSettings.SecretsEngineMountPoints.PKI, "/ca" + format, HttpMethod.Get, rawResponse: true).ConfigureAwait(_polymath.VaultClientSettings.ContinueAsyncTasksOnCapturedContext); - + return new RawCertificateData { CertificateContent = result, @@ -89,7 +90,7 @@ public async Task> ReadCertificateAsync(string serial { Checker.NotNull(serialNumber, "serialNumber"); - var certificateDataSecret = await _polymath.MakeVaultApiRequest>(pkiBackendMountPoint ?? _polymath.VaultClientSettings.SecretsEngineMountPoints.PKI, "/cert/" + serialNumber , HttpMethod.Get, unauthenticated: true).ConfigureAwait(_polymath.VaultClientSettings.ContinueAsyncTasksOnCapturedContext); + var certificateDataSecret = await _polymath.MakeVaultApiRequest>(pkiBackendMountPoint ?? _polymath.VaultClientSettings.SecretsEngineMountPoints.PKI, "/cert/" + serialNumber, HttpMethod.Get, unauthenticated: true).ConfigureAwait(_polymath.VaultClientSettings.ContinueAsyncTasksOnCapturedContext); return certificateDataSecret; } @@ -106,7 +107,7 @@ public async Task> ListRevokedCertificatesAsync(string p return result; } - + public async Task> ReadDefaultIssuerCertificateChainAsync(CertificateFormat certificateFormat, string pkiBackendMountPoint = null) { if (certificateFormat != CertificateFormat.json @@ -127,24 +128,39 @@ public async Task> ReadDefaultIssuerCertificateChainAsyn return certificateDataSecret; } - public Task> ListRolesAsync(string pkiBackendMountPoint = null) + public async Task> GenerateRootAsync(string type, GenerateRootRequest generateRootRequest, string pkiBackendMountPoint = null) + { + var path = $"/root/generate/{type}"; + var generatedRootDataSecret = await _polymath.MakeVaultApiRequest>(pkiBackendMountPoint ?? _polymath.VaultClientSettings.SecretsEngineMountPoints.PKI, path, HttpMethod.Post, generateRootRequest).ConfigureAwait(_polymath.VaultClientSettings.ContinueAsyncTasksOnCapturedContext); + return generatedRootDataSecret; + } + + public async Task> ListRolesAsync(string pkiBackendMountPoint = null) { - throw new NotImplementedException(); + var path = "/roles"; + var roleNamesSecret = await _polymath.MakeVaultApiRequest>(pkiBackendMountPoint ?? _polymath.VaultClientSettings.SecretsEngineMountPoints.PKI, path, HttpMethod.Get).ConfigureAwait(_polymath.VaultClientSettings.ContinueAsyncTasksOnCapturedContext); + return roleNamesSecret; } - public Task> ReadRoleAsync(string pkiRoleName, string pkiBackendMountPoint = null) + public async Task> ReadRoleAsync(string pkiRoleName, string pkiBackendMountPoint = null) { - throw new NotImplementedException(); + var path = $"/roles/{pkiRoleName}"; + var roleDetailsSecret = await _polymath.MakeVaultApiRequest>(pkiBackendMountPoint ?? _polymath.VaultClientSettings.SecretsEngineMountPoints.PKI, path, HttpMethod.Get).ConfigureAwait(_polymath.VaultClientSettings.ContinueAsyncTasksOnCapturedContext); + return roleDetailsSecret; } - public Task> WriteRoleAsync(string pkiRoleName, PKIRole pkiRoleDetails, string pkiBackendMountPoint = null) + public async Task> WriteRoleAsync(string pkiRoleName, PKIRole pkiRoleDetails, string pkiBackendMountPoint = null) { - throw new NotImplementedException(); + var path = $"/roles/{pkiRoleName}"; + var roleDetailsSecret = await _polymath.MakeVaultApiRequest>(pkiBackendMountPoint ?? _polymath.VaultClientSettings.SecretsEngineMountPoints.PKI, path, HttpMethod.Post, pkiRoleDetails).ConfigureAwait(_polymath.VaultClientSettings.ContinueAsyncTasksOnCapturedContext); + return roleDetailsSecret; } - public Task> PatchRoleAsync(string pkiRoleName, PKIRole pkiRoleDetails, string pkiBackendMountPoint = null) + public async Task> PatchRoleAsync(string pkiRoleName, PKIRole pkiRoleDetails, string pkiBackendMountPoint = null) { - throw new NotImplementedException(); + var path = $"/roles/{pkiRoleName}"; + var roleDetailsSecret = await _polymath.MakeVaultApiRequest>(pkiBackendMountPoint ?? _polymath.VaultClientSettings.SecretsEngineMountPoints.PKI, path, new HttpMethod("PATCH"), pkiRoleDetails).ConfigureAwait(_polymath.VaultClientSettings.ContinueAsyncTasksOnCapturedContext); + return roleDetailsSecret; } } } \ No newline at end of file diff --git a/test/VaultSharp.Samples/Backends/Secrets/PKISecretsBackendSamples.cs b/test/VaultSharp.Samples/Backends/Secrets/PKISecretsBackendSamples.cs new file mode 100644 index 00000000..be829a46 --- /dev/null +++ b/test/VaultSharp.Samples/Backends/Secrets/PKISecretsBackendSamples.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using VaultSharp.V1.SecretsEngines; +using VaultSharp.V1.SecretsEngines.PKI; +using Xunit; + +namespace VaultSharp.Samples +{ + partial class Program + { + private static void RunPKISecretsBackendSamples() + { + var pkiMountPath = Guid.NewGuid().ToString(); + + // mount a new v1 kv + var pkiSecretsEngine = new SecretsEngine + { + Type = SecretsEngineType.PKI, + Config = new Dictionary(), + Path = pkiMountPath + }; + + _authenticatedVaultClient.V1.System.MountSecretBackendAsync(pkiSecretsEngine).Wait(); + + var secretBackends = _authenticatedVaultClient.V1.System.GetSecretBackendsAsync().Result; + DisplayJson(secretBackends); + Assert.True(secretBackends.Data.Any()); + var pkiPath = Guid.NewGuid().ToString(); + + // verify the role list is initially empty + var roleListResponse = _authenticatedVaultClient.V1.Secrets.PKI.ListRolesAsync().Result; + + Assert.Empty(roleListResponse.Data.Keys); + + + // write a new role + var allowedDomain = "test.com"; + var roleName = "testRole"; + + var newRole = new PKIRole() + { + AllowedDomains = new List() { allowedDomain }, + AllowSubdomains = true + }; + + var newRoleResponse = _authenticatedVaultClient.V1.Secrets.PKI.WriteRoleAsync(roleName, newRole, pkiMountPath).Result; + Assert.True(newRoleResponse.Data.AllowedDomains.Exists(d => d == allowedDomain)); + + // verify it shows up in the list now + roleListResponse = _authenticatedVaultClient.V1.Secrets.PKI.ListRolesAsync().Result; + + Assert.Single(roleListResponse.Data.Keys); + + // generate a new root CA + var newRoot = new GenerateRootRequest() + { + CommonName = "test.com" + }; + + var newRootResponse = _authenticatedVaultClient.V1.Secrets.PKI.GenerateRootAsync("exported", newRoot).Result; + + Assert.NotEmpty(newRootResponse.Data.PrivateKey); + + // issue a cert + + var signCertOptions = new SignCertificatesRequestOptions() + { + CommonName = "my.test.com" + }; + + var newCertResponse = _authenticatedVaultClient.V1.Secrets.PKI.SignCertificateAsync(roleName, signCertOptions, pkiMountPath).Result; + + Assert.NotEmpty(newCertResponse.Data.CertificateContent); + + _authenticatedVaultClient.V1.System.UnmountSecretBackendAsync(pkiMountPath).Wait(); + } + } +} diff --git a/test/VaultSharp.Samples/Backends/SecretsBackendSamples.cs b/test/VaultSharp.Samples/Backends/SecretsBackendSamples.cs index 23e5684a..7f78e2c4 100644 --- a/test/VaultSharp.Samples/Backends/SecretsBackendSamples.cs +++ b/test/VaultSharp.Samples/Backends/SecretsBackendSamples.cs @@ -24,6 +24,9 @@ private static void RunSecretsEngineSamples() Console.WriteLine("\n RunTransitSecretsBackendSamples \n"); RunTransitSecretsBackendSamples(); + + Console.WriteLine("\n RunPkiSecretsBackendSamples \n"); + RunPKISecretsBackendSamples(); } } } From b9a75913a73e84059d5d5c4e5f0dc2ded717c4fc Mon Sep 17 00:00:00 2001 From: Joseph VanWanzeele Date: Wed, 20 Nov 2024 16:27:16 -0500 Subject: [PATCH 4/5] Set defaults, samples are passing --- .../SecretsEngines/PKI/GenerateRootRequest.cs | 15 +++++++++-- .../V1/SecretsEngines/PKI/PKIRole.cs | 26 ++++++++++++------- .../PKI/PKISecretsEngineProvider.cs | 19 ++++++++++++-- .../Secrets/PKISecretsBackendSamples.cs | 19 ++++++++------ test/VaultSharp.Samples/Program.cs | 4 +-- 5 files changed, 59 insertions(+), 24 deletions(-) diff --git a/src/VaultSharp/V1/SecretsEngines/PKI/GenerateRootRequest.cs b/src/VaultSharp/V1/SecretsEngines/PKI/GenerateRootRequest.cs index 660b0b0b..291ac2ea 100644 --- a/src/VaultSharp/V1/SecretsEngines/PKI/GenerateRootRequest.cs +++ b/src/VaultSharp/V1/SecretsEngines/PKI/GenerateRootRequest.cs @@ -32,7 +32,8 @@ public class GenerateRootRequest /// /// Format for returned data. Can be "pem", "der", or "pem_bundle". If "pem_bundle", any private key and issuing cert will be appended to the certificate pem. - /// If "der", the value will be base64 encoded. Defaults to "pem". + /// If "der", the value will be base64 encoded. + /// Defaults to "pem". /// [JsonPropertyName("format")] public string Format { get; set; } @@ -71,7 +72,9 @@ public class GenerateRootRequest public string KeyRef { get; set; } /// - /// The type of key to use; defaults to RSA. "rsa" "ec" and "ed25519" are the only valid values. + /// The type of key to use; + /// defaults to RSA. + /// "rsa" "ec" and "ed25519" are the only valid values. /// [JsonPropertyName("key_type")] public string KeyType { get; set; } @@ -203,5 +206,13 @@ public class GenerateRootRequest [JsonPropertyName("use_pss")] public bool UsePss { get; set; } + public GenerateRootRequest() + { + //set defaults + Format = "pem"; + KeyType = "rsa"; + IssuerName = string.Empty; + KeyName = string.Empty; + } } } diff --git a/src/VaultSharp/V1/SecretsEngines/PKI/PKIRole.cs b/src/VaultSharp/V1/SecretsEngines/PKI/PKIRole.cs index 3320354b..33aa4855 100644 --- a/src/VaultSharp/V1/SecretsEngines/PKI/PKIRole.cs +++ b/src/VaultSharp/V1/SecretsEngines/PKI/PKIRole.cs @@ -44,12 +44,6 @@ public class PKIRole [JsonPropertyName("allow_subdomains")] public bool AllowSubdomains { get; set; } - /// - /// - /// - [JsonPropertyName("allow_token_displayname")] - public bool AllowTokenDisplayname { get; set; } - /// /// If set, allows certificates with wildcards in the common name to be issued, conforming to RFC 6125's Section 6.4.3; e.g., /// ".example.net" or "bz.example.net". See the documentation for more information. @@ -126,16 +120,19 @@ public class PKIRole /// If set, Country will be set to this value in certificates issued by this role. /// [JsonPropertyName("country")] - public string Country { get; set; } + public List Country { get; set; } /// - /// If set, certificates are flagged for email protection use. Defaults to false. See also RFC 5280 Section 4.2.1.12. + /// If set, certificates are flagged for email protection use. + /// Defaults to false. + /// See also RFC 5280 Section 4.2.1.12. /// [JsonPropertyName("email_protection_flag")] public bool EmailProtectionFlag { get; set; } /// - /// If set, only valid host names are allowed for CN and DNS SANs, and the host part of email addresses. Defaults to true. + /// If set, only valid host names are allowed for CN and DNS SANs, and the host part of email addresses. + /// Defaults to true. /// [JsonPropertyName("enforce_hostnames")] public bool EnforceHostnames { get; set; } @@ -175,7 +172,9 @@ public class PKIRole public int KeyBits { get; set; } /// - /// The type of key to use; defaults to RSA. "rsa" "ec", "ed25519" and "any" are the only valid values. + /// The type of key to use; + /// defaults to RSA. + /// "rsa" "ec", "ed25519" and "any" are the only valid values. /// [JsonPropertyName("key_type")] public string KeyType { get; set; } @@ -312,5 +311,12 @@ public class PKIRole /// [JsonPropertyName("use_pss")] public bool UsePss { get; set; } + + + public PKIRole() + { + // set defaults + KeyType = "rsa"; + } } } diff --git a/src/VaultSharp/V1/SecretsEngines/PKI/PKISecretsEngineProvider.cs b/src/VaultSharp/V1/SecretsEngines/PKI/PKISecretsEngineProvider.cs index b19e0778..a0cfde22 100644 --- a/src/VaultSharp/V1/SecretsEngines/PKI/PKISecretsEngineProvider.cs +++ b/src/VaultSharp/V1/SecretsEngines/PKI/PKISecretsEngineProvider.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Net; using System.Net.Http; using System.Threading.Tasks; using VaultSharp.Core; @@ -137,8 +138,22 @@ public async Task> GenerateRootAsync(string type, G public async Task> ListRolesAsync(string pkiBackendMountPoint = null) { - var path = "/roles"; - var roleNamesSecret = await _polymath.MakeVaultApiRequest>(pkiBackendMountPoint ?? _polymath.VaultClientSettings.SecretsEngineMountPoints.PKI, path, HttpMethod.Get).ConfigureAwait(_polymath.VaultClientSettings.ContinueAsyncTasksOnCapturedContext); + var path = "/roles?list=true"; + var roleNamesSecret = new Secret() { Data = new PKIRoleNames() { Keys = new List() } }; + + try + { + roleNamesSecret = await _polymath.MakeVaultApiRequest>(pkiBackendMountPoint ?? _polymath.VaultClientSettings.SecretsEngineMountPoints.PKI, path, HttpMethod.Get).ConfigureAwait(_polymath.VaultClientSettings.ContinueAsyncTasksOnCapturedContext); + } + catch (VaultApiException ex) + { + // if the role list is empty, we will get a 404 response with { "errors": [] } + // make sure it's that error only, and return the empty list if so + if (ex.StatusCode != 404) + { + throw; + } + } return roleNamesSecret; } diff --git a/test/VaultSharp.Samples/Backends/Secrets/PKISecretsBackendSamples.cs b/test/VaultSharp.Samples/Backends/Secrets/PKISecretsBackendSamples.cs index be829a46..2096ddac 100644 --- a/test/VaultSharp.Samples/Backends/Secrets/PKISecretsBackendSamples.cs +++ b/test/VaultSharp.Samples/Backends/Secrets/PKISecretsBackendSamples.cs @@ -29,10 +29,9 @@ private static void RunPKISecretsBackendSamples() var pkiPath = Guid.NewGuid().ToString(); // verify the role list is initially empty - var roleListResponse = _authenticatedVaultClient.V1.Secrets.PKI.ListRolesAsync().Result; - - Assert.Empty(roleListResponse.Data.Keys); + var roleListResponse = _authenticatedVaultClient.V1.Secrets.PKI.ListRolesAsync(pkiMountPath).Result; + Assert.Empty(roleListResponse.Data?.Keys); // write a new role var allowedDomain = "test.com"; @@ -41,24 +40,26 @@ private static void RunPKISecretsBackendSamples() var newRole = new PKIRole() { AllowedDomains = new List() { allowedDomain }, - AllowSubdomains = true + AllowSubdomains = true, }; var newRoleResponse = _authenticatedVaultClient.V1.Secrets.PKI.WriteRoleAsync(roleName, newRole, pkiMountPath).Result; Assert.True(newRoleResponse.Data.AllowedDomains.Exists(d => d == allowedDomain)); // verify it shows up in the list now - roleListResponse = _authenticatedVaultClient.V1.Secrets.PKI.ListRolesAsync().Result; + roleListResponse = _authenticatedVaultClient.V1.Secrets.PKI.ListRolesAsync(pkiMountPath).Result; Assert.Single(roleListResponse.Data.Keys); // generate a new root CA var newRoot = new GenerateRootRequest() { - CommonName = "test.com" + CommonName = "test.com", + IssuerName = "testroot_test_com", + KeyName = "testroot_key", }; - var newRootResponse = _authenticatedVaultClient.V1.Secrets.PKI.GenerateRootAsync("exported", newRoot).Result; + var newRootResponse = _authenticatedVaultClient.V1.Secrets.PKI.GenerateRootAsync("exported", newRoot, pkiMountPath).Result; Assert.NotEmpty(newRootResponse.Data.PrivateKey); @@ -66,7 +67,9 @@ private static void RunPKISecretsBackendSamples() var signCertOptions = new SignCertificatesRequestOptions() { - CommonName = "my.test.com" + CommonName = "my.test.com", + Csr = "-----BEGIN CERTIFICATE REQUEST-----\r\nMIIChDCCAWwCAQAwFjEUMBIGA1UEAwwLbXkudGVzdC5jb20wggEiMA0GCSqGSIb3\r\nDQEBAQUAA4IBDwAwggEKAoIBAQCQj/loI/Us7ayc/GOQlDWWv/lH+pcJ9g3w2Q/U\r\nzl8LBR7CD6Lve7TzBHxXU77gpg/lrCksr9LfE85FofhMy2WdEDTQw2BqdA/xwphh\r\nGsmaV+gfniZT96KzTOTRfMLE8Lf88bw5us7ha12MdJEhVX72kqXs7r/Hx5wz6gyw\r\niKcuezeTjp3r0qBPUIgHDgSg2TCVPs+THuhozQd4InQFU4HIWrDnR8unm5udRWId\r\nXOfxcQcgPp+UhzHzj+H/TfhqVeDzSvjVyir0llRAg7mNZctb/8lyOFPOTZkm4dWW\r\nzWVowYctnWgL05KcFK2wOJry16+gyzJaDE4EoxA/+YXVlnsfAgMBAAGgKTAnBgkq\r\nhkiG9w0BCQ4xGjAYMBYGA1UdEQQPMA2CC215LnRlc3QuY29tMA0GCSqGSIb3DQEB\r\nCwUAA4IBAQA41h0RFx8NLNSq92sgQwAh+FjZNqcjq8sLj7P0jwYi0CJrGf2fFfw1\r\nRhcZefwc3uGPsa/LXyGeBO4k88q8hA6x0B3yUhH26nC+OI1jhfj97x5pl+JFGkYT\r\nAP5Su9vwDKc22T8cv9K7Yzor3mfGc+Vs4HPH/pasg5cTqTageXNiBMd9VZKXzvrb\r\nhQyjW1uomsKCTlqCcjkRSA6eppxdAYCOKR/cfNNai2cIhZXuLZ4Y3gLosk/J8MGP\r\npRgz1SBls41Vk9gP8XF9e8eAKXVvnxSoCbifdNfjBkLaQgfaYpCV+NKBwQBmz7ad\r\nHjirOTU4elr/qUq91gyYuakOCZGWKm0k\r\n-----END CERTIFICATE REQUEST-----", + TimeToLive = "24h" }; var newCertResponse = _authenticatedVaultClient.V1.Secrets.PKI.SignCertificateAsync(roleName, signCertOptions, pkiMountPath).Result; diff --git a/test/VaultSharp.Samples/Program.cs b/test/VaultSharp.Samples/Program.cs index f8d01b80..eea5c034 100644 --- a/test/VaultSharp.Samples/Program.cs +++ b/test/VaultSharp.Samples/Program.cs @@ -8,7 +8,7 @@ namespace VaultSharp.Samples { partial class Program { - private const string ExpectedVaultVersion = "1.17.5"; + private const string ExpectedVaultVersion = "1.17.6+ent"; private static IVaultClient _unauthenticatedVaultClient; private static IVaultClient _authenticatedVaultClient; @@ -103,7 +103,7 @@ private static VaultClientSettings GetVaultClientSettings(IAuthMethodInfo authMe Console.WriteLine(_responseContent); } }, - Namespace = "bhjk", + //Namespace = "bhjk", // ?? MyHttpClientProviderFunc = handler => new HttpClient(handler) }; From 13be5bef2d763b8ecc670f88c653fd5f380ee456 Mon Sep 17 00:00:00 2001 From: Joseph VanWanzeele Date: Thu, 21 Nov 2024 13:04:53 -0500 Subject: [PATCH 5/5] updated readme and changelog --- CHANGELOG.md | 2 ++ README.md | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fe7d44b..6371b2c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ + ## Useful Gists related to VaultSharp usage * Helper code to get Kerberos Negotiation Token using keytab and krb5 config file: https://gist.github.com/rajanadar/28c86d967695262bfe1f17ae82fb3d3d @@ -10,6 +11,7 @@ * auth/azure: Add the ```resource_id``` parameter to the login method. * [GH-365](https://github.com/rajanadar/VaultSharp/issues/365) Add new ```transit key type``` values like ```hmac-key```, ```cmac-key``` etc. * [GH-366](https://github.com/rajanadar/VaultSharp/issues/366) PKI Secrets Engine - return raw cert data, including revocation date + * [GH-373](https://github.com/rajanadar/VaultSharp/issues/373) PKI Secrets Engine - added methods for interacting with PKI Roles, and method for generating a self-signed root CA. ## 1.17.5.1 (September 18, 2024) diff --git a/README.md b/README.md index f0c01cc3..68b53162 100644 --- a/README.md +++ b/README.md @@ -1304,6 +1304,59 @@ var caCert = await vaultClient.V1.Secrets.PKI.ReadCACertificateAsync(Certificate Assert.NotNull(caCert.CertificateContent); ``` +##### Generate Root + - This endpoint generates a new self-signed CA certificate and private key. If the 'type' is `exported`, the private key will be returned in the response; + - if it is `internal` the private key will not be returned and cannot be retrieved later; + - if it is `existing`, the key specified by key_ref will be reused for this root; + - if it is `kms`, a managed key will be used. + + ```cs +var newRoot = new GenerateRootRequest() +{ + CommonName = "test.com", + IssuerName = "testroot_test_com", + KeyName = "testroot_key", +}; +var type = "exported"; +var caCert = await vaultClient.V1.Secrets.PKI.GenerateRootAsync(type, newRoot, mountpoint) +Assert.NotEmpty(newRootResponse.Data.PrivateKey); +``` + +##### List Roles + + - This endpoint lists all of the role names that have been created for the PKI secrets engine. + +```cs +var roleNames = await vaultClient.V1.Secrets.PKI.ListRolesAsync(mountpoint); +Assert.IsList(roleNames); +``` + +##### Read Role + + - This endpoint retreives the details for a role with the specified name. + +```cs +var roleDetails = await vaultClient.V1.Secrets.PKI.ReadRoleAsync(roleName, mountpoint); +``` + +##### Write Role + + - This endpoint writes a new role definition + - If the role name already exists, it will be completely overwritten + +```cs +var roleDetails = await vaultClient.V1.Secrets.PKI.ReadRoleAsync(roleName, roleDetails, mountpoint); +``` + +##### Patch Role + - This endpoint will update an existing role with provided values + - Null values in the request will not overwritten existing values in the role definition + +```cs +var updatedRoleDetails = await vaultClient.V1.Secrets.PKI.PatchRoleAsync(roleName, roleDetails, mountpoint); +``` + + #### RabbitMQ Secrets Engine ##### Generate dynamic DB credentials