diff --git a/.gitignore b/.gitignore index b7735f2..e57e8eb 100644 --- a/.gitignore +++ b/.gitignore @@ -330,3 +330,5 @@ ASALocalRun/ .mfractor/ .claude/settings.local.json sample change.txt +.claude/settings.json + diff --git a/CHANGELOG.md b/CHANGELOG.md index 82140ec..c4fe490 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# v1.0.3 +* Added support for revocation reason 0 (Unspecified) now that HydrantId accepts it +* Fixed sensitive credentials (HydrantIdAuthId, HydrantIdAuthKey) being written to trace logs in plain text; raw config JSON is now masked before logging + # v1.0.2 * Fixed revocation status handling - failed revocations no longer incorrectly set certificate status to FAILED; certificate retains its current active status * Added FlowLogger utility for structured flow diagrams across all public plugin methods diff --git a/HydrantCAProxy/Client/Models/Enums/RevocationReasons.cs b/HydrantCAProxy/Client/Models/Enums/RevocationReasons.cs index b096973..80f1e0e 100644 --- a/HydrantCAProxy/Client/Models/Enums/RevocationReasons.cs +++ b/HydrantCAProxy/Client/Models/Enums/RevocationReasons.cs @@ -16,6 +16,7 @@ namespace Keyfactor.HydrantId.Client.Models.Enums [JsonConverter(typeof(StringEnumConverter))] public enum RevocationReasons { + [EnumMember(Value = "0")] Unspecified = 0, [EnumMember(Value = "1")] KeyCompromise = 1, [EnumMember(Value = "3")] AffiliationChanged = 3, [EnumMember(Value = "4")] Superseded = 4, diff --git a/HydrantCAProxy/HydrantIdCAPlugin.cs b/HydrantCAProxy/HydrantIdCAPlugin.cs index a89c54c..cc7b611 100644 --- a/HydrantCAProxy/HydrantIdCAPlugin.cs +++ b/HydrantCAProxy/HydrantIdCAPlugin.cs @@ -52,7 +52,7 @@ public void Initialize(IAnyCAPluginConfigProvider configProvider, ICertificateDa certDataReader = certificateDataReader; Config = configProvider; var rawData = JsonConvert.SerializeObject(configProvider.CAConnectionData); - _logger.LogTrace("Initialize: raw config JSON: {Json}", rawData); + _logger.LogTrace("Initialize: config JSON (sensitive keys masked): {Json}", MaskConfigForLog(rawData)); _config = JsonConvert.DeserializeObject(rawData); }); @@ -78,6 +78,38 @@ public void Initialize(IAnyCAPluginConfigProvider configProvider, ICertificateDa } } + private static readonly HashSet _sensitiveConfigKeys = new HashSet(StringComparer.OrdinalIgnoreCase) + { + HydrantIdCAPluginConfig.ConfigConstants.HydrantIdAuthId, + HydrantIdCAPluginConfig.ConfigConstants.HydrantIdAuthKey + }; + + private static string MaskConfigForLog(string rawJson) + { + if (string.IsNullOrEmpty(rawJson)) return rawJson; + try + { + var token = Newtonsoft.Json.Linq.JToken.Parse(rawJson); + if (token is Newtonsoft.Json.Linq.JObject obj) + { + foreach (var prop in obj.Properties()) + { + if (_sensitiveConfigKeys.Contains(prop.Name) && + prop.Value.Type != Newtonsoft.Json.Linq.JTokenType.Null) + { + prop.Value = "***REDACTED***"; + } + } + return obj.ToString(Newtonsoft.Json.Formatting.None); + } + return token.ToString(Newtonsoft.Json.Formatting.None); + } + catch + { + return "***REDACTED***"; + } + } + private static List CheckRequiredValues(Dictionary connectionInfo, params string[] args) { List errors = new List(); @@ -135,7 +167,7 @@ public Task ValidateCAConnectionInfo(Dictionary connectionInfo) _logger.LogDebug("Validating HydrantId CA Connection properties"); var rawData = JsonConvert.SerializeObject(connectionInfo); - _logger.LogTrace("ValidateCAConnectionInfo: raw connectionInfo JSON: {Json}", rawData); + _logger.LogTrace("ValidateCAConnectionInfo: connectionInfo JSON (sensitive keys masked): {Json}", MaskConfigForLog(rawData)); _config = JsonConvert.DeserializeObject(rawData); diff --git a/HydrantCAProxy/RequestManager.cs b/HydrantCAProxy/RequestManager.cs index ac47a34..6a94b75 100644 --- a/HydrantCAProxy/RequestManager.cs +++ b/HydrantCAProxy/RequestManager.cs @@ -78,6 +78,9 @@ public RevocationReasons GetMapRevokeReasons(uint keyfactorRevokeReason) RevocationReasons returnStatus; switch (keyfactorRevokeReason) { + case 0: + returnStatus = RevocationReasons.Unspecified; + break; case 1: returnStatus = RevocationReasons.KeyCompromise; break; @@ -92,7 +95,7 @@ public RevocationReasons GetMapRevokeReasons(uint keyfactorRevokeReason) break; default: Log.LogError("GetMapRevokeReasons: unsupported revoke reason {Reason}", keyfactorRevokeReason); - throw new RevokeReasonNotSupportedException($"Revoke reason {keyfactorRevokeReason} is not supported. Supported values: 1 (KeyCompromise), 3 (AffiliationChanged), 4 (Superseded), 5 (CessationOfOperation)."); + throw new RevokeReasonNotSupportedException($"Revoke reason {keyfactorRevokeReason} is not supported. Supported values: 0 (Unspecified), 1 (KeyCompromise), 3 (AffiliationChanged), 4 (Superseded), 5 (CessationOfOperation)."); } Log.LogTrace("GetMapRevokeReasons: {Input} -> {Mapped}", keyfactorRevokeReason, returnStatus);