[성현모] VPKI 프로젝트 Import

This commit is contained in:
SHM
2025-04-24 15:42:47 +09:00
parent 0a6016bc95
commit 8dcddd431b
188 changed files with 280230 additions and 68 deletions

View File

@ -0,0 +1,30 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
!**/.gitignore
!.git/HEAD
!.git/config
!.git/packed-refs
!.git/refs/heads/**

View File

@ -0,0 +1,80 @@
{
"Server": {
"Address": "https://*",
"Port": 8080,
"IIS": false
},
"API": [
{
"ApiName": "vpkira",
"Address": "https://vpkira.hmckmc.co.kr/"
//"Address": "https://vpkira-dev.hmckmc.co.kr/"
},
//{
// "ApiName": "ocsp",
// "Address": "http://ocsp.hmckmc.co.kr/"
// //"Address": "http://d-ocsp.hmckmc.co.kr/"
//},
//{
// "ApiName": "d-crl",
// "Address": "http://crl.hmckmc.co.kr/"
// //"Address": "http://d-crl.hmckmc.co.kr/"
//}
],
"Auth": {
"issuer": "vpki.api",
"audience": "vpki",
"accessTokenSecret": "t6zdogyrT0U1bYw3gJvMm3JHmj2Iyawr7O2WKE2truX+MK0l/XNGmpU2ofagdUWBN4DxAUv7c8xSYVv/8abL6A==",
"accessTokenExpires": 60, //minutes
"refreshTokenSecret": "1vVuoGqIqkStFI3QUXHMr0/yO1feLPnhqcfFGjZyk478+4WY7dhrUjCfVeWjmmSZYgb+rtP0X6ec+3iL35Yezw==",
"refreshTokenExpires": 1440 //minuts, 60*24 (1day)
},
"DataBase": [
{
"IP": "127.0.0.1",
"Port": 1433,
"DBName": "VPKI_AccountDB",
"DBID": 1,
"DBContext": "VpkiAccountDbContext",
"UserID": "VPKI",
"Password": "Kefico!@34"
},
{
"IP": "127.0.0.1",
"Port": 1433,
"DBName": "VPKI_AccountDB_DEV",
"DBID": 2,
"DBContext": "VpkiAccountDbContext",
"UserID": "VPKI",
"Password": "Kefico!@34"
},
{
"IP": "127.0.0.1",
"Port": 1433,
"DBName": "VPKI_DataDB",
"DBID": 1,
"DBContext": "VpkiDataDbContext",
"UserID": "VPKI",
"Password": "Kefico!@34"
},
{
"IP": "127.0.0.1",
"Port": 1433,
"DBName": "VPKI_DataDB_DEV",
"DBID": 2,
"DBContext": "VpkiDataDbContext",
"UserID": "VPKI",
"Password": "Kefico!@34"
}
],
"Openssl": {
"Path": "C:/Program Files/OpenSSL-Win64/bin/openssl.exe",
"SubCAPath": "../SubCA/",
"SubCA": {
"prov_v1": "SubCA_P02OEM.pem",
"prov_cert": "SubCA_P20OEM.pem",
"vehicle_cert": "SubCA_P20VHC.pem"
},
"RootCA": "hkmcrootca.pem"
}
}

View File

@ -0,0 +1,14 @@
{
"Server": {
"Address": "https://*",
"Port": 8000,
"IIS": false
},
"Api": [
{
"ApiName": "VPKI.API",
"Address": "https://127.0.0.1",
"Port": 8080
}
]
}

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<log4net>
<root>
<level value="ALL"/>
<appender-ref ref="Console"/>
<appender-ref ref="file"/>
<appender-ref ref="fatal_file"/>
</root>
<appender name="Console" type="log4net.Appender.ManagedColoredConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="[%date] [%thread] %-6level: %message%newline" />
</layout>
<mapping>
<level value="FATAL" />
<foreColor value="White" />
<backColor value="Red" />
</mapping>
<mapping>
<level value="ERROR" />
<foreColor value="Red" />
</mapping>
<mapping>
<level value="WARN" />
<foreColor value="Yellow" />
</mapping>
<mapping>
<level value="INFO" />
<foreColor value="Green" />
</mapping>
<mapping>
<level value="DEBUG" />
<foreColor value="Blue" />
</mapping>
<mapping>
<level value="DB" />
<foreColor value="DarkMagenta" />
</mapping>
<mapping>
<level value="HTTP" />
<foreColor value="DarkYellow" />
</mapping>
<mapping>
<level value="SOCKET" />
<foreColor value="DarkCyan" />
</mapping>
<mapping>
<level value="CONTROLLER" />
<foreColor value="DarkGreen" />
</mapping>
</appender>
<appender name="file" type="log4net.Appender.RollingFileAppender">
<file value="log/" />
<datepattern value="yyyy////MM////yyyy-MM-dd'.log'"/>
<appendToFile value="true" />
<rollingStyle value="Date" />
<staticLogFileName value="false" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="[%date] [%thread] %level %logger - %message%newline" />
</layout>
</appender>
<appender name="fatal_file" type="log4net.Appender.RollingFileAppender">
<file value="log/" />
<datepattern value="yyyy////MM////yyyy-MM-dd'_fatal.log'"/>
<appendToFile value="true" />
<rollingStyle value="Date" />
<staticLogFileName value="false" />
<filter type="log4net.Filter.LevelRangeFilter">
<param name="LevelMin" value="FATAL" />
<param name="LevelMax" value="FATAL" />
</filter>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="[%date] [%thread] %level %logger - %message%newline" />
</layout>
</appender>
</log4net>
</configuration>

View File

@ -0,0 +1,16 @@
events {
worker_connections 1000;
}
http {
upstream vpki.web.api {
server vpki.web.api:5555;
}
server {
listen 80;
location / {
proxy_pass https://vpki.web.api/;
}
}
}

View File

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICszCCAlmgAwIBAgIUHxrrBdc1lvNKymblQjb06G6MSJYwCgYIKoZIzj0EAwI
wPjELMAkGA1UEBhMCS1IxDTALBgNVBAoMBEhLTUMxDDAKBgNVBAsMA0VDQzESMB
AGA1UEAwwJUm9vdENBMDAxMCAXDTE5MDYxMjA2MTUxNVoYDzIwNzkwNjEyMjM1O
TU5WjA8MQswCQYDVQQGEwJLUjENMAsGA1UECgwESEtNQzEMMAoGA1UECwwDRUND
MRAwDgYDVQQDDAdDQTAxMDAxMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEB0K
uXigZeI7U1McUnjEhYGL4g7zsvyzoNl8SMJ0oPxJgiXka+A37JjK4L/P85bAG7N
4C6IYuSem99P0C0vt0K6OCATMwggEvMB8GA1UdIwQYMBaAFLW9YfqauBG22R0cD
ywt08OqFD99MB0GA1UdDgQWBBQl67U3b8S3sG1J/ukFKlGcqPpw8DAOBgNVHQ8B
Af8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADBBBgNVHR8EOjA4MDagNKAyhjB
odHRwOi8vY3JsLmhtY2ttYy5jby5rci9FQ0MvUm9vdC9hcmwvQXJsMURwMS5jcm
wwgYUGCCsGAQUFBwEBBHkwdzA7BggrBgEFBQcwAoYvaHR0cDovL2NybC5obWNrb
WMuY28ua3IvRUNDL2NlcnQvaGttY3Jvb3RjYS5kZXIwOAYIKwYBBQUHMAGGLGh0
dHA6Ly9ydG9jc3AuaG1ja21jLmNvLmtyL0VDQy9PQ1NQL2hrbWNvY3NwMAoGCCq
GSM49BAMCA0gAMEUCICfXVV8IhFBXkaOHkg2Wk883y9r3B5rPtDV9JKhUQuBXAi
EAug5R/broK+ZjM3vYdU7dndBfMFkOYtCa1NBdq6ie/Fs=
-----END CERTIFICATE-----

View File

@ -0,0 +1,3 @@
-----BEGIN CERTIFICATE-----
MIICvzCCAiCgAwIBAgIUDsyHo4Z9sUNMNqyLEBC1HBmJjDIwCgYIKoZIzj0EAwQwPjELMAkGA1UEBhMCS1IxDTALBgNVBAoMBEhLTUMxDDAKBgNVBAsMA0VDQzESMBAGA1UEAwwJUm9vdENBMDAyMCAXDTIzMDEzMDAyNTAzM1oYDzIwODMwMTMwMTQ1OTU5WjA8MQswCQYDVQQGEwJLUjENMAsGA1UECgwESEtNQzEMMAoGA1UECwwDRUNDMRAwDgYDVQQDDAdDQTAxMDAyMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQB0OtZd+xmRyO4zVZxg3sHV+TsDq/eUq10gqDU8UgdWOX2dqS9ELG1atX7jZ/B7GjpkM+iHkfWT2HAdq5UbDo8hq0BnisaBxK5GCE39Qn6RRtuCaBxuxmSdLUUj/2FNun7enFzlL8hsYt3MK4iK4YLI6ibaVUvuegeYRKYai5XscAPgkajgbgwgbUwIgYDVR0jAQH/BBgwFoAURTRfYSTjJP2uBQL8GaTkuNZZ/LAwIAYDVR0OAQH/BBYEFAYMTDbCQgUerEWCQXA17t7r/CDyMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMEkGA1UdHwEB/wQ/MD0wO6A5oDeGNWh0dHA6Ly9jcmwuaG1ja21jLmNvLmtyL0VDQy9Sb290Q0EwMDIvYXJsL0FybDFEcDEuY3JsMAoGCCqGSM49BAMEA4GMADCBiAJCAPW2gWcbrJdhOPbHGiXHHrSBiV8z5BSLzmHFn4jmsmFOrVpACVZTcTXg0odfug9iyI+OjTI2yj8MqdcGpfd3CZu8AkIBazSGl8GqiCi7dJb346Y/1TFLg14R76UVG2EahQIetp4Hj8SZtN8eHcVrXmm+ksL8OKyMh9lc5S2qmTch1ZxY9Bk=
-----END CERTIFICATE-----

View File

@ -0,0 +1,3 @@
-----BEGIN CERTIFICATE-----
MIICvjCCAiCgAwIBAgIUD3/W7mdD8uEvO8tG52SdiVKJgB0wCgYIKoZIzj0EAwQwPjELMAkGA1UEBhMCS1IxDTALBgNVBAoMBEhLTUMxDDAKBgNVBAsMA0VDQzESMBAGA1UEAwwJUm9vdENBMDAyMCAXDTIzMDEzMDAzMjA1NloYDzIwODMwMTMwMTQ1OTU5WjA8MQswCQYDVQQGEwJLUjENMAsGA1UECgwESEtNQzEMMAoGA1UECwwDRUNDMRAwDgYDVQQDDAdDQTAxMDAzMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQA0eDXwzSjBaWTFcjg48QtsHGrcSJ4n++/EB76paBW8hfA1FQ3rGtZ0cVR5NPfS2+2R1sgqJz6LpGvh1LgXCV+DG8B+HpqSE7JjGaLrUhpN0GObyEmQzid4YISEqAcgXonh6l/F7C76QsSOjMQigVAxZUWk0Z7WVGi3zO+5eKAiFoztKSjgbgwgbUwIgYDVR0jAQH/BBgwFoAURTRfYSTjJP2uBQL8GaTkuNZZ/LAwIAYDVR0OAQH/BBYEFI0Kz6ICty/fnhYaFq4ckuNJoHSwMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMEkGA1UdHwEB/wQ/MD0wO6A5oDeGNWh0dHA6Ly9jcmwuaG1ja21jLmNvLmtyL0VDQy9Sb290Q0EwMDIvYXJsL0FybDFEcDEuY3JsMAoGCCqGSM49BAMEA4GLADCBhwJBAZ8WxHdQFXzR5MBbzBKrE0phkHxXNtg8ncDtusaBrCcMFLQYQUSzf1gmxcGHWGGhBEOwBpN5EU9ox7HQ6P0HYv4CQgEYx+Qu5VxjnsNkue8Xl9kaFdZp2wf5drmk67XcV7O4llCnWa/L45PS9CrpLp8qjxAQffjrqumYglJn45+EfYeO2A==
-----END CERTIFICATE-----

View File

@ -0,0 +1,12 @@
-----BEGIN CERTIFICATE-----
MIIBwTCCAWigAwIBAgIUCeUMZnQywRQfK13l7kWUsltWGOAwCgYIKoZIzj0EAwIw
PjELMAkGA1UEBhMCS1IxDTALBgNVBAoMBEhLTUMxDDAKBgNVBAsMA0VDQzESMBAG
A1UEAwwJUm9vdENBMDAxMCAXDTE5MDUwOTAwNDEwMFoYDzIxMDkwNTA5MjM1OTU5
WjA+MQswCQYDVQQGEwJLUjENMAsGA1UECgwESEtNQzEMMAoGA1UECwwDRUNDMRIw
EAYDVQQDDAlSb290Q0EwMDEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAR/ROoS
YMi4T9nJ+KGujVjnWWxHMy2+8TZ69QcfFqO5Rg0ipl7t9N1BQWj1BqfsYEjzCpQZ
9jppik7NeknLgZ9Io0IwQDAdBgNVHQ4EFgQUtb1h+pq4EbbZHRwPLC3Tw6oUP30w
DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDRwAw
RAIgPQ/HKZJI3cuz38yNJTrmlOz5vwB5QfAOU8lwk+7pmCoCIFMk1HQSpARINQH/
Pm27xACoQY4OTElHcJ1EKOTOnIDp
-----END CERTIFICATE-----

View File

@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
namespace DB.VPKI_AccountDB;
public partial class VpkiAccountDbContext : DbContext
{
public VpkiAccountDbContext()
{
}
public VpkiAccountDbContext(DbContextOptions<VpkiAccountDbContext> options)
: base(options)
{
}
public virtual DbSet<TRefreshToken> TRefreshTokens { get; set; }
public virtual DbSet<TRole> TRoles { get; set; }
public virtual DbSet<TUser> TUsers { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263.
=> optionsBuilder.UseSqlServer("server=127.0.0.1; user id=VPKI; password=Kefico!@34; database=VPKI_AccountDB; TrustServerCertificate=true;");
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<TRefreshToken>(entity =>
{
entity.HasKey(e => e.CAuid).HasName("PK__tRefresh__FBF0855408955D04");
entity.ToTable("tRefreshToken");
entity.Property(e => e.CAuid)
.HasMaxLength(250)
.HasColumnName("cAuid");
entity.Property(e => e.CRefreshToken)
.HasMaxLength(1000)
.HasColumnName("cRefreshToken");
});
modelBuilder.Entity<TRole>(entity =>
{
entity.HasKey(e => e.CAuid).HasName("PK__tRole__FBF0855456FE2E20");
entity.ToTable("tRole");
entity.Property(e => e.CAuid)
.HasMaxLength(250)
.HasColumnName("cAuid");
entity.Property(e => e.CRoleId).HasColumnName("cRoleID");
entity.Property(e => e.CRoleName)
.HasMaxLength(20)
.HasColumnName("cRoleName");
});
modelBuilder.Entity<TUser>(entity =>
{
entity.HasKey(e => e.CUserId).HasName("PK__tUser__A75DC19A1C6A7C5E");
entity.ToTable("tUser");
entity.Property(e => e.CUserId)
.HasMaxLength(50)
.HasColumnName("cUserID");
entity.Property(e => e.CAuid)
.HasMaxLength(250)
.HasColumnName("cAuid");
entity.Property(e => e.CCreateDateTime).HasColumnName("cCreateDateTime");
entity.Property(e => e.CLastLoginDateTime).HasColumnName("cLastLoginDateTime");
entity.Property(e => e.CPasswordHashed)
.HasMaxLength(250)
.HasColumnName("cPasswordHashed");
entity.Property(e => e.CState).HasColumnName("cState");
});
OnModelCreatingPartial(modelBuilder);
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}

View File

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
namespace DB.VPKI_AccountDB;
public partial class TRefreshToken
{
public string CAuid { get; set; } = null!;
public string CRefreshToken { get; set; } = null!;
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
namespace DB.VPKI_AccountDB;
public partial class TRole
{
public string CAuid { get; set; } = null!;
public byte CRoleId { get; set; }
public string CRoleName { get; set; } = null!;
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
namespace DB.VPKI_AccountDB;
public partial class TUser
{
public string CUserId { get; set; } = null!;
public string CAuid { get; set; } = null!;
public string CPasswordHashed { get; set; } = null!;
public byte CState { get; set; }
public DateTime CCreateDateTime { get; set; }
public DateTime? CLastLoginDateTime { get; set; }
}

View File

@ -0,0 +1,162 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
namespace DB.VPKI_DataDB;
public partial class VpkiDataDbContext : DbContext
{
public VpkiDataDbContext()
{
}
public VpkiDataDbContext(DbContextOptions<VpkiDataDbContext> options)
: base(options)
{
}
public virtual DbSet<TCertificate> TCertificates { get; set; }
public virtual DbSet<TOcsp> TOcsps { get; set; }
public virtual DbSet<TTbscsr> TTbscsrs { get; set; }
public virtual DbSet<TVerifyResult> TVerifyResults { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263.
=> optionsBuilder.UseSqlServer("server=127.0.0.1; user id=VPKI; password=Kefico!@34; database=VPKI_DataDB; TrustServerCertificate=true;");
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<TCertificate>(entity =>
{
entity.HasKey(e => e.CCuid).HasName("PK__tCertifi__2AA00D94B11718A8");
entity.ToTable("tCertificate");
entity.Property(e => e.CCuid)
.ValueGeneratedNever()
.HasColumnName("cCuid");
entity.Property(e => e.CBrandCode)
.HasMaxLength(20)
.HasColumnName("cBrandCode");
entity.Property(e => e.CCert)
.HasMaxLength(2048)
.HasColumnName("cCert");
entity.Property(e => e.CCsr)
.HasMaxLength(2048)
.HasColumnName("cCsr");
entity.Property(e => e.CCsrsignature)
.HasMaxLength(1024)
.HasColumnName("cCsrsignature");
entity.Property(e => e.CDateTime).HasColumnName("cDateTime");
entity.Property(e => e.CIssueCount).HasColumnName("cIssueCount");
entity.Property(e => e.CLocalCode)
.HasMaxLength(20)
.HasColumnName("cLocalCode");
entity.Property(e => e.CMessage)
.HasMaxLength(250)
.HasColumnName("cMessage");
entity.Property(e => e.CTierCode)
.HasMaxLength(20)
.HasColumnName("cTierCode");
entity.Property(e => e.CUnitCode)
.HasMaxLength(20)
.HasColumnName("cUnitCode");
entity.Property(e => e.CVehicleCode)
.HasMaxLength(20)
.HasColumnName("cVehicleCode");
});
modelBuilder.Entity<TOcsp>(entity =>
{
entity.HasKey(e => e.CCuid).HasName("PK__tOcsp__2AA00D941E2BA200");
entity.ToTable("tOcsp");
entity.Property(e => e.CCuid)
.ValueGeneratedNever()
.HasColumnName("cCuid");
entity.Property(e => e.CDateTime).HasColumnName("cDateTime");
entity.Property(e => e.COcsp).HasColumnName("cOcsp");
entity.Property(e => e.CStatus)
.HasMaxLength(20)
.HasColumnName("cStatus");
entity.Property(e => e.CVerify)
.HasMaxLength(20)
.HasColumnName("cVerify");
});
modelBuilder.Entity<TTbscsr>(entity =>
{
entity.HasKey(e => e.CCuid).HasName("PK__tTbscsr__2AA00D94155F4FC3");
entity.ToTable("tTbscsr");
entity.Property(e => e.CCuid).HasColumnName("cCuid");
entity.Property(e => e.CCertType)
.HasMaxLength(20)
.HasColumnName("cCertType");
entity.Property(e => e.CDateTime).HasColumnName("cDateTime");
entity.Property(e => e.CDc)
.HasMaxLength(20)
.HasColumnName("cDc");
entity.Property(e => e.CDn)
.HasMaxLength(250)
.HasColumnName("cDn");
entity.Property(e => e.CHashedTbscsr)
.HasMaxLength(1024)
.HasColumnName("cHashedTbscsr");
entity.Property(e => e.CIdType)
.HasMaxLength(10)
.HasColumnName("cIdType");
entity.Property(e => e.CIftid)
.HasMaxLength(100)
.HasColumnName("cIftid");
entity.Property(e => e.CMacaddr)
.HasMaxLength(100)
.HasColumnName("cMacaddr");
entity.Property(e => e.COriginTbscsr)
.HasMaxLength(4000)
.HasColumnName("cOriginTbscsr");
entity.Property(e => e.CPcid)
.HasMaxLength(50)
.HasColumnName("cPcid");
entity.Property(e => e.CPublickey)
.HasMaxLength(1024)
.HasColumnName("cPublickey");
entity.Property(e => e.CSupplierId)
.HasMaxLength(10)
.HasColumnName("cSupplierId");
entity.Property(e => e.CTierCode)
.HasMaxLength(20)
.HasColumnName("cTierCode");
entity.Property(e => e.CUnitCode)
.HasMaxLength(20)
.HasColumnName("cUnitCode");
entity.Property(e => e.CWmi)
.HasMaxLength(20)
.HasColumnName("cWmi");
});
modelBuilder.Entity<TVerifyResult>(entity =>
{
entity.HasKey(e => e.CCuid).HasName("PK__tVerifyR__2AA00D94A7EE4DE6");
entity.ToTable("tVerifyResult");
entity.Property(e => e.CCuid)
.ValueGeneratedNever()
.HasColumnName("cCuid");
entity.Property(e => e.CResult)
.HasMaxLength(20)
.IsFixedLength()
.HasColumnName("cResult");
});
OnModelCreatingPartial(modelBuilder);
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
namespace DB.VPKI_DataDB;
public partial class TCertificate
{
public long CCuid { get; set; }
public string CCsrsignature { get; set; } = null!;
public string CTierCode { get; set; } = null!;
public string CUnitCode { get; set; } = null!;
public string CVehicleCode { get; set; } = null!;
public string CLocalCode { get; set; } = null!;
public string CBrandCode { get; set; } = null!;
public string CCsr { get; set; } = null!;
public string CCert { get; set; } = null!;
public string CMessage { get; set; } = null!;
public int CIssueCount { get; set; }
public DateTime CDateTime { get; set; }
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
namespace DB.VPKI_DataDB;
public partial class TOcsp
{
public long CCuid { get; set; }
public string CStatus { get; set; } = null!;
public string CVerify { get; set; } = null!;
public string? COcsp { get; set; }
public DateTime CDateTime { get; set; }
}

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
namespace DB.VPKI_DataDB;
public partial class TTbscsr
{
public long CCuid { get; set; }
public string CIftid { get; set; } = null!;
public string CMacaddr { get; set; } = null!;
public string CWmi { get; set; } = null!;
public string CIdType { get; set; } = null!;
public string CSupplierId { get; set; } = null!;
public string CDc { get; set; } = null!;
public string CTierCode { get; set; } = null!;
public string CUnitCode { get; set; } = null!;
public string CPublickey { get; set; } = null!;
public string CCertType { get; set; } = null!;
public string COriginTbscsr { get; set; } = null!;
public string CHashedTbscsr { get; set; } = null!;
public string CPcid { get; set; } = null!;
public string CDn { get; set; } = null!;
public DateTime CDateTime { get; set; }
}

View File

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
namespace DB.VPKI_DataDB;
public partial class TVerifyResult
{
public long CCuid { get; set; }
public string CResult { get; set; } = null!;
}

View File

@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<NoWarn>1701;1702;1030</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<NoWarn>1701;1702;1030</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.11">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.11">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace VPKI.Library.Config.Model
{
public class Api
{
[JsonPropertyName("ApiName")]
public string? ApiName { get; set; }
[JsonPropertyName("Address")]
public string? Address { get; set; }
[JsonPropertyName("Port")]
public int Port { get; set; }
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace VPKI.Library.Config.Model
{
public class Auth
{
//jwt common
[JsonPropertyName("issuer")]
public string? issuer { get; set; }
[JsonPropertyName("audience")]
public string? audience { get; set; }
//access token
[JsonPropertyName("accessTokenSecret")]
public string? accessTokenSecret { get; set; }
[JsonPropertyName("accessTokenExpires")]
public uint? accessTokenExpires { get; set; }
//refresh token
[JsonPropertyName("refreshTokenSecret")]
public string? refreshTokenSecret { get; set; }
[JsonPropertyName("refreshTokenExpires")]
public uint? refreshTokenExpires { get; set; }
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace VPKI.Library.Config.Model
{
public class DataBase
{
[JsonPropertyName("IP")]
public string? IP { get; set; }
[JsonPropertyName("Port")]
public int Port { get; set; }
[JsonPropertyName("DBName")]
public string? DBName { get; set; }
[JsonPropertyName("DBID")]
public int DBID { get; set; }
[JsonPropertyName("DBContext")]
public string? DBContext { get; set; }
[JsonPropertyName("UserID")]
public string? UserID { get; set; }
[JsonPropertyName("Password")]
public string? Password { get; set; }
}
}

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace VPKI.Library.Config.Model
{
public class Openssl
{
[JsonPropertyName("Path")]
public string? Path { get; set; }
[JsonPropertyName("SubCAPath")]
public string? SubCAPath { get; set; }
[JsonPropertyName("SubCA")]
public SubCA? SubCA { get; set; }
[JsonPropertyName("RootCA")]
public string? RootCA { get; set; }
}
public class SubCA
{
[JsonPropertyName("prov_v1")]
public string? prov_v1 { get; set; }
[JsonPropertyName("prov_cert")]
public string? prov_cert { get; set; }
[JsonPropertyName("vehicle_cert")]
public string? vehicle_cert { get; set; }
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace VPKI.Library.Config.Model
{
public class Server
{
[JsonPropertyName("Address")]
public string? Address { get; set; }
[JsonPropertyName("Port")]
public int Port { get; set; }
[JsonPropertyName("IIS")]
public bool IIS { get; set; }
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using VPKI.Library.Config.Model;
namespace VPKI.Library.Config
{
public class WebApiConfig : WebCommonConfig
{
[JsonPropertyName("Api")]
public List<Api>? Api { get; set; }
[JsonPropertyName("Auth")]
public Auth? Auth { get; set; }
[JsonPropertyName("DataBase")]
public List<DataBase>? DataBase { get; set; }
[JsonPropertyName("Openssl")]
public Openssl? Openssl { get; set; }
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using VPKI.Library.Config.Model;
namespace VPKI.Library.Config
{
public class WebClientConfig : WebCommonConfig
{
[JsonPropertyName("Api")]
public List<Api>? Api { get; set; }
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using VPKI.Library.Config.Model;
namespace VPKI.Library.Config
{
public class WebCommonConfig
{
[JsonPropertyName("Server")]
public Server Server { get; set; } = new();
}
}

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public class Consts
{
//vpki fixex value
public static readonly string VPKI_API = "VPKI.API";
public static readonly string VPKI_API_VPKIRA = "VPKIRA";
//certificate fixed value
public static readonly string BEGIN_CERTIFICATE_REQUEST = "-----BEGIN CERTIFICATE_REQUEST-----";
public static readonly string END_CERTIFICATE_REQUEST = "-----END CERTIFICATE_REQUEST-----";
public static readonly string BEGIN_CERTIFICATE = $"-----BEGIN CERTIFICATE-----";
public static readonly string END_CERTIFICATE = $"-----END CERTIFICATE-----";
public static readonly string BEGIN_PUBLIC_KEY = $"-----BEGIN PUBLIC KEY-----";
public static readonly string END_PUBLIC_KEY = $"-----END PUBLIC KEY-----";
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VPKI.Library.Enums
{
public enum ECDSAType
{
NONEWITHECDSA = 1,
SHA256WITHECDSA = 2,
}
public enum CurveName
{
secp256r1 = 0,
secp521r1 = 1,
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VPKI.Library.Enums
{
public enum ERROR_CODE
{
//기본 에러
EC_NONE = 0,
EC_OK = 1,
//DB 관련 에러
EC_DEFAULT_ERROR = 1000,
EC_DATABASE_ERROR = 1001,
//유저 관련 에러
EC_USER_REGISTER_FAILED = 2000,
EC_USER_REGISTER_EXIST = 2001,
EC_USER_REGISTER_CONFIRM_PASSWORD_ERROR = 2002,
EC_USER_LOGIN_FAILED = 2100,
EC_USER_LOGIN_NOT_EXIST = 2101,
EC_USER_LOGIN_INVALID_PASSWORD = 2102,
EC_USER_LOGIN_INAVTIVE = 2103,
EC_USER_LOGIN_BLOCKED = 2104,
EC_USER_LOGOUT_FAILED = 2200
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VPKI.Library.Enums
{
//VPKI 응답 상태, API 문서 참고
public enum Status
{
none = 0,
success = 1,
error = 2,
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VPKI.Library.Enums
{
//User 권한
public enum UserRole : byte
{
None = 0,
Ananymous = 1,
User = 10,
Admin = 11,
SuperUser = 20,
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VPKI.Library.Enums
{
public enum UserState: byte
{
Inactive = 0,
Active = 1,
Block = 2
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VPKI.Library.Enums
{
public enum VpkiType
{
prov_v1 = 1,
prov_cert = 2,
vehicle_cert = 3,
evcc_cert = 10,
}
public enum VpkiIsoType
{
ISO15118_02 = 1,
ISO15118_20 = 2,
}
}

View File

@ -0,0 +1,25 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VPKI.Library.Model;
public static class LogModelExtend
{
public static string LogModelToString(this LogModel log, string header = "")
{
string logString = string.Empty;
if (string.IsNullOrEmpty(header) == false)
{
logString += $"{header}::";
}
logString += JsonConvert.SerializeObject(log, Formatting.Indented);
logString += Environment.NewLine;
return logString;
}
}

View File

@ -0,0 +1,35 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
public static class ObjectExtend
{
public static string ToJson(this object obj)
{
string result = string.Empty;
try
{
result = JsonConvert.SerializeObject(obj, Formatting.Indented);
}
catch(Exception e)
{
result = e.Message.ToString();
}
result += Environment.NewLine;
return result;
}
public static T? DeepCopy<T>(this T obj) where T : class, new()
{
string originJson = obj.ToJson();
T? clone = JsonConvert.DeserializeObject<T>(originJson);
return clone;
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
public static class StringExtend
{
/// <summary>
/// String To SHA256 Hash Base64
/// </summary>
public static string StringToSHA256Base64(this string str)
{
var hashedBytes = SHA256.HashData(Encoding.UTF8.GetBytes(str));
var hashedString = Convert.ToBase64String(hashedBytes);
return hashedString;
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VPKI.Library.Enums;
namespace VPKI.Library.Model
{
//로그인 요청 모델
public class LoginModel
{
[Required]
public string? UserID { get; set; }
[Required]
public string? Password { get; set; }
}
//로그인 응답 모델
public class LoginResponseModel
{
public string? UserID { get; set; }
public UserRole Role { get; set; }
public string? RoleName { get; set; }
public string? AccessToken { get; set; }
public long AccessTokenExpired { get; set; }
public string? RefreshToken { get; set; }
public ERROR_CODE? EC { get; set; }
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VPKI.Library.Enums;
namespace VPKI.Library.Model.Auth
{
public class LogoutModel
{
[Required]
public string? UserID { get; set; }
}
public class LogoutResponseModel
{
public string? UserID { get; set; }
public ERROR_CODE? EC { get; set; }
}
}

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VPKI.Library.Enums;
namespace VPKI.Library.Model
{
//유저 등록 모델
public class RegisterModel
{
[Required]
public string UserID { get; set; } = string.Empty;
[Required]
public string Password { get; set; } = string.Empty;
[Required]
public string PasswordConfirm { get; set; } = string.Empty;
[Required]
public UserRole Role { get; set; } = UserRole.User;
}
public class RegisterResponseModel
{
public string? UserID { get; set; }
public UserRole Role { get; set; }
public string? RoleName { get; set; }
public ERROR_CODE? EC { get; set; } = ERROR_CODE.EC_USER_REGISTER_FAILED;
}
}

View File

@ -0,0 +1,18 @@
using DB.VPKI_AccountDB;
using DB.VPKI_DataDB;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VPKI.Library.Model
{
public class CertificateHistoryModel
{
public TTbscsr? TTbscsr { get; set; }
public TCertificate? TCertificate { get; set; }
public TVerifyResult? TVerifyResult { get; set; }
public TOcsp? TOcsp { get; set; }
}
}

View File

@ -0,0 +1,34 @@
using DB.VPKI_DataDB;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace VPKI.Library.Model
{
public class CsrCompleteModel
{
public byte[]? Tbscsr { get; set; }
public byte[]? Signature { get; set; }
public Oid? SignatureAlgorithm { get; set; }
public byte[] CreateCertificateRequest()
{
// TBSCsr, 서명 알고리즘, 서명 데이터를 결합하여 최종 CSR 포맷을 만든다.
var data = Encoding.ASCII.GetBytes($"{SignatureAlgorithm?.Value}");
using (var ms = new MemoryStream())
{
if (Tbscsr != null && Signature != null)
{
ms.Write(Tbscsr, 0, Tbscsr.Length);
ms.Write(Encoding.ASCII.GetBytes($"{SignatureAlgorithm?.Value}"), 0, data.Length);
ms.Write(Signature, 0, Signature.Length);
}
return ms.ToArray();
}
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VPKI.Library.Model
{
public class CsrHashedModel
{
public string? SignedCsr { get; set; } = string.Empty;
public string? EncodedSignedCsr { get; set; } = string.Empty;
public bool Verify { get; set; } = false;
}
}

View File

@ -0,0 +1,19 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace VPKI.Library.Model
{
public class LogModel
{
public string? ClientIP { get; set; }
public string? Method { get; set; }
public string? Action { get; set; }
public string? RequestUrl { get; set; }
public dynamic? Body { get; set; }
}
}

View File

@ -0,0 +1,15 @@
using DB.VPKI_AccountDB;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VPKI.Library.Model
{
public class UserModel
{
public TUser? TUser { get; set; }
public TRole? TRole { get; set; }
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VPKI.Library.Enums;
namespace VPKI.Library.Packet
{
public class CommonPacket
{
}
//request
public class CommonPacketRequest : CommonPacket
{
}
//response
public class CommonPacketResponse : CommonPacket
{
public string? status { get; set; } = Status.success.ToString();
public string? message { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//VPKI <-> OEM PKI(auto ever srver)
namespace VPKI.Library.Packet
{
#region / (ISO15118-2/ISO15118-20)
public class Request_CertificateOEM : CommonPacketRequest
{
public string? csr { get; set; }
public string? unitCode { get; set; }
public string? tierCode { get; set; }
public string? vehicleCode { get; set; }
public string? localCode { get; set; }
public string? brandCode { get; set; }
}
public class Response_CertificateOEM : CommonPacketResponse
{
public string? cert { get; set; }
public string? resultMessage { get; set; }
}
#endregion
#region (ISO15118-2/ISO15118-20)
public class Request_CertificateRevokeOEM : CommonPacketRequest
{
public string dn { get; set; } = string.Empty;
}
public class Response_CertificateRevokeOEM : CommonPacketResponse
{
}
#endregion
}

View File

@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//client <-> vPKI(VPKI Server)
namespace VPKI.Library.Packet
{
#region CSR (ISO15118-2)
public class Request_Tbscsr : CommonPacketRequest
{
public string iftid { get; set; } = string.Empty;
public CnInfo cnInfo { get; set; } = new();
public CertInfo certInfo { get; set; } = new();
public string publickey { get; set; } = string.Empty;
#region InnerClass
public class CnInfo
{
public string macaddr { get; set; } = string.Empty;
public string wmi { get; set; } = string.Empty;
public string idType { get; set; } = string.Empty;
public string supplierId { get; set; } = string.Empty;
}
public class CertInfo
{
public string dc { get; set; } = string.Empty;
public string tierCode { get; set; } = string.Empty;
public string unitCode { get; set; } = string.Empty;
}
public string certType { get; set; } = string.Empty;
#endregion
}
public class Response_Tbscsr : CommonPacketResponse
{
public Data? data { get; set; }
#region InnerClass
public class Data
{
public string? hashedtbscsr { get; set; }
public string? pcid { get; set; }
public string? evccid { get; set; }
}
#endregion
}
#endregion
#region (ISO15118-2/ISO15118-20)
public class Request_Certificate : CommonPacketRequest
{
public string? iftid { get; set; }
public string? csrsignature { get; set; }
public string? unitCode { get; set; }
public string? tierCode { get; set; }
public string? vehicleCode { get; set; }
public string? localCode { get; set; }
public string? brandCode { get; set; }
}
public class Response_Certificate : CommonPacketResponse
{
public Data? data { get; set; }
#region InnerClass
public class Data
{
public string? leafcertificate { get; set; }
public string? subcacertificate { get; set; }
}
#endregion
}
#endregion
#region (ISO15118-2/ISO15118-20)
public class Request_Verifyresult : CommonPacketRequest
{
public string? iftid { get; set; }
public string? pcid { get; set; }
public string? result { get; set; }
}
public class Response_Verifyresult : CommonPacketResponse
{
public Data? data { get; set; }
#region InnerClass
public class Data
{
public string? iftid { get; set; }
public string? pcid{ get; set; }
public string? ift_result{ get; set; }
public string? server_result{ get; set; }
}
#endregion
}
#endregion
}

View File

@ -0,0 +1,960 @@
using Azure.Core;
using DB.VPKI_DataDB;
using DocumentFormat.OpenXml.Bibliography;
using DocumentFormat.OpenXml.Office2016.Excel;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.IsisMtt.Ocsp;
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.Ocsp;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.Sec;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Cms;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Agreement;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Operators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Ocsp;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Pqc.Asn1;
using Org.BouncyCastle.Pqc.Crypto;
using Org.BouncyCastle.Pqc.Crypto.Lms;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Tls;
using Org.BouncyCastle.Utilities.IO.Pem;
using Org.BouncyCastle.X509;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Formats.Asn1;
using System.Linq;
using System.Security.AccessControl;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml.Linq;
using VPKI.Library.Config;
using VPKI.Library.Enums;
using VPKI.Library.Model;
using VPKI.Library.Packet;
namespace VPKI.Library.Services
{
public class CertificateService : CommonService
{
private Dictionary<char, string> WordMap = new Dictionary<char, string>();
private readonly int PublicKey256Length = 66;
private readonly int PublicKey256Offset = 25;
private readonly int PublickKey512Length = 134;
private readonly int PublickKey512Offset = 24;
public CertificateService()
{
InitMapTable();
}
/// <summary>
/// sha256 base64
/// </summary>
public string CreateCsr256(string dn, string publicKey)
{
string csrBase64 = string.Empty;
try
{
using (ECDsa ecdsa = ECDsa.Create(System.Security.Cryptography.ECCurve.NamedCurves.nistP256))
{
//load public key
var publicKeyPem = ConvertToPEM(Convert.FromBase64String(publicKey));
Log4net.WriteLine($"Input Public Key (Base64): {publicKey}", LogType.Info);
Log4net.WriteLine($"Input Public Key Pem (Base64): {publicKeyPem}", LogType.Debug);
//create csr
X500DistinguishedName subjectName = new X500DistinguishedName(dn);
var csrRequest = new System.Security.Cryptography.X509Certificates.CertificateRequest(subjectName, ecdsa, HashAlgorithmName.SHA256);
byte[] csrBytes = csrRequest.CreateSigningRequest();
byte[] publicKeyArr = Convert.FromBase64String(publicKeyPem);
//input public key
int startOffset = 0;
for(int i = 2; i < csrBytes.Length - 2 ; i++)
{
if (csrBytes[i - 2] == 3 && csrBytes[i - 1] == 66 && csrBytes[i] == 0 && csrBytes[i + 1] == 4)
{
startOffset = i;
break;
}
}
for (int i = 0; i < PublicKey256Length; i++)
{
csrBytes[i + startOffset] = publicKeyArr[i + PublicKey256Offset];
}
Asn1Sequence sequence = (Asn1Sequence)Asn1Object.FromByteArray(csrBytes);
var writer = new AsnWriter(AsnEncodingRules.DER);
writer.WriteEncodedValue(sequence[0].GetEncoded());
//csr origin
csrBase64 = Convert.ToBase64String(writer.Encode());
Log4net.WriteLine($"Origin CSR (Base64): {csrBase64}", LogType.Debug);
}
}
catch (Exception e)
{
Log4net.WriteLine(e);
}
return csrBase64;
}
/// <summary>
/// sha256 base64
/// </summary>
public string CreateCsr512(string dn, string publicKey)
{
string csrBase64 = string.Empty;
try
{
using (ECDsa ecdsa = ECDsa.Create(System.Security.Cryptography.ECCurve.NamedCurves.nistP521))
{
//load public key
var publicKeyPem = ConvertToPEM(Convert.FromBase64String(publicKey));
Log4net.WriteLine($"Input Public Key (Base64): {publicKey}", LogType.Info);
Log4net.WriteLine($"Input Public Key Pem (Base64): {publicKeyPem}", LogType.Debug);
//create csr
X500DistinguishedName subjectName = new X500DistinguishedName(dn);
var csrRequest = new System.Security.Cryptography.X509Certificates.CertificateRequest(subjectName, ecdsa, HashAlgorithmName.SHA256);
byte[] csrBytes = csrRequest.CreateSigningRequest();
byte[] publicKeyArr = Convert.FromBase64String(publicKeyPem);
//input public key
int startOffset = 0;
for (int i = 2; i < csrBytes.Length - 2; i++)
{
if (csrBytes[i - 2] == 129 && csrBytes[i - 1] == 134 && csrBytes[i] == 0 && csrBytes[i + 1] == 4)
{
startOffset = i;
break;
}
}
for (int i = 0; i < PublickKey512Length; i++)
{
csrBytes[i + startOffset] = publicKeyArr[i + PublickKey512Offset];
}
Asn1Sequence sequence = (Asn1Sequence)Asn1Object.FromByteArray(csrBytes);
var writer = new AsnWriter(AsnEncodingRules.DER);
writer.WriteEncodedValue(sequence[0].GetEncoded());
//csr origin
csrBase64 = Convert.ToBase64String(writer.Encode());
Log4net.WriteLine($"Origin CSR (Base64): {csrBase64}", LogType.Debug);
}
}
catch (Exception e)
{
Log4net.WriteLine(e);
}
return csrBase64;
}
public AsymmetricKeyParameter LoadPublicKey(string publicKeyPem)
{
using (StringReader reader = new StringReader(publicKeyPem))
{
var pemObje = (AsymmetricKeyParameter)new Org.BouncyCastle.OpenSsl.PemReader(reader).ReadObject();
return pemObje;
}
}
/// <summary>
/// pcid
/// </summary>
public string CreatePCID(Request_Tbscsr request, VpkiType vpkiType)
{
string pcid = string.Empty;
switch(vpkiType)
{
case VpkiType.prov_v1:
pcid = CreatePCIDV1(request);
break;
case VpkiType.prov_cert:
pcid = CreatePCIDV2Prov(request);
break;
case VpkiType.vehicle_cert:
pcid = CreatePCIDV2Vehicle(request);
break;
}
return pcid;
}
private string CreatePCIDV1(Request_Tbscsr request)
{
var wmi = $"{request?.cnInfo?.wmi}";
var idType = $"{request?.cnInfo?.idType}";
var supplierId = $"{request?.cnInfo?.supplierId}";
string strMacEnc = EncodeMacAddress($"{request?.cnInfo?.macaddr}");
string strCvVal = ConvertNotation(strMacEnc);
string strIDVal = wmi + idType + supplierId + strCvVal;
string strValueString = GeneratePCID(strIDVal);
return strValueString;
}
private string CreatePCIDV2Prov(Request_Tbscsr request)
{
var wmi = $"{request?.cnInfo?.wmi}";
var idType = $"{request?.cnInfo?.idType}";
var supplierId = $"{request?.cnInfo?.supplierId}";
string strMacEnc = EncodeMacAddress($"{request?.cnInfo?.macaddr}");
string strCvVal = ConvertNotation(strMacEnc);
string strSID = (Convert.ToInt32(supplierId) + 1).ToString();
string strIDVal = wmi + idType + strSID + strCvVal;
string strValueString = GeneratePCID(strIDVal);
return strValueString;
}
private string CreatePCIDV2Vehicle(Request_Tbscsr request)
{
var wmi = $"{request?.cnInfo?.wmi}";
var idType = $"{request?.cnInfo?.idType}";
var supplierId = $"{request?.cnInfo?.supplierId}";
string strMacEnc = EncodeMacAddress($"{request?.cnInfo?.macaddr}");
string strCvVal = ConvertNotation(strMacEnc);
string strIDVal = wmi + idType + supplierId + "00" + strCvVal;
string strValueString = GeneratePCID(strIDVal);
return strValueString;
}
string GetChecksumString(string strInput)
{
List<long> vnCheckUnit = new List<long>();
for (int i = 0; i < strInput.Length; i++)
{
char cVal = strInput.ElementAt(i);
int nVal = Convert.ToInt32(cVal.ToString());
//Console.WriteLine(nVal);
long nFactor = Convert.ToInt64(Math.Pow(2, i));
vnCheckUnit.Add(nVal * nFactor);
}
long nSum = vnCheckUnit.Sum();
return nSum.ToString();
}
string GeneratePCID(string strIDVal)
{
string strValueString = string.Empty;
foreach (char cVal in strIDVal.ToArray())
strValueString += WordMap[cVal];
int nLengthStr = strValueString.Length;
string strChecksumStr = GetChecksumString(strValueString);
long nModuloCRC11 = Convert.ToInt64(strChecksumStr) % 11;
if (nModuloCRC11 == 10)
strIDVal = strIDVal + "X";
else
strIDVal = strIDVal + nModuloCRC11.ToString();
return strIDVal;
}
string ConvertNotation(string strHexValue)
{
string strOriginVal = strHexValue;
string strNewVal = string.Empty;
if (strOriginVal.Length % 2 > 0)
strOriginVal = "0" + strOriginVal;
List<string> vstrNewVals = new List<string>();
for (int i = 0; i < strOriginVal.Length - 1; i = i + 2)
{
string strHexUnit = strOriginVal.Substring(i, 2);
int nVal = int.Parse(strHexUnit, System.Globalization.NumberStyles.HexNumber);
string strTHVal = ConvertBase(nVal, 32);
vstrNewVals.Add(strTHVal);
}
foreach (string strUnit in vstrNewVals)
strNewVal += strUnit;
return strNewVal;
}
private string ConvertBase(int nVal, int nBase)
{
string strVal = string.Empty;
int k = nVal / nBase;
int nRest = (nVal % nBase);
strVal = nRest.ToString() + strVal; // 나머지를 뒤로 이동    
if (k >= nBase)
ConvertBase(k, nBase); // 나눌게 있다면? 재귀함수    
else
{
if (nRest >= 10 && nRest < nBase)
strVal = (Convert.ToChar(55 + nRest)).ToString();
strVal = k.ToString() + strVal; // 나눌게 없다면 ? 몫}
}
return strVal;
}
public string EncodeMacAddress(string strMacID)
{
List<byte> vnMacAddr = StringToByteArray(strMacID).ToList();
List<byte> vnMacAddrEnc = (from nUnit in vnMacAddr let nEnc = Convert.ToByte(nUnit ^ 0xFF) select nEnc).ToList();
string strEncMacID = ByteArrayToHexString(vnMacAddrEnc.ToArray());
return strEncMacID;
}
public string ByteArrayToHexString(byte[] bytearray)
{
string hex = BitConverter.ToString(bytearray);
hex = hex.Replace("-", "");
return hex;
}
public byte[] StringToByteArray(string str)
{
int length = str.Length / 2; // 짝수일 경우만 고려함 --> 홀수의 경우 어떻게 자를지 고민해야 할듯(필요없을 수도)
byte[] byteArray = new byte[length];
for (int i = 0; i < length; i++)
{
string strUnit = str.Substring(i * 2, 2);
byteArray[i] = Convert.ToByte(strUnit, 16);
}
return byteArray;
}
void InitMapTable()
{
WordMap.Add('0', "0");
WordMap.Add('1', "1");
WordMap.Add('2', "2");
WordMap.Add('3', "3");
WordMap.Add('4', "4");
WordMap.Add('5', "5");
WordMap.Add('6', "6");
WordMap.Add('7', "7");
WordMap.Add('8', "8");
WordMap.Add('9', "9");
WordMap.Add('A', "10");
WordMap.Add('B', "11");
WordMap.Add('C', "12");
WordMap.Add('D', "13");
WordMap.Add('E', "14");
WordMap.Add('F', "15");
WordMap.Add('G', "16");
WordMap.Add('H', "17");
WordMap.Add('I', "18");
WordMap.Add('J', "19");
WordMap.Add('K', "20");
WordMap.Add('L', "21");
WordMap.Add('M', "22");
WordMap.Add('N', "23");
WordMap.Add('O', "24");
WordMap.Add('P', "25");
WordMap.Add('Q', "26");
WordMap.Add('R', "27");
WordMap.Add('S', "28");
WordMap.Add('T', "29");
WordMap.Add('U', "30");
WordMap.Add('V', "31");
WordMap.Add('W', "32");
WordMap.Add('X', "33");
WordMap.Add('Y', "34");
WordMap.Add('Z', "35");
}
#region MacAddress Decode
private string DecodeMacAddress(string macAddress)
{
var decodeByteArray = HexStringToByteArray(macAddress);
List<byte> vnMacAddrEnc =
(from nUnit in decodeByteArray let nEnc = Convert.ToByte(nUnit ^ 0xFF) select nEnc).ToList();
string macAddressHexString = ByteArrayToHexString(vnMacAddrEnc.ToArray());
return macAddressHexString;
}
private byte[] HexStringToByteArray(string str)
{
int length = str.Length / 2;
byte[] byteArray = new byte[length];
for (int i = 0; i < length; i++)
{
string strUnit = str.Substring(i * 2, 2);
byteArray[i] = Convert.ToByte(strUnit, 16);
}
return byteArray;
}
#endregion
#region BouncyCastle
public AsymmetricCipherKeyPair GenerateBouncyCastleKeyPairECDH(string curveName)
{
ECKeyPairGenerator gen = new ECKeyPairGenerator("ECDSA");
SecureRandom rndSecure = new SecureRandom();
X9ECParameters ecp = SecNamedCurves.GetByName($"{curveName}");
ECDomainParameters ecSpec = new ECDomainParameters(ecp.Curve, ecp.G, ecp.N, ecp.H, ecp.GetSeed());
ECKeyGenerationParameters ecgp = new ECKeyGenerationParameters(ecSpec, rndSecure);
gen.Init(ecgp);
AsymmetricCipherKeyPair eckp = gen.GenerateKeyPair();
ECPublicKeyParameters ecPub = (ECPublicKeyParameters)eckp.Public;
ECPrivateKeyParameters ecPri = (ECPrivateKeyParameters)eckp.Private;
byte[] publicKeyBytes = ecPub.Q.GetEncoded();
byte[] privateKey = ecPri.D.ToByteArrayUnsigned();
string strValue = GetKeyString(publicKeyBytes.ToList());
return eckp;
}
public string GetKeyString(List<byte> vnData)
{
string strResult = string.Empty;
for (int i = 0; i < vnData.Count; i++)
strResult += Convert.ToChar(vnData[i]).ToString();
int nLen = strResult.Length;
return strResult;
}
public List<byte> GetKeyArray(string strKey)
{
List<byte> vstrResult = new List<byte>();
for (int i = 0; i < strKey.Length; i++)
vstrResult.Add(Convert.ToByte(strKey[i]));
return vstrResult;
}
public List<byte>? GetPublicKeyArrayBC(ECPublicKeyParameters? pubKey)
{
if (pubKey != null)
{
byte[] publicKeyBytes = pubKey.Q.GetEncoded();
List<byte> vnX = pubKey.Q.AffineXCoord.GetEncoded().ToList();
List<byte> vnY = pubKey.Q.AffineYCoord.GetEncoded().ToList();
return publicKeyBytes.ToList();
}
return null;
}
public List<byte>? GetPrivateKeyArrayBC(ECPrivateKeyParameters? ecPri)
{
if (ecPri != null)
{
byte[] privateKey = ecPri.D.ToByteArrayUnsigned();
return privateKey.ToList();
}
return null;
}
public ECPublicKeyParameters? GetPublicKeyBC(AsymmetricCipherKeyPair? keyPair)
{
if (keyPair != null)
{
ECPublicKeyParameters ecPub = (ECPublicKeyParameters)keyPair.Public;
return ecPub;
}
return null;
}
public ECPrivateKeyParameters? GetPrivateKeyBC(AsymmetricCipherKeyPair? keyPair)
{
if (keyPair != null)
{
ECPrivateKeyParameters ecPri = (ECPrivateKeyParameters)keyPair.Private;
return ecPri;
}
return null;
}
public string GetKeyBase64Encrypted(List<byte> keyValue)
{
List<byte> publicKey = keyValue;
string strKey = GetKeyString(publicKey);
int nOrgKeyLen = strKey.Length;
string strEnc64Val = Convert.ToBase64String(publicKey.ToArray());
return strEnc64Val;
}
public List<byte> GetKeyDecrypted(string strEncKey)
{
string strResult = string.Empty;
List<byte> vnStrDecKey = Convert.FromBase64String(strEncKey).ToList();
string strStrDecKey = GetKeyString(vnStrDecKey);
int nOutLength = 0;
List<byte> vnDerDecKey = AsnDecoder.ReadIntegerBytes(vnStrDecKey.ToArray(), AsnEncodingRules.DER, out nOutLength).ToArray().ToList();
string strDerDecKey = GetKeyString(vnDerDecKey);
List<byte> derDecStr = GetKeyArray(strDerDecKey);
return derDecStr;
}
public CsrHashedModel SignHashed02(string? csr, AsymmetricCipherKeyPair? keyPair)
{
CsrHashedModel hashedCsr = new CsrHashedModel();
if (string.IsNullOrWhiteSpace(csr))
return hashedCsr;
List<byte> vnRHashVal = Convert.FromBase64String(csr).ToList();
List<byte> vnSignedHash = GetBouncyCastleECDSASignedHashKey(vnRHashVal, GetPrivateKeyBC(keyPair), ECDSAType.SHA256WITHECDSA);
List<byte> vnDEREncVal = EncodeDERSignature_02(vnSignedHash);
bool bVerfRst = VerifyHashBC(vnSignedHash, vnRHashVal, GetPublicKeyBC(keyPair), ECDSAType.SHA256WITHECDSA);
hashedCsr.SignedCsr = GetKeyBase64Encrypted(vnSignedHash);
hashedCsr.EncodedSignedCsr = GetKeyBase64Encrypted(vnDEREncVal);
hashedCsr.Verify = bVerfRst;
var decoded = DecodeDERSignature_02(vnDEREncVal);
return hashedCsr;
}
public CsrHashedModel SignHashed20(string? csr, AsymmetricCipherKeyPair? keyPair)
{
CsrHashedModel hashedCsr = new CsrHashedModel();
if (string.IsNullOrWhiteSpace(csr))
return hashedCsr;
List<byte> vnRHashVal = Convert.FromBase64String(csr).ToList();
List<byte> vnSignedHash = GetBouncyCastleECDSASignedHashKey(vnRHashVal, GetPrivateKeyBC(keyPair), ECDSAType.NONEWITHECDSA);
List<byte> vnDEREncVal = EncodeDERSignature_20(vnSignedHash);
bool bVerfRst = VerifyHashBC(vnSignedHash, vnRHashVal, GetPublicKeyBC(keyPair), ECDSAType.NONEWITHECDSA);
hashedCsr.SignedCsr = GetKeyBase64Encrypted(vnSignedHash);
hashedCsr.EncodedSignedCsr = GetKeyBase64Encrypted(vnDEREncVal);
hashedCsr.Verify = bVerfRst;
return hashedCsr;
}
public List<byte> GetBouncyCastleECDSASignedHashKey(List<byte> vnRHashVal, ECPrivateKeyParameters? priKey, ECDSAType ecdsaType)
{
var signer = new ECDsaSigner();
signer.Init(true, priKey);
var anSign = signer.GenerateSignature(vnRHashVal.ToArray());
byte[] signatureBytes = ConvertSignatureToByteArray(anSign);
return signatureBytes.ToList();
}
byte[] ConvertSignatureToByteArray(BigInteger[] signature)
{
// 서명은 r과 s 값으로 이루어져 있습니다.
byte[] rBytes = signature[0].ToByteArrayUnsigned(); // r 값
byte[] sBytes = signature[1].ToByteArrayUnsigned(); // s 값
// r과 s를 DER 형식으로 결합
var seq = new DerSequence(new DerInteger(signature[0]), new DerInteger(signature[1]));
return seq.GetEncoded(); // DER 인코딩된 byte[] 반환
}
public List<byte> EncodeDERSignature_02(List<byte> signature)
{
List<byte> resultData = new List<byte>();
byte[] r = new byte[32];
byte[] s = new byte[32];
int lengthOfR = signature[3];
int lengthOfS = signature[lengthOfR + 5];
if (lengthOfR == 33)
r = signature.GetRange(5, lengthOfR - 1).ToArray();
else if (lengthOfR == 32)
r = signature.GetRange(4, lengthOfR).ToArray();
else
{
List<byte> rawData = signature.GetRange(4, lengthOfR);
for (int nBI = 32 - lengthOfR; nBI < lengthOfR; nBI++)
r[nBI] = rawData[nBI - 32 + lengthOfR];
}
if (lengthOfS == 33)
s = signature.GetRange(lengthOfR + 7, lengthOfS - 1).ToArray();
else if (lengthOfS == 32)
s = signature.GetRange(lengthOfR + 6, lengthOfS).ToArray();
else
{
List<byte> rawData = signature.GetRange(lengthOfR + 6, lengthOfS);
for (int nBI = 32 - lengthOfS; nBI < lengthOfS; nBI++)
s[nBI] = rawData[nBI - 32 + lengthOfS];
}
resultData.AddRange(r);
resultData.AddRange(s);
return resultData;
}
public List<byte> DecodeDERSignature_02(List<byte> signature)
{
List<List<byte>> decodeData = new List<List<byte>>();
List<byte> r = signature.GetRange(0, 32);
List<byte> s = signature.GetRange(32, 32);
for (int i = 0; i < 4; i++)
{
List<byte> caseData = new List<byte>();
//header 고정값 1자리
caseData.Add(48);
if (i == 0)
{
caseData.Add(68);
caseData.Add(2);
caseData.Add(32);
caseData.AddRange(r);
caseData.Add(2);
caseData.Add(32);
caseData.AddRange(s);
}
if (i == 1)
{
caseData.Add(69);
caseData.Add(2);
caseData.Add(32);
caseData.AddRange(r);
caseData.Add(2);
caseData.Add(33);
caseData.Add(0);
caseData.AddRange(s);
}
if (i == 2)
{
caseData.Add(69);
caseData.Add(2);
caseData.Add(33);
caseData.Add(0);
caseData.AddRange(r);
caseData.Add(2);
caseData.Add(32);
caseData.AddRange(s);
}
if (i == 3)
{
caseData.Add(70);
caseData.Add(2);
caseData.Add(33);
caseData.Add(0);
caseData.AddRange(r);
caseData.Add(2);
caseData.Add(33);
caseData.Add(0);
caseData.AddRange(s);
}
decodeData.Add(caseData.ToList());
}
return decodeData.Last();
}
public bool VerifyHashBC(List<byte> signedHash, List<byte> unsignedHash, ECPublicKeyParameters? publicKey, ECDSAType ecdsaType)
{
ISigner signer = SignerUtilities.GetSigner($"{ecdsaType.ToString()}");
List<byte> vnRHashVal = unsignedHash;
signer.Init(false, publicKey);
signer.BlockUpdate(vnRHashVal.ToArray(), 0, vnRHashVal.Count);
bool bResult = signer.VerifySignature(signedHash.ToArray());
return bResult;
}
public List<byte> GetKeyBase64Decrypted(string strKey)
{
List<byte> vnKey = Convert.FromBase64String(strKey).ToList();
return vnKey;
}
public List<byte> EncodeDERSignature_20(List<byte> signature)
{
List<byte> resultData = new List<byte>();
List<byte> r = new List<byte>(66);
List<byte> s = new List<byte>(66);
int lengthOfR = signature[4];
int lengthOfS = signature[lengthOfR + 6];
if (lengthOfR == 66)
r.AddRange(signature.GetRange(5, lengthOfR));
else if (lengthOfR == 65)
{
r.Add(00);
r.AddRange(signature.GetRange(5, lengthOfR));
}
if (lengthOfS == 66)
s.AddRange(signature.GetRange(lengthOfR + 7, lengthOfS));
else if (lengthOfS == 65)
{
s.Add(00);
s.AddRange(signature.GetRange(lengthOfR + 7, lengthOfS));
}
resultData.AddRange(r);
resultData.AddRange(s);
return resultData;
}
public List<byte> DecodeDERSignature_20(List<byte> signature)
{
List<List<byte>> decodeData = new List<List<byte>>();
List<byte> r = signature.GetRange(0, 66);
List<byte> s = signature.GetRange(66, 66);
for(int i = 0 ; i < 4 ; i++)
{
List<byte> caseData = new List<byte>();
//header 고정값 2자리
caseData.Add(48);
caseData.Add(129);
if (i == 0)
{
caseData.Add(134);
caseData.Add(2);
caseData.Add(65);
caseData.AddRange(r.GetRange(1, 65));
caseData.Add(2);
caseData.Add(65);
caseData.AddRange(s.GetRange(1, 65));
}
else if (i == 1)
{
caseData.Add(135);
caseData.Add(2);
caseData.Add(65);
caseData.AddRange(r.GetRange(1,65));
caseData.Add(2);
caseData.Add(66);
caseData.AddRange(s);
}
else if(i == 2)
{
caseData.Add(135);
caseData.Add(2);
caseData.Add(66);
caseData.AddRange(r);
caseData.Add(2);
caseData.Add(65);
caseData.AddRange(s.GetRange(1,65));
}
else if (i == 3)
{
caseData.Add(136);
caseData.Add(2);
caseData.Add(66);
caseData.AddRange(r);
caseData.Add(2);
caseData.Add(66);
caseData.AddRange(s);
}
decodeData.Add(caseData);
}
return decodeData.Last();
}
#endregion
#region CRC16
private ushort[] CRCTable =
{
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};
//==========================================================================//
public List<byte> MakeCRC16_CCIT(List<byte> bytes, int ilen)
{
int icrc = 0xFFFF;
for (int i = 0; i < ilen; i++)
{
icrc = (ushort)((icrc << 8) ^ CRCTable[((icrc >> 8) ^ (bytes[i]) & 0xFF)]);
}
List<byte> ret = BitConverter.GetBytes(icrc).ToList();
return ret;
}
#endregion
public string ConvertToPEM(byte[] rawPublicKey)
{
if (rawPublicKey.Count() > 65)
{
// ECDSA 공개키를 X.509 형식으로 변환
var ecParameters = new ECParameters
{
Q = new System.Security.Cryptography.ECPoint
{
X = rawPublicKey.Skip(1).Take(66).ToArray(), // X 좌표 (66바이트)
Y = rawPublicKey.Skip(67).Take(132).ToArray() // Y 좌표 (66바이트)
},
Curve = System.Security.Cryptography.ECCurve.NamedCurves.nistP521
};
using (var ecdsa = ECDsa.Create(ecParameters))
{
// 공개키를 X.509 형식으로 변환
byte[] x509PublicKey = ecdsa.ExportSubjectPublicKeyInfo();
// PEM 형식으로 변환
return Convert.ToBase64String(x509PublicKey);
}
}
else
{
// ECDSA 공개키를 X.509 형식으로 변환
var ecParameters = new ECParameters
{
Q = new System.Security.Cryptography.ECPoint
{
X = rawPublicKey.Skip(1).Take(32).ToArray(), // X 좌표 (32바이트)
Y = rawPublicKey.Skip(33).Take(32).ToArray() // Y 좌표 (32바이트)
},
Curve = System.Security.Cryptography.ECCurve.NamedCurves.nistP256
};
using (var ecdsa = ECDsa.Create(ecParameters))
{
// 공개키를 X.509 형식으로 변환
byte[] x509PublicKey = ecdsa.ExportSubjectPublicKeyInfo();
// PEM 형식으로 변환
return Convert.ToBase64String(x509PublicKey);
}
}
}
}
}

View File

@ -0,0 +1,18 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace VPKI.Library.Services
{
public class CommonService
{
public CommonService()
{
}
}//end class
}//end namespace

View File

@ -0,0 +1,39 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VPKI.Library.Config;
namespace VPKI.Library.Services
{
public class ConfigService<T> where T : WebCommonConfig
{
public static T? Config { get; set; }
public bool OpenConfig(string path)
{
bool result = true;
try
{
string clientConfigJson = File.ReadAllText(path);
Config = JsonConvert.DeserializeObject<T>(clientConfigJson);
}
catch (Exception e)
{
Console.WriteLine("Config Init Error");
Console.WriteLine(e.Message);
result = false;
}
return result;
}
public T? GetConfig()
{
return Config;
}
}
}

View File

@ -0,0 +1,112 @@
using System.Net.Http.Json;
namespace VPKI.Library.Services
{
public class HttpService : CommonService
{
public HttpService()
: base()
{
}
/// <summary>
/// PostJsonAsync
/// </summary>
/// <param name="url">https://127.0.0.1:443</param>
/// <param name="timeOutSeconds">Range 5~30 secconds</param>
public virtual async Task<RESPONSE?> PostJsonAsync<REQUEST, RESPONSE>(string url, REQUEST request, short timeOutSeconds = 5) where REQUEST : class where RESPONSE : class
{
RESPONSE? response = default(RESPONSE);
Guid guid = Guid.NewGuid();
using (HttpClient httpClient = new HttpClient(GetClientHandler()))
{
int retry = 0;
while (true)
{
await Task.Delay(1);
try
{
var timeOutSec = SetTimeout(timeOutSeconds);
httpClient.Timeout = new TimeSpan(0, 0, timeOutSec);
httpClient.BaseAddress = new Uri($"{url}");
Log4net.WriteLine($"[POST] Request({guid})::{url}{Environment.NewLine}{request?.ToJson()}", LogType.Warn);
DateTime requestTime = DateTime.Now;
var res = await httpClient.PostAsJsonAsync(url, request);
response = await res.Content.ReadFromJsonAsync<RESPONSE>();
Log4net.WriteLine($"[POST] Rseponse({guid}) ({(DateTime.Now - requestTime).TotalSeconds} sec)::{url}{Environment.NewLine}{response?.ToJson()}", LogType.Warn);
break;
}
catch (Exception e)
{
Log4net.WriteLine(e);
retry += 1;
}
if (retry > 1)
break;
}
}
return response;
}
/// <summary>
/// GetJsonAsnyc
/// </summary>
/// <param name="url">https://127.0.0.1:443</param>
/// <param name="timeOutSeconds">Range 5~30 secconds</param>
public virtual async Task<RESPONSE?> GetJsonAsync<RESPONSE>(string url, short timeOutSeconds = 10) where RESPONSE : class
{
RESPONSE? response = default(RESPONSE);
Guid guid = Guid.NewGuid();
using (HttpClient httpClient = new HttpClient(GetClientHandler()))
{
try
{
var timeOutSec = SetTimeout(timeOutSeconds);
httpClient.Timeout = new TimeSpan(0, 0, timeOutSec);
httpClient.BaseAddress = new Uri($"{url}");
Log4net.WriteLine($"[GET] Request({guid})::{url}", LogType.Warn);
DateTime requestTime = DateTime.Now;
response = await httpClient.GetFromJsonAsync<RESPONSE>(url);
Log4net.WriteLine($"[GET] Rseponse({guid}) ({(DateTime.Now - requestTime).TotalSeconds} sec)::{url}", LogType.Warn);
}
catch (Exception e)
{
Log4net.WriteLine(e);
}
}
return response;
}
protected HttpClientHandler GetClientHandler()
{
HttpClientHandler clientHandler = new HttpClientHandler();
clientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) =>
{
return true;
};
return clientHandler;
}
protected short SetTimeout(short timeOutSeconds)
{
short timeoutMin = 5;
short timeoutMax = 30;
timeOutSeconds = Math.Clamp(timeOutSeconds, timeoutMin, timeoutMax);
return timeOutSeconds;
}
}
}

View File

@ -0,0 +1,139 @@
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata.Ecma335;
using System.Text;
using System.Threading.Tasks;
namespace VPKI.Library.Static
{
public static class Document4net
{
public static List<string> ConvertToCsvFile<T>(this IEnumerable<T> data)
{
string separator = ",";
List<string> result = new List<string>();
try
{
Type t = typeof(T);
var properties = t.GetProperties();
//header
List<string> headers = GetHeader(properties);
result.Add(string.Join(separator, headers.ToArray()));
//data
foreach (var d in data)
{
var getValues = GetValue(d);
string rsultValue = string.Empty;
foreach (var h in headers)
{
bool isFindHeader = false;
foreach(var v in getValues)
{
if (v.Header == h)
{
isFindHeader = true;
rsultValue += $"{v.Value}{separator}";//string.Join(separator, v.Value);
break;
//result.Add(string.Join(separator, getValues.ToArray()));
}
}
if(isFindHeader == false)
rsultValue += $"{separator}";
}
result.Add(rsultValue);
}
}
catch(Exception ex)
{
Log4net.WriteLine("Document4net ExportToCsv Exception",LogType.Error);
Log4net.WriteLine(ex);
}
return result;
}
private static List<string> GetHeader(PropertyInfo[] propertyInfo)
{
List<string> result = new List<string>();
foreach(var prop in propertyInfo)
{
if (prop != null)
{
//객체일때 재귀
if (prop.PropertyType!.IsClass == true && prop.PropertyType.Name.ToLower().Contains("string") == false)
{
result.AddRange(GetHeader(prop.PropertyType.GetProperties()));
}
else
{
result.Add($"{prop.DeclaringType!.Name}.{prop.Name}");
}
}
}
return result;
}
private static List<CSV> GetValue<T>(T data)
{
List<CSV> result = new List<CSV>();
if(data != null)
{
foreach (var prop in data.GetType().GetProperties())
{
//객체일때 재귀
if (prop.PropertyType!.IsClass == true && prop.PropertyType.Name.ToLower().Contains("string") == false)
{
result.AddRange(GetValue(prop.GetValue(data)));
}
else
{
if (prop.PropertyType == typeof(DateTime))
{
var datetTime = Convert.ToDateTime(prop!.GetValue(data));
result.Add(new CSV
{
Header = $"{prop.DeclaringType!.Name}.{prop.Name}",
Value = $"\"{datetTime.ToString("yyyy-MM-dd HH:mm:ss")}\"".Replace("\r", "").Replace("\n", "")
});
}
else
{
result.Add(new CSV
{
Header = $"{prop.DeclaringType!.Name}.{prop.Name}",
Value = $"\"{prop!.GetValue(data)}\"".Replace("\r", "").Replace("\n", "")
});
}
}
}
}
return result;
}
}
public class CSV
{
public string? Header { get; set; }
public string? Value { get; set; }
}
}

View File

@ -0,0 +1,45 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.5.0" />
<PackageReference Include="DocumentFormat.OpenXml" Version="3.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.15">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.15">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VPKI.Library.DB\VPKI.Library.DB.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="SystemX.Core">
<HintPath>..\..\..\DLL\SystemX.Core.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "8.0.10",
"commands": [
"dotnet-ef"
]
}
}
}

View File

@ -0,0 +1,151 @@
using Azure;
using Azure.Core;
using DB.VPKI_AccountDB;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
using VPKI.Library.Enums;
using VPKI.Library.Model;
using VPKI.Library.Model.Auth;
using VPKI.Library.Services;
using VPKI.Web.Api.Services;
namespace VPKI.Web.Api.Controllers
{
[Tags("Auth")]
[Route("api/auth")]
[ApiController]
[ApiExplorerSettings(IgnoreApi = true)]
public class AuthController : CommonController
{
private readonly AuthService _authService;
public AuthController(IServiceProvider serviceProvider, IHttpContextAccessor httpContextAccessor,
AuthService authService)
: base(serviceProvider, httpContextAccessor)
{
_authService = authService;
}
[HttpPost("regisger")]
public async Task<IResult> Register([FromBody] RegisterModel request)
{
Log4net.WriteLine(GetRequestLog(request).LogModelToString("Request Auth"), LogType.Debug);
RegisterResponseModel response = new RegisterResponseModel();
if (request?.UserID != null && request?.Password != null)
{
response = await _authService.CreateUser(request);
}
Log4net.WriteLine(GetResponseLog(response).LogModelToString("Response Auth"), LogType.Debug);
return Results.Ok(response);
}
[HttpPost("login")]
public async Task<IResult> Login([FromBody] LoginModel request)
{
Log4net.WriteLine(GetRequestLog(request).LogModelToString("Request Auth"), LogType.Debug);
LoginResponseModel response = new LoginResponseModel();
response.UserID = request.UserID;
response.EC = ERROR_CODE.EC_USER_LOGIN_FAILED;
if (request.UserID != null && request.Password != null)
{
response = await _authService.SelectUser(request);
if (response.EC == ERROR_CODE.EC_OK)
{
double convertExpires = Convert.ToDouble(_configService?.GetConfig()?.Auth?.accessTokenExpires);
response.AccessToken = GenerateJwtToken(response);
response.AccessTokenExpired = DateTime.UtcNow.AddMinutes(convertExpires).ToUnixTime();
response.RefreshToken = GenerateJwtToken(response, true);
}
await _authService.UpdateLoginInfo(request, response.RefreshToken);
}
Log4net.WriteLine(GetResponseLog(response).LogModelToString("Response Auth"), LogType.Debug);
return Results.Ok(response);
}
[HttpPost("logout")]
public async Task<IResult> Logout([FromBody] LogoutModel request)
{
Log4net.WriteLine(GetRequestLog(request).LogModelToString("Request Auth"), LogType.Debug);
var response = _authService.LogoutUser(request);
await Task.CompletedTask;
Log4net.WriteLine(GetResponseLog(response).LogModelToString("Response Auth"), LogType.Debug);
return Results.Ok(response);
}
[Authorize]
[HttpPost("validate")]
public ActionResult<string> Validate([FromBody] string authToken)
{
return "";
}
private TokenValidationParameters GetValidationParameters()
{
return new TokenValidationParameters()
{
ValidateLifetime = true,
ValidateAudience = true,
ValidateIssuer = true,
ValidIssuer = $"{_configService?.GetConfig()?.Auth?.issuer}",
ValidAudience = $"{_configService?.GetConfig()?.Auth?.issuer}",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes($"{_configService?.GetConfig()?.Auth?.accessTokenSecret}"))
};
}
private string GenerateJwtToken(LoginResponseModel loginResponseModel, bool isRefreshToken = false)
{
var claims = new[]
{
new Claim(ClaimTypes.Name, $"{loginResponseModel.UserID}"),
new Claim(ClaimTypes.Role, $"{loginResponseModel.RoleName}"),
};
string secret = $"{_configService?.GetConfig()?.Auth?.accessTokenSecret}";
double convertExpires = Convert.ToDouble(_configService?.GetConfig()?.Auth?.accessTokenExpires);
if (isRefreshToken == true)
{
secret = $"{_configService?.GetConfig()?.Auth?.refreshTokenSecret}";
convertExpires = Convert.ToDouble(_configService?.GetConfig()?.Auth?.refreshTokenExpires);
}
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: $"{_configService?.GetConfig()?.Auth?.issuer}",
audience: $"{_configService?.GetConfig()?.Auth?.audience}",
claims: claims,
expires: DateTime.UtcNow.AddMinutes(convertExpires),
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
}

View File

@ -0,0 +1,106 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Newtonsoft.Json;
using System.Runtime.CompilerServices;
using VPKI.Library.Config;
using VPKI.Library.Model;
using VPKI.Library.Services;
namespace VPKI.Web.Api.Controllers
{
public class CommonController
{
public readonly IServiceProvider _serviceProvider;
public readonly IHttpContextAccessor _httpContextAccessor;
public readonly ConfigService<WebApiConfig>? _configService;
protected static Guid guid { get; private set; } = Guid.NewGuid();
public CommonController(IServiceProvider serviceProvider, IHttpContextAccessor httpContextAccessor)
{
//provider
_serviceProvider = serviceProvider;
_httpContextAccessor = httpContextAccessor;
//service
_configService = _serviceProvider.GetService<ConfigService<WebApiConfig>>();
}
/// <summary>
/// Request 클라이언트 IP
/// </summary>
protected virtual string? GetClientIP()
{
return _httpContextAccessor?.HttpContext?.Connection?.RemoteIpAddress?.ToString();
}
/// <summary>
/// Request 클라이언트 Url
/// </summary>
protected virtual string? GetRequestUrl()
{
return _httpContextAccessor?.HttpContext?.Request?.Path;
}
/// <summary>
/// Request 클라이언트 method: [GET] or [POST]
/// </summary>
protected virtual string? GetRequestMethod()
{
return _httpContextAccessor?.HttpContext?.Request?.Method;
}
/// <summary>
/// 현재 Action(함수) 이름 가져오기
/// </summary>
protected virtual string GetMethodName([CallerMemberName] string callerMemberName = "")
{
return callerMemberName;
}
#region System Log
//request log
protected virtual LogModel GetRequestLog([CallerMemberName] string callerMemberName = "")
{
return new LogModel
{
ClientIP = $"{GetClientIP()}",
Method = $"{GetRequestMethod()}",
RequestUrl = $"{GetRequestUrl()}",
Action = $"{callerMemberName}",
Body = null,
};
}
protected virtual LogModel GetRequestLog<T>(T fromBody, [CallerMemberName] string callerMemberName = "")
{
var requestLog = GetRequestLog(callerMemberName);
requestLog.Body = fromBody;
return requestLog;
}
//response log
protected virtual LogModel GetResponseLog([CallerMemberName] string callerMemberName = "")
{
return new LogModel
{
ClientIP = $"{GetClientIP()}",
Method = $"{GetRequestMethod()}",
RequestUrl = $"{GetRequestUrl()}",
Action = $"{callerMemberName}",
Body = null,
};
}
protected virtual LogModel GetResponseLog<T>(T fromBody, [CallerMemberName] string callerMemberName = "")
{
var responseLog = GetResponseLog(callerMemberName);
responseLog.Body = fromBody;
return responseLog;
}
#endregion
}
}

View File

@ -0,0 +1,46 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using VPKI.Library.Model;
using VPKI.Web.Api.Services;
namespace VPKI.Web.Api.Controllers
{
[Route("api/user/[Action]")]
[Authorize]
[ApiController]
[ApiExplorerSettings(IgnoreApi = true)]
public class UserController : CommonController
{
private readonly UserService _userService;
public UserController(IServiceProvider serviceProvider, IHttpContextAccessor httpContextAccessor,
UserService userService)
: base(serviceProvider, httpContextAccessor)
{
_userService = userService;
}
[HttpGet]
public async Task<IEnumerable<UserModel>> GetAllUser(string url = "")
{
Log4net.WriteLine(GetRequestLog().LogModelToString("Request Auth"), LogType.Debug);
var response = await _userService.GetAllUsers();
Log4net.WriteLine(GetRequestLog(response).LogModelToString("Response Auth"), LogType.Debug);
return response;
}
[HttpPost]
[Authorize]
public async Task<UserModel> UpdateUser(UserModel user)
{
Log4net.WriteLine(GetRequestLog().LogModelToString("Request Auth"), LogType.Debug);
var response = await _userService.UpdateUser(user);
Log4net.WriteLine(GetRequestLog(response).LogModelToString("Response Auth"), LogType.Debug);
return response;
}
}
}

View File

@ -0,0 +1,133 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using VPKI.Library.Enums;
using VPKI.Library.Model;
using VPKI.Web.Api.Services;
namespace VPKI.Web.Api.Controllers
{
public class VPKIBaseController : CommonController
{
public VPKIBaseController(IServiceProvider serviceProvider, IHttpContextAccessor _httpContextAccessor)
:base(serviceProvider, _httpContextAccessor)
{
}
/// <summary>
/// tbscsr데이터 조회 (ISO15118-2)
/// </summary>
[Tags("VPKI API Base")]
[HttpGet]
[Route("history")]
public async Task<IResult> TbscsrHistory(DateTime startDate, DateTime endDate)
{
List<CertificateHistoryModel> response = new List<CertificateHistoryModel>();
Log4net.WriteLine(GetRequestLog().LogModelToString("Request"), LogType.Debug);
var service = _serviceProvider.GetService<VpkiBaseService>();
if (service != null)
{
response = await service.History(DateOnly.FromDateTime(startDate), DateOnly.FromDateTime(endDate));
}
long min = 0;
if(response.Count > 0)
{
response.Min(x => x!.TTbscsr!.CCuid);
}
Log4net.WriteLine($"Ressponse::Start CCUID:{min}, Count:{response.Count}", LogType.Debug);
return Results.Ok(response);
}
/// <summary>
/// tbscsr데이터 조회 (ISO15118-2)
/// </summary>
[Tags("VPKI API Base")]
[HttpGet]
[Route("history/ccuid")]
public async Task<IResult> TbscsrHistoryByCcuid(long ccuid = 0)
{
CertificateHistoryModel response = new CertificateHistoryModel();
Log4net.WriteLine(GetRequestLog().LogModelToString("Request"), LogType.Debug);
var service = _serviceProvider.GetService<VpkiBaseService>();
if (service != null)
{
response = await service.History(ccuid);
}
Log4net.WriteLine(GetResponseLog(response).LogModelToString("Response"), LogType.Debug);
return Results.Ok(response);
}
/// <summary>
/// prov
/// </summary>
[Tags("VPKI API Base")]
[HttpGet]
[Route("certificate/GetProv")]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IResult> GetProv(VpkiType vpkiType)
{
string response = string.Empty;
Log4net.WriteLine(GetRequestLog().LogModelToString("Request"), LogType.Debug);
var service = _serviceProvider.GetService<VpkiBaseService>();
if (service != null)
{
response = service.GetSubCA(vpkiType, false);
}
await Task.CompletedTask;
Log4net.WriteLine(GetResponseLog(response).LogModelToString("Response"), LogType.Debug);
return Results.Ok(response);
}
/// <summary>
/// certificate info
/// </summary>
[Tags("VPKI API Base")]
[HttpGet]
[Route("certificate/GetCertificateInfo")]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IResult> GetCertificateInfo(long ccuid = 0)
{
string response = string.Empty;
Log4net.WriteLine(GetRequestLog().LogModelToString("Request"), LogType.Debug);
var service = _serviceProvider.GetService<VpkiBaseService>();
if (service != null)
{
response = await service.GetCertificateInfo(ccuid);
}
Log4net.WriteLine(GetResponseLog(response).LogModelToString("Response"), LogType.Debug);
return Results.Ok(response);
}
/// <summary>
/// certificate info
/// </summary>
[Tags("VPKI API Base")]
[HttpGet]
[Route("certificate/ocsp")]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IResult> Ocsp(long ccuid = 0)
{
string response = string.Empty;
Log4net.WriteLine(GetRequestLog().LogModelToString("Request"), LogType.Debug);
var service = _serviceProvider.GetService<VpkiBaseService>();
if (service != null)
{
response = await service.Ocsp(ccuid);
}
Log4net.WriteLine(GetResponseLog(response).LogModelToString("Response"), LogType.Debug);
return Results.Ok(response);
}
}
}

View File

@ -0,0 +1,143 @@
using DB.VPKI_DataDB;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Primitives;
using Microsoft.OpenApi.Validations.Rules;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using VPKI.Library.Model;
using VPKI.Library.Packet;
using VPKI.Web.Api.Services;
namespace VPKI.Web.Api.Controllers
{
[Tags("VPKI API v1")]
[ApiController()]
[Route("api/v1")]
public class VPKIv1_Controller : VPKIBaseController
{
public VPKIv1_Controller(IServiceProvider serviceProvider, IHttpContextAccessor _httpContextAccessor)
: base(serviceProvider, _httpContextAccessor)
{
}
/// <summary>
/// CSR 생성 요청 (ISO15118-2)
/// </summary>
[HttpPost]
[Route("tbscsr")]
public async Task<IResult> Tbscsr([FromBody] Request_Tbscsr _request)
{
Response_Tbscsr response = new Response_Tbscsr();
Guid guid = Guid.NewGuid();
Log4net.WriteLine(GetRequestLog(_request).LogModelToString($"Request ({guid})"), LogType.Info);
var service = _serviceProvider.GetService<ISO15118_02Service>();
if (service != null)
{
response = await service.RequestTbscsr(_request);
}
Log4net.WriteLine(GetResponseLog(response).LogModelToString($"Response ({guid})"), LogType.Info);
return Results.Ok(response);
}
/// <summary>
/// 인증서 발급 요청 (ISO15118-2)
/// </summary>
[HttpPost]
[Route("certificate")]
public async Task<IResult> Certificate([FromBody] Request_Certificate _request)
{
Response_Certificate response = new Response_Certificate();
Guid guid = Guid.NewGuid();
Log4net.WriteLine(GetRequestLog(_request).LogModelToString($"Request ({guid})"));
var service = _serviceProvider.GetService<ISO15118_02Service>();
if (service != null)
{
response = await service.RequestCertificate(_request);
}
Log4net.WriteLine(GetResponseLog(response).LogModelToString($"Response ({guid})"));
return Results.Ok(response);
}
/// <summary>
/// 인증서 검증 결과 전송 (ISO15118-2)
/// </summary>
[HttpPost]
[Route("verifyresult")]
public async Task<IResult> Verifyresult([FromBody] Request_Verifyresult request)
{
Response_Verifyresult response = new Response_Verifyresult();
Guid guid = Guid.NewGuid();
Log4net.WriteLine(GetRequestLog(request).LogModelToString("Request"));
var service = _serviceProvider.GetService<ISO15118_02Service>();
if(service != null)
{
response = await service.RequestVerifyresult(request);
}
Log4net.WriteLine(GetResponseLog(response).LogModelToString($"Response ({guid})"));
return Results.Ok(response);
}
/// <summary>
/// 인증서 재발급 (ISO15118-2)
/// </summary>
[HttpPost]
[Route("reissue")]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IResult> Reissue([FromBody] Request_Certificate request)
{
//Response_Certificate response = new Response_Certificate();
//Log4net.WriteLine(GetRequestLog(request).LogModelToString("Request"));
//var service = _serviceProvider.GetService<ISO15118_02Service>();
//if (service != null)
//{
// response = await service.RequestCertificateReissue(request);
//}
//Log4net.WriteLine(GetResponseLog(response).LogModelToString($"Response ({guid})"));
//return Results.Ok(response);
await Task.CompletedTask;
return Results.NotFound();
}
/// <summary>
/// 인증서 폐기 (ISO15118-2)
/// </summary>
[HttpPost]
[Route("revoke")]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IResult> Revoke([FromBody] Request_CertificateRevokeOEM request)
{
//Response_CertificateRevokeOEM response = new Response_CertificateRevokeOEM();
//Guid guid = Guid.NewGuid();
//Log4net.WriteLine(GetRequestLog(request).LogModelToString($"Request ({guid})"));
//var service = _serviceProvider.GetService<ISO15118_02Service>();
//if (service != null)
//{
// response = await service.RequestCertificateRevoke(request);
//}
//Log4net.WriteLine(GetResponseLog(response).LogModelToString($"Response ({guid})"));
//return Results.Ok(response);
await Task.CompletedTask;
return Results.NotFound();
}
}
}

View File

@ -0,0 +1,275 @@
using Azure;
using Microsoft.AspNetCore.Mvc;
using VPKI.Library.Model;
using VPKI.Library.Packet;
using VPKI.Web.Api.Services;
namespace VPKI.Web.Api.Controllers
{
[ApiController()]
[Route("api/v2")]
public class VPKIv2_Controller : VPKIBaseController
{
public VPKIv2_Controller(IServiceProvider serviceProvider, IHttpContextAccessor _httpContextAccessor)
: base(serviceProvider, _httpContextAccessor)
{
}
#region prov
/// <summary>
/// CSR 생성 요청 (ISO15118-20) prov
/// </summary>
[Tags("VPKI API v2 prov")]
[HttpPost]
[Route("prov/tbscsr")]
public async Task<IResult> ProvTbscsr([FromBody] Request_Tbscsr _request)
{
Response_Tbscsr response = new Response_Tbscsr();
Guid guid = Guid.NewGuid();
Log4net.WriteLine(GetRequestLog(_request).LogModelToString($"Request ({guid})"));
var service = _serviceProvider.GetService<ISO15118_20Service>();
if(service != null)
{
response = await service.RequestTbscsr(_request, Library.Enums.VpkiType.prov_cert);
}
Log4net.WriteLine(GetResponseLog(response).LogModelToString($"Response ({guid})"));
return Results.Ok(response);
}
/// <summary>
/// 인증서 발급 요청 (ISO15118-20) prov
/// </summary>
[Tags("VPKI API v2 prov")]
[HttpPost]
[Route("prov/certificate")]
public async Task<IResult> ProvCertificate([FromBody] Request_Certificate _request)
{
Response_Certificate response = new Response_Certificate();
Guid guid = Guid.NewGuid();
Log4net.WriteLine(GetRequestLog(_request).LogModelToString($"Request ({guid})"));
var service = _serviceProvider.GetService<ISO15118_20Service>();
if(service != null)
{
response = await service.RequestCertificate(_request, Library.Enums.VpkiType.prov_cert);
}
Log4net.WriteLine(GetResponseLog(response).LogModelToString($"Response ({guid})"));
return Results.Ok(response);
}
/// <summary>
/// 인증서 검증 결과 전송 (ISO15118-20) prov
/// </summary>
[Tags("VPKI API v2 prov")]
[HttpPost]
[Route("prov/verifyresult")]
public async Task<IResult> ProvVerifyresult([FromBody] Request_Verifyresult request)
{
Response_Verifyresult response = new Response_Verifyresult();
Guid guid = Guid.NewGuid();
Log4net.WriteLine(GetRequestLog(request).LogModelToString($"Request ({guid})"));
var service = _serviceProvider.GetService<ISO15118_20Service>();
if (service != null)
{
response = await service.RequestVerifyresult(request);
}
Log4net.WriteLine(GetResponseLog(response).LogModelToString($"Response ({guid})"));
return Results.Ok(response);
}
/// <summary>
/// 인증서 재발급 (ISO15118-20)
/// </summary>
[Tags("VPKI API v2 prov")]
[HttpPost]
[Route("prov/reissue")]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IResult> ProvReissue([FromBody] Request_Certificate request)
{
//Response_Certificate response = new Response_Certificate();
//Guid guid = Guid.NewGuid();
//Log4net.WriteLine(GetRequestLog(request).LogModelToString($"Request ({guid})"));
//var service = _serviceProvider.GetService<ISO15118_20Service>();
//if (service != null)
//{
// response = await service.RequestCertificateReissue(request, Library.Enums.VpkiType.prov_cert);
//}
//Log4net.WriteLine(GetResponseLog(response).LogModelToString($"Response ({guid})"));
//return Results.Ok(response);
await Task.CompletedTask;
return Results.NotFound();
}
/// <summary>
/// 인증서 폐기 (ISO15118-20)
/// </summary>
[Tags("VPKI API v2 prov")]
[HttpPost]
[Route("prov/revoke")]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IResult> ProvRevoke([FromBody] Request_CertificateRevokeOEM request)
{
//Response_CertificateRevokeOEM response = new Response_CertificateRevokeOEM();
//Guid guid = Guid.NewGuid();
//Log4net.WriteLine(GetRequestLog(request).LogModelToString($"Request ({guid})"));
//var service = _serviceProvider.GetService<ISO15118_20Service>();
//if (service != null)
//{
// response = await service.RequestCertificateRevoke(request, Library.Enums.VpkiType.prov_cert);
//}
//Log4net.WriteLine(GetResponseLog(response).LogModelToString($"Response ({guid})"));
//return Results.Ok(response);
await Task.CompletedTask;
return Results.NotFound();
}
#endregion
#region vehicle
/// <summary>
/// CSR 생성 요청 (ISO15118-20) vehicle
/// </summary>
[Tags("VPKI API v2 vehicle")]
[HttpPost]
[Route("vehicle/tbscsr")]
public async Task<IResult> VehicleTbscsr([FromBody] Request_Tbscsr _request)
{
Response_Tbscsr response = new Response_Tbscsr();
Guid guid = Guid.NewGuid();
Log4net.WriteLine(GetRequestLog(_request).LogModelToString($"Request ({guid})"));
var service = _serviceProvider.GetService<ISO15118_20Service>();
if (service != null)
{
response = await service.RequestTbscsr(_request, Library.Enums.VpkiType.vehicle_cert);
}
Log4net.WriteLine(GetResponseLog(response).LogModelToString($"Response ({guid})"));
return Results.Ok(response);
}
/// <summary>
/// 인증서 발급 요청 (ISO15118-20) vehicle
/// </summary>
[Tags("VPKI API v2 vehicle")]
[HttpPost]
[Route("vehicle/certificate")]
public async Task<IResult> VehicleCertificate([FromBody] Request_Certificate _request)
{
Response_Certificate response = new Response_Certificate();
Guid guid = Guid.NewGuid();
Log4net.WriteLine(GetRequestLog(_request).LogModelToString($"Request ({guid})"));
var service = _serviceProvider.GetService<ISO15118_20Service>();
if (service != null)
{
response = await service.RequestCertificate(_request, Library.Enums.VpkiType.vehicle_cert);
}
Log4net.WriteLine(GetResponseLog(response).LogModelToString($"Response ({guid})"));
return Results.Ok(response);
}
/// <summary>
/// 인증서 검증 결과 전송 (ISO15118-20) vehicle
/// </summary>
[Tags("VPKI API v2 vehicle")]
[HttpPost]
[Route("vehicle/verifyresult")]
public async Task<IResult> VehicleVerifyresult([FromBody] Request_Verifyresult request)
{
Response_Verifyresult response = new Response_Verifyresult();
Guid guid = Guid.NewGuid();
Log4net.WriteLine(GetRequestLog(request).LogModelToString($"Request ({guid})"));
var service = _serviceProvider.GetService<ISO15118_20Service>();
if (service != null)
{
response = await service.RequestVerifyresult(request);
}
Log4net.WriteLine(GetResponseLog(response).LogModelToString($"Response ({guid})"));
return Results.Ok(response);
}
/// <summary>
/// 인증서 재발급 (ISO15118-20)
/// </summary>
[Tags("VPKI API v2 vehicle")]
[HttpPost]
[Route("vehicle/reissue")]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IResult> VehicleReissue([FromBody] Request_Certificate request)
{
//Response_Certificate response = new Response_Certificate();
//Guid guid = Guid.NewGuid();
//Log4net.WriteLine(GetRequestLog(request).LogModelToString($"Request ({guid})"));
//var service = _serviceProvider.GetService<ISO15118_20Service>();
//if (service != null)
//{
// response = await service.RequestCertificateReissue(request, Library.Enums.VpkiType.vehicle_cert);
//}
//Log4net.WriteLine(GetResponseLog(response).LogModelToString($"Response ({guid})"));
//return Results.Ok(response);
await Task.CompletedTask;
return Results.NotFound();
}
/// <summary>
/// 인증서 폐기 (ISO15118-20)
/// </summary>
[Tags("VPKI API v2 vehicle")]
[HttpPost]
[Route("vehicle/revoke")]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IResult> VehicleRevoke([FromBody] Request_CertificateRevokeOEM request)
{
//Response_CertificateRevokeOEM response = new Response_CertificateRevokeOEM();
//Guid guid = Guid.NewGuid();
//Log4net.WriteLine(GetRequestLog(request).LogModelToString($"Request ({guid})"));
//var service = _serviceProvider.GetService<ISO15118_20Service>();
//if (service != null)
//{
// response = await service.RequestCertificateRevoke(request, Library.Enums.VpkiType.vehicle_cert);
//}
//Log4net.WriteLine(GetResponseLog(response).LogModelToString($"Response ({guid})"));
//return Results.Ok(response);
await Task.CompletedTask;
return Results.NotFound();
}
#endregion
}
}

View File

@ -0,0 +1,35 @@
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
#Depending on the operating system of the host machines(s) that will build or run the containers, the image specified in the FROM statement may need to be changed.
#For more information, please see https://aka.ms/containercompat
FROM dotnet8 AS base
EXPOSE 5555
WORKDIR /
RUN mkdir /Config
RUN dotnet dev-certs https --trust
COPY ["Config/*", "/Config/"]
WORKDIR /app
FROM dotnet8 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["VPKI.Web.Api/VPKI.Web.Api.csproj", "VPKI.Web.Api/"]
COPY ["VPKI.Library.DB/VPKI.Library.DB.csproj", "VPKI.Library.DB/"]
COPY ["VPKI.Library/VPKI.Library.csproj", "VPKI.Library/"]
RUN dotnet restore "./VPKI.Web.Api/VPKI.Web.Api.csproj"
COPY . .
WORKDIR "/src/VPKI.Web.Api"
RUN dotnet build "./VPKI.Web.Api.csproj" -c %BUILD_CONFIGURATION% -o /app/build
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./VPKI.Web.Api.csproj" -c %BUILD_CONFIGURATION% -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "VPKI.Web.Api.dll"]

View File

@ -0,0 +1,138 @@
using DB.VPKI_AccountDB;
using DB.VPKI_DataDB;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.Identity.Client;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using System.Security.Cryptography.Xml;
using System.Text;
using VPKI.Library.Config;
using VPKI.Library.Services;
using VPKI.Web.Api.Services;
string configDir = @"../Config";
//raed log4net config
if (Log4net.IsConfigLoad == true)
{
Log4net.WriteLine("Log4net Init Success");
}
else
{
Log4net.WriteLine("Log4net Init Failed", LogType.Error);
}
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddHttpContextAccessor();
//singleton
builder.Services.AddSingleton<ConfigService<WebApiConfig>>();
builder.Services.AddSingleton<CertificateService>();
//scoped
builder.Services.AddScoped<VpkiBaseService>();
builder.Services.AddScoped<ISO15118_02Service>();
builder.Services.AddScoped<ISO15118_20Service>();
builder.Services.AddScoped<AuthService>();
builder.Services.AddScoped<UserService>();
//dbContext
builder.Services.AddDbContext<VpkiAccountDbContext>();
builder.Services.AddDbContext<VpkiDataDbContext>();
//config preload
ConfigService<WebApiConfig> preloadConfig = new ConfigService<WebApiConfig>();
if (preloadConfig.OpenConfig($@"{configDir}/VPKI.WebApiConfig.json") == true)
{
var config = preloadConfig.GetConfig();
//auth
builder.Services
.AddAuthentication(option =>
{
option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.Zero,
ValidIssuer = $"{config?.Auth?.issuer}",
ValidAudience = $"{config?.Auth?.audience}",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes($"{config?.Auth?.accessTokenSecret}"))
};
});
}
else
{
Console.WriteLine("Config Preload Load Error.");
return;
}
var app = builder.Build();
//read api config and set
string serverUrl = string.Empty;
var configService = app.Services.GetService<ConfigService<WebApiConfig>>();
bool isIIS = false;
if (configService?.OpenConfig($@"{configDir}/VPKI.WebApiConfig.json") == true)
{
Log4net.WriteLine("WebApi Config Success.");
var apiConfig = ConfigService<WebApiConfig>.Config;
if(apiConfig != null)
{
serverUrl = $"{apiConfig?.Server?.Address}:{apiConfig?.Server?.Port}";
isIIS = apiConfig!.Server.IIS;
}
}
else
{
Log4net.WriteLine("WebApi Config Error.");
return;
}
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
Log4net.WriteLine($"IsDevelopment:{app.Environment.IsDevelopment()}");
Log4net.WriteLine($"Swagger Url: {serverUrl}/swagger");
app.UseSwagger();
app.UseSwaggerUI(option => {
option.DefaultModelsExpandDepth(-1);
});
}
app.UseAuthentication();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
if (isIIS == true)
{
app.Run();
}
else
{
Log4net.WriteLine($"Operation Url: {serverUrl}");
app.Run($"{serverUrl}");
}

View File

@ -0,0 +1,51 @@
{
"profiles": {
"http": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "http://localhost:5013"
},
"https": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "https://localhost:7296;http://localhost:5013"
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Container (Dockerfile)": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
"environmentVariables": {
"ASPNETCORE_HTTP_PORTS": "5555"
},
"publishAllPorts": true,
"useSSL": true
}
},
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:48976",
"sslPort": 44335
}
}
}

View File

@ -0,0 +1,265 @@
using Azure;
using DB.VPKI_AccountDB;
using DocumentFormat.OpenXml.Presentation;
using Microsoft.EntityFrameworkCore;
using System;
using System.Data;
using SystemX.Core.DB;
using VPKI.Library.Config;
using VPKI.Library.Enums;
using VPKI.Library.Model;
using VPKI.Library.Model.Auth;
using VPKI.Library.Services;
namespace VPKI.Web.Api.Services
{
public class AuthService
{
private readonly IServiceProvider _serviceProvider;
private readonly IServiceScopeFactory _scopeFactory;
private readonly ConfigService<WebApiConfig>? _configService;
private readonly Library.Config.Model.DataBase? _accountDB;
private static List<LoginResponseModel> Session = new List<LoginResponseModel>();
public AuthService(IServiceProvider serviceProvider, IServiceScopeFactory scopeFactory, ConfigService<WebApiConfig> configSerice)
{
_serviceProvider = serviceProvider;
_configService = configSerice;
_scopeFactory = scopeFactory;
_accountDB = _configService?.GetConfig()?.DataBase?.Find(x=>x.DBContext == "VpkiAccountDbContext");
}
/// <summary>
/// create new user
/// </summary>
public async Task<RegisterResponseModel> CreateUser(RegisterModel registerModel)
{
//response
RegisterResponseModel response = new RegisterResponseModel();
response.EC = ERROR_CODE.EC_USER_REGISTER_FAILED;
response.UserID = registerModel.UserID;
response.Role = registerModel.Role;
response.RoleName = registerModel.Role.ToString();
//context
using (var scope = _scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<VpkiAccountDbContext>();
if (context is not null)
{
var user = await context.TUsers.AsNoTracking().Where(x => x.CUserId.ToLower() == registerModel.UserID.ToLower()).ToListAsync();
if (user?.Count <= 0)
{
string auid = Guid.NewGuid().ToString();
//user
TUser newUser = new TUser
{
CAuid = auid,
CUserId = registerModel.UserID,
CPasswordHashed = registerModel.Password,
CCreateDateTime = DateTime.Now,
CLastLoginDateTime = new DateTime()
};
//role
TRole newUserRole = new TRole
{
CAuid = auid,
CRoleId = Convert.ToByte(registerModel.Role),
CRoleName = registerModel.Role.ToString()
};
using (var transaction = await context.CreateTransactionAsync())
{
await context.AddAsync(newUser);
await context.AddAsync(newUserRole);
var result = await context.CloseTransactionAsync(transaction);
if (result == true)
{
response.EC = ERROR_CODE.EC_OK;
}
}
}
}
}
return response;
}
/// <summary>
/// select user(login)
/// </summary>
public async Task<LoginResponseModel> SelectUser(LoginModel loginModel)
{
//response
LoginResponseModel response = new LoginResponseModel();
response.EC = ERROR_CODE.EC_USER_LOGIN_FAILED;
response.UserID = loginModel.UserID;
//var session = Session.Find(x => x.UserID?.ToLower() == loginModel.UserID?.ToLower());
//if (session?.AccessTokenExpired < DateTime.Now.ToUnixTime())
//{
// Session.Remove(session);
//}
//기존 로그인 체크
// if (Session.Exists(x => x.UserID == $"{loginModel.UserID?.ToLower()}") == false)
{
if (loginModel != null)
{
//context
using (var scope = _scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<VpkiAccountDbContext>();
if (context is not null)
{
try
{
using (var transaction = await context.CreateTransactionAsync(IsolationLevel.ReadUncommitted))
{
//select user
var selectUser = await context.TUsers.AsNoTracking().FirstOrDefaultAsync(x => x.CUserId.ToLower() == loginModel!.UserID!.ToLower());
if (selectUser is not null)
{
if (selectUser.CPasswordHashed == loginModel?.Password)
{
//select role
var selectRole = await context.TRoles.FindAsync(selectUser.CAuid);
if (selectRole != null)
{
response.Role = (UserRole)Enum.Parse(typeof(UserRole), selectRole.CRoleId.ToString());
response.RoleName = selectRole.CRoleName;
}
// Session.Add(response);
if (selectUser.CState == (byte)UserState.Active)
{
response.EC = ERROR_CODE.EC_OK;
}
else if (selectUser.CState == (byte)UserState.Inactive)
{
response.EC = ERROR_CODE.EC_USER_LOGIN_INAVTIVE;
}
else if (selectUser.CState == (byte)UserState.Block)
{
response.EC = ERROR_CODE.EC_USER_LOGIN_BLOCKED;
}
}
else
{
response.EC = ERROR_CODE.EC_USER_LOGIN_INVALID_PASSWORD;
}
}
else
{
response.EC = ERROR_CODE.EC_USER_LOGIN_NOT_EXIST;
Log4net.WriteLine($"{response.EC}", LogType.Error);
}
await context.CloseTransactionAsync(transaction);
}
}
catch (Exception e)
{
Log4net.WriteLine($"Select User Transaction Error", LogType.Error);
Log4net.WriteLine(e);
}
}
}
}
}
return response;
}
public async Task<bool> UpdateLoginInfo(LoginModel loginModel, string? RefreshToken = "")
{
bool result = false;
bool transactionResult = true;
using (var scope = _scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<VpkiAccountDbContext>();
if (context is not null)
{
var selectUser = await context.TUsers.AsNoTracking().FirstOrDefaultAsync(x => x.CUserId.ToLower() == loginModel!.UserID!.ToLower());
if (selectUser is not null)
{
using (var transaction = await context.CreateTransactionAsync())
{
try
{
//user info
selectUser.CLastLoginDateTime = DateTime.Now;
context.Update(selectUser);
//refresh token
var findRefreshToken = await context.TRefreshTokens.AsNoTracking().FirstOrDefaultAsync(x => x.CAuid == selectUser.CAuid);
//null이면(없으면) add
if (findRefreshToken == null)
{
await context.AddAsync(new TRefreshToken
{
CAuid = selectUser.CAuid,
CRefreshToken = $"{RefreshToken}"
});
}
//있으면 update
else
{
findRefreshToken.CRefreshToken = $"{RefreshToken}";
context.Update(findRefreshToken);
}
//commit
Log4net.WriteLine(findRefreshToken?.ToJson(), LogType.Debug);
result = true;
}
catch (Exception ex)
{
Log4net.WriteLine(ex);
}
transactionResult = await context.CloseTransactionAsync(transaction);
}
}
else
{
Log4net.WriteLine($"Not Exist User {loginModel.UserID}", LogType.Error);
}
//db error
if (transactionResult == false)
{
Log4net.WriteLine($"Transaction Error", LogType.Error);
}
else
{
Log4net.WriteLine($"Transaction Success", LogType.DB);
}
}
}
return result;
}
public LogoutResponseModel LogoutUser(LogoutModel logoutModel)
{
LogoutResponseModel response = new LogoutResponseModel();
response.UserID = logoutModel.UserID;
response.EC = ERROR_CODE.EC_USER_LOGOUT_FAILED;
var session = Session.Find(x => x.UserID?.ToLower() == logoutModel?.UserID?.ToLower());
if (session != null)
{
Session.Remove(session);
response.EC = ERROR_CODE.EC_OK;
}
return response;
}
}
}

View File

@ -0,0 +1,373 @@
using DB.VPKI_DataDB;
using Microsoft.EntityFrameworkCore;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Pkcs;
using System.Security.Cryptography;
using System.Text;
using SystemX.Core.DB;
using VPKI.Library.Config;
using VPKI.Library.Enums;
using VPKI.Library.Packet;
using VPKI.Library.Services;
namespace VPKI.Web.Api.Services
{
public class ISO15118_02Service : VpkiBaseService
{
private readonly IServiceScopeFactory _scopeFactory;
private readonly string URL_V1_CERTIFICATE_ISSUE = "api/v1/{1}/issue";
public ISO15118_02Service(ConfigService<WebApiConfig> configSerice, CertificateService certificateService, IServiceScopeFactory scopeFactory)
:base(configSerice, certificateService, scopeFactory)
{
_scopeFactory = scopeFactory;
}
/// <summary>
/// tbscsr
/// </summary>
public async Task<Response_Tbscsr> RequestTbscsr(Request_Tbscsr request, VpkiType vpkiType = VpkiType.prov_v1)
{
Response_Tbscsr response = new Response_Tbscsr();
if (request != null)
{
string pcid = _certificateService.CreatePCID(request, VpkiType.prov_v1);
string dn = $"cn={pcid},ou={request.certInfo.tierCode},ou={request.certInfo.unitCode},ou=ECC,o=HKMC,dc={request.certInfo.dc}";
string csrOrigin = _certificateService.CreateCsr256(dn, request.publickey);
string tbaCsrHashed = string.Empty;
//sha 256
using (SHA256 sha256 = SHA256.Create())
{
byte[] hashed = sha256.ComputeHash(Convert.FromBase64String(csrOrigin));
tbaCsrHashed = Convert.ToBase64String(hashed);
Log4net.WriteLine($"Hashed CSR (Base64): {tbaCsrHashed}", LogType.Info);
}
//csr error
if (string.IsNullOrEmpty(csrOrigin) == true)
{
response.status = Status.error.ToString();
response.message = "create csr error";
}
//pcid error
else if (string.IsNullOrEmpty(pcid) == true)
{
response.status = Status.error.ToString();
response.message = "create pcid error";
}
//success
else
{
var tbscsr = new TTbscsr
{
CIftid = $"{request?.iftid}",
CMacaddr = $"{request?.cnInfo?.macaddr}",
CWmi = $"{request?.cnInfo?.wmi}",
CIdType = $"{request?.cnInfo?.idType}",
CSupplierId = $"{request?.cnInfo?.supplierId}",
CDc = $"{request?.certInfo?.dc}",
CTierCode = $"{request?.certInfo?.tierCode}",
CUnitCode = $"{request?.certInfo?.unitCode}",
CPublickey = $"{request?.publickey}",
CCertType = "prov_v1",
COriginTbscsr = csrOrigin,
CHashedTbscsr = tbaCsrHashed,
CPcid = pcid,
CDn = $"{dn}",
CDateTime = DateTime.Now
};
//db context
bool transactionResult = true;
bool isExist = false;
using (var scope = _scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<VpkiDataDbContext>();
if (context != null)
{
//pcid 중복 체크, 있으면 에러
if (await context.TTbscsrs.AsNoTracking().FirstOrDefaultAsync(x => x.CPcid == tbscsr.CPcid) != null)
{
isExist = true;
}
//success
else
{
using (var transaction = await context.CreateTransactionAsync())
{
await context.AddAsync(tbscsr);
transactionResult = await context.CloseTransactionAsync(transaction);
}
}
}
}
//db error
if (transactionResult == false)
{
response.status = Status.error.ToString();
response.message = "ISO15118-02 Tbscsr database error";
}
if (isExist == true)
{
response.status = Status.error.ToString();
response.message = $"pcid aleady exist";
}
}
response.data = new Response_Tbscsr.Data
{
hashedtbscsr = $"{tbaCsrHashed}",
pcid = $"{pcid}",
evccid = $"{pcid}",
};
}
return response;
}
/// <summary>
/// certificate
/// </summary>
public async Task<Response_Certificate> RequestCertificate(Request_Certificate request)
{
Response_Certificate response = new Response_Certificate();
Request_CertificateOEM requestOem = new Request_CertificateOEM();
Response_CertificateOEM? responseOem = new Response_CertificateOEM();
bool isVerify = false;
bool transactionResult = true;
using (var scope = _scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<VpkiDataDbContext>();
if (context != null)
{
string cCertType = "prov_v1";
TTbscsr tbscsr = new TTbscsr();
foreach (var csr in await context.TTbscsrs.AsNoTracking().Where(x => x.CCertType == cCertType).OrderByDescending(x => x.CCuid).ToListAsync())
{
try
{
using (ECDsa ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256))
{
var publicKeyPem = $"{_certificateService.ConvertToPEM(Convert.FromBase64String(csr.CPublickey))}";
ecdsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicKeyPem), out _);
var hachedTbsCsr = Convert.FromBase64String(csr.CHashedTbscsr);
isVerify = ecdsa.VerifyHash(hachedTbsCsr, Convert.FromBase64String($"{request.csrsignature}"));
}
if (isVerify == true)
{
tbscsr = csr;
break;
}
}
catch (Exception e)
{
e.ToString();
}
}
if (isVerify == true)
{
var decode = _certificateService.DecodeDERSignature_02(Convert.FromBase64String($"{request.csrsignature}").ToList());
Log4net.WriteLine($"Decode Csrsignature: {Convert.ToBase64String(decode.ToArray())}", LogType.Debug);
CertificationRequestInfo certificationRequestInfo = CertificationRequestInfo.GetInstance(Convert.FromBase64String($"{tbscsr.COriginTbscsr}"));
CertificationRequest certificationRequest = new CertificationRequest(
certificationRequestInfo,
new AlgorithmIdentifier(X9ObjectIdentifiers.ECDsaWithSha256),
new DerBitString(decode.ToArray()));
Pkcs10CertificationRequest pKCS10CertificationRequest = new Pkcs10CertificationRequest(certificationRequest.GetEncoded());
byte[] completeCsr = pKCS10CertificationRequest.GetEncoded();
requestOem = new Request_CertificateOEM
{
csr = $"{Consts.BEGIN_CERTIFICATE_REQUEST}{Convert.ToBase64String(completeCsr)}{Consts.END_CERTIFICATE_REQUEST}",
unitCode = request.unitCode,
tierCode = request.tierCode,
vehicleCode = request.vehicleCode,
localCode = request.localCode,
brandCode = request.brandCode
};
string url = $"{URL_VPKI_BASE}{URL_V1_CERTIFICATE_ISSUE}";
url = url.Replace("{1}", "prov");
responseOem = await PostJsonAsync<Request_CertificateOEM, Response_CertificateOEM>($"{url}", requestOem);
response.status = responseOem?.status;
response.message = $"{responseOem?.resultMessage}";
//db처리
using (var transaction = await context.CreateTransactionAsync())
{
if (await context.TCertificates.AsNoTracking().FirstOrDefaultAsync(x => x.CCuid == tbscsr.CCuid) != null)
{
var certificate = new TCertificate
{
CCuid = tbscsr.CCuid,
CCsrsignature = $"{request.csrsignature}",
CCsr = $"{requestOem?.csr}",
CTierCode = $"{requestOem?.tierCode}",
CUnitCode = $"{requestOem?.unitCode}",
CVehicleCode = $"{requestOem?.vehicleCode}",
CLocalCode = $"{requestOem?.localCode}",
CBrandCode = $"{requestOem?.brandCode}",
CCert = $"{responseOem?.cert}",
CMessage = $"{responseOem?.resultMessage}",
CIssueCount = 1,
CDateTime = DateTime.Now
};
await context.AddAsync(certificate);
}
transactionResult = await context.CloseTransactionAsync(transaction);
}
//ocsp
var ocsp = await Ocsp(tbscsr.CCuid);
using (var transaction = await context.CreateTransactionAsync())
{
if (await context.TOcsps.AsNoTracking().FirstOrDefaultAsync(x => x.CCuid == tbscsr.CCuid) != null)
{
var certificate = new TOcsp
{
CCuid = tbscsr.CCuid,
CStatus = GetOcspStatus(tbscsr.CPcid, ocsp),
CVerify = GetOcspVerify(ocsp),
COcsp = $"{ocsp}",
CDateTime = DateTime.Now
};
await context.AddAsync(certificate);
}
transactionResult = await context.CloseTransactionAsync(transaction);
}
}
//verify failed
else
{
response.status = Status.error.ToString();
response.message += "verify fialed";
}
//db error
if (transactionResult == false)
{
response.status = Status.error.ToString();
response.message += "database error";
}
}
}
string cert = $"{responseOem?.cert}";
cert = cert.Replace($"{Consts.BEGIN_CERTIFICATE}","").Replace($"{Consts.END_CERTIFICATE}", "");
cert = cert.Replace($"\r","").Replace($"\n", "");
response.data = new Response_Certificate.Data
{
leafcertificate = $"{cert}",
subcacertificate = $"{GetSubCA(VpkiType.prov_v1)}"
};
return response;
}
/// <summary>
/// verifyresult
/// </summary>
public async Task<Response_Verifyresult> RequestVerifyresult(Request_Verifyresult request)
{
Response_Verifyresult response = new Response_Verifyresult();
bool transactionResult = true;
string serverResult = "success";
using (var scope = _scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<VpkiDataDbContext>();
if (context != null)
{
var tbscsr = await context.TTbscsrs.AsNoTracking().FirstOrDefaultAsync(x => x.CIftid == request.iftid && x.CPcid == request.pcid);
if (tbscsr != null)
{
using (var transaction = await context.CreateTransactionAsync())
{
var findVerifyResult = await context.TVerifyResults.AsNoTracking().FirstOrDefaultAsync(x => x.CCuid == tbscsr.CCuid);
if (findVerifyResult == null)
{
var verifyResult = new TVerifyResult
{
CCuid = tbscsr.CCuid,
CResult = $"{request.result}"
};
await context.AddAsync(verifyResult);
}
else
{
findVerifyResult.CResult = $"{request.result}";
context.Update(findVerifyResult);
}
transactionResult = await context.CloseTransactionAsync(transaction);
}
//db error
if (transactionResult == false)
{
response.status = Status.error.ToString();
response.message += "database error";
}
//server_result
var certificate = await context.TCertificates.AsNoTracking().FirstOrDefaultAsync(x => x.CCuid == tbscsr.CCuid);
if (certificate != null)
{
//인증서 없다
if (string.IsNullOrEmpty(certificate.CCert) == true)
{
serverResult = "empty";
}
//폐기된 인증서
else if (certificate.CIssueCount < 0)
{
serverResult = "revoked";
}
}
}
//일치하는 데이터 없으면
else
{
response.status = Status.error.ToString();
response.message = "Not matched ift and pcid";
serverResult = "error";
}
}
}
response.data = new Response_Verifyresult.Data
{
iftid = $"{request.iftid}",
pcid = $"{request.pcid}",
ift_result = $"{request.result}",
server_result=$"{serverResult}"
};
return response;
}
}
}

View File

@ -0,0 +1,364 @@
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Pkcs;
using System.Security.Cryptography;
using System.Text;
using VPKI.Library.Config;
using VPKI.Library.Enums;
using VPKI.Library.Packet;
using VPKI.Library.Services;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X9;
using DB.VPKI_DataDB;
using Microsoft.EntityFrameworkCore;
using SystemX.Core.DB;
namespace VPKI.Web.Api.Services
{
public class ISO15118_20Service : VpkiBaseService
{
private readonly IServiceScopeFactory _scopeFactory;
private readonly string URL_V2_CERTIFICATE_ISSUE = "api/v2/{1}/issue";
public ISO15118_20Service(ConfigService<WebApiConfig> configService, CertificateService certificateService, IServiceScopeFactory scopeFactory)
: base(configService, certificateService, scopeFactory)
{
_scopeFactory = scopeFactory;
}
/// <summary>
/// tbscsr
/// </summary>
public async Task<Response_Tbscsr> RequestTbscsr(Request_Tbscsr request, VpkiType vpkiType)
{
Response_Tbscsr response = new Response_Tbscsr();
if (request != null)
{
if (request.certType?.Contains(VpkiType.evcc_cert.ToString()) == true)
{
request.certType = VpkiType.vehicle_cert.ToString();
}
string pcid = _certificateService.CreatePCID(request, VpkiType.prov_v1);
string dn = $"cn={_certificateService.CreatePCID(request, VpkiType.prov_cert)},ou={request.certInfo.tierCode},ou={request.certInfo.unitCode},ou=ECC,o=HKMC,dc={request.certInfo.dc}";
if (request.certType == VpkiType.vehicle_cert.ToString() || request.certType == VpkiType.evcc_cert.ToString())
dn = $"cn={_certificateService.CreatePCID(request, VpkiType.vehicle_cert)},ou={request.certInfo.tierCode},ou={request.certInfo.unitCode},ou=ECC,o=HKMC,dc={request.certInfo.dc}";
string csrOrigin = _certificateService.CreateCsr512(dn, request.publickey);
string tbaCsrHashed = string.Empty;
//sha 512
using (SHA512 sha512 = SHA512.Create())
{
byte[] hashed = sha512.ComputeHash(Convert.FromBase64String(csrOrigin));
tbaCsrHashed = Convert.ToBase64String(hashed);
Log4net.WriteLine($"Hashed CSR (Base64): {tbaCsrHashed}", LogType.Debug);
}
//csr error
if (string.IsNullOrEmpty(csrOrigin) == true)
{
response.status = Status.error.ToString();
response.message = "create csr error";
}
//pcid error
else if (string.IsNullOrEmpty(pcid) == true)
{
response.status = Status.error.ToString();
response.message = "create pcid error";
}
//success
else
{
var tbscsr = new TTbscsr
{
CIftid = $"{request?.iftid}",
CMacaddr = $"{request?.cnInfo?.macaddr}",
CWmi = $"{request?.cnInfo?.wmi}",
CIdType = $"{request?.cnInfo?.idType}",
CSupplierId = $"{request?.cnInfo?.supplierId}",
CDc = $"{request?.certInfo?.dc}",
CTierCode = $"{request?.certInfo?.tierCode}",
CUnitCode = $"{request?.certInfo?.unitCode}",
CPublickey = $"{request?.publickey}",
CCertType = $"{request?.certType}",
COriginTbscsr = csrOrigin,
CHashedTbscsr = tbaCsrHashed,
CPcid = pcid,
CDn = $"{dn}",
CDateTime = DateTime.Now
};
//db context
bool transactionResult = true;
bool isExist = false;
using (var scope = _scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<VpkiDataDbContext>();
if (context != null)
{
//pcid 중복 체크, 있으면 업데이트
if (await context.TTbscsrs.AsNoTracking().FirstOrDefaultAsync(x => x.CPcid == tbscsr.CPcid) != null)
{
isExist = true;
}
//success
else
{
using (var transaction = await context.CreateTransactionAsync())
{
await context.AddAsync(tbscsr);
transactionResult = await context.CloseTransactionAsync(transaction);
}
}
}
//db error
if (transactionResult == false)
{
response.status = Status.error.ToString();
response.message = "ISO15118-20 Tbscsr database error";
}
if (isExist == true)
{
response.status = Status.error.ToString();
response.message = $"pcid aleady exist";
}
}
}
response.data = new Response_Tbscsr.Data
{
hashedtbscsr = $"{tbaCsrHashed}",
pcid = $"{pcid}",
evccid = $"{pcid}"
};
}
Log4net.WriteLine(response.ToJson(), response!.status!.Contains("error") ? LogType.Error : LogType.Info);
return response;
}
/// <summary>
/// certificate
/// </summary>
public async Task<Response_Certificate> RequestCertificate(Request_Certificate request, VpkiType vpkiType)
{
if (vpkiType == VpkiType.evcc_cert)
vpkiType = VpkiType.vehicle_cert;
Response_Certificate response = new Response_Certificate();
Request_CertificateOEM requestOem = new Request_CertificateOEM();
Response_CertificateOEM? responseOem = new Response_CertificateOEM();
bool isVerify = false;
bool transactionResult = true;
using (var scope = _scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<VpkiDataDbContext>();
if (context != null)
{
string cCertType = vpkiType.ToString();
TTbscsr tbscsr = new TTbscsr();
foreach (var csr in await context.TTbscsrs.AsNoTracking().Where(x => x.CCertType == cCertType).OrderByDescending(x => x.CCuid).ToListAsync())
{
try
{
using (ECDsa ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP521))
{
var publicKeyPem = $"{_certificateService.ConvertToPEM(Convert.FromBase64String(csr.CPublickey))}";
ecdsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicKeyPem), out _);
var hachedTbsCsr = Convert.FromBase64String(csr.CHashedTbscsr);
isVerify = ecdsa.VerifyHash(hachedTbsCsr, Convert.FromBase64String($"{request.csrsignature}"));
}
if (isVerify == true)
{
tbscsr = csr;
break;
}
}
catch (Exception e)
{
e.ToString();
}
}
if (isVerify == true)
{
var decode = _certificateService.DecodeDERSignature_20(Convert.FromBase64String($"{request.csrsignature}").ToList());
Log4net.WriteLine($"Decode Csrsignature: {Convert.ToBase64String(decode.ToArray())}", LogType.Debug);
CertificationRequestInfo certificationRequestInfo = CertificationRequestInfo.GetInstance(Convert.FromBase64String($"{tbscsr.COriginTbscsr}"));
CertificationRequest certificationRequest = new CertificationRequest(
certificationRequestInfo,
new AlgorithmIdentifier(X9ObjectIdentifiers.ECDsaWithSha512),
new DerBitString(decode.ToArray()));
Pkcs10CertificationRequest pKCS10CertificationRequest = new Pkcs10CertificationRequest(certificationRequest.GetEncoded());
byte[] completeCsr = pKCS10CertificationRequest.GetEncoded();//csr.CreateCertificateRequest();
requestOem = new Request_CertificateOEM
{
csr = $"{Consts.BEGIN_CERTIFICATE_REQUEST}{Convert.ToBase64String(completeCsr)}{Consts.END_CERTIFICATE_REQUEST}",
unitCode = request.unitCode,
tierCode = request.tierCode,
vehicleCode = request.vehicleCode,
localCode = request.localCode,
brandCode = request.brandCode
};
string url = $"{URL_VPKI_BASE}{URL_V2_CERTIFICATE_ISSUE}";
url = url.Replace("{1}", $"{vpkiType.ToString().Split("_").FirstOrDefault()}");
responseOem = await PostJsonAsync<Request_CertificateOEM, Response_CertificateOEM>(url, requestOem);
response.status = responseOem?.status;
response.message = $"{responseOem?.resultMessage}";
using (var transaction = await context.CreateTransactionAsync())
{
if (await context.TCertificates.AsNoTracking().FirstOrDefaultAsync(x => x.CCuid == tbscsr.CCuid) != null)
{
var certificate = new TCertificate
{
CCuid = tbscsr.CCuid,
CCsrsignature = $"{request.csrsignature}",
CCsr = requestOem.csr,
CTierCode = $"{requestOem?.tierCode}",
CUnitCode = $"{requestOem?.unitCode}",
CVehicleCode = $"{requestOem?.vehicleCode}",
CLocalCode = $"{requestOem?.localCode}",
CBrandCode = $"{requestOem?.brandCode}",
CCert = $"{responseOem?.cert}",
CMessage = $"{responseOem?.resultMessage}",
CIssueCount = 1,
CDateTime = DateTime.Now
};
await context.AddAsync(certificate);
}
transactionResult = await context.CloseTransactionAsync(transaction);
}
}
//verify failed
else
{
response.status = Status.error.ToString();
response.message += "verify fialed";
}
//db error
if (transactionResult == false)
{
response.status = Status.error.ToString();
response.message += "database error";
}
}
}
string cert = $"{responseOem?.cert}";
cert = cert.Replace($"{Consts.BEGIN_CERTIFICATE}", "").Replace($"{Consts.END_CERTIFICATE}", "");
cert = cert.Replace($"\r", "").Replace($"\n", "");
response.data = new Response_Certificate.Data
{
leafcertificate = $"{cert}",
subcacertificate = $"{GetSubCA(vpkiType)}"
};
return response;
}
/// <summary>
/// verifytresult
/// </summary>
public async Task<Response_Verifyresult> RequestVerifyresult(Request_Verifyresult request)
{
Response_Verifyresult response = new Response_Verifyresult();
bool transactionResult = true;
string serverResult = "success";
using (var scope = _scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<VpkiDataDbContext>();
if (context != null)
{
var tbscsr = await context.TTbscsrs.AsNoTracking().FirstOrDefaultAsync(x => x.CIftid == request.iftid && x.CPcid == request.pcid);
if (tbscsr != null)
{
using (var transaction = await context.CreateTransactionAsync())
{
var findVerifyResult = await context.TVerifyResults.AsNoTracking().FirstOrDefaultAsync(x => x.CCuid == tbscsr.CCuid);
if (findVerifyResult == null)
{
var verifyResult = new TVerifyResult
{
CCuid = tbscsr.CCuid,
CResult = $"{request.result}"
};
await context.AddAsync(verifyResult);
}
else
{
findVerifyResult.CResult = $"{request.result}";
context.Update(findVerifyResult);
}
transactionResult = await context.CloseTransactionAsync(transaction);
}
//db error
if (transactionResult == false)
{
response.status = Status.error.ToString();
response.message += "database error";
}
//server_result
var certificate = context.TCertificates.ToList().Find(x => x.CCuid == tbscsr.CCuid);
if (certificate != null)
{
//인증서 없다
if (string.IsNullOrEmpty(certificate.CCert) == true)
{
serverResult = "empty";
}
//폐기된 인증서
else if (certificate.CIssueCount < 0)
{
serverResult = "revoked";
}
}
}
//일치하는 데이터 없으면
else
{
response.status = Status.error.ToString();
response.message = "Not matched ift and pcid";
serverResult = "error";
}
}
}
response.data = new Response_Verifyresult.Data
{
iftid = $"{request.iftid}",
pcid = $"{request.pcid}",
ift_result = $"{request.result}",
server_result = $"{serverResult}"
};
return response;
}
}
}

View File

@ -0,0 +1,95 @@
using Azure;
using DB.VPKI_AccountDB;
using DB.VPKI_DataDB;
using DocumentFormat.OpenXml.Spreadsheet;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using Org.BouncyCastle.Pqc.Crypto.Lms;
using System;
using System.Data;
using SystemX.Core.DB;
using VPKI.Library.Config;
using VPKI.Library.Enums;
using VPKI.Library.Model;
using VPKI.Library.Services;
namespace VPKI.Web.Api.Services
{
public class UserService
{
private readonly IServiceProvider _serviceProvider;
private readonly IServiceScopeFactory _scopeFactory;
private readonly ConfigService<WebApiConfig>? _configService;
private readonly Library.Config.Model.DataBase? _accountDB;
public UserService(IServiceProvider serviceProvider, IServiceScopeFactory scopeFactory,
ConfigService<WebApiConfig> configSerice)
{
_serviceProvider = serviceProvider;
_scopeFactory = scopeFactory;
_configService = configSerice;
_accountDB = _configService?.GetConfig()?.DataBase?.Find(x => x.DBContext == "VpkiAccountDbContext");
}
public async Task<IEnumerable<UserModel>> GetAllUsers()
{
List<UserModel> Users = new List<UserModel>();
//context
using (var scope = _scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<VpkiAccountDbContext>();
if (context is not null)
{
using (var transaction = await context.CreateTransactionAsync(IsolationLevel.ReadUncommitted))
{
Users = await context.TUsers.AsNoTracking().Join(context.TRoles.AsNoTracking(), x => x.CAuid, y => y.CAuid,
(x, y) => new UserModel { TUser = x, TRole = y }).ToListAsync();
await context.CloseTransactionAsync(transaction);
}
}
}
return Users;
}
public async Task<UserModel> UpdateUser(UserModel user)
{
bool transactionResult = false;
//context
if(user.TUser != null && user.TRole != null)
{
using (var scope = _scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<VpkiAccountDbContext>();
if (context is not null)
{
using (var transaction = await context.CreateTransactionAsync())
{
context.Update(user.TUser);
context.Update(user.TRole);
transactionResult = await context.CloseTransactionAsync(transaction);
}
}
//db error
if (transactionResult == false)
{
Log4net.WriteLine($"Transaction Error::", LogType.Error);
}
else
{
Log4net.WriteLine($"Transaction Success", LogType.DB);
}
}
}
return user;
}
}
}

View File

@ -0,0 +1,298 @@
using Azure;
using DB.VPKI_DataDB;
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
using Microsoft.Extensions.FileSystemGlobbing.Internal;
using Microsoft.Identity.Client;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Utilities.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using SystemX.Core.DB;
using VPKI.Library.Config;
using VPKI.Library.Config.Model;
using VPKI.Library.Enums;
using VPKI.Library.Model;
using VPKI.Library.Packet;
using VPKI.Library.Services;
namespace VPKI.Web.Api.Services
{
public class VpkiBaseService : HttpService
{
private readonly IServiceScopeFactory _scopeFactory;
protected string URL_VPKI_BASE = string.Empty;
protected readonly ConfigService<WebApiConfig> _configService;
protected readonly CertificateService _certificateService;
public VpkiBaseService(ConfigService<WebApiConfig> configService, CertificateService certificateService, IServiceScopeFactory scopeFactory)
: base()
{
_configService = configService;
_certificateService = certificateService;
_scopeFactory = scopeFactory;
var api = _configService!.GetConfig()?.Api?.Find(x => x.ApiName?.ToLower() == Consts.VPKI_API_VPKIRA.ToLower());
URL_VPKI_BASE = $"{api?.Address}";
}
#region
public async Task<List<CertificateHistoryModel>> History(DateOnly startDate, DateOnly endDate)
{
List<CertificateHistoryModel> response = new List<CertificateHistoryModel>();
using (var scope = _scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<VpkiDataDbContext>();
if (context != null)
{
using (var transaction = await context.CreateTransactionAsync(System.Data.IsolationLevel.ReadUncommitted))
{
var getList = await context.TTbscsrs.AsNoTracking().Where(x => startDate <= DateOnly.FromDateTime(x.CDateTime.Date)
&& DateOnly.FromDateTime(x.CDateTime.Date) <= endDate).ToListAsync();
response.AddRange(getList.Select(x => new CertificateHistoryModel { TTbscsr = x, TCertificate = null }));
foreach (var r in response)
{
var certificate = await context.TCertificates.AsNoTracking().FirstOrDefaultAsync(x => x.CCuid == r!.TTbscsr!.CCuid);
if (certificate != null)
r.TCertificate = certificate;
var verifyResult = await context.TVerifyResults.AsNoTracking().FirstOrDefaultAsync(x => x.CCuid == r!.TTbscsr!.CCuid);
if (verifyResult != null)
r.TVerifyResult = verifyResult;
var ocspResult = await context.TOcsps.AsNoTracking().FirstOrDefaultAsync(x => x.CCuid == r!.TTbscsr!.CCuid);
if (ocspResult != null)
r.TOcsp = ocspResult;
}
await context.CloseTransactionAsync(transaction);
}
}
}
return response;
}
public async Task<CertificateHistoryModel> History(long ccuid)
{
CertificateHistoryModel response = new CertificateHistoryModel();
using (var scope = _scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<VpkiDataDbContext>();
if (context != null)
{
using (var transaction = await context.CreateTransactionAsync(System.Data.IsolationLevel.ReadUncommitted))
{
var searchData = await context.TTbscsrs.AsNoTracking().FirstOrDefaultAsync(x => x.CCuid == ccuid);
if (searchData != null)
{
response.TTbscsr = searchData;
var certificate = await context.TCertificates.AsNoTracking().FirstOrDefaultAsync(x => x.CCuid == searchData!.CCuid);
if (certificate != null)
response.TCertificate = certificate;
var verifyResult = await context.TVerifyResults.AsNoTracking().FirstOrDefaultAsync(x => x.CCuid == searchData!.CCuid);
if (verifyResult != null)
response.TVerifyResult = verifyResult;
var ocspResult = await context.TOcsps.AsNoTracking().FirstOrDefaultAsync(x => x.CCuid == searchData!.CCuid);
if (ocspResult != null)
response.TOcsp = ocspResult;
}
await context.CloseTransactionAsync(transaction);
}
}
}
return response;
}
//SubCA
public virtual string GetSubCA(VpkiType vpkiType, bool isEncoding = true)
{
string result = string.Empty;
var openSslConfig = $"{_configService?.GetConfig()?.Openssl?.SubCAPath}";
string subCaName = FindSubCaName(vpkiType);
var subCA = File.ReadAllText(@$"{openSslConfig}{subCaName}");
if (isEncoding == false)
{
result = subCA;
}
else
{
subCA = subCA.Replace($"{Consts.BEGIN_CERTIFICATE}", "").Replace($"{Consts.END_CERTIFICATE}", "");
subCA = subCA.Replace("\r", "").Replace("\n", "");
var subCaAscii = Encoding.ASCII.GetBytes(subCA).ToList();
if (subCaAscii != null)
{
result = Convert.ToBase64String(subCaAscii.ToArray());
}
}
return subCA;
}
public async Task<string> GetCertificateInfo(long ccuid)
{
string result = string.Empty;
//인증서 Temp에 저장
string tempPath = @$"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}/temp/";
var history = await History(ccuid);
if (Directory.Exists(tempPath) == false)
Directory.CreateDirectory(tempPath);
string pcid = $"{history?.TTbscsr?.CPcid}";
File.WriteAllText($"{tempPath}{pcid}.pem", history?.TCertificate?.CCert);
// 프로세스 설정
string command = $"x509 -in {tempPath}{pcid}.pem -text";
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = @$"{_configService?.GetConfig()?.Openssl?.Path}", // cmd.exe로 명령어 실행
Arguments = $"{command}", // /c는 명령어 실행 후 종료하는 옵션
RedirectStandardOutput = true, // 표준 출력 리디렉션
RedirectStandardError = true, // 표준 오류 리디렉션
UseShellExecute = false, // 셸을 사용하지 않도록 설정
CreateNoWindow = true // 새 창을 열지 않도록 설정,
};
// 프로세스 실행
using (Process? process = Process.Start(startInfo))
{
if (process != null)
{
// 출력 결과를 읽기
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
// 출력 결과 또는 오류 표시
if (!string.IsNullOrEmpty(output))
result = output;
if (!string.IsNullOrEmpty(error))
result += output;
}
}
return result;
}
public async Task<string> Ocsp(long ccuid)
{
string result = string.Empty;
//인증서 Temp에 저장
string tempPath = @$"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}/temp/";
string subCaPath = @$"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}/../SubCA/";
var history = await History(ccuid);
if (Directory.Exists(tempPath) == false)
Directory.CreateDirectory(tempPath);
string pcid = $"{history?.TTbscsr?.CPcid}";
File.WriteAllText($"{tempPath}{pcid}.pem", history?.TCertificate?.CCert);
string certificateInfo = await GetCertificateInfo(ccuid);
string url = GetOcspUrl(certificateInfo);
var vpkiType = (VpkiType)Enum.Parse(typeof(VpkiType), $"{history?.TTbscsr?.CCertType}");
string subCaFile = string.Empty;
var openSslConfig = _configService?.GetConfig()?.Openssl?.SubCA;
var prop = openSslConfig?.GetType()?.GetProperty($"{vpkiType}");
subCaFile = $"{prop?.GetValue(openSslConfig)?.ToString()}";
// 프로세스 설정
string command = $"ocsp -issuer {subCaPath}{subCaFile} -CAfile {subCaPath}hkmcrootca.pem -cert {tempPath}{pcid}.pem -url {url} -text -timeout 3";
Log4net.WriteLine($"{command}", LogType.Info);
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = @$"{_configService?.GetConfig()?.Openssl?.Path}", // cmd.exe로 명령어 실행
Arguments = $"{command}", // /c는 명령어 실행 후 종료하는 옵션
RedirectStandardOutput = true, // 표준 출력 리디렉션
RedirectStandardError = true, // 표준 오류 리디렉션
UseShellExecute = false, // 셸을 사용하지 않도록 설정
CreateNoWindow = true // 새 창을 열지 않도록 설정,
};
// 프로세스 실행
using (Process? process = Process.Start(startInfo))
{
if (process != null)
{
// 출력 결과를 읽기
string output = process.StandardOutput.ReadToEnd();
string response = process.StandardError.ReadToEnd();
// 출력 결과 또는 오류 표시
if (!string.IsNullOrEmpty(output))
result = output;
if (!string.IsNullOrEmpty(response))
result += response;
}
}
result = result.Replace($"{tempPath}","");
GetOcspStatus(pcid, result);
GetOcspVerify(result);
return result;
}
protected virtual string GetOcspStatus(string pcid, string ocsp)
{
var ocspLines = ocsp.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None).ToList();
var statusLine = ocspLines.Find(x => x.Contains($"{pcid}.pem"));
string? status = statusLine?.Split(":")?.Last();
return $"{status}";
}
protected virtual string GetOcspVerify(string ocsp)
{
var ocspLines = ocsp.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None).ToList();
var verifyLine = ocspLines.Find(x => x.ToLower().Contains($"response verify"));
string? verify = verifyLine?.Split(" ")?.Last();
return $"{verify}";
}
protected virtual string GetOcspUrl(string certificateInfo)
{
string finder = "OCSP - URI:";
var certificateLines = certificateInfo.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None).ToList();
var ocspUrl = certificateLines.Find(x => x.Contains($"{finder}"));
string? url = ocspUrl?.Replace(finder, "");
return $"{url}";
}
protected virtual string FindSubCaName(VpkiType vpkiType)
{
var openSslConfig = _configService?.GetConfig()?.Openssl?.SubCA;
var prop = openSslConfig?.GetType()?.GetProperty($"{vpkiType}");
string subCaFile = $"{prop?.GetValue(openSslConfig)?.ToString()}";
return subCaFile;
}
#endregion
}
}

View File

@ -0,0 +1,62 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>a5eca56b-f522-474b-8bf2-451ed90325f3</UserSecretsId>
<DockerDefaultTargetOS>Windows</DockerDefaultTargetOS>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<Compile Remove="temp\**" />
<Content Remove="temp\**" />
<EmbeddedResource Remove="temp\**" />
<None Remove="temp\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.5.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.15">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.15">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.2.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.2.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VPKI.Library.DB\VPKI.Library.DB.csproj" />
<ProjectReference Include="..\VPKI.Library\VPKI.Library.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="SystemX.Core">
<HintPath>..\..\..\DLL\SystemX.Core.dll</HintPath>
</Reference>
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="xcopy /E /I /Y &quot;$(ProjectDir)/../SubCA&quot; &quot;$(OutDir)/../SubCA&quot;&#xD;&#xA;xcopy /E /I /Y &quot;$(ProjectDir)/../Config&quot; &quot;$(OutDir)/../Config&quot;" />
</Target>
</Project>

View File

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "8.0.10",
"commands": [
"dotnet-ef"
]
}
}
}

View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>VPKI Operation</title>
<base href="/" />
<link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
<link rel="stylesheet" href="app.css" />
<link rel="stylesheet" href="VPKI.Web.Client.styles.css" />
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet />
<RadzenTheme Theme="software-dark" @rendermode="new InteractiveServerRenderMode(prerender: false)" />
</head>
<body>
<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />
<script src="_framework/blazor.web.js"></script>
<script src="_content/Radzen.Blazor/Radzen.Blazor.js?v=@(typeof(Radzen.Colors).Assembly.GetName().Version)"></script>
<script src="js/scripts.js"></script>
</body>
</html>

View File

@ -0,0 +1,22 @@
@page "/loading/{Message?}"
<div style="text-align: center;">
<div class="lds-ring">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div class="message">
@Message
</div>
</div>
@code {
[Parameter]
public string? Message { get; set; }
protected override void OnParametersSet()
{
}
}

View File

@ -0,0 +1,57 @@
.message {
/* change color here */
color: #f8b26a;
text-align: center;
}
.lds-ring {
/* change color here */
color: #f8b26a;
}
.lds-ring,
.lds-ring div {
box-sizing: border-box;
}
.lds-ring {
display: inline-block;
position: relative;
width: 80px;
height: 80px;
}
.lds-ring div {
box-sizing: border-box;
display: block;
position: absolute;
width: 64px;
height: 64px;
margin: 8px;
border: 8px solid currentColor;
border-radius: 50%;
animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
border-color: currentColor transparent transparent transparent;
}
.lds-ring div:nth-child(1) {
animation-delay: -0.45s;
}
.lds-ring div:nth-child(2) {
animation-delay: -0.3s;
}
.lds-ring div:nth-child(3) {
animation-delay: -0.15s;
}
@keyframes lds-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

