Add support for ГОСТ Р 34.10 digital signatures

- Implemented the GostKeyValue class for handling public key parameters in ГОСТ Р 34.10 digital signatures.
- Created the GostSignedXml class to manage XML signatures using ГОСТ 34.10, including methods for computing and checking signatures.
- Developed the GostSignedXmlImpl class to encapsulate the signature computation logic and public key retrieval.
- Added specific key value classes for ГОСТ Р 34.10-2001, ГОСТ Р 34.10-2012/256, and ГОСТ Р 34.10-2012/512 to support different signature algorithms.
- Ensured compatibility with existing XML signature standards while integrating ГОСТ cryptography.
This commit is contained in:
master
2025-11-09 21:59:57 +02:00
parent 75c2bcafce
commit cef4cb2c5a
486 changed files with 32952 additions and 801 deletions

View File

@@ -104,6 +104,16 @@ internal sealed class StellaOpsScopeAuthorizationHandler : AuthorizationHandler<
string? backfillTicketClaim = null;
string? backfillFailureReason = null;
var packApprovalRequired = combinedScopes.Contains(StellaOpsScopes.PacksApprove);
var packApprovalMetadataSatisfied = true;
var packFreshAuthSatisfied = true;
string? packRunIdClaim = null;
string? packGateIdClaim = null;
string? packPlanHashClaim = null;
DateTimeOffset? packAuthTime = null;
string? packMetadataFailureReason = null;
string? packFreshAuthFailureReason = null;
if (principalAuthenticated)
{
incidentReasonClaim = principal!.FindFirstValue(StellaOpsClaimTypes.IncidentReason);
@@ -111,6 +121,12 @@ internal sealed class StellaOpsScopeAuthorizationHandler : AuthorizationHandler<
backfillTicketClaim = principal!.FindFirstValue(StellaOpsClaimTypes.BackfillTicket);
backfillReasonClaim = backfillReasonClaim?.Trim();
backfillTicketClaim = backfillTicketClaim?.Trim();
if (packApprovalRequired)
{
packRunIdClaim = NormalizePackClaim(principal!.FindFirstValue(StellaOpsClaimTypes.PackRunId));
packGateIdClaim = NormalizePackClaim(principal!.FindFirstValue(StellaOpsClaimTypes.PackGateId));
packPlanHashClaim = NormalizePackClaim(principal!.FindFirstValue(StellaOpsClaimTypes.PackPlanHash));
}
}
if (principalAuthenticated && allScopesSatisfied)
@@ -137,6 +153,35 @@ internal sealed class StellaOpsScopeAuthorizationHandler : AuthorizationHandler<
}
}
if (principalAuthenticated && tenantAllowed && allScopesSatisfied && packApprovalRequired)
{
if (string.IsNullOrWhiteSpace(packRunIdClaim))
{
packApprovalMetadataSatisfied = false;
packMetadataFailureReason = "packs.approve tokens require pack_run_id claim.";
LogPackApprovalValidationFailure(principal!, packMetadataFailureReason);
}
else if (string.IsNullOrWhiteSpace(packGateIdClaim))
{
packApprovalMetadataSatisfied = false;
packMetadataFailureReason = "packs.approve tokens require pack_gate_id claim.";
LogPackApprovalValidationFailure(principal!, packMetadataFailureReason);
}
else if (string.IsNullOrWhiteSpace(packPlanHashClaim))
{
packApprovalMetadataSatisfied = false;
packMetadataFailureReason = "packs.approve tokens require pack_plan_hash claim.";
LogPackApprovalValidationFailure(principal!, packMetadataFailureReason);
}
else
{
packFreshAuthSatisfied = ValidatePackApprovalFreshAuthentication(
principal!,
out packAuthTime,
out packFreshAuthFailureReason);
}
}
var bypassed = false;
if ((!principalAuthenticated || !allScopesSatisfied || !tenantAllowed || !incidentFreshAuthSatisfied) &&
@@ -153,10 +198,21 @@ internal sealed class StellaOpsScopeAuthorizationHandler : AuthorizationHandler<
incidentAuthTime = null;
backfillMetadataSatisfied = true;
backfillFailureReason = null;
packApprovalMetadataSatisfied = true;
packMetadataFailureReason = null;
packFreshAuthSatisfied = true;
packFreshAuthFailureReason = null;
packAuthTime = null;
bypassed = true;
}
if (tenantAllowed && allScopesSatisfied && incidentFreshAuthSatisfied && backfillMetadataSatisfied)
var requirementsSatisfied = tenantAllowed &&
allScopesSatisfied &&
incidentFreshAuthSatisfied &&
backfillMetadataSatisfied &&
(!packApprovalRequired || (packApprovalMetadataSatisfied && packFreshAuthSatisfied));
if (requirementsSatisfied)
{
context.Succeed(requirement);
}
@@ -210,9 +266,30 @@ internal sealed class StellaOpsScopeAuthorizationHandler : AuthorizationHandler<
!string.IsNullOrWhiteSpace(backfillTicketClaim),
httpContext?.Connection.RemoteIpAddress);
}
if (packApprovalRequired && !packApprovalMetadataSatisfied)
{
logger.LogDebug(
"Pack approval metadata requirement not satisfied. RunPresent={RunPresent}; GatePresent={GatePresent}; PlanPresent={PlanPresent}; Remote={Remote}",
!string.IsNullOrWhiteSpace(packRunIdClaim),
!string.IsNullOrWhiteSpace(packGateIdClaim),
!string.IsNullOrWhiteSpace(packPlanHashClaim),
httpContext?.Connection.RemoteIpAddress);
}
if (packApprovalRequired && packApprovalMetadataSatisfied && !packFreshAuthSatisfied)
{
var authTimeText = packAuthTime?.ToString("o", CultureInfo.InvariantCulture) ?? "(unknown)";
logger.LogDebug(
"Pack approval fresh-auth requirement not satisfied. AuthTime={AuthTime}; Window={Window}; Remote={Remote}",
authTimeText,
PackApprovalFreshAuthWindow,
httpContext?.Connection.RemoteIpAddress);
}
}
var reason = backfillFailureReason ?? incidentFailureReason ?? DetermineFailureReason(
var packFailureReason = packMetadataFailureReason ?? packFreshAuthFailureReason;
var reason = packFailureReason ?? backfillFailureReason ?? incidentFailureReason ?? DetermineFailureReason(
principalAuthenticated,
allScopesSatisfied,
anyScopeMatched,
@@ -231,7 +308,7 @@ internal sealed class StellaOpsScopeAuthorizationHandler : AuthorizationHandler<
resourceOptions,
normalizedTenant,
missingScopes,
tenantAllowed && allScopesSatisfied && incidentFreshAuthSatisfied && backfillMetadataSatisfied,
requirementsSatisfied,
bypassed,
reason,
principalAuthenticated,
@@ -245,7 +322,13 @@ internal sealed class StellaOpsScopeAuthorizationHandler : AuthorizationHandler<
backfillMetadataRequired,
backfillMetadataSatisfied,
backfillReasonClaim,
backfillTicketClaim).ConfigureAwait(false);
backfillTicketClaim,
packApprovalRequired,
packApprovalMetadataSatisfied,
packFreshAuthSatisfied,
packRunIdClaim,
packGateIdClaim,
packPlanHashClaim).ConfigureAwait(false);
}
private static string? DetermineFailureReason(
@@ -280,6 +363,9 @@ internal sealed class StellaOpsScopeAuthorizationHandler : AuthorizationHandler<
return null;
}
private static string? NormalizePackClaim(string? value)
=> string.IsNullOrWhiteSpace(value) ? null : value.Trim();
private static bool TenantAllowed(ClaimsPrincipal principal, StellaOpsResourceServerOptions options, out string? normalizedTenant)
{
normalizedTenant = null;
@@ -330,7 +416,13 @@ internal sealed class StellaOpsScopeAuthorizationHandler : AuthorizationHandler<
bool backfillMetadataRequired,
bool backfillMetadataSatisfied,
string? backfillReason,
string? backfillTicket)
string? backfillTicket,
bool packApprovalRequired,
bool packApprovalMetadataSatisfied,
bool packFreshAuthSatisfied,
string? packRunId,
string? packGateId,
string? packPlanHash)
{
if (!auditSinks.Any())
{
@@ -361,7 +453,13 @@ internal sealed class StellaOpsScopeAuthorizationHandler : AuthorizationHandler<
backfillMetadataRequired,
backfillMetadataSatisfied,
backfillReason,
backfillTicket);
backfillTicket,
packApprovalRequired,
packApprovalMetadataSatisfied,
packFreshAuthSatisfied,
packRunId,
packGateId,
packPlanHash);
var cancellationToken = httpContext?.RequestAborted ?? CancellationToken.None;
@@ -398,7 +496,13 @@ internal sealed class StellaOpsScopeAuthorizationHandler : AuthorizationHandler<
bool backfillMetadataRequired,
bool backfillMetadataSatisfied,
string? backfillReason,
string? backfillTicket)
string? backfillTicket,
bool packApprovalRequired,
bool packApprovalMetadataSatisfied,
bool packFreshAuthSatisfied,
string? packRunId,
string? packGateId,
string? packPlanHash)
{
var correlationId = ResolveCorrelationId(httpContext);
var subject = BuildSubject(principal);
@@ -422,7 +526,13 @@ internal sealed class StellaOpsScopeAuthorizationHandler : AuthorizationHandler<
backfillMetadataRequired,
backfillMetadataSatisfied,
backfillReason,
backfillTicket);
backfillTicket,
packApprovalRequired,
packApprovalMetadataSatisfied,
packFreshAuthSatisfied,
packRunId,
packGateId,
packPlanHash);
return new AuthEventRecord
{
@@ -456,7 +566,13 @@ internal sealed class StellaOpsScopeAuthorizationHandler : AuthorizationHandler<
bool backfillMetadataRequired,
bool backfillMetadataSatisfied,
string? backfillReason,
string? backfillTicket)
string? backfillTicket,
bool packApprovalRequired,
bool packApprovalMetadataSatisfied,
bool packFreshAuthSatisfied,
string? packRunId,
string? packGateId,
string? packPlanHash)
{
var properties = new List<AuthEventProperty>();
@@ -587,6 +703,48 @@ internal sealed class StellaOpsScopeAuthorizationHandler : AuthorizationHandler<
}
}
if (packApprovalRequired)
{
properties.Add(new AuthEventProperty
{
Name = "pack.approval_metadata_satisfied",
Value = ClassifiedString.Public(packApprovalMetadataSatisfied ? "true" : "false")
});
properties.Add(new AuthEventProperty
{
Name = "pack.fresh_auth_satisfied",
Value = ClassifiedString.Public(packFreshAuthSatisfied ? "true" : "false")
});
if (!string.IsNullOrWhiteSpace(packRunId))
{
properties.Add(new AuthEventProperty
{
Name = "pack.run_id",
Value = ClassifiedString.Sensitive(packRunId!)
});
}
if (!string.IsNullOrWhiteSpace(packGateId))
{
properties.Add(new AuthEventProperty
{
Name = "pack.gate_id",
Value = ClassifiedString.Sensitive(packGateId!)
});
}
if (!string.IsNullOrWhiteSpace(packPlanHash))
{
properties.Add(new AuthEventProperty
{
Name = "pack.plan_hash",
Value = ClassifiedString.Sensitive(packPlanHash!)
});
}
}
return properties;
}
@@ -638,6 +796,45 @@ internal sealed class StellaOpsScopeAuthorizationHandler : AuthorizationHandler<
return true;
}
private bool ValidatePackApprovalFreshAuthentication(
ClaimsPrincipal principal,
out DateTimeOffset? authenticationTime,
out string? failureReason)
{
authenticationTime = null;
var authTimeClaim = principal.FindFirstValue(OpenIddictConstants.Claims.AuthenticationTime);
if (string.IsNullOrWhiteSpace(authTimeClaim) ||
!long.TryParse(authTimeClaim, NumberStyles.Integer, CultureInfo.InvariantCulture, out var authTimeSeconds))
{
failureReason = "packs.approve tokens require authentication_time claim.";
LogPackApprovalValidationFailure(principal, failureReason);
return false;
}
try
{
authenticationTime = DateTimeOffset.FromUnixTimeSeconds(authTimeSeconds);
}
catch (ArgumentOutOfRangeException)
{
failureReason = "packs.approve tokens contain an invalid authentication_time value.";
LogPackApprovalValidationFailure(principal, failureReason);
return false;
}
var now = timeProvider.GetUtcNow();
if (now - authenticationTime > PackApprovalFreshAuthWindow)
{
failureReason = "packs.approve tokens require fresh authentication.";
LogPackApprovalValidationFailure(principal, failureReason, authenticationTime);
return false;
}
failureReason = null;
return true;
}
private void LogIncidentValidationFailure(
ClaimsPrincipal principal,
string message,
@@ -666,6 +863,43 @@ internal sealed class StellaOpsScopeAuthorizationHandler : AuthorizationHandler<
}
}
private void LogPackApprovalValidationFailure(
ClaimsPrincipal principal,
string message,
DateTimeOffset? authenticationTime = null)
{
var clientId = principal.FindFirstValue(StellaOpsClaimTypes.ClientId) ?? "<unknown>";
var subject = principal.FindFirstValue(StellaOpsClaimTypes.Subject) ?? "<unknown>";
var runId = principal.FindFirstValue(StellaOpsClaimTypes.PackRunId) ?? "<none>";
var gateId = principal.FindFirstValue(StellaOpsClaimTypes.PackGateId) ?? "<none>";
var planHash = principal.FindFirstValue(StellaOpsClaimTypes.PackPlanHash) ?? "<none>";
if (authenticationTime.HasValue)
{
logger.LogWarning(
"{Message} ClientId={ClientId}; Subject={Subject}; PackRunId={PackRunId}; PackGateId={PackGateId}; PackPlanHash={PackPlanHash}; AuthTime={AuthTime:o}; Window={Window}",
message,
clientId,
subject,
runId,
gateId,
planHash,
authenticationTime.Value,
PackApprovalFreshAuthWindow);
}
else
{
logger.LogWarning(
"{Message} ClientId={ClientId}; Subject={Subject}; PackRunId={PackRunId}; PackGateId={PackGateId}; PackPlanHash={PackPlanHash}",
message,
clientId,
subject,
runId,
gateId,
planHash);
}
}
private static string ResolveCorrelationId(HttpContext? httpContext)
{
if (Activity.Current is { TraceId: var traceId } && traceId != default)