Zero Confusion Policy: Part 3 – Solution Design Using AI

In this article, I’ll walk you through how I designed a complete solution for a user story using ChatGPT prompts — without writing a single line of code in an IDE. All of this was done in the browser!

Note: This is not a tutorial on using ChatGPT to design software — this is about how we used it to design a feature and link it to a user story. If a developer implements a feature based on this solution design, it can be 5x faster and significantly reduce bugs and design flaws that cost time and quality.

🧩 Problem Statement

The client requested an eSignature feature to sign PDF documents. Users should be able to draw signatures using an iPad, and these should be applied directly to the signature area of the PDF.

💡 Solution Design with AI

I started breaking down the problem using a sequence of prompts to ChatGPT.

Prompt 1
"I want to integrate a document eSignature SDK into my .NET Core 9 app. Is there any free, open-source library for this? Also, I need to deploy it on a Linux-based server, so it must be platform-independent."

ChatGPT listed several free and open-source eSignature SDKs along with their pros, cons, and recommendations.

Prompt 2
"I need a digital signature feature, where the user can sign on the iPad screen."

ChatGPT suggested using Signature Pad JS for the frontend and iText7 (Community Edition) for the backend. It gave a full implementation guide, this time using Angular instead of Blazor.

Prompt 3
"Can you provide a complete digital signature implementation using iText7 with .NET Core 9 and an Angular frontend?"

Now it gave a full solution with frontend and backend code. But I needed to clarify one last thing…

Prompt 4
"How can I draw a signature on the canvas? I need to open a PDF document in a PDF viewer, and the user should be able to sign directly on the actual signature area. How can I achieve this?"

This final prompt triggered ChatGPT to give exactly what I needed — a complete Angular + .NET Core solution to:

  • Draw a signature on the PDF
  • Save it as an image
  • Embed it on the PDF
  • Apply a legal digital signature

✅ Final Solution: eSignature SDK Integration

📋 Step-by-Step Flow

1️⃣ Open the PDF in a Viewer (Frontend)
2️⃣ Allow Users to Draw Their Signature on the PDF (Frontend)
3️⃣ Capture the Signature as an Image (PNG/SVG) (Frontend)
4️⃣ Embed the Signature on the PDF in the Correct Location (Backend with iText7)
5️⃣ Digitally Sign the PDF (Backend with iText7)

Note: The .NET Core 9 API signs the PDF using an X.509 certificate (PFX file) and Legally binding digital signature with SHA-256 encryption.

🌐 Angular Frontend

1️⃣ Install PDF.js for PDF Viewing

Install the ngx-extended-pdf-viewer package for rendering PDFs in Angular.

npm install ngx-extended-pdf-viewer
2️⃣ Signature Component

📌 signature.component.ts

import { Component, ViewChild, ElementRef } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-signature',
  templateUrl: './signature.component.html',
  styleUrls: ['./signature.component.css']
})
export class SignatureComponent {
  @ViewChild('canvas', { static: false }) canvas!: ElementRef<HTMLCanvasElement>;
  private ctx!: CanvasRenderingContext2D | null;
  isDrawing = false;
  selectedFile: File | null = null;

  constructor(private http: HttpClient) {}

  ngAfterViewInit() {
    this.ctx = this.canvas.nativeElement.getContext('2d');
  }

  onMouseDown(event: MouseEvent) {
    this.isDrawing = true;
    this.ctx!.beginPath();
    this.ctx!.moveTo(event.offsetX, event.offsetY);
  }

  onMouseMove(event: MouseEvent) {
    if (!this.isDrawing) return;
    this.ctx!.lineTo(event.offsetX, event.offsetY);
    this.ctx!.stroke();
  }

  onMouseUp() {
    this.isDrawing = false;
  }

  clearCanvas() {
    this.ctx!.clearRect(0, 0, this.canvas.nativeElement.width, this.canvas.nativeElement.height);
  }

  saveSignature() {
    const signatureData = this.canvas.nativeElement.toDataURL('image/png');
    const formData = new FormData();
    formData.append('file', this.selectedFile!);
    formData.append('signature', signatureData);

    this.http.post('http://localhost:5000/api/signature/sign', formData, { responseType: 'blob' })
      .subscribe((blob: Blob) => {
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = 'Signed_' + this.selectedFile!.name;
        document.body.appendChild(a);
        a.click();
        window.URL.revokeObjectURL(url);
      });
  }

  onFileSelected(event: any) {
    this.selectedFile = event.target.files[0];
  }
}

📌 signature.component.html

<div class="container">
  <h2>Upload PDF & Draw Signature</h2>
  <input type="file" (change)="onFileSelected($event)" accept="application/pdf">
  
  <ngx-extended-pdf-viewer [src]="selectedFile ? selectedFile.name : ''" [height]="'500px'">
  </ngx-extended-pdf-viewer>

  <h3>Draw Your Signature</h3>
  <canvas #canvas width="400" height="200" 
          (mousedown)="onMouseDown($event)" 
          (mousemove)="onMouseMove($event)" 
          (mouseup)="onMouseUp()"></canvas>
  <button (click)="clearCanvas()">Clear</button>
  <button (click)="saveSignature()">Sign & Download</button>
