From da66998e053f995f183b2f7d0651aa46ada45ec0 Mon Sep 17 00:00:00 2001 From: Erkki Arus Date: Mon, 17 Jul 2023 16:30:14 +0300 Subject: [PATCH 1/2] Adds 'TEST of KLASS3-SK 2016' certificate and organisation certs support * Added TEST of KLASS3-SK 2016 to trusted CA list; * Uses Common Name in case of an organisation certificate instead of first and last name; * Merged with current main branch. Signed-off-by: Erkki Arus --- README.md | 30 +++++++++++++- .../Dev/TEST_of_KLASS3-SK_2016.cer | Bin 0 -> 1741 bytes .../Prod/KLASS3-SK_2016_EECCRCA_SHA384.cer | Bin 0 -> 1669 bytes .../Controllers/Api/AuthController.cs | 37 ++++++++++++++---- .../Pages/Welcome.cshtml.cs | 22 ++++++++--- .../Properties/launchSettings.json | 9 +++++ .../SessionBackedChallengeNonceStore.cs | 2 +- .../WebEid.AspNetCore.Example.csproj | 14 +++++++ 8 files changed, 98 insertions(+), 16 deletions(-) create mode 100644 src/WebEid.AspNetCore.Example/Certificates/Dev/TEST_of_KLASS3-SK_2016.cer create mode 100644 src/WebEid.AspNetCore.Example/Certificates/Prod/KLASS3-SK_2016_EECCRCA_SHA384.cer diff --git a/README.md b/README.md index 566c931..3c0919d 100644 --- a/README.md +++ b/README.md @@ -36,16 +36,42 @@ The algorithm, which performs the validation of the Web eID authentication token In case you need to provide your own CA certificates, add the `.cer` files to the `src/WebEid.AspNetCore.Example/Certificates/{Dev,Prod}` profile-specific directory. ### 3. Setup the `libdigidocpp` library for signing + `libdigidocpp` is a library for creating, signing and verifying digitally signed documents according to XAdES and XML-DSIG standards. It is a C++ library that has [SWIG](http://swig.org/) bindings for C#. Set up the `libdigidocpp` library as follows: +#### For MS Windows + 1. Install the _libdigidocpp-3.14.4.msi_ package or higher. The installation packages are available from [https://github.com/open-eid/libdigidocpp/releases](https://github.com/open-eid/libdigidocpp/releases). 2. Copy the C# source files from the `libdigidocpp` installation folder `include\digidocpp_csharp` to the `src\WebEid.AspNetCore.Example\DigiDoc` folder. 3. Copy all files from either the `x64` subfolder of the `libdigidocpp` installation folder to the example application build output folder `bin\...\net60` (after building, see next step). When building custom applications, choose `x64` if your application is 64-bit and `x86` if it is 32-bit. 4. When running in the `Development` profile, create an empty file named `EE_T.xml` for TSL cache as described in the [_Using test TSL lists_](https://github.com/open-eid/libdigidocpp/wiki/Using-test-TSL-lists#preconditions) section of the `libdigidocpp` wiki. -Further information is available in the [libdigidocpp example C# application](https://github.com/open-eid/libdigidocpp/tree/master/examples/DigiDocCSharp) and in the [`libdigidocpp` wiki](https://github.com/open-eid/libdigidocpp/wiki). +#### For Ubuntu Linux + +1. Add RIA repository to install the official _libdigidocpp-csharp_ package: +```sh +wget https://github.com/web-eid/web-eid-asp-dotnet-example/raw/main/src/ria_public_key.gpg +cp ria_public_key.gpg /usr/share/keyrings/ria-repository.gpg +echo "deb [signed-by=/usr/share/keyrings/ria-repository.gpg] https://installer.id.ee/media/ubuntu/ $(lsb_release -cs) main" > /etc/apt/sources.list.d/ria-repository.list +``` +2. Install the _libdigidocpp-csharp_ package: +```sh +apt update +apt install -y --no-install-recommends libdigidocpp-csharp +``` + +#### For macOS + +1. Install SWIG and build and install _libdigidocpp_ like described in [README.md](https://github.com/open-eid/libdigidocpp/blob/master/README.md). +2. Copy the C# source files from `/Library/libdigidocpp/include/digidocpp_csharp` directory to `src/WebEid.AspNetCore.Example/DigiDoc` directory. +3. Go to `src/WebEid.AspNetCore.Example/bin/.../net60` directory and create symbolic link to `/Library/libdigidocpp/lib/libdigidoc_csharp.dylib` library: +```cmd +ln -s /Library/libdigidocpp/lib/libdigidoc_csharp.dylib +``` + +Further information is available in the [libdigidocpp example C# application source code](https://github.com/open-eid/libdigidocpp/tree/master/examples/DigiDocCSharp) and in the [`libdigidocpp` Wiki](https://github.com/open-eid/libdigidocpp/wiki). ### 4. Build the application @@ -60,7 +86,7 @@ dotnet build If you have a test eID card, use the `Development` profile. In this case access to paid services is not required, but you need to upload the authentication and signing certificates of the test card to the test OCSP responder database as described in section _[Using DigiDoc4j in test mode with the `dev` profile](https://github.com/web-eid/web-eid-spring-boot-example#using-digidoc4j-in-test-mode-with-the-dev-profile)_ of the Web eID Java example application documentation. The`Development` profile is activated by default. -If you only have a production eID card, use the `Production` profile. You can still test authentication without further configuration; however, for digital signing to work, you need access to a paid timestamping service as described in section [_Using DigiDoc4j in production mode with the `prod` profile_](https://github.com/web-eid/web-eid-spring-boot-example#using-digidoc4j-in-production-mode-with-the-prod-profile) of the Web eID Java example documentation. +If you only have a production eID card, i.e. an eID card issued to a real person or organization, use the `Production` profile. You can still test authentication without further configuration; however, for digital signing to work, you need access to a paid timestamping service as described in section [_Using DigiDoc4j in production mode with the `prod` profile_](https://github.com/web-eid/web-eid-spring-boot-example#using-digidoc4j-in-production-mode-with-the-prod-profile) of the Web eID Java example documentation. You can specify the profile as an environment variable `ASPNETCORE_ENVIRONMENT` when running the application. To set the profile for the current session before starting the app using [`dotnet run`](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-run), use the following command: ```cmd diff --git a/src/WebEid.AspNetCore.Example/Certificates/Dev/TEST_of_KLASS3-SK_2016.cer b/src/WebEid.AspNetCore.Example/Certificates/Dev/TEST_of_KLASS3-SK_2016.cer new file mode 100644 index 0000000000000000000000000000000000000000..190f6de7e83bbb980d0f5c932dff55022fbd8f47 GIT binary patch literal 1741 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 Ub0)LK$sz1;#Hc#msEJYbH?#v~_5c6? literal 0 HcmV?d00001 diff --git a/src/WebEid.AspNetCore.Example/Controllers/Api/AuthController.cs b/src/WebEid.AspNetCore.Example/Controllers/Api/AuthController.cs index c4874d4..738cf84 100644 --- a/src/WebEid.AspNetCore.Example/Controllers/Api/AuthController.cs +++ b/src/WebEid.AspNetCore.Example/Controllers/Api/AuthController.cs @@ -29,6 +29,8 @@ using System.Threading.Tasks; using Security.Challenge; using WebEid.AspNetCore.Example.Dto; + using System; + using Microsoft.Extensions.Logging; [Route("[controller]")] [ApiController] @@ -36,25 +38,45 @@ public class AuthController : BaseController { private readonly IAuthTokenValidator authTokenValidator; private readonly IChallengeNonceStore challengeNonceStore; + private readonly ILogger logger; - public AuthController(IAuthTokenValidator authTokenValidator, IChallengeNonceStore challengeNonceStore) + public AuthController(IAuthTokenValidator authTokenValidator, IChallengeNonceStore challengeNonceStore, ILogger logger) { this.authTokenValidator = authTokenValidator; this.challengeNonceStore = challengeNonceStore; + this.logger = logger; } [HttpPost] [Route("login")] public async Task Login([FromBody] AuthenticateRequestDto authToken) { - var certificate = await this.authTokenValidator.Validate(authToken.AuthToken, this.challengeNonceStore.GetAndRemove().Base64EncodedNonce); - var claims = new List + var certificate = await authTokenValidator.Validate(authToken.AuthToken, challengeNonceStore.GetAndRemove().Base64EncodedNonce); + + Dictionary> claimDataGetters = new() { - new Claim(ClaimTypes.GivenName, certificate.GetSubjectGivenName()), - new Claim(ClaimTypes.Surname, certificate.GetSubjectSurname()), - new Claim(ClaimTypes.NameIdentifier, certificate.GetSubjectIdCode()) + { ClaimTypes.GivenName, certificate.GetSubjectGivenName }, + { ClaimTypes.Surname, certificate.GetSubjectSurname }, + { ClaimTypes.NameIdentifier, certificate.GetSubjectIdCode }, + { ClaimTypes.Name, certificate.GetSubjectCn } }; + List claims = new(); + foreach (var claimGetter in claimDataGetters) + { + try + { + // GivenName and Surname are not presented in case of organization certificates. + // Attempt to get these throw ArgumentOutOfRangeException type exception. + string claimData = claimGetter.Value.Invoke(); + claims.Add(new Claim(claimGetter.Key, claimData)); + } + catch (ArgumentOutOfRangeException) + { + logger.LogWarning("Claim {0} not presented", claimGetter.Key); + } + } + var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); var authProperties = new AuthenticationProperties @@ -77,8 +99,7 @@ await HttpContext.SignInAsync( public async Task Logout() { RemoveUserContainerFile(); - await HttpContext.SignOutAsync( - CookieAuthenticationDefaults.AuthenticationScheme); + await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); } } } diff --git a/src/WebEid.AspNetCore.Example/Pages/Welcome.cshtml.cs b/src/WebEid.AspNetCore.Example/Pages/Welcome.cshtml.cs index fcd2c03..f77c375 100644 --- a/src/WebEid.AspNetCore.Example/Pages/Welcome.cshtml.cs +++ b/src/WebEid.AspNetCore.Example/Pages/Welcome.cshtml.cs @@ -32,10 +32,22 @@ private static string GetPrincipalName(ClaimsIdentity identity) var givenName = identity.Claims.Where(claim => claim.Type == ClaimTypes.GivenName) .Select(claim => claim.Value) .SingleOrDefault(); - var surname = identity.Claims.Where(claim => claim.Type == ClaimTypes.Surname) - .Select(claim => claim.Value) - .SingleOrDefault(); - return $"{givenName} {surname}"; + + if (!string.IsNullOrEmpty(givenName)) + { + var surname = identity.Claims.Where(claim => claim.Type == ClaimTypes.Surname) + .Select(claim => claim.Value) + .SingleOrDefault(); + return $"{givenName} {surname}"; + } + else + { + // In case of organizations the Given Name and Surname are empty, + // and we use Common Name instead. + return identity.Claims.Where(claim => claim.Type == ClaimTypes.Name) + .Select(claim => claim.Value) + .SingleOrDefault(); + } } } -} \ No newline at end of file +} diff --git a/src/WebEid.AspNetCore.Example/Properties/launchSettings.json b/src/WebEid.AspNetCore.Example/Properties/launchSettings.json index a2e5ff7..08db888 100644 --- a/src/WebEid.AspNetCore.Example/Properties/launchSettings.json +++ b/src/WebEid.AspNetCore.Example/Properties/launchSettings.json @@ -20,8 +20,17 @@ "commandName": "Docker", "launchBrowser": true, "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", + "environmentVariables": {}, "publishAllPorts": true, "useSSL": true + }, + "WebEid.AspNetCore.Example": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Production" + } } } } \ No newline at end of file diff --git a/src/WebEid.AspNetCore.Example/SessionBackedChallengeNonceStore.cs b/src/WebEid.AspNetCore.Example/SessionBackedChallengeNonceStore.cs index e269f36..39839ff 100644 --- a/src/WebEid.AspNetCore.Example/SessionBackedChallengeNonceStore.cs +++ b/src/WebEid.AspNetCore.Example/SessionBackedChallengeNonceStore.cs @@ -50,4 +50,4 @@ public ChallengeNonce GetAndRemoveImpl() return null; } } -} \ No newline at end of file +} diff --git a/src/WebEid.AspNetCore.Example/WebEid.AspNetCore.Example.csproj b/src/WebEid.AspNetCore.Example/WebEid.AspNetCore.Example.csproj index ceaae77..92c9c4c 100644 --- a/src/WebEid.AspNetCore.Example/WebEid.AspNetCore.Example.csproj +++ b/src/WebEid.AspNetCore.Example/WebEid.AspNetCore.Example.csproj @@ -10,6 +10,14 @@ true + + 1701;1702;VSSpell001;VSSpell002 + + + + 1701;1702;VSSpell001;VSSpell002 + + @@ -29,6 +37,12 @@ Always + + PreserveNewest + + + PreserveNewest + From 85574bf763b5a6523e2276c1366be263d031806b Mon Sep 17 00:00:00 2001 From: Erkki Arus Date: Mon, 29 Apr 2024 18:04:00 +0300 Subject: [PATCH 2/2] Target x64 platform Replaced AnyCPU configuration with x64 as target platform while libdigidocpp is provided as x64 binaries only. Signed-off-by: Erkki Arus --- src/Dockerfile | 2 +- src/WebEid.AspNetCore.Example.sln | 12 ++++++------ .../WebEid.AspNetCore.Example.csproj | 5 +++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Dockerfile b/src/Dockerfile index 8c8a127..ef9ad92 100644 --- a/src/Dockerfile +++ b/src/Dockerfile @@ -13,7 +13,7 @@ RUN echo "deb [signed-by=/usr/share/keyrings/ria-repository.gpg] https://install apt-get clean && \ rm -rf /var/lib/apt/lists -COPY ./WebEid.AspNetCore.Example/bin/Release/net6.0/publish/ . +COPY ./WebEid.AspNetCore.Example/bin/x64/Release/net6.0/publish/ . ENV ASPNETCORE_ENVIRONMENT=Production diff --git a/src/WebEid.AspNetCore.Example.sln b/src/WebEid.AspNetCore.Example.sln index 72f5596..f4d8b82 100644 --- a/src/WebEid.AspNetCore.Example.sln +++ b/src/WebEid.AspNetCore.Example.sln @@ -7,14 +7,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebEid.AspNetCore.Example", EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {573AD725-C52C-40DE-8E70-6DF4E4227120}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {573AD725-C52C-40DE-8E70-6DF4E4227120}.Debug|Any CPU.Build.0 = Debug|Any CPU - {573AD725-C52C-40DE-8E70-6DF4E4227120}.Release|Any CPU.ActiveCfg = Release|Any CPU - {573AD725-C52C-40DE-8E70-6DF4E4227120}.Release|Any CPU.Build.0 = Release|Any CPU + {573AD725-C52C-40DE-8E70-6DF4E4227120}.Debug|x64.ActiveCfg = Debug|x64 + {573AD725-C52C-40DE-8E70-6DF4E4227120}.Debug|x64.Build.0 = Debug|x64 + {573AD725-C52C-40DE-8E70-6DF4E4227120}.Release|x64.ActiveCfg = Release|x64 + {573AD725-C52C-40DE-8E70-6DF4E4227120}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/WebEid.AspNetCore.Example/WebEid.AspNetCore.Example.csproj b/src/WebEid.AspNetCore.Example/WebEid.AspNetCore.Example.csproj index 92c9c4c..d14b5b8 100644 --- a/src/WebEid.AspNetCore.Example/WebEid.AspNetCore.Example.csproj +++ b/src/WebEid.AspNetCore.Example/WebEid.AspNetCore.Example.csproj @@ -8,13 +8,14 @@ Linux WebEid.AspNetCore.Example true + x64 - + 1701;1702;VSSpell001;VSSpell002 - + 1701;1702;VSSpell001;VSSpell002