Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public ResponseEntity<String> sign(@RequestParam UUID keyId,

@PostMapping("/verify")
@Transactional(readOnly = true)
public ResponseEntity<String> verify(@RequestParam UUID keyId,
public ResponseEntity<?> verify(@RequestParam UUID keyId,
@RequestParam(required = false) Integer version,
@RequestBody VerifyRequestDTO req) {
try {
Expand All @@ -45,7 +45,7 @@ public ResponseEntity<String> 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());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -16,6 +17,7 @@
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Entity
@Table(name = "keys")
@EntityListeners(EntityLogger.class)
public class KeyMetadata {
@EqualsAndHashCode.Include
@Id
Expand Down
3 changes: 3 additions & 0 deletions MiniKmsClient/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,8 @@
}
}
}
},
"cli": {
"analytics": false
}
}
80 changes: 78 additions & 2 deletions MiniKmsClient/src/app/api.service.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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'
})
Expand Down Expand Up @@ -49,4 +68,61 @@ export class ApiService {
deleteKey(id: string): Observable<void> {
return this.http.delete<void>(`${this.baseUrl}/keys/${id}`);
}
}

encrypt(cryptoDTO: CryptoDTO, encryptType: EncryptType): Observable<string> {
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<string> {
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<string> {
return this.http.post(`${this.baseUrl}/crypto/compute/hmac`, cryptoDTO, { responseType: 'text' });
}

verifyHmac(cryptoDTO: CryptoDTO): Observable<boolean> {
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<string> {
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<boolean> {
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
);
}
}
11 changes: 9 additions & 2 deletions MiniKmsClient/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' },
Expand All @@ -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'] }
},
Expand Down
5 changes: 5 additions & 0 deletions MiniKmsClient/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
Crypto
</a>

<!-- Both manager and user can see sertificates -->
<a mat-button routerLink="/sertificates">
Sertificates
</a>

<!-- Logout button for authenticated users -->
<button mat-button (click)="logout()">
<mat-icon>logout</mat-icon>
Expand Down
6 changes: 4 additions & 2 deletions MiniKmsClient/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
import { ManageKeysComponent } from './manage-keys/manage-keys.component';
import { CryptoComponent } from './crypto/crypto.component';
import { MaterialModule } from './common/material/material.module';
import { AuthInterceptorService } from './auth/auth.interceptor';
import { CryptoOpsComponent } from './crypto/crypto-ops/crypto-ops.component';
import { SignatureOpsComponent } from './crypto/signature-ops/signature-ops.component';

@NgModule({
declarations: [
AppComponent,
LoginComponent,
ManageKeysComponent,
CryptoComponent
CryptoOpsComponent,
SignatureOpsComponent,
],
imports: [
BrowserModule,
Expand Down
15 changes: 13 additions & 2 deletions MiniKmsClient/src/app/auth/auth.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,22 @@ export const authInterceptor: HttpInterceptorFn = (req, next) => {
});
}

console.log('Outgoing request:', {
url: authReq.url,
method: authReq.method,
headers: authReq.headers.keys().reduce((acc, key) => {
acc[key] = authReq.headers.get(key);
return acc;
}, {} as any),
body: authReq.body,
params: authReq.params.toString()
});

return next(authReq).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === 401 || error.status === 403) {
localStorage.removeItem('token');
router.navigate(['/login']);
// localStorage.removeItem('token');
// router.navigate(['/login']);
}
return throwError(() => error);
})
Expand Down
48 changes: 48 additions & 0 deletions MiniKmsClient/src/app/crypto/crypto-ops/crypto-ops.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<div class="crypto-form">

<mat-form-field appearance="outline" class="full-width">
<mat-label>Message</mat-label>
<textarea matInput rows="4" [(ngModel)]="message"></textarea>
</mat-form-field>

<div class="row">
<mat-form-field appearance="outline" class="flex-2">
<mat-label>Key ID (UUID)</mat-label>
<input matInput [(ngModel)]="keyId">
</mat-form-field>

<mat-form-field appearance="outline" class="flex-1">
<mat-label>Version</mat-label>
<input matInput type="number" [(ngModel)]="version">
</mat-form-field>
</div>

<mat-form-field appearance="outline" class="full-width">
<mat-label>Encrypt Type</mat-label>
<mat-select [(ngModel)]="encryptType">
<mat-option value="SYMMETRIC">Symmetric</mat-option>
<mat-option value="ASYMMETRIC">Asymmetric</mat-option>
</mat-select>
</mat-form-field>

<mat-form-field appearance="outline" class="full-width">
<mat-label>HMAC (Base64)</mat-label>
<textarea matInput rows="2" [(ngModel)]="hmacBase64"></textarea>
</mat-form-field>

<div class="row buttons">
<button mat-raised-button color="primary" (click)="doEncrypt()">Encrypt</button>
<button mat-raised-button color="accent" (click)="doDecrypt()">Decrypt</button>
<button mat-raised-button color="primary" (click)="computeHmac()">Compute HMAC</button>
<button mat-raised-button color="accent" (click)="verifyHmac()">Verify HMAC</button>
</div>

<div class="result-box" *ngIf="result">
<mat-card>
<mat-card-title>Result</mat-card-title>
<mat-card-content>{{ result }}</mat-card-content>
</mat-card>
</div>

<mat-progress-spinner *ngIf="isLoading" mode="indeterminate"></mat-progress-spinner>
</div>
51 changes: 51 additions & 0 deletions MiniKmsClient/src/app/crypto/crypto-ops/crypto-ops.component.scss
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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<CryptoComponent>;
describe('CryptoOpsComponent', () => {
let component: CryptoOpsComponent;
let fixture: ComponentFixture<CryptoOpsComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [CryptoComponent]
declarations: [CryptoOpsComponent]
})
.compileComponents();

fixture = TestBed.createComponent(CryptoComponent);
fixture = TestBed.createComponent(CryptoOpsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
Expand Down
Loading
Loading