mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-04-25 01:06:15 -04:00
feat(new tool): pdf signature checker (#745)
This commit is contained in:
parent
205e360400
commit
478192065e
20 changed files with 448 additions and 21 deletions
|
@ -60,9 +60,11 @@ const ibanExamples = [
|
|||
<div>
|
||||
<c-input-text v-model:value="rawIban" placeholder="Enter an IBAN to check for validity..." test-id="iban-input" />
|
||||
|
||||
<c-key-value-list :items="ibanInfo" my-5 data-test-id="iban-info" />
|
||||
<c-card v-if="ibanInfo.length > 0" mt-5>
|
||||
<c-key-value-list :items="ibanInfo" data-test-id="iban-info" />
|
||||
</c-card>
|
||||
|
||||
<c-card title="Valid IBAN examples">
|
||||
<c-card title="Valid IBAN examples" mt-5>
|
||||
<div v-for="iban in ibanExamples" :key="iban">
|
||||
<c-text-copyable :value="iban" font-mono :displayed-value="friendlyFormatIBAN(iban)" />
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { tool as base64FileConverter } from './base64-file-converter';
|
||||
import { tool as base64StringConverter } from './base64-string-converter';
|
||||
import { tool as basicAuthGenerator } from './basic-auth-generator';
|
||||
import { tool as pdfSignatureChecker } from './pdf-signature-checker';
|
||||
import { tool as numeronymGenerator } from './numeronym-generator';
|
||||
import { tool as macAddressGenerator } from './mac-address-generator';
|
||||
import { tool as textToBinary } from './text-to-binary';
|
||||
|
@ -78,7 +79,7 @@ import { tool as xmlFormatter } from './xml-formatter';
|
|||
export const toolsByCategory: ToolCategory[] = [
|
||||
{
|
||||
name: 'Crypto',
|
||||
components: [tokenGenerator, hashText, bcrypt, uuidGenerator, ulidGenerator, cypher, bip39, hmacGenerator, rsaKeyPairGenerator, passwordStrengthAnalyser],
|
||||
components: [tokenGenerator, hashText, bcrypt, uuidGenerator, ulidGenerator, cypher, bip39, hmacGenerator, rsaKeyPairGenerator, passwordStrengthAnalyser, pdfSignatureChecker],
|
||||
},
|
||||
{
|
||||
name: 'Converter',
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
<script setup lang="ts">
|
||||
import type { SignatureInfo } from '../pdf-signature-checker.types';
|
||||
|
||||
const props = defineProps<{ signature: SignatureInfo }>();
|
||||
const { signature } = toRefs(props);
|
||||
|
||||
const tableHeaders = {
|
||||
validityPeriod: 'Validity period',
|
||||
issuedBy: 'Issued by',
|
||||
issuedTo: 'Issued to',
|
||||
pemCertificate: 'PEM certificate',
|
||||
};
|
||||
|
||||
const certs = computed(() => signature.value.meta.certs.map((certificate, index) => ({
|
||||
...certificate,
|
||||
validityPeriod: {
|
||||
notBefore: new Date(certificate.validityPeriod.notBefore).toLocaleString(),
|
||||
notAfter: new Date(certificate.validityPeriod.notAfter).toLocaleString(),
|
||||
},
|
||||
certificateName: `Certificate ${index + 1}`,
|
||||
})),
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div flex flex-col gap-2>
|
||||
<c-table :data="certs" :headers="tableHeaders">
|
||||
<template #validityPeriod="{ value }">
|
||||
<c-key-value-list
|
||||
:items="[{
|
||||
label: 'Not before',
|
||||
value: value.notBefore,
|
||||
}, {
|
||||
label: 'Not after',
|
||||
value: value.notAfter,
|
||||
}]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #issuedBy="{ value }">
|
||||
<c-key-value-list
|
||||
:items="[{
|
||||
label: 'Common name',
|
||||
value: value.commonName,
|
||||
}, {
|
||||
label: 'Organization name',
|
||||
value: value.organizationName,
|
||||
}, {
|
||||
label: 'Country name',
|
||||
value: value.countryName,
|
||||
}, {
|
||||
label: 'Locality name',
|
||||
value: value.localityName,
|
||||
}, {
|
||||
label: 'Organizational unit name',
|
||||
value: value.organizationalUnitName,
|
||||
}, {
|
||||
label: 'State or province name',
|
||||
value: value.stateOrProvinceName,
|
||||
}]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #issuedTo="{ value }">
|
||||
<c-key-value-list
|
||||
:items="[{
|
||||
label: 'Common name',
|
||||
value: value.commonName,
|
||||
}, {
|
||||
label: 'Organization name',
|
||||
value: value.organizationName,
|
||||
}, {
|
||||
label: 'Country name',
|
||||
value: value.countryName,
|
||||
}, {
|
||||
label: 'Locality name',
|
||||
value: value.localityName,
|
||||
}, {
|
||||
label: 'Organizational unit name',
|
||||
value: value.organizationalUnitName,
|
||||
}, {
|
||||
label: 'State or province name',
|
||||
value: value.stateOrProvinceName,
|
||||
}]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #pemCertificate="{ value }">
|
||||
<c-modal-value :value="value" label="View PEM cert">
|
||||
<template #value>
|
||||
<div break-all text-xs>
|
||||
{{ value }}
|
||||
</div>
|
||||
</template>
|
||||
</c-modal-value>
|
||||
</template>
|
||||
</c-table>
|
||||
</div>
|
||||
</template>
|
12
src/tools/pdf-signature-checker/index.ts
Normal file
12
src/tools/pdf-signature-checker/index.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { defineTool } from '../tool';
|
||||
import FileCertIcon from '~icons/mdi/file-certificate-outline';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'PDF signature checker',
|
||||
path: '/pdf-signature-checker',
|
||||
description: 'Verify the signatures of a PDF file. A signed PDF file contains one or more signatures that may be used to determine whether the contents of the file have been altered since the file was signed.',
|
||||
keywords: ['pdf', 'signature', 'checker', 'verify', 'validate', 'sign'],
|
||||
component: () => import('./pdf-signature-checker.vue'),
|
||||
icon: FileCertIcon,
|
||||
createdAt: new Date('2023-12-09'),
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
import { expect, test } from '@playwright/test';
|
||||
|
||||
test.describe('Tool - Pdf signature checker', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/pdf-signature-checker');
|
||||
});
|
||||
|
||||
test('Has correct title', async ({ page }) => {
|
||||
await expect(page).toHaveTitle('PDF signature checker - IT Tools');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,39 @@
|
|||
export interface SignatureInfo {
|
||||
verified: boolean
|
||||
authenticity: boolean
|
||||
integrity: boolean
|
||||
expired: boolean
|
||||
meta: {
|
||||
certs: {
|
||||
clientCertificate?: boolean
|
||||
issuedBy: {
|
||||
commonName: string
|
||||
organizationalUnitName?: string
|
||||
organizationName: string
|
||||
countryName?: string
|
||||
localityName?: string
|
||||
stateOrProvinceName?: string
|
||||
}
|
||||
issuedTo: {
|
||||
commonName: string
|
||||
serialNumber?: string
|
||||
organizationalUnitName?: string
|
||||
organizationName: string
|
||||
countryName?: string
|
||||
localityName?: string
|
||||
stateOrProvinceName?: string
|
||||
}
|
||||
validityPeriod: {
|
||||
notBefore: string
|
||||
notAfter: string
|
||||
}
|
||||
pemCertificate: string
|
||||
}[]
|
||||
signatureMeta: {
|
||||
reason: string
|
||||
contactInfo: string | null
|
||||
location: string
|
||||
name: string | null
|
||||
}
|
||||
}
|
||||
}
|
59
src/tools/pdf-signature-checker/pdf-signature-checker.vue
Normal file
59
src/tools/pdf-signature-checker/pdf-signature-checker.vue
Normal file
|
@ -0,0 +1,59 @@
|
|||
<script setup lang="ts">
|
||||
import verifyPDF from 'pdf-signature-reader';
|
||||
import type { SignatureInfo } from './pdf-signature-checker.types';
|
||||
import { formatBytes } from '@/utils/convert';
|
||||
|
||||
const signatures = ref<SignatureInfo[]>([]);
|
||||
const status = ref<'idle' | 'parsed' | 'error' | 'loading'>('idle');
|
||||
const file = ref<File | null>(null);
|
||||
|
||||
async function onVerifyClicked(uploadedFile: File) {
|
||||
file.value = uploadedFile;
|
||||
const fileBuffer = await uploadedFile.arrayBuffer();
|
||||
|
||||
status.value = 'loading';
|
||||
try {
|
||||
const { signatures: parsedSignatures } = verifyPDF(fileBuffer);
|
||||
signatures.value = parsedSignatures;
|
||||
status.value = 'parsed';
|
||||
}
|
||||
catch (e) {
|
||||
signatures.value = [];
|
||||
status.value = 'error';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="flex: 0 0 100%">
|
||||
<div mx-auto max-w-600px>
|
||||
<c-file-upload title="Drag and drop a PDF file here, or click to select a file" accept=".pdf" @file-upload="onVerifyClicked" />
|
||||
|
||||
<c-card v-if="file" mt-4 flex gap-2>
|
||||
<div font-bold>
|
||||
{{ file.name }}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{ formatBytes(file.size) }}
|
||||
</div>
|
||||
</c-card>
|
||||
|
||||
<div v-if="status === 'error'">
|
||||
<c-alert mt-4>
|
||||
No signatures found in the provided file.
|
||||
</c-alert>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="status === 'parsed' && signatures.length" style="flex: 0 0 100%" mt-5 flex flex-col gap-4>
|
||||
<div v-for="(signature, index) of signatures" :key="index">
|
||||
<div mb-2 font-bold>
|
||||
Signature {{ index + 1 }} certificates :
|
||||
</div>
|
||||
|
||||
<pdf-signature-details :signature="signature" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
Loading…
Add table
Add a link
Reference in a new issue