View File

@ -0,0 +1,86 @@
@using System.Security.Claims
@using VPKI.Library.Model.Auth
@inject NavigationManager NavigationManager
@inject AuthenticationStateProvider AuthStateProvider
@inject ApiService ApiService
<div class="d-flex justify-content-between w-100 px-4">
<div>
<RadzenLabel Style="font-size:20px;" Text="VPKI Operation" />
</div>
<div>
<AuthorizeView>
<Authorized>
<RadzenLabel Style="font-size: 15px; margin-right: 10px;" Text="@($"Session Time {TimeSpan.FromMilliseconds(LostLoginTime * 1000)}")" />
<RadzenLabel Style="font-size: 15px;" Text="@($"{@context.User.Identity?.Name}")"/>
<RadzenLabel Style="font-size: 15px; margin-right: 8px;" Text="@($"({Role})")" />
<RadzenLabel Style="cursor:pointer; font-size: 15px;" @onclick="@OnClickLogout" Text="Logout"></RadzenLabel>
</Authorized>
<NotAuthorized>
<RadzenLabel Style="cursor:pointer; font-size: 15px;" @onclick="@OnClickLogin" Text="Login"></RadzenLabel>
</NotAuthorized>
</AuthorizeView>
</div>
</div>
@code{
private string Role = string.Empty;
private long LostLoginTime = 0;
protected override void OnInitialized()
{
Task.Run(async() =>
{
var state = await((ExtendAuthenticationStatProvicer)AuthStateProvider).GetAuthenticationStateAsync();
if (state?.User?.Identity?.IsAuthenticated == true)
{
Role = $"{state?.User?.Claims.Where(x => x.Type == ClaimTypes.Role)?.First().Value}";
await InvokeAsync(StateHasChanged);
}
});
//자동 로그아웃
Task.Run(async () =>
{
while (true)
{
var state = await ((ExtendAuthenticationStatProvicer)AuthStateProvider).GetAuthenticationStateAsync();
if (state?.User?.Identity?.IsAuthenticated == true)
{
//expired
var expired = await ((ExtendAuthenticationStatProvicer)AuthStateProvider).GetExpiredTimeAsync();
LostLoginTime = expired - DateTime.Now.ToUnixTime();
if (LostLoginTime < 0)
{
await ((ExtendAuthenticationStatProvicer)AuthStateProvider).MakeUserLogout();
NavigationManager.NavigateTo("/login");
}
}
await Task.Delay(1000);
await InvokeAsync(StateHasChanged);
}
});
}
private async Task OnClickLogout()
{
var state = await ((ExtendAuthenticationStatProvicer)AuthStateProvider).GetAuthenticationStateAsync();
LogoutModel logoutModel = new LogoutModel
{
UserID = state?.User?.Identity?.Name
};
await((ExtendAuthenticationStatProvicer)AuthStateProvider).MakeUserLogout();
var res = await ApiService.PostJsonAsyncBearer<LogoutModel, LogoutResponseModel>("/api/auth/logout", logoutModel);
NavigationManager.NavigateTo("/login");
}
private async Task OnClickLogin()
{
await Task.Delay(1);
//await ((ExtendAuthenticationStatProvicer)AuthStateProvider).MakeUserLogout();
NavigationManager.NavigateTo("/login");
}
}