</div>

📌 signature.component.css

.container {
  text-align: center;
  padding: 20px;
}
canvas {
  border: 1px solid black;
  background-color: white;
  cursor: crosshair;
}
button {
  margin-top: 10px;
  padding: 10px;
  background-color: blue;
  color: white;
  border: none;
  cursor: pointer;
}
button:hover {
  background-color: darkblue;
}

🖥️ Backend (.NET Core 9 + iText7)

1️⃣ Install NuGet Packages
dotnet add package itext7
dotnet add package BouncyCastle.NetCore
dotnet add package SkiaSharp
2️⃣ DigitalSignatureController.cs
using iText.Kernel.Pdf;
using iText.Signatures;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using SkiaSharp;

[Route("api/signature")]
[ApiController]
public class DigitalSignatureController : ControllerBase
{
    private readonly string _pfxPath = "wwwroot/certificates/signature.pfx"; 
    private readonly string _pfxPassword = "123456"; 

    [HttpPost("sign")]
    public async Task<IActionResult> SignPdf([FromForm] IFormFile file, [FromForm] string signature)
    {
        if (file == null || file.Length == 0)
            return BadRequest("No file uploaded");

        byte[] imageBytes = Convert.FromBase64String(signature.Replace("data:image/png;base64,", ""));
        string tempFilePath = Path.GetTempFileName();
        await using (var stream = new FileStream(tempFilePath, FileMode.Create))
        {
            await file.CopyToAsync(stream);
        }

        string signedFilePath = Path.Combine(Path.GetTempPath(), "Signed_" + file.FileName);
        EmbedSignatureOnPdf(tempFilePath, signedFilePath, imageBytes);
        DigitallySignPdf(signedFilePath, _pfxPath, _pfxPassword);

        byte[] signedPdfBytes = await System.IO.File.ReadAllBytesAsync(signedFilePath);
        return File(signedPdfBytes, "application/pdf", "Signed_" + file.FileName);
    }

    private void EmbedSignatureOnPdf(string inputPdf, string outputPdf, byte[] signatureImage)
    {
        using (var reader = new PdfReader(inputPdf))
        using (var writer = new PdfWriter(outputPdf))
        using (var pdfDoc = new PdfDocument(reader, writer))
        {
            var document = new iText.Layout.Document(pdfDoc);
            var page = pdfDoc.GetPage(1);

            var img = new iText.IO.Image.ImageDataFactory.Create(signatureImage);
            var image = new iText.Layout.Element.Image(img)
                .SetFixedPosition(100, 100) // Set X, Y Position
                .ScaleToFit(150, 50); // Resize Signature

            document.Add(image);
        }
    }

    private void DigitallySignPdf(string pdfPath, string pfxPath, string pfxPassword)
    {
        Pkcs12Store store = new Pkcs12Store(new FileStream(pfxPath, FileMode.Open, FileAccess.Read), pfxPassword.ToCharArray());
        string alias = store.Aliases.Cast<string>().FirstOrDefault(store.IsKeyEntry);
        ICipherParameters privateKey = store.GetKey(alias).Key;
        var chain = store.GetCertificateChain(alias).Select(c => c.Certificate).ToArray();

        using (var reader = new PdfReader(pdfPath))
        using (var writer = new PdfWriter(pdfPath + "_signed.pdf"))
        using (var signer = new PdfSigner(reader, writer, new StampingProperties()))
        {
            PdfSignatureAppearance appearance = signer.GetSignatureAppearance();
            appearance.SetReason("User Signed")
                      .SetLocation("Online");

            IExternalSignature pks = new PrivateKeySignature(privateKey, DigestAlgorithms.SHA256);
            IExternalDigest digest = new BouncyCastleDigest();
            signer.SignDetached(digest, pks, chain, null, null, null, 0, PdfSigner.CryptoStandard.CMS);
        }
    }
}
  • Reads uploaded PDF and signature
  • Embeds the signature image
  • Digitally signs the PDF using a .pfx certificate
3️⃣ Self-Signed Certificate (For Testing)
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365
openssl pkcs12 -export -out signature.pfx -inkey key.pem -in cert.pem

🎯 Result

With just a few prompts, I created a fully functional, production-grade feature. The AI-designed solution can be handed directly to a developer for faster, mistake-free implementation — all without diving into an IDE!

Angular PDF Viewer to load PDFs
Canvas-based Signature Drawing
Signature embedded into PDF at correct location
Digitally signed with iText7
Deployable on Linux

💬 Final Thoughts

This is the power of modern software design using AI. ChatGPT isn’t just a coding assistant — it’s a solution design partner. And when you link that design to a real user story, you’re enabling your team to move faster, make fewer mistakes, and ship better software.

🎯 Reference

https://github.com/itext/itext-dotnet

https://kb.itextpdf.com/itext/digital-signatures-hub