diff --git a/MiniKms/src/main/java/ftn/security/minikms/controller/SignatureController.java b/MiniKms/src/main/java/ftn/security/minikms/controller/SignatureController.java index 75f69f0..579f2c6 100644 --- a/MiniKms/src/main/java/ftn/security/minikms/controller/SignatureController.java +++ b/MiniKms/src/main/java/ftn/security/minikms/controller/SignatureController.java @@ -35,7 +35,7 @@ public ResponseEntity sign(@RequestParam UUID keyId, @PostMapping("/verify") @Transactional(readOnly = true) - public ResponseEntity verify(@RequestParam UUID keyId, + public ResponseEntity verify(@RequestParam UUID keyId, @RequestParam(required = false) Integer version, @RequestBody VerifyRequestDTO req) { try { @@ -45,7 +45,7 @@ public ResponseEntity verify(@RequestParam UUID keyId, Base64.getDecoder().decode(req.getSignature()), version ); - return ResponseEntity.ok(valid ? "VALID" : "INVALID"); + return ResponseEntity.ok(valid); } catch (GeneralSecurityException | IllegalArgumentException e) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage()); } diff --git a/MiniKms/src/main/java/ftn/security/minikms/entity/KeyMetadata.java b/MiniKms/src/main/java/ftn/security/minikms/entity/KeyMetadata.java index a138c3d..db04627 100644 --- a/MiniKms/src/main/java/ftn/security/minikms/entity/KeyMetadata.java +++ b/MiniKms/src/main/java/ftn/security/minikms/entity/KeyMetadata.java @@ -2,6 +2,7 @@ import ftn.security.minikms.enumeration.KeyOperation; import ftn.security.minikms.enumeration.KeyType; +import ftn.security.minikms.logging.EntityLogger; import jakarta.persistence.*; import lombok.Data; import lombok.EqualsAndHashCode; @@ -16,6 +17,7 @@ @EqualsAndHashCode(onlyExplicitlyIncluded = true) @Entity @Table(name = "keys") +@EntityListeners(EntityLogger.class) public class KeyMetadata { @EqualsAndHashCode.Include @Id diff --git a/MiniKmsClient/angular.json b/MiniKmsClient/angular.json index c9444ff..556a3aa 100644 --- a/MiniKmsClient/angular.json +++ b/MiniKmsClient/angular.json @@ -111,5 +111,8 @@ } } } + }, + "cli": { + "analytics": false } } diff --git a/MiniKmsClient/src/app/api.service.ts b/MiniKmsClient/src/app/api.service.ts index 409f9f5..7c6f69e 100644 --- a/MiniKmsClient/src/app/api.service.ts +++ b/MiniKmsClient/src/app/api.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { Observable } from 'rxjs'; +import { map, Observable } from 'rxjs'; import { environment } from '../environments/environment'; export interface LoginRequest { @@ -22,6 +22,25 @@ export interface KeyMetadata { rotatedAt?: Date; } +export interface CryptoDTO { + message: string; + keyId: string; + version: number | null; + hmacBase64: string | null; +} + +export interface SignRequestDTO{ + message: string; + version: number | null; +} + +export interface VerifyRequestDTO{ + message: string; + signature: string; +} + +export type EncryptType = "SYMMETRIC" | "ASYMMETRIC"; + @Injectable({ providedIn: 'root' }) @@ -49,4 +68,61 @@ export class ApiService { deleteKey(id: string): Observable { return this.http.delete(`${this.baseUrl}/keys/${id}`); } -} + + encrypt(cryptoDTO: CryptoDTO, encryptType: EncryptType): Observable { + const url = encryptType === 'SYMMETRIC' + ? `${this.baseUrl}/crypto/encrypt/symmetric` + : `${this.baseUrl}/crypto/encrypt/asymmetric`; + + return this.http.post(url, cryptoDTO, { responseType: 'text' }); + } + + decrypt(cryptoDTO: CryptoDTO, encryptType: EncryptType): Observable { + const url = encryptType === 'SYMMETRIC' + ? `${this.baseUrl}/crypto/decrypt/symmetric` + : `${this.baseUrl}/crypto/decrypt/asymmetric`; + + return this.http.post(url, cryptoDTO, { responseType: 'text' }); + } + + computeHmac(cryptoDTO: CryptoDTO): Observable { + return this.http.post(`${this.baseUrl}/crypto/compute/hmac`, cryptoDTO, { responseType: 'text' }); + } + + verifyHmac(cryptoDTO: CryptoDTO): Observable { + return this.http.post(`${this.baseUrl}/crypto/verify/hmac`, cryptoDTO, { responseType: 'text' }) + .pipe( + map(res => res === 'true') // convert "true"/"false" string into boolean + ); + } + + + sign(keyId: string, signRequestDTO: SignRequestDTO): Observable { + return this.http.post( + `${this.baseUrl}/signatures/sign`, + signRequestDTO, + { + params: { keyId }, + responseType: 'text' // <-- treat response as plain text + } + ); + } + + verify(keyId: string, verifyRequestDTO: VerifyRequestDTO, version: number | null): Observable { + const params: any = { keyId }; + if (version !== null) { + params.version = version.toString(); + } + + return this.http.post( + `${this.baseUrl}/signatures/verify`, + verifyRequestDTO, + { + params, + responseType: 'text' // <-- treat response as plain text + } + ).pipe( + map(res => res === 'true') // convert "true"/"false" string into boolean + ); + } +} \ No newline at end of file diff --git a/MiniKmsClient/src/app/app-routing.module.ts b/MiniKmsClient/src/app/app-routing.module.ts index bafe6d1..cf3c7f4 100644 --- a/MiniKmsClient/src/app/app-routing.module.ts +++ b/MiniKmsClient/src/app/app-routing.module.ts @@ -3,7 +3,8 @@ import { RouterModule, Routes } from '@angular/router'; import { authGuard } from './auth/auth.guard'; import { ManageKeysComponent } from './manage-keys/manage-keys.component'; import { LoginComponent } from './login/login.component'; -import { CryptoComponent } from './crypto/crypto.component'; +import { CryptoOpsComponent } from './crypto/crypto-ops/crypto-ops.component'; +import { SignatureOpsComponent } from './crypto/signature-ops/signature-ops.component'; const routes: Routes = [ { path: '', redirectTo: '/login', pathMatch: 'full' }, @@ -16,7 +17,13 @@ const routes: Routes = [ }, { path: 'crypto', - component: CryptoComponent, + component: CryptoOpsComponent, + canActivate: [authGuard], + data: { roles: ['manager', 'user'] } + }, + { + path: 'sertificates', + component: SignatureOpsComponent, canActivate: [authGuard], data: { roles: ['manager', 'user'] } }, diff --git a/MiniKmsClient/src/app/app.component.html b/MiniKmsClient/src/app/app.component.html index 1e7dd38..2841d95 100644 --- a/MiniKmsClient/src/app/app.component.html +++ b/MiniKmsClient/src/app/app.component.html @@ -16,6 +16,11 @@ Crypto + + + Sertificates + + + + + + + +
+ + Result + {{ result }} + +
+ + + diff --git a/MiniKmsClient/src/app/crypto/crypto-ops/crypto-ops.component.scss b/MiniKmsClient/src/app/crypto/crypto-ops/crypto-ops.component.scss new file mode 100644 index 0000000..666ba5a --- /dev/null +++ b/MiniKmsClient/src/app/crypto/crypto-ops/crypto-ops.component.scss @@ -0,0 +1,51 @@ +:host { + display: flex; + justify-content: center; + padding: 30px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + min-height: 100%; + box-sizing: border-box; +} + +.crypto-form { + width: 70%; + max-width: 900px; + background: white; + padding: 24px; + border-radius: 12px; + box-shadow: 0 6px 18px rgba(0,0,0,0.2); + display: flex; + flex-direction: column; + gap: 20px; +} + +.row { + display: flex; + gap: 16px; + + .flex-1 { flex: 1; } + .flex-2 { flex: 2; } +} + +.full-width { + width: 100%; +} + +.buttons { + justify-content: center; + flex-wrap: wrap; + gap: 16px; +} + +.result-box { + margin-top: 20px; + width: 100%; + display: block; +} + +.result-box mat-card { + word-break: break-word; + display: flex; + flex-direction: column; + gap: 20px; +} diff --git a/MiniKmsClient/src/app/crypto/crypto.component.spec.ts b/MiniKmsClient/src/app/crypto/crypto-ops/crypto-ops.component.spec.ts similarity index 53% rename from MiniKmsClient/src/app/crypto/crypto.component.spec.ts rename to MiniKmsClient/src/app/crypto/crypto-ops/crypto-ops.component.spec.ts index f8a710b..d57041d 100644 --- a/MiniKmsClient/src/app/crypto/crypto.component.spec.ts +++ b/MiniKmsClient/src/app/crypto/crypto-ops/crypto-ops.component.spec.ts @@ -1,18 +1,18 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { CryptoComponent } from './crypto.component'; +import { CryptoOpsComponent } from './crypto-ops.component'; -describe('CryptoComponent', () => { - let component: CryptoComponent; - let fixture: ComponentFixture; +describe('CryptoOpsComponent', () => { + let component: CryptoOpsComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [CryptoComponent] + declarations: [CryptoOpsComponent] }) .compileComponents(); - fixture = TestBed.createComponent(CryptoComponent); + fixture = TestBed.createComponent(CryptoOpsComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/MiniKmsClient/src/app/crypto/crypto-ops/crypto-ops.component.ts b/MiniKmsClient/src/app/crypto/crypto-ops/crypto-ops.component.ts new file mode 100644 index 0000000..bed50fd --- /dev/null +++ b/MiniKmsClient/src/app/crypto/crypto-ops/crypto-ops.component.ts @@ -0,0 +1,61 @@ +import { Component } from '@angular/core'; +import { ApiService, CryptoDTO, EncryptType } from '../../api.service'; + +@Component({ + selector: 'app-crypto-ops', + templateUrl: './crypto-ops.component.html', + styleUrls: ['./crypto-ops.component.scss'] +}) +export class CryptoOpsComponent { + message: string = ''; + keyId: string = ''; + version: number | null = null; + hmacBase64: string | null = null; + encryptType: EncryptType = 'SYMMETRIC'; + + result: string = ''; + isLoading = false; + + constructor(private apiService: ApiService) {} + + private buildDTO(): CryptoDTO { + return { + message: this.message, + keyId: this.keyId, + version: this.version, + hmacBase64: this.hmacBase64 + }; + } + + doEncrypt() { + this.isLoading = true; + this.apiService.encrypt(this.buildDTO(), this.encryptType).subscribe({ + next: res => { this.result = res; this.isLoading = false; }, + error: (err) => {this.isLoading = false; console.log(err);} + }); + } + + doDecrypt() { + this.isLoading = true; + this.apiService.decrypt(this.buildDTO(), this.encryptType).subscribe({ + next: res => { this.result = res; this.isLoading = false; }, + error: (err) => {this.isLoading = false; console.log(err);} + }); + } + + computeHmac() { + this.isLoading = true; + this.apiService.computeHmac(this.buildDTO()).subscribe({ + next: res => { this.result = res; this.isLoading = false; }, + error: () => this.isLoading = false + }); + } + + verifyHmac() { + this.isLoading = true; + this.apiService.verifyHmac(this.buildDTO()).subscribe({ + next: res => { this.result = res ? "VALID" : "INVALID"; this.isLoading = false; }, + error: () => this.isLoading = false + }); + } +} diff --git a/MiniKmsClient/src/app/crypto/crypto.component.html b/MiniKmsClient/src/app/crypto/crypto.component.html deleted file mode 100644 index 25ec4ed..0000000 --- a/MiniKmsClient/src/app/crypto/crypto.component.html +++ /dev/null @@ -1 +0,0 @@ -

crypto works!

diff --git a/MiniKmsClient/src/app/crypto/crypto.component.scss b/MiniKmsClient/src/app/crypto/crypto.component.scss deleted file mode 100644 index e69de29..0000000 diff --git a/MiniKmsClient/src/app/crypto/crypto.component.ts b/MiniKmsClient/src/app/crypto/crypto.component.ts deleted file mode 100644 index 026de30..0000000 --- a/MiniKmsClient/src/app/crypto/crypto.component.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-crypto', - templateUrl: './crypto.component.html', - styleUrl: './crypto.component.scss' -}) -export class CryptoComponent { - -} diff --git a/MiniKmsClient/src/app/crypto/signature-ops/signature-ops.component.html b/MiniKmsClient/src/app/crypto/signature-ops/signature-ops.component.html new file mode 100644 index 0000000..27bc5eb --- /dev/null +++ b/MiniKmsClient/src/app/crypto/signature-ops/signature-ops.component.html @@ -0,0 +1,38 @@ +
+ + + Message + + + +
+ + Key ID (UUID) + + + + + Version (optional) + + +
+ + + Signature (Base64) + + + +
+ + +
+ +
+ + Result + {{ result }} + +
+ + +
diff --git a/MiniKmsClient/src/app/crypto/signature-ops/signature-ops.component.scss b/MiniKmsClient/src/app/crypto/signature-ops/signature-ops.component.scss new file mode 100644 index 0000000..ab7a834 --- /dev/null +++ b/MiniKmsClient/src/app/crypto/signature-ops/signature-ops.component.scss @@ -0,0 +1,53 @@ +:host { + display: flex; + justify-content: center; + padding: 30px; + background: linear-gradient(135deg, #43cea2 0%, #185a9d 100%); + min-height: 100%; + box-sizing: border-box; +} + +.signature-form { + width: 70%; + max-width: 900px; + background: white; + padding: 24px; + border-radius: 12px; + box-shadow: 0 6px 18px rgba(0,0,0,0.2); + display: flex; + flex-direction: column; + gap: 20px; +} + +.row { + display: flex; + gap: 16px; + + .flex-1 { flex: 1; } + .flex-2 { flex: 2; } +} + +.full-width { + width: 100%; +} + +.buttons { + justify-content: center; + flex-wrap: wrap; + gap: 16px; +} + +.result-box { + margin-top: 20px; + width: 100%; + display: block; /* ensures it’s a block element */ +} + +.result-box mat-card { + word-break: break-word; /* allows long Base64 strings to wrap */ + display: flex; + flex-direction: column; + gap: 20px; +} + + diff --git a/MiniKmsClient/src/app/crypto/signature-ops/signature-ops.component.spec.ts b/MiniKmsClient/src/app/crypto/signature-ops/signature-ops.component.spec.ts new file mode 100644 index 0000000..c2d7adc --- /dev/null +++ b/MiniKmsClient/src/app/crypto/signature-ops/signature-ops.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SignatureOpsComponent } from './signature-ops.component'; + +describe('SignatureOpsComponent', () => { + let component: SignatureOpsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [SignatureOpsComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(SignatureOpsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/MiniKmsClient/src/app/crypto/signature-ops/signature-ops.component.ts b/MiniKmsClient/src/app/crypto/signature-ops/signature-ops.component.ts new file mode 100644 index 0000000..b0ad25b --- /dev/null +++ b/MiniKmsClient/src/app/crypto/signature-ops/signature-ops.component.ts @@ -0,0 +1,51 @@ +import { Component } from '@angular/core'; +import { ApiService, SignRequestDTO, VerifyRequestDTO } from '../../api.service'; + +@Component({ + selector: 'app-signature-ops', + templateUrl: './signature-ops.component.html', + styleUrls: ['./signature-ops.component.scss'] +}) +export class SignatureOpsComponent { + keyId: string = ''; + version: number | null = null; + + message: string = ''; + signatureBase64: string = ''; + + result: string = ''; + isLoading = false; + + constructor(private apiService: ApiService) {} + + doSign() { + console.log('Token:', localStorage.getItem('token')); + this.isLoading = true; + const dto: SignRequestDTO = { message: this.message, version: this.version }; + + this.apiService.sign(this.keyId, dto).subscribe({ + next: res => { + console.log("YEEEEY"); + this.result = res; + this.isLoading = false; + }, + error: (err) => {this.isLoading = false; console.log("Noooo"); console.log(err);} + }); + } + + doVerify() { + this.isLoading = true; + const dto: VerifyRequestDTO = { + message: this.message, + signature: this.signatureBase64 + }; + + this.apiService.verify(this.keyId, dto, this.version).subscribe({ + next: res => { + this.result = res ? 'VALID' : 'INVALID'; + this.isLoading = false; + }, + error: () => this.isLoading = false + }); + } +}