View File

@ -0,0 +1,36 @@
@using Microsoft.AspNetCore.Components.Authorization
@inherits LayoutComponentBase
@rendermode InteractiveServer
<RadzenLayout>
<RadzenHeader>
<RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" Gap="0">
<RadzenSidebarToggle Click="@(() => sidebar1Expanded = !sidebar1Expanded)" />
<HeaderBar />
</RadzenStack>
</RadzenHeader>
<RadzenSidebar @bind-Expanded="@sidebar1Expanded">
<RadzenPanelMenu>
<RadzenPanelMenuItem Text="Home" Icon="home" Path="/" />
<RadzenPanelMenuItem Text="VPKI.API" Icon="compare_arrows" Path="/vpki/api" />
<RadzenPanelMenuItem Text="VPKI.Manager" Icon="description" Path="/vpki/manager" />
<RadzenPanelMenuItem Text="Users" Icon="account_box" Path="/user" />
</RadzenPanelMenu>
</RadzenSidebar>
<RadzenBody>
<div class="rz-p-4">
@Body
</div>
</RadzenBody>
<RadzenFooter>
<div>
<RadzenLabel Text="@("Copyright © 2024, SystemX Inc. All rights reserved.")"></RadzenLabel>
</div>
</RadzenFooter>
</RadzenLayout>
<RadzenComponents @rendermode="InteractiveServer" />
@code {
bool sidebar1Expanded = true;
}

