mirror of
https://github.com/aliasvault/aliasvault.git
synced 2025-12-23 22:28:22 -05:00
Add certificate generation for DataProtection API for both local dev and Docker containers (#130)
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
API_URL=
|
||||
JWT_KEY=
|
||||
DATA_PROTECTION_CERT_PASS=
|
||||
ADMIN_PASSWORD_HASH=
|
||||
ADMIN_PASSWORD_GENERATED=2024-01-01T00:00:00Z
|
||||
PRIVATE_EMAIL_DOMAINS=
|
||||
SMTP_TLS_ENABLED=false
|
||||
|
||||
4
certificates/README.md
Normal file
4
certificates/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
This is the default location where (self-generated) certificates are stored.
|
||||
|
||||
For example, the API and Admin projects make use of the .NET DataProtection API that depends on
|
||||
certificates for encrypting various types of application data such as authentication cookies, anti-forgery tokens etc.
|
||||
@@ -7,6 +7,7 @@ services:
|
||||
ports:
|
||||
- "8080:8082"
|
||||
volumes:
|
||||
- ./certificates:/certificates:rw
|
||||
- ./database:/database:rw
|
||||
- ./logs:/logs:rw
|
||||
restart: always
|
||||
@@ -31,6 +32,7 @@ services:
|
||||
ports:
|
||||
- "81:8081"
|
||||
volumes:
|
||||
- ./certificates:/certificates:rw
|
||||
- ./database:/database:rw
|
||||
- ./logs:/logs:rw
|
||||
env_file:
|
||||
|
||||
22
install.sh
22
install.sh
@@ -119,6 +119,11 @@ generate_jwt_key() {
|
||||
dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 | head -c 32
|
||||
}
|
||||
|
||||
# Function to generate a new 60-character DATA_PROTECTION_CERT_PASS
|
||||
generate_data_protection_cert_pass() {
|
||||
dd if=/dev/urandom bs=1 count=60 2>/dev/null | base64 | head -c 60
|
||||
}
|
||||
|
||||
# Function to create .env file from .env.example if it doesn't exist
|
||||
create_env_file() {
|
||||
printf "${CYAN}> Creating .env file...${NC}\n"
|
||||
@@ -170,6 +175,22 @@ populate_jwt_key() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to check and populate the .env file with DATA_PROTECTION_CERT_PASS
|
||||
populate_data_protection_cert_pass() {
|
||||
printf "${CYAN}> Checking DATA_PROTECTION_CERT_PASS...${NC}\n"
|
||||
if ! grep -q "^DATA_PROTECTION_CERT_PASS=" "$ENV_FILE" || [ -z "$(grep "^DATA_PROTECTION_CERT_PASS=" "$ENV_FILE" | cut -d '=' -f2)" ]; then
|
||||
DATA_PROTECTION_CERT_PASS=$(generate_data_protection_cert_pass)
|
||||
if grep -q "^DATA_PROTECTION_CERT_PASS=" "$ENV_FILE"; then
|
||||
awk -v key="DATA_PROTECTION_CERT_PASS" '/^DATA_PROTECTION_CERT_PASS=/ {$0="DATA_PROTECTION_CERT_PASS="key} 1' "$ENV_FILE" > "$ENV_FILE.tmp" && mv "$ENV_FILE.tmp" "$ENV_FILE"
|
||||
else
|
||||
echo "DATA_PROTECTION_CERT_PASS=${DATA_PROTECTION_CERT_PASS}" >> "$ENV_FILE"
|
||||
fi
|
||||
printf "${GREEN}> DATA_PROTECTION_KEY has been generated and added to $ENV_FILE.${NC}\n"
|
||||
else
|
||||
printf "${GREEN}> DATA_PROTECTION_KEY already exists and has a value in $ENV_FILE.${NC}\n"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to ask the user for PRIVATE_EMAIL_DOMAINS
|
||||
set_private_email_domains() {
|
||||
printf "${CYAN}> Setting PRIVATE_EMAIL_DOMAINS...${NC}\n"
|
||||
@@ -316,6 +337,7 @@ main() {
|
||||
create_env_file || exit $?
|
||||
populate_api_url || exit $?
|
||||
populate_jwt_key || exit $?
|
||||
populate_data_protection_cert_pass || exit $?
|
||||
set_private_email_domains || exit $?
|
||||
set_smtp_tls_enabled || exit $?
|
||||
generate_admin_password || exit $?
|
||||
|
||||
@@ -20,9 +20,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.7">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
@@ -47,6 +45,7 @@
|
||||
<ProjectReference Include="..\Databases\AliasServerDb\AliasServerDb.csproj" />
|
||||
<ProjectReference Include="..\Utilities\AliasVault.Logging\AliasVault.Logging.csproj" />
|
||||
<ProjectReference Include="..\Utilities\AliasVault.RazorComponents\AliasVault.RazorComponents.csproj" />
|
||||
<ProjectReference Include="..\Utilities\Cryptography\Cryptography.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -8,13 +8,16 @@
|
||||
using System.Data.Common;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using AliasServerDb;
|
||||
using AliasVault.Admin;
|
||||
using AliasVault.Admin.Auth.Providers;
|
||||
using AliasVault.Admin.Main;
|
||||
using AliasVault.Admin.Services;
|
||||
using AliasVault.Logging;
|
||||
using Cryptography;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.Data.Sqlite;
|
||||
@@ -93,6 +96,37 @@ builder.Services.AddIdentityCore<AdminUser>(options =>
|
||||
.AddSignInManager()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
// Generate or load the certificate
|
||||
X509Certificate2 cert;
|
||||
string certPath = "../../certificates/AliasVault.DataProtection.pfx";
|
||||
string certPassword = Environment.GetEnvironmentVariable("DATA_PROTECTION_CERT_PASS") ?? throw new KeyNotFoundException("DATA_PROTECTION_CERT_PASS environment variable is not set.");
|
||||
if (certPassword == "Development")
|
||||
{
|
||||
// For development use local certificate so it doesn't interfere with Docker setup which uses a unique generated password.
|
||||
certPath = Path.Combine(AppContext.BaseDirectory, "AliasVault.DataProtection.Development.pfx");
|
||||
}
|
||||
|
||||
if (!File.Exists(certPath))
|
||||
{
|
||||
cert = CertificateGenerator.GeneratePfx("AliasVault.DataProtection", certPassword);
|
||||
CertificateGenerator.SaveCertificateToFile(cert, certPassword, certPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
cert = new X509Certificate2(certPath, certPassword);
|
||||
}
|
||||
|
||||
builder.Services.AddDataProtection()
|
||||
.ProtectKeysWithCertificate(cert)
|
||||
.PersistKeysToDbContext<AliasServerDbContext>()
|
||||
.SetApplicationName("AliasVault.Admin");
|
||||
|
||||
builder.Services.Configure<DataProtectionTokenProviderOptions>(options =>
|
||||
{
|
||||
options.TokenLifespan = TimeSpan.FromDays(30);
|
||||
options.Name = "AliasVault.Admin";
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"ADMIN_PASSWORD_HASH": "AQAAAAIAAYagAAAAEKWfKfa2gh9Z72vjAlnNP1xlME7FsunRznzyrfqFte40FToufRwa3kX8wwDwnEXZag==",
|
||||
"ADMIN_PASSWORD_GENERATED": "2024-01-01T00:00:00Z"
|
||||
"ADMIN_PASSWORD_GENERATED": "2024-01-01T00:00:00Z",
|
||||
"DATA_PROTECTION_CERT_PASS": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
|
||||
@@ -21,18 +21,14 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Asp.Versioning.Mvc" Version="8.1.0" />
|
||||
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.7">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.0.1" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -7,13 +7,16 @@
|
||||
|
||||
using System.Data.Common;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using AliasServerDb;
|
||||
using AliasVault.Api.Jwt;
|
||||
using AliasVault.Logging;
|
||||
using AliasVault.Shared.Providers.Time;
|
||||
using Asp.Versioning;
|
||||
using Cryptography;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -50,12 +53,31 @@ builder.Services.AddDbContextFactory<AliasServerDbContext>((container, options)
|
||||
options.UseSqlite(connection).UseLazyLoadingProxies();
|
||||
});
|
||||
|
||||
builder.Services.AddDataProtection();
|
||||
builder.Services.Configure<DataProtectionTokenProviderOptions>(options =>
|
||||
// Generate or load the DataProtection certificate.
|
||||
X509Certificate2 cert;
|
||||
string certPath = "../../certificates/AliasVault.DataProtection.pfx";
|
||||
string certPassword = Environment.GetEnvironmentVariable("DATA_PROTECTION_CERT_PASS") ?? throw new KeyNotFoundException("DATA_PROTECTION_CERT_PASS environment variable is not set.");
|
||||
if (certPassword == "Development")
|
||||
{
|
||||
options.TokenLifespan = TimeSpan.FromDays(30);
|
||||
options.Name = "AliasVault";
|
||||
});
|
||||
// For development use local certificate so it doesn't interfere with Docker setup which uses a unique generated password.
|
||||
certPath = Path.Combine(AppContext.BaseDirectory, "AliasVault.DataProtection.Development.pfx");
|
||||
}
|
||||
|
||||
if (!File.Exists(certPath))
|
||||
{
|
||||
cert = CertificateGenerator.GeneratePfx("AliasVault.DataProtection", certPassword);
|
||||
CertificateGenerator.SaveCertificateToFile(cert, certPassword, certPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
cert = new X509Certificate2(certPath, certPassword);
|
||||
}
|
||||
|
||||
builder.Services.AddDataProtection()
|
||||
.ProtectKeysWithCertificate(cert)
|
||||
.PersistKeysToDbContext<AliasServerDbContext>()
|
||||
.SetApplicationName("AliasVault.Api");
|
||||
|
||||
builder.Services.AddIdentity<AliasVaultUser, AliasVaultRole>(options =>
|
||||
{
|
||||
options.Password.RequireDigit = false;
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"JWT_KEY": "12345678901234567890123456789012"
|
||||
"JWT_KEY": "12345678901234567890123456789012",
|
||||
"DATA_PROTECTION_CERT_PASS": "Development"
|
||||
},
|
||||
"dotnetRunMessages": true,
|
||||
"applicationUrl": "https://localhost:7223;http://localhost:5092"
|
||||
|
||||
@@ -16,15 +16,17 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.7">
|
||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
namespace AliasServerDb;
|
||||
|
||||
using AliasVault.WorkerStatus.Database;
|
||||
using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
@@ -17,7 +18,7 @@ using Microsoft.Extensions.Configuration;
|
||||
/// we have two separate user objects, one for the admin panel and one for the vault. We manually
|
||||
/// define the Identity tables in the OnModelCreating method.
|
||||
/// </summary>
|
||||
public class AliasServerDbContext : WorkerStatusDbContext
|
||||
public class AliasServerDbContext : WorkerStatusDbContext, IDataProtectionKeyContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AliasServerDbContext"/> class.
|
||||
@@ -35,6 +36,11 @@ public class AliasServerDbContext : WorkerStatusDbContext
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DataProtectionKeys DbSet.
|
||||
/// </summary>
|
||||
public DbSet<DataProtectionKey> DataProtectionKeys { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the AliasVaultUser DbSet.
|
||||
/// </summary>
|
||||
|
||||
724
src/Databases/AliasServerDb/Migrations/20240821164021_AddDataProtectionDatabaseTable.Designer.cs
generated
Normal file
724
src/Databases/AliasServerDb/Migrations/20240821164021_AddDataProtectionDatabaseTable.Designer.cs
generated
Normal file
@@ -0,0 +1,724 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using AliasServerDb;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace AliasServerDb.Migrations
|
||||
{
|
||||
[DbContext(typeof(AliasServerDbContext))]
|
||||
[Migration("20240821164021_AddDataProtectionDatabaseTable")]
|
||||
partial class AddDataProtectionDatabaseTable
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.8")
|
||||
.HasAnnotation("Proxies:ChangeTracking", false)
|
||||
.HasAnnotation("Proxies:CheckEquality", false)
|
||||
.HasAnnotation("Proxies:LazyLoading", true);
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.AdminRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("AdminRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.AdminUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("LastPasswordChanged")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("AdminUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.AliasVaultRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("AliasVaultRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.AliasVaultUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Salt")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Verifier")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("AliasVaultUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.AliasVaultUserRefreshToken", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DeviceIdentifier")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ExpireDate")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AliasVaultUserRefreshTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.Email", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("Date")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("DateSystem")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EncryptedSymmetricKey")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("From")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FromDomain")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FromLocal")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("MessageHtml")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("MessagePlain")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("MessagePreview")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("MessageSource")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("PushNotificationSent")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Subject")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("To")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ToDomain")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ToLocal")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("UserEncryptionKeyId")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Visible")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Date");
|
||||
|
||||
b.HasIndex("DateSystem");
|
||||
|
||||
b.HasIndex("PushNotificationSent");
|
||||
|
||||
b.HasIndex("ToLocal");
|
||||
|
||||
b.HasIndex("UserEncryptionKeyId");
|
||||
|
||||
b.HasIndex("Visible");
|
||||
|
||||
b.ToTable("Emails");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.EmailAttachment", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<byte[]>("Bytes")
|
||||
.IsRequired()
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<DateTime>("Date")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EmailId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Filename")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Filesize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("MimeType")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("EmailId");
|
||||
|
||||
b.ToTable("EmailAttachments");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.Log", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Application")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Exception")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Level")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("LogEvent")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("LogEvent");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("MessageTemplate")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Properties")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("TimeStamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Application");
|
||||
|
||||
b.HasIndex("TimeStamp");
|
||||
|
||||
b.ToTable("Logs", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.UserEmailClaim", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Address")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AddressDomain")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AddressLocal")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Address")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("UserEmailClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.UserEncryptionKey", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsPrimary")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("PublicKey")
|
||||
.IsRequired()
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("UserEncryptionKeys");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.Vault", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("FileSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("VaultBlob")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Version")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Vaults");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasVault.WorkerStatus.Database.WorkerServiceStatus", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("CurrentStatus")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DesiredStatus")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("Heartbeat")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ServiceName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("varchar");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("WorkerServiceStatuses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("FriendlyName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Xml")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("DataProtectionKeys");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("RoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("UserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.ToTable("UserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.ToTable("UserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("UserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.AliasVaultUserRefreshToken", b =>
|
||||
{
|
||||
b.HasOne("AliasServerDb.AliasVaultUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.Email", b =>
|
||||
{
|
||||
b.HasOne("AliasServerDb.UserEncryptionKey", "EncryptionKey")
|
||||
.WithMany("Emails")
|
||||
.HasForeignKey("UserEncryptionKeyId")
|
||||
.OnDelete(DeleteBehavior.NoAction)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("EncryptionKey");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.EmailAttachment", b =>
|
||||
{
|
||||
b.HasOne("AliasServerDb.Email", "Email")
|
||||
.WithMany("Attachments")
|
||||
.HasForeignKey("EmailId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Email");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.UserEmailClaim", b =>
|
||||
{
|
||||
b.HasOne("AliasServerDb.AliasVaultUser", "User")
|
||||
.WithMany("EmailClaims")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.UserEncryptionKey", b =>
|
||||
{
|
||||
b.HasOne("AliasServerDb.AliasVaultUser", "User")
|
||||
.WithMany("EncryptionKeys")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.Vault", b =>
|
||||
{
|
||||
b.HasOne("AliasServerDb.AliasVaultUser", "User")
|
||||
.WithMany("Vaults")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.AliasVaultUser", b =>
|
||||
{
|
||||
b.Navigation("EmailClaims");
|
||||
|
||||
b.Navigation("EncryptionKeys");
|
||||
|
||||
b.Navigation("Vaults");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.Email", b =>
|
||||
{
|
||||
b.Navigation("Attachments");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AliasServerDb.UserEncryptionKey", b =>
|
||||
{
|
||||
b.Navigation("Emails");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace AliasServerDb.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddDataProtectionDatabaseTable : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "DataProtectionKeys",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
FriendlyName = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Xml = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_DataProtectionKeys", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "DataProtectionKeys");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ namespace AliasServerDb.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.7")
|
||||
.HasAnnotation("ProductVersion", "8.0.8")
|
||||
.HasAnnotation("Proxies:ChangeTracking", false)
|
||||
.HasAnnotation("Proxies:CheckEquality", false)
|
||||
.HasAnnotation("Proxies:LazyLoading", true);
|
||||
@@ -523,6 +523,23 @@ namespace AliasServerDb.Migrations
|
||||
b.ToTable("WorkerServiceStatuses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("FriendlyName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Xml")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("DataProtectionKeys");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
||||
67
src/Utilities/Cryptography/CertificateGenerator.cs
Normal file
67
src/Utilities/Cryptography/CertificateGenerator.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// <copyright file="CertificateGenerator.cs" company="lanedirt">
|
||||
// Copyright (c) lanedirt. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
|
||||
// </copyright>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace Cryptography;
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
/// <summary>
|
||||
/// Helper methods to generate certificates.
|
||||
/// </summary>
|
||||
public static class CertificateGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// Generate a self-signed Pfx certificate.
|
||||
/// </summary>
|
||||
/// <param name="subjectName">Subject name.</param>
|
||||
/// <param name="password">Password for certificate.</param>
|
||||
/// <param name="validityYears">Validity in years.</param>
|
||||
/// <returns>X509Certificate2 instance.</returns>
|
||||
public static X509Certificate2 GeneratePfx(string subjectName, string password, int validityYears = 100)
|
||||
{
|
||||
using (RSA rsa = RSA.Create(4096))
|
||||
{
|
||||
var request = new CertificateRequest(
|
||||
$"CN={subjectName}",
|
||||
rsa,
|
||||
HashAlgorithmName.SHA256,
|
||||
RSASignaturePadding.Pkcs1);
|
||||
|
||||
request.CertificateExtensions.Add(
|
||||
new X509KeyUsageExtension(
|
||||
X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature,
|
||||
false));
|
||||
|
||||
request.CertificateExtensions.Add(
|
||||
new X509EnhancedKeyUsageExtension(
|
||||
new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));
|
||||
|
||||
var certificate = request.CreateSelfSigned(
|
||||
DateTimeOffset.UtcNow.AddDays(-1),
|
||||
DateTimeOffset.UtcNow.AddYears(validityYears));
|
||||
|
||||
return new X509Certificate2(
|
||||
certificate.Export(X509ContentType.Pfx, password),
|
||||
password,
|
||||
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the certificate to a file.
|
||||
/// </summary>
|
||||
/// <param name="cert">The certificate.</param>
|
||||
/// <param name="password">Password of certificate.</param>
|
||||
/// <param name="filePath">Path to save certificate to.</param>
|
||||
public static void SaveCertificateToFile(X509Certificate2 cert, string password, string filePath)
|
||||
{
|
||||
File.WriteAllBytes(filePath, cert.Export(X509ContentType.Pfx, password));
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Security;
|
||||
|
||||
/// <summary>
|
||||
/// SrpArgonEncryption class.
|
||||
/// RSA/AES and Argon2id encryption methods.
|
||||
/// </summary>
|
||||
public static class Encryption
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user