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

Adds 'TEST of KLASS3-SK 2016' certificate and organization certs support #7

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to use an organization certificate in a Development mode? Similarly to the test ID-card.


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
Expand Down
Binary file not shown.
Binary file not shown.
37 changes: 29 additions & 8 deletions src/WebEid.AspNetCore.Example/Controllers/Api/AuthController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,32 +29,54 @@
using System.Threading.Tasks;
using Security.Challenge;
using WebEid.AspNetCore.Example.Dto;
using System;
using Microsoft.Extensions.Logging;

[Route("[controller]")]
[ApiController]
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<Claim>
var certificate = await authTokenValidator.Validate(authToken.AuthToken, challengeNonceStore.GetAndRemove().Base64EncodedNonce);

Dictionary<string, Func<string>> claimDataGetters = new()
Copy link
Contributor

@rabadashTheFool rabadashTheFool May 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since Dictionary is unordered and Claims in ClaimsIdentity are ordered, we should try to maintain that order.
Instead of that, we could introduce a helper method for adding new claims to claims list, such as:
AddNewClaimIfCertificateHasData(List claims, string claimType, Func dataGetter)

This would also make the code more readable and remove the necessity of creating new temporary dictionaries and loops.

{
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<Claim> 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)
Copy link
Contributor

@rabadashTheFool rabadashTheFool May 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When creating a new Claim, the exception thrown is ArgumentNullException when either the type or value is null.
Furthermore, we should not try to add Claims with missing values but rather skip them.

{
logger.LogWarning("Claim {0} not presented", claimGetter.Key);
Copy link
Contributor

@rabadashTheFool rabadashTheFool May 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When using an organization certificate, we would not want to log missing fields which we were not expecting in the first place.
As such, Logger is not yet necessary in this file.

}
}

var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);

var authProperties = new AuthenticationProperties
Expand All @@ -77,8 +99,7 @@ await HttpContext.SignInAsync(
public async Task Logout()
{
RemoveUserContainerFile();
await HttpContext.SignOutAsync(
CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
}
}
22 changes: 17 additions & 5 deletions src/WebEid.AspNetCore.Example/Pages/Welcome.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
}
}
}
9 changes: 9 additions & 0 deletions src/WebEid.AspNetCore.Example/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@ public ChallengeNonce GetAndRemoveImpl()
return null;
}
}
}
}
14 changes: 14 additions & 0 deletions src/WebEid.AspNetCore.Example/WebEid.AspNetCore.Example.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should set x64 target. For best compatibility

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the best compatibility it should be AnyCPU while in that case the architecture stays unspecified in IL and it is able to run on any target platform. x64 limits it with 64-bit platforms.

Copy link
Contributor

@metsma metsma Apr 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only provide x64 binaries for libdigidocpp. I cannot sign anything under arm64 windows and ubuntu

<NoWarn>1701;1702;VSSpell001;VSSpell002</NoWarn>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<NoWarn>1701;1702;VSSpell001;VSSpell002</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.4" NoWarn="NU1605" />
Expand All @@ -29,6 +37,12 @@
<None Update="Certificates\Prod\ESTEID2018.cer">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Certificates\Prod\KLASS3-SK_2016_EECCRCA_SHA384.cer">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Certificates\Dev\TEST_of_KLASS3-SK_2016.cer">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

<ItemGroup>
Expand Down
Loading