View File

@ -0,0 +1,96 @@
.page {
position: relative;
display: flex;
flex-direction: column;
}
main {
flex: 1;
}
.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}
.top-row {
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
height: 3.5rem;
display: flex;
align-items: center;
}
.top-row ::deep a, .top-row ::deep .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
text-decoration: none;
}
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
text-decoration: underline;
}
.top-row ::deep a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 640.98px) {
.top-row {
justify-content: space-between;
}
.top-row ::deep a, .top-row ::deep .btn-link {
margin-left: 0;
}
}
@media (min-width: 641px) {
.page {
flex-direction: row;
}
.sidebar {
width: 250px;
height: 100vh;
position: sticky;
top: 0;
}
.top-row {
position: sticky;
top: 0;
z-index: 1;
}
.top-row.auth ::deep a:first-child {
flex: 1;
text-align: right;
width: 0;
}
.top-row, article {
padding-left: 2rem !important;
padding-right: 1.5rem !important;
}
}
#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}

View File

@ -0,0 +1,213 @@
@using System.Security.Cryptography
@using System.Text
@using Org.BouncyCastle.Crypto
@using Org.BouncyCastle.Crypto.Generators
@using Org.BouncyCastle.Crypto.Parameters
@using Org.BouncyCastle.OpenSsl
@using Org.BouncyCastle.Security
@using VPKI.Library.Packet
@inject CertificateService CertificateService
@inject ApiService ApiService
@inject VpkiDialogService DialogService
<RadzenFieldset Text="Certificate">
<div style="display:flex">
<!--generator-->
<RadzenFieldset Style="width: 100%; margin-right: 20px;" Text="Certificate Generator">
<div style="margin-bottom: 10px;">
<RadzenButton Style="width:100%;" Text="@($"Signed Hash ({StrISO})")" Click="OnClickSignedHash"></RadzenButton>
<div style="margin-top: 10px;">
<RadzenTextArea Style="width:100%; height: 130px;" @bind-Value=@StringCsrHashed>
</RadzenTextArea>
</div>
</div>
<div style="margin-bottom: 10px;">
<RadzenLabel Style="width: 130px;" Text="Device ID"></RadzenLabel>
<RadzenTextBox Style="margin-right: 10px;" @bind-Value="@CertificateContainer.RequestCertificate.iftid"></RadzenTextBox>
</div>
<div style="margin-bottom: 10px;">
<RadzenLabel Style="width: 130px;" Text="Tier Code"></RadzenLabel>
<RadzenTextBox Style="margin-right: 10px;" @bind-Value="@CertificateContainer.RequestCertificate.tierCode"></RadzenTextBox>
</div>
<div style="margin-bottom: 10px;">
<RadzenLabel Style="width: 130px;" Text="Unit Code"></RadzenLabel>
<RadzenTextBox Style="margin-right: 10px;" @bind-Value="@CertificateContainer.RequestCertificate.unitCode"></RadzenTextBox>
</div>
<div style="margin-bottom: 10px;">
<RadzenLabel Style="width: 130px;" Text="Vehicle Code"></RadzenLabel>
<RadzenTextBox Style="margin-right: 10px;" @bind-Value="@CertificateContainer.RequestCertificate.vehicleCode"></RadzenTextBox>
</div>
<div style="margin-bottom: 10px;">
<RadzenLabel Style="width: 130px;" Text="Local Code"></RadzenLabel>
<RadzenTextBox Style="margin-right: 10px;" @bind-Value="@CertificateContainer.RequestCertificate.localCode"></RadzenTextBox>
</div>
<div style="margin-bottom: 10px;">
<RadzenLabel Style="width: 130px;" Text="Brand Code"></RadzenLabel>
<RadzenTextBox Style="margin-right: 10px;" @bind-Value="@CertificateContainer.RequestCertificate.brandCode"></RadzenTextBox>
</div>
</RadzenFieldset>
<!--TX RX-->
<RadzenFieldset Style="width:100%;" Text="Call API">
<div>
<RadzenButton Style="width:100%; margin-bottom: 0.5rem;" Text="@($"Generate Request {StrISO}")" Click="@OnClickGenerateCertificate"></RadzenButton>
<RadzenButton Style="width:100%;" Text="@($"Send Request")" Click="@OnClickSendCertificate"></RadzenButton>
</div>
<div>
<div>
<RadzenLabel Text="Request"></RadzenLabel>
</div>
<div>
<RadzenTextArea Style="width:100%; height: 180px;" @bind-Value=@CertificateContainer.StrRequest>
</RadzenTextArea>
</div>
<div>
<RadzenLabel Text="Response"></RadzenLabel>
</div>
<div>
<RadzenTextArea Style="width:100%; height: 180px;" @bind-Value="@CertificateContainer.StrResponse">
</RadzenTextArea>
</div>
</div>
</RadzenFieldset>
</div>
</RadzenFieldset>
@code {
[Parameter, EditorRequired]
public CertificateContainer CertificateContainer { get; set; } = new CertificateContainer();
[Parameter]
public EventCallback<CertificateContainer> CertificateContainerChanged { get; set; }
[Parameter, EditorRequired]
public string ServerAddress { get; set; } = string.Empty;
[Parameter]
public TbscsrContainer TbscsrContainer { get; set; } = new TbscsrContainer();
//ISO
private string StrISO = string.Empty;
private string StringCsrHashed = string.Empty;
protected override void OnInitialized()
{
StrISO = $"{TbscsrContainer.ISOType.ToString().Replace("_", "-")}";
}
protected override void OnParametersSet()
{
string ecdsa = "";
if (TbscsrContainer.VpkiType == VpkiType.prov_v1)
{
ecdsa = ECDSAType.SHA256WITHECDSA.ToString();
}
else if (TbscsrContainer.VpkiType == VpkiType.prov_cert || TbscsrContainer.VpkiType == VpkiType.vehicle_cert)
{
ecdsa = ECDSAType.NONEWITHECDSA.ToString();
}
CreateCsrHash(ecdsa);
}
private void OnClickSignedHash()
{
SignedHash();
}
private void SignedHash()
{
if (TbscsrContainer.KeyPair != null && TbscsrContainer.ResponseTbscsr.data != null)
{
var publicKey = (ECPublicKeyParameters)TbscsrContainer.KeyPair.Public;
string ecdsa = "";
if (TbscsrContainer.VpkiType == VpkiType.prov_v1)
{
ecdsa = ECDSAType.SHA256WITHECDSA.ToString();
var pKey = TbscsrContainer.PublicKey;
CertificateContainer.CsrHashed = CertificateService.SignHashed02(TbscsrContainer.ResponseTbscsr.data.hashedtbscsr, TbscsrContainer.KeyPair);
}
else if (TbscsrContainer.VpkiType == VpkiType.prov_cert || TbscsrContainer.VpkiType == VpkiType.vehicle_cert)
{
ecdsa = ECDSAType.NONEWITHECDSA.ToString();
CertificateContainer.CsrHashed = CertificateService.SignHashed20(TbscsrContainer.ResponseTbscsr.data.hashedtbscsr, TbscsrContainer.KeyPair);
}
CreateCsrHash(ecdsa);
}
else
{
StringCsrHashed += "Request tbscsr first";
}
}
private void CreateCsrHash(string ecdsa)
{
if(string.IsNullOrEmpty(CertificateContainer.CsrHashed.SignedCsr) == false)
{
StringCsrHashed = $"{StrISO} Signed {ecdsa}:{Environment.NewLine}";
StringCsrHashed += $"{CertificateContainer.CsrHashed.SignedCsr}{Environment.NewLine}";
StringCsrHashed += $"{Environment.NewLine}";
StringCsrHashed += $"{StrISO} Signed {ecdsa}(DER ENCODED):{Environment.NewLine}";
StringCsrHashed += $"{CertificateContainer.CsrHashed.EncodedSignedCsr}{Environment.NewLine}";
StringCsrHashed += $"{Environment.NewLine}";
StringCsrHashed += $"Verify:{CertificateContainer.CsrHashed.Verify}{Environment.NewLine}";
}
}
private void OnClickGenerateCertificate()
{
CertificateContainer.RequestCertificate.csrsignature = CertificateContainer.CsrHashed.EncodedSignedCsr;
if (TbscsrContainer.VpkiType == VpkiType.prov_cert || TbscsrContainer.VpkiType == VpkiType.vehicle_cert)
{
CertificateContainer.RequestCertificate.csrsignature = CertificateContainer.CsrHashed.EncodedSignedCsr;
}
CertificateContainer.StrRequest = $"{CertificateContainer.RequestCertificate.ToJson()}";
}
private async Task OnClickSendCertificate()
{
DialogService.OpenIndicator();
try
{
var request = JsonConvert.DeserializeObject<Request_Certificate>(CertificateContainer.StrRequest);
if (request != null)
{
string url = $"api/v1/certificate";
if (TbscsrContainer.VpkiType == VpkiType.prov_cert)
{
url = $"api/v2/prov/certificate";
}
else if (TbscsrContainer.VpkiType == VpkiType.vehicle_cert)
{
url = $"api/v2/vehicle/certificate";
}
var Response = await ApiService.PostJsonAsync<Request_Certificate, Response_Certificate>($"https://{ServerAddress}/{url}", request);
if (Response != null)
{
CertificateContainer.ResponseCertificate = Response;
CertificateContainer.StrResponse = $"{Response.ToJson()}";
}
}
else
{
TbscsrContainer.StrResponse = "Request Context Error";
}
}
catch (Exception ex)
{
Log4net.WriteLine(ex);
TbscsrContainer.StrResponse = $"Request Context Error{Environment.NewLine}{ex.Message}";
}
DialogService.CloseIndicator();
}
}

