Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed race condition with digidoc initialize/terminate #17

Merged
merged 5 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
authProperties);

// Assign a unique ID within the session to enable the use of a unique temporary container name across successive requests.
// A unique temporary container name is required to facilitate simultaneous signing from multiple browsers.
SetUniqueIdInSession();
}

[HttpGet]
Expand Down
20 changes: 15 additions & 5 deletions src/WebEid.AspNetCore.Example/Controllers/Api/BaseController.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
namespace WebEid.AspNetCore.Example.Controllers.Api
{
using System.Security;
using System.Security.Claims;
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

public abstract class BaseController : ControllerBase
{
const string uniqueIdKey = "UniqueId";

protected void RemoveUserContainerFile()
{
System.IO.File.Delete(GetUserContainerName());
}

protected void SetUniqueIdInSession()
{
HttpContext.Session.SetString(uniqueIdKey, Guid.NewGuid().ToString());
}

private string GetUniqueIdFromSession()
{
return HttpContext.Session.GetString(uniqueIdKey);
}

protected string GetUserContainerName()
{
var identity = (ClaimsIdentity)this.HttpContext.User?.Identity ??
throw new SecurityException("User is not logged in");
return identity.GetIdCode();
return $"container_{GetUniqueIdFromSession()}";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Services;
Expand Down
112 changes: 61 additions & 51 deletions src/WebEid.AspNetCore.Example/Signing/SigningService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,23 @@
using Microsoft.Extensions.Logging;
using WebEid.Security.Util;

public class SigningService
public class SigningService : IDisposable
{
private static readonly string FileToSign = Path.Combine("wwwroot", "files", "example-for-signing.txt");
private readonly DigiDocConfiguration configuration;
private readonly ILogger logger;
private bool _disposedValue;

public SigningService(DigiDocConfiguration configuration, ILogger logger)
{
this.configuration = configuration;
this.logger = logger;

// The current implementation of the static DigiDoc library assumes that SigningService is used as a singleton.
// In the ASP.NET Core application, we initialize it using the AddSingleton method in Startup.cs.
// The DigiDoc library is initialized in the constructor and terminated in Dispose.
configuration.Initialize();
digidoc.initialize("WebEidExample");
}

public DigestDto PrepareContainer(CertificateDto data, ClaimsIdentity identity, string tempContainerName)
Expand All @@ -32,48 +39,29 @@ public DigestDto PrepareContainer(CertificateDto data, ClaimsIdentity identity,
"Authenticated subject ID code differs from signing certificate subject ID code");
}

configuration.Initialize();
digidoc.initialize("WebEidExample");
try
{
this.logger?.LogDebug("Creating container file: '{0}'", tempContainerName);
Container container = Container.create(tempContainerName);
container.addDataFile(FileToSign, "application/octet-stream");
logger?.LogInformation("Preparing container for signing for file '{0}'", tempContainerName);
var signature =
container.prepareWebSignature(certificate.Export(X509ContentType.Cert), "time-stamp");
var hashFunction = GetSupportedHashAlgorithm(data.SupportedSignatureAlgorithms, signature.signatureMethod());
container.save();
return new DigestDto
{
Hash = Convert.ToBase64String(signature.dataToSign()),
HashFunction = hashFunction
};
}
finally
this.logger?.LogDebug("Creating container file: '{0}'", tempContainerName);
Container container = Container.create(tempContainerName);
container.addDataFile(FileToSign, "application/octet-stream");
logger?.LogInformation("Preparing container for signing for file '{0}'", tempContainerName);
var signature =
container.prepareWebSignature(certificate.Export(X509ContentType.Cert), "time-stamp");
var hashFunction = GetSupportedHashAlgorithm(data.SupportedSignatureAlgorithms, signature.signatureMethod());
container.save();
return new DigestDto
{
digidoc.terminate();
}
Hash = Convert.ToBase64String(signature.dataToSign()),
HashFunction = hashFunction
};
}


public void SignContainer(SignatureDto signatureDto, string tempContainerName)
{
configuration.Initialize();
digidoc.initialize("WebEidExample");
try
{
var container = Container.open(tempContainerName);
var signatureBytes = Convert.FromBase64String(signatureDto.Signature);
var signature = container.signatures().First(); // Container must have one signature as it was added in PrepareContainer
signature.setSignatureValue(signatureBytes);
signature.extendSignatureProfile("BES/time-stamp");
container.save();
}
finally
{
digidoc.terminate();
}
var container = Container.open(tempContainerName);
var signatureBytes = Convert.FromBase64String(signatureDto.Signature);
var signature = container.signatures().First(); // Container must have one signature as it was added in PrepareContainer
signature.setSignatureValue(signatureBytes);
signature.extendSignatureProfile("BES/time-stamp");
container.save();
}

private static string GetSupportedHashAlgorithm(IList<SignatureAlgorithmDto> supportedSignatureAlgorithms, string signatureMethod)
Expand All @@ -86,22 +74,44 @@ private static string GetSupportedHashAlgorithm(IList<SignatureAlgorithmDto> sup
throw new ArgumentException("Supported signature algorithm not found");
}

private static bool FragmentEquals(string framgent, SignatureAlgorithmDto signatureAlgorithm) =>
private static bool FragmentEquals(string fragment, SignatureAlgorithmDto signatureAlgorithm) =>
signatureAlgorithm switch
{
{ CryptoAlgorithm: "ECC", PaddingScheme: "NONE", HashFunction: "SHA-224" } => framgent == "#ecdsa-sha224",
{ CryptoAlgorithm: "ECC", PaddingScheme: "NONE", HashFunction: "SHA-256" } => framgent == "#ecdsa-sha256",
{ CryptoAlgorithm: "ECC", PaddingScheme: "NONE", HashFunction: "SHA-384" } => framgent == "#ecdsa-sha384",
{ CryptoAlgorithm: "ECC", PaddingScheme: "NONE", HashFunction: "SHA-512" } => framgent == "#ecdsa-sha512",
{ CryptoAlgorithm: "RSA", PaddingScheme: "PKCS1.5", HashFunction: "SHA-224" } => framgent == "#rsa-sha224",
{ CryptoAlgorithm: "RSA", PaddingScheme: "PKCS1.5", HashFunction: "SHA-256" } => framgent == "#rsa-sha256",
{ CryptoAlgorithm: "RSA", PaddingScheme: "PKCS1.5", HashFunction: "SHA-384" } => framgent == "#rsa-sha384",
{ CryptoAlgorithm: "RSA", PaddingScheme: "PKCS1.5", HashFunction: "SHA-512" } => framgent == "#rsa-sha512",
{ CryptoAlgorithm: "RSA", PaddingScheme: "PSS", HashFunction: "SHA-224" } => framgent == "#sha224-rsa-MGF1",
{ CryptoAlgorithm: "RSA", PaddingScheme: "PSS", HashFunction: "SHA-256" } => framgent == "#sha256-rsa-MGF1",
{ CryptoAlgorithm: "RSA", PaddingScheme: "PSS", HashFunction: "SHA-384" } => framgent == "#sha384-rsa-MGF1",
{ CryptoAlgorithm: "RSA", PaddingScheme: "PSS", HashFunction: "SHA-512" } => framgent == "#sha512-rsa-MGF1",
{ CryptoAlgorithm: "ECC", PaddingScheme: "NONE", HashFunction: "SHA-224" } => fragment == "#ecdsa-sha224",
{ CryptoAlgorithm: "ECC", PaddingScheme: "NONE", HashFunction: "SHA-256" } => fragment == "#ecdsa-sha256",
{ CryptoAlgorithm: "ECC", PaddingScheme: "NONE", HashFunction: "SHA-384" } => fragment == "#ecdsa-sha384",
{ CryptoAlgorithm: "ECC", PaddingScheme: "NONE", HashFunction: "SHA-512" } => fragment == "#ecdsa-sha512",
{ CryptoAlgorithm: "RSA", PaddingScheme: "PKCS1.5", HashFunction: "SHA-224" } => fragment == "#rsa-sha224",
{ CryptoAlgorithm: "RSA", PaddingScheme: "PKCS1.5", HashFunction: "SHA-256" } => fragment == "#rsa-sha256",
{ CryptoAlgorithm: "RSA", PaddingScheme: "PKCS1.5", HashFunction: "SHA-384" } => fragment == "#rsa-sha384",
{ CryptoAlgorithm: "RSA", PaddingScheme: "PKCS1.5", HashFunction: "SHA-512" } => fragment == "#rsa-sha512",
{ CryptoAlgorithm: "RSA", PaddingScheme: "PSS", HashFunction: "SHA-224" } => fragment == "#sha224-rsa-MGF1",
{ CryptoAlgorithm: "RSA", PaddingScheme: "PSS", HashFunction: "SHA-256" } => fragment == "#sha256-rsa-MGF1",
{ CryptoAlgorithm: "RSA", PaddingScheme: "PSS", HashFunction: "SHA-384" } => fragment == "#sha384-rsa-MGF1",
{ CryptoAlgorithm: "RSA", PaddingScheme: "PSS", HashFunction: "SHA-512" } => fragment == "#sha512-rsa-MGF1",
_ => false
};

~SigningService() => Dispose(false);

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

private void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
// You can release managed resources here if needed.
}

digidoc.terminate();
_disposedValue = true;
}
}
}
}
Loading