View File

@ -0,0 +1,218 @@
@using Org.BouncyCastle.Crypto
@using Org.BouncyCastle.Crypto.Parameters
@using VPKI.Library.Packet
@inject CertificateService CertificateService
@inject ApiService ApiService
@inject VpkiDialogService DialogService
<RadzenFieldset Text="TBSCSR">
<div style="display:flex">
<!--csr generate-->
<RadzenFieldset Style="width:100%; margin-right: 20px;" Text="Key Generate">
@if (TbscsrContainer?.RequestTbscsr != null)
{
<div style="margin-bottom: 10px;">
<RadzenLabel Style="width: 130px;" Text="IFT ID"></RadzenLabel>
<RadzenTextBox Style="margin-right: 10px;" @bind-Value="@TbscsrContainer.RequestTbscsr.iftid"></RadzenTextBox>
</div>
<div style="margin-bottom: 10px;">
<RadzenLabel Style="width: 130px;" Text="Priefix Value"></RadzenLabel>
<RadzenTextBox Style="margin-right: 10px;" @bind-Value="@TbscsrContainer.RequestTbscsr.cnInfo.idType"></RadzenTextBox>
<RadzenTextBox Style="margin-right: 10px;" @bind-Value="@TbscsrContainer.RequestTbscsr.cnInfo.supplierId"></RadzenTextBox>
</div>
<div style="margin-bottom: 10px;">
<RadzenLabel Style="width: 130px;" Text="MAC Address"></RadzenLabel>
<RadzenTextBox Style="margin-right: 10px;" @bind-Value="@TbscsrContainer.RequestTbscsr.cnInfo.macaddr"></RadzenTextBox>
<RadzenButton Text="Random" Click="@OnClickRandomMacaddresss"></RadzenButton>
</div>
<div style="margin-bottom: 10px;">
<RadzenLabel Style="width: 130px;" Text="WMI"></RadzenLabel>
<RadzenTextBox Style="margin-right: 10px;" @bind-Value="@TbscsrContainer.RequestTbscsr.cnInfo.wmi"></RadzenTextBox>
</div>
<div style="margin-bottom: 10px;">
<RadzenLabel Style="width: 130px;" Text="Domain Component"></RadzenLabel>
<RadzenTextBox Style="margin-right: 10px;" @bind-Value="@TbscsrContainer.RequestTbscsr.certInfo.dc"></RadzenTextBox>
</div>
<div style="margin-bottom: 10px;">
<RadzenLabel Style="width: 130px;" Text="Tier Code"></RadzenLabel>
<RadzenTextBox Style="margin-right: 10px;" @bind-Value="@TbscsrContainer.RequestTbscsr.certInfo.tierCode"></RadzenTextBox>
</div>
<div style="margin-bottom: 10px;">
<RadzenLabel Style="width: 130px;" Text="Unit Code"></RadzenLabel>
<RadzenTextBox Style="margin-right: 10px;" @bind-Value="@TbscsrContainer.RequestTbscsr.certInfo.unitCode"></RadzenTextBox>
</div>
<!--Generate Key-->
<div style="margin-top: 20px; margin-bottom:10px;">
<RadzenButton Style="width:100%;" Text="@($"Generate for {StrISO}")" Click="@OnClickGenerateKey"></RadzenButton>
</div>
<div style="margin-top: 20px; margin-bottom:10px;">
<RadzenTextArea Style="width:100%; height: 130px;" @bind-Value="@KeyPairString">
</RadzenTextArea>
</div>
}
</RadzenFieldset>
<!--Tx Rx-->
<RadzenFieldset Style="width: 100%;" Text="Call API">
<div>
<RadzenButton Style="width:100%; margin-bottom: 0.5rem;" Text="@($"Generate Request {StrISO}")" Click="@OnClickGenerateTbscsr"></RadzenButton>
<RadzenButton Style="width:100%;" Text="@($"Send Request")" Click="@OnClickSendTbscsr"></RadzenButton>
</div>
<div>
<div>
<RadzenLabel Text="Request"></RadzenLabel>
</div>
<div>
<RadzenTextArea Style="width:100%; height: 180px;" @bind-Value=@TbscsrContainer.StrRequest>
</RadzenTextArea>
</div>
<div>
<RadzenLabel Text="Response"></RadzenLabel>
</div>
<div>
<RadzenTextArea Style="width:100%; height: 180px;" @bind-Value=TbscsrContainer.StrResponse>
</RadzenTextArea>
</div>
</div>
</RadzenFieldset>
</div>
</RadzenFieldset>
@code {
[Parameter, EditorRequired]
public TbscsrContainer TbscsrContainer { get; set; } = new TbscsrContainer();
[Parameter]
public EventCallback<TbscsrContainer> TbscsrContainerChanged { get; set; }
[Parameter, EditorRequired]
public string ServerAddress { get; set; } = string.Empty;
[Parameter]
public EventCallback<TbscsrContainer> TbscsrCallback { get; set; }
//ISO
private string StrISO = string.Empty;
//Key
private string KeyPairString = string.Empty;
protected override void OnParametersSet()
{
if (string.IsNullOrEmpty(TbscsrContainer.RequestTbscsr.cnInfo.macaddr) == true)
{
TbscsrContainer.RequestTbscsr.cnInfo.macaddr = CreateRandomMacAddress();
}
StrISO = $"{TbscsrContainer.ISOType.ToString().Replace("_", "-")}";
SetKeyPairString();
}
//mac address
private void OnClickRandomMacaddresss()
{
var randomMac = CreateRandomMacAddress();
TbscsrContainer.RequestTbscsr.cnInfo.macaddr = randomMac;
}
private string CreateRandomMacAddress()
{
Random rand = new Random();
byte[] buffer = new byte[6];
rand.NextBytes(buffer);
return string.Join("", buffer.Select(x => x.ToString("X2")));
}
//generate key
private void OnClickGenerateKey()
{
GenerateKey();
}
private void GenerateKey()
{
if (TbscsrContainer.ISOType == VpkiIsoType.ISO15118_02)
{
TbscsrContainer.KeyPair = CertificateService.GenerateBouncyCastleKeyPairECDH($"{CurveName.secp256r1}");
var pKey = TbscsrContainer.PublicKey;
}
else if (TbscsrContainer.ISOType == VpkiIsoType.ISO15118_20)
{
TbscsrContainer.KeyPair = CertificateService.GenerateBouncyCastleKeyPairECDH($"{CurveName.secp521r1}");
}
//public key
var vnPubKey = CertificateService.GetPublicKeyArrayBC(CertificateService.GetPublicKeyBC(TbscsrContainer.KeyPair));
if (vnPubKey != null)
TbscsrContainer.PublicKey = CertificateService.GetKeyBase64Encrypted(vnPubKey);
//private key
var vnPriKey = CertificateService.GetPrivateKeyArrayBC(CertificateService.GetPrivateKeyBC(TbscsrContainer.KeyPair));
if (vnPriKey != null)
TbscsrContainer.PrivateKey = CertificateService.GetKeyBase64Encrypted(vnPriKey);
List<byte> vnPubKeyRcv = CertificateService.GetKeyBase64Decrypted(TbscsrContainer.PublicKey);
var publicKey = (ECPublicKeyParameters)TbscsrContainer.KeyPair.Public;
SetKeyPairString();
}
private void SetKeyPairString()
{
if (string.IsNullOrEmpty(TbscsrContainer.PublicKey) == false && string.IsNullOrEmpty(TbscsrContainer.PrivateKey) == false)
{
KeyPairString = $"{StrISO} Public Key ({TbscsrContainer.PublicKey.Length} Bytes): {Environment.NewLine}{TbscsrContainer.PublicKey}{Environment.NewLine}{Environment.NewLine}Private Key ({TbscsrContainer.PrivateKey.Length} Bytes): {Environment.NewLine}{TbscsrContainer.PrivateKey}";
}
}
//request tbscsr
private void OnClickGenerateTbscsr()
{
TbscsrContainer.RequestTbscsr.publickey = TbscsrContainer.PublicKey;
TbscsrContainer.StrRequest = $"{TbscsrContainer.RequestTbscsr.ToJson()}";
}
private async Task OnClickSendTbscsr()
{
DialogService.OpenIndicator();
try
{
var request = JsonConvert.DeserializeObject<Request_Tbscsr>(TbscsrContainer.StrRequest);
if (request != null)
{
string url = $"api/v1/tbscsr";
if (TbscsrContainer.VpkiType == VpkiType.prov_cert)
{
url = $"api/v2/prov/tbscsr";
}
else if (TbscsrContainer.VpkiType == VpkiType.vehicle_cert)
{
url = $"api/v2/vehicle/tbscsr";
}
var Response = await ApiService.PostJsonAsync<Request_Tbscsr, Response_Tbscsr>($"https://{ServerAddress}/{url}", request);
if (Response != null)
{
TbscsrContainer.ResponseTbscsr = Response;
TbscsrContainer.StrResponse = $"{Response.ToJson()}";
await TbscsrCallback.InvokeAsync(TbscsrContainer);
}
}
else
{
TbscsrContainer.StrResponse = "Request Context Error";
}
}
catch (Exception ex)
{
Log4net.WriteLine(ex);
TbscsrContainer.StrResponse = $"Request Context Error{Environment.NewLine}{ex.Message}";
}
DialogService.CloseIndicator();
}
}

View File

@ -0,0 +1,107 @@
@using VPKI.Library.Packet
@inject CertificateService CertificateService
@inject ApiService ApiService
@inject VpkiDialogService DialogService
<RadzenFieldset Text="VerifyResult">
<div style="display:flex">
<RadzenFieldset Style="width: 100%; margin-right: 20px;" Text="Request VerifyResult">
<div style="margin-bottom: 10px;">
<RadzenLabel Style="width: 130px;" Text="IFT ID"></RadzenLabel>
<RadzenTextBox Style="margin-right: 10px;" @bind-Value="@VerifyResultContainer.RequestVerifyresult.iftid"></RadzenTextBox>
</div>
<div style="margin-bottom: 10px;">
<RadzenLabel Style="width: 130px;" Text="PCID"></RadzenLabel>
<RadzenTextBox Style="margin-right: 10px;" @bind-Value="@VerifyResultContainer.RequestVerifyresult.pcid"></RadzenTextBox>
</div>
<div style="margin-bottom: 10px;">
<RadzenLabel Style="width: 130px;" Text="RESULT"></RadzenLabel>
<RadzenTextBox Style="margin-right: 10px;" @bind-Value="@VerifyResultContainer.RequestVerifyresult.result"></RadzenTextBox>
</div>
</RadzenFieldset>
<RadzenFieldset Style="width:100%;" Text="Call API">
<div>
<RadzenButton Style="width:100%; margin-bottom: 0.5rem;" Text="@($"Generate Request {StrISO}")" Click="@OnClickGenerateVerifyResult"></RadzenButton>
<RadzenButton Style="width:100%;" Text="Send Request" Click="@OnClickSendVerifyResult"></RadzenButton>
</div>
<div>
<div>
<RadzenLabel Text="Request"></RadzenLabel>
</div>
<div>
<RadzenTextArea Style="width:100%; height: 180px;" @bind-Value=@VerifyResultContainer.StrRequest>
</RadzenTextArea>
</div>
<div>
<RadzenLabel Text="Response"></RadzenLabel>
</div>
<div>
<RadzenTextArea Style="width:100%; height: 180px;" @bind-Value="@VerifyResultContainer.StrResponse">
</RadzenTextArea>
</div>
</div>
</RadzenFieldset>
</div>
</RadzenFieldset>
@code {
[Parameter, EditorRequired]
public VerifyResultContainer VerifyResultContainer { get; set; } = new VerifyResultContainer();
[Parameter]
public EventCallback<VerifyResultContainer> VerifyResultContainerChanged { get; set; }
[Parameter, EditorRequired]
public string ServerAddress { get; set; } = string.Empty;
[Parameter]
public TbscsrContainer TbscsrContainer { get; set; } = new TbscsrContainer();
//ISO
private string StrISO = string.Empty;
protected override void OnInitialized()
{
StrISO = $"{TbscsrContainer.ISOType.ToString().Replace("_", "-")}";
}
private void OnClickGenerateVerifyResult()
{
VerifyResultContainer.StrRequest = $"{VerifyResultContainer.RequestVerifyresult.ToJson()}";
}
private async Task OnClickSendVerifyResult()
{
DialogService.OpenIndicator();
try
{
var request = JsonConvert.DeserializeObject<Request_Verifyresult>(VerifyResultContainer.StrRequest);
if (request != null)
{
string url = $"api/v1/verifyresult";
if (TbscsrContainer.VpkiType == VpkiType.prov_cert)
{
url = $"api/v2/prov/verifyresult";
}
else if (TbscsrContainer.VpkiType == VpkiType.vehicle_cert)
{
url = $"api/v2/vehicle/verifyresult";
}
var Response = await ApiService.PostJsonAsync<Request_Verifyresult, Response_Verifyresult>($"https://{ServerAddress}/{url}", request);
if (Response != null)
{
VerifyResultContainer.ResponseVerifyresult = Response;
VerifyResultContainer.StrResponse = $"{Response.ToJson()}";
}
}
}
catch(Exception ex)
{
Log4net.WriteLine(ex);
VerifyResultContainer.StrResponse = $"Request Context Error{Environment.NewLine}{ex.Message}";
}
DialogService.CloseIndicator();
}
}

View File

@ -0,0 +1,36 @@
@page "/Error"
@using System.Diagnostics
<PageTitle>Error</PageTitle>
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
@code{
[CascadingParameter]
private HttpContext? HttpContext { get; set; }
private string? RequestId { get; set; }
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
protected override void OnInitialized() =>
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
}

View File

@ -0,0 +1,10 @@
@page "/"
@using Microsoft.AspNetCore.Components.Authorization
<PageTitle>Home</PageTitle>
<AuthorizeView>
<h1>Hello, @context.User.Identity?.Name!</h1>
</AuthorizeView>
Welcome to your new app.

View File

@ -0,0 +1,68 @@
@page "/login"
@inject NavigationManager NavigationManager
@inject ApiService ApiService
@inject AuthenticationStateProvider AuthStateProvider
@inject VpkiDialogService DialogService
@inject IJSRuntime JS
@inject NotificationService NotiService
<AuthorizeView Context="ContextAuth">
<NotAuthorized>
<div class="p-5">
<EditForm Model="@LoginModel" FormName="Login" OnValidSubmit="HandleLogin" Context="ContextLogin">
<DataAnnotationsValidator />
<div class="form-group">
<label for="username">UserID</label>
<InputText id="username" class="form-control" @bind-Value="@LoginModel.UserID"></InputText>
</div>
<br />
<div class="form-group">
<label for="password">Password</label>
<InputText id="password" class="form-control" type="password" @bind-Value="@LoginModel.Password"></InputText>
</div>
<button type="submit" class="btn btn-primary mt-3">Login</button>
</EditForm>
<RadzenLabel Style="color: red; margin-top: 5px;">@ErrorCode</RadzenLabel>
</div>
</NotAuthorized>
</AuthorizeView>
@code {
private LoginModel LoginModel = new LoginModel();
private string ErrorCode = string.Empty;
protected override async Task OnInitializedAsync()
{
var auth = await((ExtendAuthenticationStatProvicer)AuthStateProvider).GetAuthenticationStateAsync();
if(auth?.User?.Identity?.IsAuthenticated == true)
{
NavigationManager.NavigateTo("/");
}
}
private async Task HandleLogin()
{
DialogService.OpenIndicator("Login");
//password hash
LoginModel.Password = $"{LoginModel.Password?.StringToSHA256Base64()}";
ErrorCode = string.Empty;
var res = await ApiService.PostJsonAsyncBearer<LoginModel, LoginResponseModel>($"/api/auth/login", LoginModel);
if (res?.AccessToken != null)
{
await ((ExtendAuthenticationStatProvicer)AuthStateProvider).MakeUserAsAuthenticated(res);
NavigationManager.NavigateTo("/", true);
NotiService.Notify(NotificationSeverity.Success, "Login", "Login Success");
}
else
{
LoginModel.Password = string.Empty;
ErrorCode = $"{res?.EC.ToString()}";
NotiService.Notify(NotificationSeverity.Error, "Login", "Login Failed");
}
DialogService.CloseIndicator();
}
}

View File

@ -0,0 +1,84 @@
@page "/register"
@inject NavigationManager NavigationManager
@inject ApiService ApiService
@inject AuthenticationStateProvider AuthStateProvider
<AuthorizeView Context="ContextAuth">
<NotAuthorized>
@if (string.IsNullOrEmpty(RegisterUserID) == false)
{
<h5>Register Success!</h5>
<h5>@($"UserID: {RegisterUserID}")</h5>
<br />
<RadzenButton Click="@OnClickGotoLogin">Go to Login</RadzenButton>
}
else
{
<div class="p-5">
<EditForm Model="@RegisterModel" FormName="Register" OnValidSubmit="HandleRegister" Context="ContextRegister">
<DataAnnotationsValidator />
<div class="form-group">
<label for="username">UserID</label>
<InputText id="username" class="form-control" @bind-Value="@RegisterModel.UserID"></InputText>
</div>
<br />
<div class="form-group">
<label for="password">Password</label>
<InputText id="password" class="form-control" type="password" @bind-Value="@RegisterModel.Password"></InputText>
</div>
<br />
<div class="form-group">
<label for="password">Password Confirm</label>
<InputText id="password" class="form-control" type="password" @bind-Value="@RegisterModel.PasswordConfirm"></InputText>
</div>
<button type="submit" class="btn btn-primary mt-3">Register</button>
</EditForm>
<RadzenLabel Style="color: red; margin-top: 5px;">@ErrorCode</RadzenLabel>
</div>
}
</NotAuthorized>
</AuthorizeView>
@code {
private RegisterModel RegisterModel = new RegisterModel();
private string ErrorCode = string.Empty;
private string RegisterUserID = string.Empty;
protected override async Task OnInitializedAsync()
{
RegisterUserID = string.Empty;
var auth = await ((ExtendAuthenticationStatProvicer)AuthStateProvider).GetAuthenticationStateAsync();
if (auth?.User?.Identity?.IsAuthenticated == true)
{
NavigationManager.NavigateTo("/");
}
}
private async Task HandleRegister()
{
ErrorCode = string.Empty;
if (RegisterModel.Password != RegisterModel.PasswordConfirm)
{
ErrorCode = ERROR_CODE.EC_USER_REGISTER_CONFIRM_PASSWORD_ERROR.ToString();
return;
}
//password hash
RegisterModel.Password = $"{RegisterModel.Password?.StringToSHA256Base64()}";
RegisterModel.PasswordConfirm = $"{RegisterModel.PasswordConfirm?.StringToSHA256Base64()}";
var res = await ApiService.PostJsonAsyncBearer<RegisterModel, RegisterResponseModel>($"/api/auth/regisger", RegisterModel);
if (res != null)
{
if (res.EC == ERROR_CODE.EC_OK)
{
RegisterUserID = $"{res.UserID}";
}
}
}
private void OnClickGotoLogin()
{
NavigationManager.NavigateTo("/login");
}
}

View File

@ -0,0 +1,123 @@
@page "/user"
@inject ApiService ApiService
@inject VpkiDialogService DialogService
@inject IJSRuntime JS
@inject NotificationService NotiService
<AuthorizeView Roles="@($"{UserRole.SuperUser.ToString()}")">
<Authorized>
<!--DataGrid-->
@if (data?.Count > 0)
{
<RadzenDataGrid @ref="@grid" Data="@data" TItem="UserModel" EditMode="DataGridEditMode.Single">
<Columns>
<RadzenDataGridColumn Title="UserID" Property="UserModel.TUser.CUserId" />
<RadzenDataGridColumn Title="Role" Property="UserModel.TRole.CRoleName">
<Template Context="user">
@{
string? state = $"auth-{user.TRole.CRoleName.ToLower()}";
}
<label class="@state">@($"{(UserRole)Enum.Parse(typeof(UserRole), user.TRole.CRoleId.ToString())}")</label>
</Template>
<EditTemplate Context="user">
@{
var roleList = Enum.GetValues(typeof(UserRole)).Cast<UserRole>().Where(x => 10 <= (byte)x);
<RadzenDropDown Data="@roleList" @bind-Value="@user.TRole.CRoleId">
<Template Context="role">
@($"{Enum.GetName(typeof(UserRole), role)}")
</Template>
</RadzenDropDown>
}
</EditTemplate>
</RadzenDataGridColumn>
<RadzenDataGridColumn Title="CreateDateTime" Property="UserModel.TUser.CCreateDateTime" />
<RadzenDataGridColumn Title="LastLoginDateTime" Property="UserModel.TUser.CLastLoginDateTime" />
<RadzenDataGridColumn Title="State" Property="UserModel.TUser.CState">
<Template Context="user">
@{
string? state = $"user-{Enum.GetName(typeof(UserState), user.TUser.CState).ToLower()}";
}
<label class="@state">@($"{(UserState)Enum.Parse(typeof(UserState), user.TUser.CState.ToString())} ({user.TUser.CState})")</label>
</Template>
<EditTemplate Context="user">
@{
var stateList = Enum.GetValues(typeof(UserState)).Cast<UserState>();
<RadzenDropDown Data="@stateList" @bind-Value="@user.TUser.CState">
<Template Context="state">
@($"{Enum.GetName(typeof(UserState), state)} ({Convert.ToByte(state)})")
</Template>
</RadzenDropDown>
}
</EditTemplate>
</RadzenDataGridColumn>
<!--edit-->
<RadzenDataGridColumn Title="Edit" TextAlign="TextAlign.Center">
<Template Context="user">
@if (user.TRole.CAuid.ToLower() != UserRole.SuperUser.ToString().ToLower())
{
<RadzenButton Text="Edit" Style="background: #7E57C2;" Click="@(()=>OnClickUserEdit(user))" Disabled="@isEditing" />
}
</Template>
<EditTemplate Context="user">
<RadzenButton Text="Apply" Style="color:black;" ButtonStyle="ButtonStyle.Warning" Click="@(()=>OnClickUserEditApply(user))"></RadzenButton>
<RadzenButton Text="Cancel" Style="color:black;" ButtonStyle="ButtonStyle.Danger" Click="@(()=>OnClickUserEditCancel(user))"></RadzenButton>
</EditTemplate>
</RadzenDataGridColumn>
</Columns>
</RadzenDataGrid>
}
</Authorized>
<NotAuthorized>
<RadzenLabel Text="Not Authorized"></RadzenLabel>
</NotAuthorized>
</AuthorizeView>
@code {
List<UserModel> data = new List<UserModel>();
RadzenDataGrid<UserModel>? grid;
bool isEditing = false;
protected override async Task OnInitializedAsync()
{
await GetAllUsers();
}
private async Task OnClickUserEdit(UserModel user)
{
isEditing = true;
await grid?.EditRow(user);
}
private async Task OnClickUserEditApply(UserModel user)
{
bool confirm = await DialogService.Confirm("Edit User", "Apply Edit User?");
if(confirm == true)
{
user.TRole.CRoleName = $"{Enum.GetName(typeof(UserRole), user.TRole.CRoleId)}";
var retunUser = await ApiService.PostJsonAsyncBearer<UserModel, UserModel>("/api/user/UpdateUser", user);
if (retunUser != null)
{
await GetAllUsers();
NotiService.Notify(NotificationSeverity.Success, "Edit User",$"Edit '{user.TUser.CUserId}' Update Success");
}
}
isEditing = false;
}
private async Task OnClickUserEditCancel(UserModel user)
{
await GetAllUsers();
isEditing = false;
}
private async Task GetAllUsers()
{
var response = await ApiService.GetJsonAsyncBearer<List<UserModel>>("/api/user/GetAllUser");
if (response != null)
{
data = response.OrderBy(x=>x.TUser.CCreateDateTime).ToList();
}
}
}

View File

@ -0,0 +1,23 @@
.user-inactive{
color: yellow;
}
.user-active {
color: lime;
}
.user-block{
color: #f9777f;
}
.auth-superuser {
color: #FF5722;
}
.auth-admin {
color: #FFEB3B;
}
.auth-user {
color: #03A9F4;
}

View File

@ -0,0 +1,232 @@
@page "/Vpki/Api"
@using Newtonsoft.Json
@using Org.BouncyCastle.Crypto
@using VPKI.Library.Packet
@using VPKI.Library.Services
@inject CertificateService csrSrvice;
@inject ApiService ApiService;
<h3>VPKI API</h3>
<AuthorizeView Roles="@($"{UserRole.Admin.ToString()},{UserRole.SuperUser.ToString()}")">
<Authorized>
<RadzenFieldset Style="margin-bottom: 10px;" Text="Server Info">
<div style="margin-bottom: 10px;">
<RadzenLabel Style="width: 130px;" Text="Server Address"></RadzenLabel>
<RadzenTextBox Style="margin-right: 10px;" @bind-Value="@ServerAddress"></RadzenTextBox>
</div>
</RadzenFieldset>
<RadzenTabs>
<Tabs>
@foreach (var vpkiType in Enum.GetValues(typeof(VpkiType)))
{
if (DicTbscsrContainer.TryGetValue((VpkiType)vpkiType, out var outTbscsr) == true &&
DicCertificateContainer.TryGetValue((VpkiType)vpkiType, out var outCertificate) == true &&
DicVerifyResultContainer.TryGetValue((VpkiType)vpkiType, out var outVeryfiResult) == true)
{
<RadzenTabsItem Text="@vpkiType.ToString()">
<div @key=@vpkiType>
<!--CSR-->
<VPKIApiTbscsr @bind-TbscsrContainer=@outTbscsr ServerAddress="@ServerAddress"
TbscsrCallback="@TbscsrCallback"/>
<br />
<!--certificate-->
<VPKIApiCertificate @bind-CertificateContainer=@outCertificate ServerAddress="@ServerAddress" TbscsrContainer="@outTbscsr" />
<br />
<!--certificate-->
<VPKIApiVerifyResult @bind-VerifyResultContainer=@outVeryfiResult ServerAddress="@ServerAddress" TbscsrContainer="@outTbscsr" />
</div>
</RadzenTabsItem>
}
}
</Tabs>
</RadzenTabs>
<br />
</Authorized>
<NotAuthorized>
<RadzenLabel Text="Not Authorized"></RadzenLabel>
</NotAuthorized>
</AuthorizeView>
@code {
private string ServerAddress = "127.0.0.1:8080";
Dictionary<VpkiType, TbscsrContainer> DicTbscsrContainer = new Dictionary<VpkiType, TbscsrContainer>();
Dictionary<VpkiType, CertificateContainer> DicCertificateContainer = new Dictionary<VpkiType, CertificateContainer>();
Dictionary<VpkiType, VerifyResultContainer> DicVerifyResultContainer = new Dictionary<VpkiType, VerifyResultContainer>();
protected override void OnInitialized()
{
SetDefaultValue();
}
private void SetDefaultValue()
{
//tbscsr
DicTbscsrContainer.Add(VpkiType.prov_v1, new TbscsrContainer
{
RequestTbscsr = new Request_Tbscsr
{
iftid = "ift_1",
certInfo = new Request_Tbscsr.CertInfo
{
tierCode = "P002",
unitCode = "C001",
dc = "OEM"
},
cnInfo = new Request_Tbscsr.CnInfo
{
idType = "P",
supplierId = "2",
wmi = "KM8",
macaddr = ""
}
},
VpkiType = VpkiType.prov_v1,
ISOType = VpkiIsoType.ISO15118_02
});
DicTbscsrContainer.Add(VpkiType.prov_cert, new TbscsrContainer
{
RequestTbscsr = new Request_Tbscsr
{
iftid = "ift_2",
certInfo = new Request_Tbscsr.CertInfo
{
tierCode = "P002",
unitCode = "C001",
dc = "OEM20"
},
cnInfo = new Request_Tbscsr.CnInfo
{
idType = "P",
supplierId = "2",
wmi = "KM7",
macaddr = ""
},
certType = $"{VpkiType.prov_cert}"
},
VpkiType = VpkiType.prov_cert,
ISOType = VpkiIsoType.ISO15118_20
});
DicTbscsrContainer.Add(VpkiType.vehicle_cert, new TbscsrContainer
{
RequestTbscsr = new Request_Tbscsr
{
iftid = "ift_3",
certInfo = new Request_Tbscsr.CertInfo
{
tierCode = "P002",
unitCode = "C001",
dc = "EV"
},
cnInfo = new Request_Tbscsr.CnInfo
{
idType = "V",
supplierId = "2",
wmi = "KM7",
macaddr = ""
},
certType = $"{VpkiType.vehicle_cert}"
},
VpkiType = VpkiType.vehicle_cert,
ISOType = VpkiIsoType.ISO15118_20
});
//certificate
DicCertificateContainer.Add(VpkiType.prov_v1, new CertificateContainer
{
RequestCertificate = new Request_Certificate
{
iftid = "ift_1",
csrsignature = string.Empty,
tierCode = "P002",
unitCode = "C001",
vehicleCode = "MV EV",
localCode = "EU",
brandCode = "KMC"
}
});
DicCertificateContainer.Add(VpkiType.prov_cert, new CertificateContainer
{
RequestCertificate = new Request_Certificate
{
iftid = "ift_2",
csrsignature = string.Empty,
tierCode = "P002",
unitCode = "C001",
vehicleCode = "MV EV",
localCode = "EU",
brandCode = "KMC"
}
});
DicCertificateContainer.Add(VpkiType.vehicle_cert, new CertificateContainer
{
RequestCertificate = new Request_Certificate
{
iftid = "ift_3",
csrsignature = string.Empty,
tierCode = "P002",
unitCode = "C001",
vehicleCode = "MV EV",
localCode = "EU",
brandCode = "KMC"
}
});
DicVerifyResultContainer.Add(VpkiType.prov_v1, new VerifyResultContainer
{
RequestVerifyresult = new Request_Verifyresult
{
iftid = "ift_1",
pcid = "",
result = "success"
}
});
DicVerifyResultContainer.Add(VpkiType.prov_cert, new VerifyResultContainer
{
RequestVerifyresult = new Request_Verifyresult
{
iftid = "ift_2",
pcid = "",
result = "success"
}
});
DicVerifyResultContainer.Add(VpkiType.vehicle_cert, new VerifyResultContainer
{
RequestVerifyresult = new Request_Verifyresult
{
iftid = "ift_3",
pcid = "",
result = "success"
}
});
}
private async Task TbscsrCallback(TbscsrContainer args)
{
if (DicCertificateContainer.TryGetValue(args.VpkiType, out var outCertificate) == true)
{
outCertificate.RequestCertificate.iftid = args.RequestTbscsr.iftid;
outCertificate.RequestCertificate.tierCode = args.RequestTbscsr.certInfo.tierCode;
outCertificate.RequestCertificate.unitCode = args.RequestTbscsr.certInfo.unitCode;
outCertificate.RequestCertificate.csrsignature = args.ResponseTbscsr.data.hashedtbscsr;
}
if (DicVerifyResultContainer.TryGetValue(args.VpkiType, out var outVerifyResult) == true)
{
outVerifyResult.RequestVerifyresult.iftid = args.RequestTbscsr.iftid;
outVerifyResult.RequestVerifyresult.pcid = args.ResponseTbscsr.data.pcid;
}
await Task.CompletedTask;
}
}

View File

@ -0,0 +1,520 @@
@page "/vpki/manager"
@using DB.VPKI_DataDB
@using System.Text
@using VPKI.Library.Packet
@using VPKI.Library.Static
@inject ApiService ApiService
@inject VpkiDialogService DialogService
@inject IJSRuntime JS
@inject NotificationService NotiService
@inject TooltipService TooltipService
<h3>VPKI MANAGER</h3>
<AuthorizeView Roles="@($"{UserRole.Admin.ToString()},{UserRole.SuperUser.ToString()}")">
<Authorized Context="AuthContext">
<RadzenFieldset Style="margin-bottom: 10px;" Text="Server Info">
<div style="margin-bottom: 10px;">
<RadzenLabel Style="width: 130px;" Text="Server Address"></RadzenLabel>
<RadzenTextBox Style="margin-right: 10px;" @bind-Value="@ServerAddress"></RadzenTextBox>
</div>
</RadzenFieldset>
<RadzenFieldset Style="margin-bottom: 10px;" Text="View Options">
<div style="margin-bottom: 10px;">
<!--date-->
<RadzenLabel Style="margin-right: 5px;" Text="Date"></RadzenLabel>
<RadzenDatePicker Style="margin-right: 5px;" @bind-Value="@SearchStartDate" DateFormat="yyyy/MM/dd" />
<RadzenLabel Style="margin-right: 5px;" Text="~"></RadzenLabel>
<RadzenDatePicker Style="margin-right: 10px;" @bind-Value="@SearchEndDate" DateFormat="yyyy/MM/dd" />
<RadzenButton Style="margin-right: 30px;" Click="@(()=>Search(0))">Search</RadzenButton>
<!--vew Count-->
<RadzenLabel Style="margin-right: 5px;" Text="ViewCount (20~100)"></RadzenLabel>
<RadzenNumeric @bind-Value=@ViewCount Min=20 Max=100></RadzenNumeric>
</div>
</RadzenFieldset>
<br />
<!--count-->
<div style="display: flex; justify-content: space-between; margin-bottom: 0.7rem;">
<div style="display: flex; align-items:center;">
<RadzenLabel Style="color:#BA68C8; font-weight: 600; font-size: 1.1rem; margin-right: 1.2rem;" Text=@($"Total Count: {history.Count}")></RadzenLabel>
<RadzenLabel Style="color:#64B5F6; font-weight: 600; font-size: 1.1rem;" Text=@($"Filter Count: {grid.View.Count()}")></RadzenLabel>
</div>
<div>
<RadzenButton Style="background: #7E57C2; margin-right: 0.5rem;" Text="Export CSV(Full Data)" Click="@(()=>OnClickExportCSV(false))"></RadzenButton>
<RadzenButton Style="background: #1976D2;" Text="Export CSV(Filter Data)" Click="@(()=>OnClickExportCSV(true))"></RadzenButton>
</div>
</div>
<RadzenDataGrid @ref="@grid" Data="@history" TItem="CertificateHistoryModel" AllowFiltering FilterMode="FilterMode.Simple"
AllowPaging PageNumbersCount="10" PageSize="@ViewCount" PagerHorizontalAlign="HorizontalAlign.Center" AllowSorting RowExpand="@((args)=>OnExpand(args))"
FilterCaseSensitivity="FilterCaseSensitivity.CaseInsensitive" Filter="@OnChangeFilter" FilterCleared="@OnChangeFilter">
<Columns>
<!--index or no-->
@* <RadzenDataGridColumn Title="No">
<Template>
@(history.IndexOf(context)+1)
</Template>
</RadzenDataGridColumn> *@
<!--data-->
@foreach (var prop in typeof(TTbscsr).GetProperties())
{
if (prop.Name.ToLower().Contains("hashedtbscsr") || prop.Name.ToLower().Contains("publickey") || prop.Name.ToLower().Contains("origintbscsr")
|| prop.Name.ToLower().Contains("csrsignature") || prop.Name.ToLower().Contains("datetime") || prop.Name.ToLower().Contains("cdn"))
continue;
string propName = $"{prop.Name}";
<RadzenDataGridColumn Title="@propName" Property=@($"TTbscsr.{propName}") />
}
<!--date time-->
<RadzenDataGridColumn Width="9rem" Title="CDATETIME">
<Template>
@{
<div style="font-size: 0.8rem;">
<RadzenLabel Text="Tbscsr DateTime"></RadzenLabel>
<br />
<RadzenLabel Text="@context?.TTbscsr?.CDateTime.ToString("yyyy-MM-dd HH:mm:ss")"></RadzenLabel>
<br />
<RadzenLabel Text="Certificate DateTime"></RadzenLabel>
<br />
<RadzenLabel Text="@context?.TCertificate?.CDateTime.ToString("yyyy-MM-dd HH:mm:ss")"></RadzenLabel>
</div>
}
</Template>
</RadzenDataGridColumn>
<!--Reissue, Revoke-->
@* <RadzenDataGridColumn Title="Reissue">
<Template>
@{
bool canReissue = string.IsNullOrEmpty($"{context.TCertificate?.CCsr}");
<RadzenButton Style="width:100%; color:black;" Text="Reissue" Disabled=@canReissue
ButtonStyle="ButtonStyle.Warning" Click="@(()=>OnClickReissue(context))"></RadzenButton>
}
</Template>
</RadzenDataGridColumn> *@
@* <RadzenDataGridColumn Title="Revoke">
<Template>
@{
bool canRevoke = true;
canRevoke = !(context.TCertificate?.CIssueCount > 0);
}
<RadzenButton Style="width:100%; color: black;" Text="Revoke" Disabled=@canRevoke
ButtonStyle="ButtonStyle.Danger" Click="@(()=>OnClickRevoke(context))"></RadzenButton>
</Template>
</RadzenDataGridColumn> *@
</Columns>
<Template>
<!--tbscsr-->
<div style="display:flex; align-items: center; margin-bottom: 0.2rem;">
<RadzenLabel Text="Tbscsr" Style="margin-right: 0.5rem;"></RadzenLabel>
<RadzenIcon Icon="description" Style="cursor: pointer;" MouseEnter="@(args => TooltipService.Open(args, "Copy"))" @onclick="@(()=>OnClickCopy(context?.TTbscsr?.ToJson()))"></RadzenIcon>
</div>
<div>
<RadzenTextArea Style="width:100%; height:10rem;" Value="@context?.TTbscsr?.ToJson()" ReadOnly></RadzenTextArea>
</div>
<br />
<!--certificate-->
<RadzenLabel Text="TCertificate"></RadzenLabel>
<div>
<RadzenTextArea Style="width:100%; height:10rem;" Value="@context?.TCertificate?.ToJson()" ReadOnly></RadzenTextArea>
</div>
@{
bool canDownload = string.IsNullOrEmpty($"{context.TCertificate?.CCert}");
}
<RadzenButton Style="background: #7E57C2;" Text="Certificate Download" Click="@(()=>OnClickExportCertificate(context))" Disabled="@canDownload"></RadzenButton>
<RadzenButton Style="background: #1976D2;" Text="SubCA Certificate Download" Click="@(()=>OnClickExportSubCACertificate(context))" Disabled="@canDownload"></RadzenButton>
<br />
<br />
<!--verify result-->
<RadzenLabel Text="TVerifyResult"></RadzenLabel>
<div>
<RadzenTextArea Style="width:100%; height:10rem;" Value="@context?.TVerifyResult?.ToJson()" ReadOnly></RadzenTextArea>
</div>
<br />
@if (context.TTbscsr.CCertType == VpkiType.prov_v1.ToString())
{
<!--certificate info-->
<RadzenLabel Text="TOcsp"></RadzenLabel>
<div>
<RadzenTextArea Style="width:100%; height:10rem;" Value="@context.TOcsp?.ToJson()" ReadOnly></RadzenTextArea>
</div>
<br />
<!--ocsp-->
<RadzenLabel Text="OCSP"></RadzenLabel>
<div>
<RadzenTextArea Style="width:100%; height:10rem;" Value="@context.TOcsp?.COcsp" ReadOnly></RadzenTextArea>
</div>
<RadzenButton Style="background: #7E57C2;" Text="OCSP Download" Click="@(()=>OnClickOcspDownload(context))"></RadzenButton>
}
</Template>
</RadzenDataGrid>
</Authorized>
<NotAuthorized>
<RadzenLabel Text="Not Authorized"></RadzenLabel>
</NotAuthorized>
</AuthorizeView>
@code {
private string ServerAddress = "127.0.0.1:8080";
private DateOnly SearchStartDate = DateOnly.FromDateTime(DateTime.Now.AddDays(-7));
private DateOnly SearchEndDate = DateOnly.FromDateTime(DateTime.Now);
private string FilterIft = string.Empty;
private string FilterWMI = string.Empty;
private string FilterDC = string.Empty;
private string FilterCertType = string.Empty;
private int ViewCount = 20;
List<CertificateHistoryModel> history = new List<CertificateHistoryModel>();
RadzenDataGrid<CertificateHistoryModel> grid = new();
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
//await Search(0);
}
await Task.CompletedTask;
}
private async Task Search(int startCcuid)
{
DialogService.OpenIndicator("Search History");
string requestUrl = $"https://{ServerAddress}/history?";
//Default filter
requestUrl += $"startDate={SearchStartDate}&";
requestUrl += $"endDate={SearchEndDate}&";
requestUrl += $"startCcuid={startCcuid}&";
//optional filter
if (string.IsNullOrEmpty(FilterIft) == false)
{
requestUrl += $"ift={FilterIft}&";
}
if (string.IsNullOrEmpty(FilterWMI) == false)
{
requestUrl += $"wmi={FilterWMI}&";
}
if (string.IsNullOrEmpty(FilterDC) == false)
{
requestUrl += $"dc={FilterDC}&";
}
if (string.IsNullOrEmpty(FilterCertType) == false)
{
requestUrl += $"certType={FilterCertType}&";
}
history?.Clear();
var result = await ApiService.GetJsonAsync<List<CertificateHistoryModel>>(requestUrl);
if (result != null)
{
history.AddRange(result);
history.Reverse();
}
grid?.Reload();
DialogService.CloseIndicator();
}
private async Task OnClickReissue(CertificateHistoryModel args)
{
var confirmRsult = await DialogService.Confirm("Reissue", "Are you sure certificate reissue?");
if (confirmRsult == false)
return;
DialogService.OpenIndicator("Reissue");
Request_Certificate requestReissue = new Request_Certificate
{
iftid = args.TTbscsr.CIftid,
csrsignature = args.TCertificate.CCsrsignature,
tierCode = args.TCertificate.CTierCode,
unitCode = args.TCertificate.CUnitCode,
vehicleCode = args.TCertificate.CVehicleCode,
localCode = args.TCertificate.CLocalCode,
brandCode = args.TCertificate.CBrandCode
};
string api = string.Empty;
if (args.TTbscsr.CCertType.ToLower() == VpkiType.prov_v1.ToString())
{
api = "api/v1/reissue";
}
else if (args.TTbscsr.CCertType.ToLower() == VpkiType.prov_cert.ToString())
{
api = "api/v2/prov/reissue";
}
else if (args.TTbscsr.CCertType.ToLower() == VpkiType.vehicle_cert.ToString())
{
api = "api/v2/vehicle/reissue";
}
var result = await ApiService.PostJsonAsync<Request_Certificate, Response_Certificate>($"https://{ServerAddress}/{api}", requestReissue);
if (result != null)
{
string requestUrl = $"https://{ServerAddress}/history/ccuid?ccuid={args.TTbscsr.CCuid}";
var updateResult = await ApiService.GetJsonAsync<CertificateHistoryModel>(requestUrl);
if (updateResult != null)
{
args.TTbscsr = updateResult.TTbscsr;
args.TCertificate = updateResult.TCertificate;
args.TVerifyResult = updateResult.TVerifyResult;
args.TOcsp = updateResult.TOcsp;
}
NotiService.Notify(NotificationSeverity.Success, "Reissue", "Reissue Success");
}
else
{
NotiService.Notify(NotificationSeverity.Error, "Reissue", "Reissue Failed");
}
await grid?.CollapseAll();
await grid?.Reload();
StateHasChanged();
DialogService.CloseIndicator();
}
private async Task OnClickRevoke(CertificateHistoryModel args)
{
var confirmRsult = await DialogService.Confirm("Revoke", "Are you sure certificate revoke?");
if (confirmRsult == false)
return;
DialogService.OpenIndicator("Revoke");
Request_CertificateRevokeOEM requestRevoke = new Request_CertificateRevokeOEM
{
dn = args.TTbscsr.CDn
};
string api = string.Empty;
if (args.TTbscsr.CCertType.ToLower() == VpkiType.prov_v1.ToString())
{
api = "api/v1/revoke";
}
else if (args.TTbscsr.CCertType.ToLower() == VpkiType.prov_cert.ToString())
{
api = "api/v2/prov/revoke";
}
else if (args.TTbscsr.CCertType.ToLower() == VpkiType.vehicle_cert.ToString())
{
api = "api/v2/vehicle/revoke";
}
var result = await ApiService.PostJsonAsync<Request_CertificateRevokeOEM, Response_CertificateRevokeOEM>($"https://{ServerAddress}/{api}", requestRevoke);
if (result != null)
{
string requestUrl = $"https://{ServerAddress}/history/ccuid?ccuid={args.TTbscsr.CCuid}";
var updateResult = await ApiService.GetJsonAsync<CertificateHistoryModel>(requestUrl);
if (updateResult != null)
{
args.TTbscsr = updateResult.TTbscsr;
args.TCertificate = updateResult.TCertificate;
args.TVerifyResult = updateResult.TVerifyResult;
args.TOcsp = updateResult.TOcsp;
}
NotiService.Notify(NotificationSeverity.Success, "Revoke", "Revoke Success");
}
else
{
NotiService.Notify(NotificationSeverity.Error, "Revoke", "Revoke Failed");
}
await grid?.CollapseAll();
await grid?.Reload();
StateHasChanged();
DialogService.CloseIndicator();
}
async Task OnClickExportCSV(bool isFilter = false)
{
DialogService.OpenIndicator("Export to CSV");
List<string> exportData = new List<string>();
if (isFilter == true)
{
//필터링된 데이터만
exportData.AddRange(grid.View.ToList().ConvertToCsvFile());
}
else
{
//전체 데이터
exportData.AddRange(history.ConvertToCsvFile());
}
if (exportData?.Count > 0)
{
string fileName = $"VPKI_{DateTime.Now.ToString("yyyyMMdd_HHmmss")}.csv";
bool exportResult = await JS.InvokeAsync<bool>("openSaveFileCSV", fileName, exportData.ToJson());
if (exportResult == true)
{
NotiService.Notify(new NotificationMessage
{
Severity = NotificationSeverity.Success,
Summary = "Success",
Detail = "CSV Export Success"
});
}
else
{
NotiService.Notify(new NotificationMessage
{
Severity = NotificationSeverity.Error,
Summary = "Failed",
Detail = "CSV Export Failed",
});
}
}
DialogService.CloseIndicator();
}
void OnChangeFilter()
{
StateHasChanged();
}
async Task OnClickExportCertificate(CertificateHistoryModel? certificate)
{
DialogService.OpenIndicator("Export to Prov Certificate");
if (certificate != null)
{
string fileName = $"{certificate?.TTbscsr?.CPcid}.pem";
bool exportResult = await JS.InvokeAsync<bool>("openSaveFileStr", fileName, certificate?.TCertificate?.CCert);
if (exportResult == true)
{
NotiService.Notify(new NotificationMessage
{
Severity = NotificationSeverity.Success,
Summary = "Success",
Detail = "Certificate Export Success"
});
}
else
{
NotiService.Notify(new NotificationMessage
{
Severity = NotificationSeverity.Error,
Summary = "Failed",
Detail = "Certificate Export Failed",
});
}
}
DialogService.CloseIndicator();
}
async Task OnClickExportSubCACertificate(CertificateHistoryModel? certificate)
{
DialogService.OpenIndicator("Export to Prov Certificate");
if (certificate != null)
{
string requestUrl = $"https://{ServerAddress}/certificate/GetProv?vpkiType={certificate.TTbscsr.CCertType}";
var response = await ApiService.GetJsonAsync<string>(requestUrl);
string fileName = $"SubCA.pem";
bool exportResult = await JS.InvokeAsync<bool>("openSaveFileStr", fileName, response);
if (exportResult == true)
{
NotiService.Notify(new NotificationMessage
{
Severity = NotificationSeverity.Success,
Summary = "Success",
Detail = "SubCA Certificate Export Success"
});
}
else
{
NotiService.Notify(new NotificationMessage
{
Severity = NotificationSeverity.Error,
Summary = "Failed",
Detail = "SubCA Certificate Export Failed",
});
}
}
DialogService.CloseIndicator();
}
async Task OnClickOcspDownload(CertificateHistoryModel? certificate)
{
DialogService.OpenIndicator("Export to Prov Certificate");
if (certificate != null)
{
string fileName = $"OCSP_{certificate.TTbscsr.CPcid}.txt";
bool exportResult = await JS.InvokeAsync<bool>("openSaveFileStr", fileName, certificate.TOcsp.COcsp);
if (exportResult == true)
{
NotiService.Notify(new NotificationMessage
{
Severity = NotificationSeverity.Success,
Summary = "Success",
Detail = "OCSP Export Success"
});
}
else
{
NotiService.Notify(new NotificationMessage
{
Severity = NotificationSeverity.Error,
Summary = "Failed",
Detail = "OCSP Certificate Export Failed",
});
}
}
DialogService.CloseIndicator();
}
private async Task OnExpand(CertificateHistoryModel history)
{
await Task.CompletedTask;
}
private async Task OnClickCopy(string? text)
{
await CopyToClipboard(text);
}
private async Task CopyToClipboard(string? text)
{
bool result = await JS.InvokeAsync<bool>("copyTextToClipboard", text);
if (result == true)
{
NotiService.Notify(new NotificationMessage
{
Severity = NotificationSeverity.Success,
Summary = "Success",
Detail = "Copy To Clipboard Success."
});
}
else
{
NotiService.Notify(new NotificationMessage
{
Severity = NotificationSeverity.Error,
Summary = "Failed",
Detail = "Copy To Clipboard Failed.",
});
}
}
}

View File

@ -0,0 +1,7 @@
@using Microsoft.AspNetCore.Components.Authorization
<Router AppAssembly="typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
</Router>

View File

@ -0,0 +1,24 @@
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using Radzen
@using Radzen.Blazor
@using Newtonsoft.Json
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using VPKI.Library.Model
@using VPKI.Library.Enums
@using VPKI.Library.Services
@using VPKI.Web.Client
@using VPKI.Web.Client.Components
@using VPKI.Web.Client.Services
@using VPKI.Web.Client.Model
@using VPKI.Web.Client.Components.Module

View File

@ -0,0 +1,15 @@
using VPKI.Library.Model;
using VPKI.Library.Packet;
namespace VPKI.Web.Client.Model
{
public class CertificateContainer
{
public Request_Certificate RequestCertificate { get; set; } = new();
public Response_Certificate ResponseCertificate { get; set; } = new();
public CsrHashedModel CsrHashed { get; set; } = new();
public string StrRequest { get; set; } = string.Empty;
public string StrResponse { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,23 @@
using Org.BouncyCastle.Crypto;
using VPKI.Library.Enums;
using VPKI.Library.Packet;
namespace VPKI.Web.Client.Model
{
public class TbscsrContainer
{
public Request_Tbscsr RequestTbscsr { get; set; } = new();
public Response_Tbscsr ResponseTbscsr { get; set; } = new();
public VpkiType VpkiType { get; set; }
public VpkiIsoType ISOType { get; set; }
public AsymmetricCipherKeyPair? KeyPair { get; set; }
public string PublicKey { get; set; } = string.Empty;
public string PrivateKey { get; set; } = string.Empty;
public string StrRequest { get; set; } = string.Empty;
public string StrResponse { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,13 @@
using VPKI.Library.Packet;
namespace VPKI.Web.Client.Model
{
public class VerifyResultContainer
{
public Request_Verifyresult RequestVerifyresult { get; set; } = new();
public Response_Verifyresult ResponseVerifyresult { get; set; } = new();
public string StrRequest { get; set; } = string.Empty;
public string StrResponse { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,89 @@
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Identity.Client;
using Radzen;
using VPKI.Library.Config;
using VPKI.Library.Services;
using VPKI.Web.Client.Components;
using VPKI.Web.Client.Services;
string configDir = @"../Config";
if (Log4net.IsConfigLoad == true)
{
Log4net.WriteLine("Log4net Init Success");
}
else
{
Log4net.WriteLine("Log4net Init Failed", LogType.Error);
}
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRadzenComponents();
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
//singleton
builder.Services.AddSingleton<ConfigService<WebClientConfig>>();
builder.Services.AddAuthentication();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped<AuthenticationStateProvider, ExtendAuthenticationStatProvicer>();
builder.Services.AddOutputCache();
//scope
builder.Services.AddScoped<ApiService>();
builder.Services.AddScoped<CertificateService>();
builder.Services.AddScoped<VpkiDialogService>();
var app = builder.Build();
//read client config and set
string serverUrl = string.Empty;
var configService = app.Services.GetService<ConfigService<WebClientConfig>>();
bool isIIS = false;
if (configService?.OpenConfig($@"{configDir}/VPKI.WebClientConfig.json") == true)
{
Log4net.WriteLine("WebClient Config Success.");
var clientConfig = ConfigService<WebClientConfig>.Config;
serverUrl = $"{clientConfig?.Server?.Address}:{clientConfig?.Server?.Port}";
isIIS = clientConfig!.Server.IIS;
}
else
{
Log4net.WriteLine("WebClient Config Error.");
return;
}
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
Log4net.WriteLine($"IsDevelopment:{app.Environment.IsDevelopment()}");
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
if (isIIS == true)
{
app.Run();
}
else
{
Log4net.WriteLine($"Operation Url: {serverUrl}");
app.Run($"{serverUrl}");
}

View File

@ -0,0 +1,38 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:9092",
"sslPort": 44389
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5231",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7139;http://localhost:5231",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,98 @@
using Azure;
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using System.Net;
using System.Net.Http;
using VPKI.Library.Config;
using VPKI.Library.Model;
using VPKI.Library.Services;
namespace VPKI.Web.Client.Services
{
public class ApiService : HttpService
{
private readonly ProtectedLocalStorage _localStorage;
private readonly ConfigService<WebClientConfig> _configService;
private readonly string SESSION_STATE = "sessionState";
public ApiService(ConfigService<WebClientConfig> configService,
ProtectedLocalStorage localStorage)
:base()
{
_localStorage = localStorage;
_configService = configService;
}
public async Task<RESPONSE?> PostJsonAsyncBearer<REQUEST, RESPONSE>(string url, REQUEST request, short timeOutSeconds = 5) where REQUEST : class where RESPONSE : class
{
RESPONSE? response = default(RESPONSE);
Guid guid = Guid.NewGuid();
var apiConfig = _configService?.GetConfig()?.Api?.Find(x => x.ApiName?.ToLower() == Consts.VPKI_API.ToLower());
string apiUrl = $"{apiConfig.Address}:{apiConfig.Port}";
using (HttpClient httpClient = new HttpClient(GetClientHandler()))
{
try
{
//bearer token set
var sessionModel = (await _localStorage.GetAsync<LoginResponseModel>(SESSION_STATE)).Value;
var token = sessionModel?.AccessToken;
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", $"{token}");
var timeOutSec = SetTimeout(timeOutSeconds);
httpClient.Timeout = new TimeSpan(0, 0, timeOutSec);
httpClient.BaseAddress = new Uri($"{apiUrl}{url}");
Log4net.WriteLine($"[POST] Request({guid})::{url}{Environment.NewLine}{request?.ToJson()}", LogType.Warn);
var res = await httpClient.PostAsJsonAsync(url, request);
response = await res.Content.ReadFromJsonAsync<RESPONSE>();
Log4net.WriteLine($"[POST] Rseponse({guid})::{url}{Environment.NewLine}{response?.ToJson()}", LogType.Warn);
}
catch (Exception e)
{
Log4net.WriteLine(e);
}
}
return response;
}
public async Task<RESPONSE?> GetJsonAsyncBearer<RESPONSE>(string url, short timeOutSeconds = 5) where RESPONSE : class
{
RESPONSE? response = default(RESPONSE);
Guid guid = Guid.NewGuid();
var apiConfig = _configService?.GetConfig()?.Api?.Find(x => x.ApiName?.ToLower() == Consts.VPKI_API.ToLower());
string apiUrl = $"{apiConfig.Address}:{apiConfig.Port}";
using (HttpClient httpClient = new HttpClient(GetClientHandler()))
{
try
{
//bearer token set
var sessionModel = (await _localStorage.GetAsync<LoginResponseModel>(SESSION_STATE)).Value;
var token = sessionModel?.AccessToken;
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", $"{token}");
var timeOutSec = SetTimeout(timeOutSeconds);
httpClient.Timeout = new TimeSpan(0, 0, timeOutSec);
httpClient.BaseAddress = new Uri($"{apiUrl}{url}");
Log4net.WriteLine($"[GET] Request({guid})::{url}{Environment.NewLine}", LogType.Warn);
var res = await httpClient.GetAsync(url);
response = await res.Content.ReadFromJsonAsync<RESPONSE>();
Log4net.WriteLine($"[GET] Rseponse({guid})::{url}{Environment.NewLine}{response?.ToJson()}", LogType.Warn);
}
catch (Exception e)
{
Log4net.WriteLine(e);
}
}
return response;
}
}
}

View File

@ -0,0 +1,75 @@
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using System.IdentityModel.Tokens.Jwt;
using System.Runtime.CompilerServices;
using System.Security.Claims;
using VPKI.Library.Model;
namespace VPKI.Web.Client.Services
{
public class ExtendAuthenticationStatProvicer(ProtectedLocalStorage localStorage) : AuthenticationStateProvider
{
public async override Task<AuthenticationState> GetAuthenticationStateAsync()
{
var sessionModel = (await localStorage.GetAsync<LoginResponseModel>("sessionState")).Value;
ClaimsIdentity identity = new ClaimsIdentity();
if(sessionModel?.AccessToken != null)
{
identity = sessionModel == null ? new ClaimsIdentity() : GetClaimsIdentity(sessionModel.AccessToken);
var user = new ClaimsPrincipal(identity);
return new AuthenticationState(user);
}
return new AuthenticationState(new ClaimsPrincipal(identity));
}
public async Task<string> GetAccessToken()
{
var sessionModel = (await localStorage.GetAsync<LoginResponseModel>("sessionState")).Value;
return $"{sessionModel?.AccessToken}";
}
public async Task<long> GetExpiredTimeAsync()
{
var sessionModel = (await localStorage.GetAsync<LoginResponseModel>("sessionState")).Value;
ClaimsIdentity identity = new ClaimsIdentity();
if (sessionModel?.AccessToken != null)
{
return sessionModel.AccessTokenExpired;
}
return -1;
}
public async Task MakeUserAsAuthenticated(LoginResponseModel model)
{
string accessToken = string.Empty;
if (model?.AccessToken != null)
{
accessToken = model.AccessToken;
await localStorage.SetAsync("sessionState", model);
var identity = GetClaimsIdentity(accessToken);
var user = new ClaimsPrincipal(identity);
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user)));
}
}
public async Task MakeUserLogout()
{
await localStorage.DeleteAsync("sessionState");
var identity = new ClaimsIdentity();
var user = new ClaimsPrincipal(identity);
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user)));
}
private ClaimsIdentity GetClaimsIdentity(string token)
{
var handler = new JwtSecurityTokenHandler();
var jwtToken = handler.ReadJwtToken(token);
var claims = jwtToken.Claims;
return new ClaimsIdentity(claims,"jwt");
}
}
}

View File

@ -0,0 +1,45 @@
using Microsoft.AspNetCore.Components;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Radzen;
using VPKI.Web.Client.Components.Dialog;
namespace VPKI.Web.Client.Services
{
public class VpkiDialogService
{
public DialogService _dialogService;
public VpkiDialogService(DialogService dialogService)
{
_dialogService = dialogService;
}
public async Task<bool> Confirm(string title, string message)
{
bool result = false;
result = await _dialogService.Confirm(message, title, new ConfirmOptions
{
OkButtonText = "OK",
CancelButtonText = "Cancel",
})??false;
return result;
}
public void OpenIndicator(string message = "")
{
_dialogService.Open<Loading>("",
new Dictionary<string, object>() { { "Message", message } },
new DialogOptions
{
ShowClose = false,
ShowTitle =false,
Style = "box-shadow: none; background: transparent; min-width:0; min-height:0; width:auto;"
});
}
public void CloseIndicator()
{
_dialogService.Close();
}
}
}

Some files were not shown because too many files have changed in this diff Show More