feat(new tool): Certificate/Key Parser and infos
Parse Certificate and Keys (Public, Private, Signature, Fingerprint...) and displays infos Fix #671
This commit is contained in:
		
							parent
							
								
									e073b2babf
								
							
						
					
					
						commit
						ce8199e338
					
				| @ -62,6 +62,7 @@ | |||||||
|     "highlight.js": "^11.7.0", |     "highlight.js": "^11.7.0", | ||||||
|     "iarna-toml-esm": "^3.0.5", |     "iarna-toml-esm": "^3.0.5", | ||||||
|     "ibantools": "^4.3.3", |     "ibantools": "^4.3.3", | ||||||
|  |     "js-base64": "^3.7.7", | ||||||
|     "json5": "^2.2.3", |     "json5": "^2.2.3", | ||||||
|     "jwt-decode": "^3.1.2", |     "jwt-decode": "^3.1.2", | ||||||
|     "libphonenumber-js": "^1.10.28", |     "libphonenumber-js": "^1.10.28", | ||||||
|  | |||||||
							
								
								
									
										19
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										19
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @ -86,6 +86,9 @@ dependencies: | |||||||
|   ibantools: |   ibantools: | ||||||
|     specifier: ^4.3.3 |     specifier: ^4.3.3 | ||||||
|     version: 4.3.3 |     version: 4.3.3 | ||||||
|  |   js-base64: | ||||||
|  |     specifier: ^3.7.7 | ||||||
|  |     version: 3.7.7 | ||||||
|   json5: |   json5: | ||||||
|     specifier: ^2.2.3 |     specifier: ^2.2.3 | ||||||
|     version: 2.2.3 |     version: 2.2.3 | ||||||
| @ -3378,7 +3381,7 @@ packages: | |||||||
|     dependencies: |     dependencies: | ||||||
|       '@unhead/dom': 0.5.1 |       '@unhead/dom': 0.5.1 | ||||||
|       '@unhead/schema': 0.5.1 |       '@unhead/schema': 0.5.1 | ||||||
|       '@vueuse/shared': 10.7.2(vue@3.3.4) |       '@vueuse/shared': 10.9.0(vue@3.3.4) | ||||||
|       unhead: 0.5.1 |       unhead: 0.5.1 | ||||||
|       vue: 3.3.4 |       vue: 3.3.4 | ||||||
|     transitivePeerDependencies: |     transitivePeerDependencies: | ||||||
| @ -4020,10 +4023,10 @@ packages: | |||||||
|       - vue |       - vue | ||||||
|     dev: false |     dev: false | ||||||
| 
 | 
 | ||||||
|   /@vueuse/shared@10.7.2(vue@3.3.4): |   /@vueuse/shared@10.9.0(vue@3.3.4): | ||||||
|     resolution: {integrity: sha512-qFbXoxS44pi2FkgFjPvF4h7c9oMDutpyBdcJdMYIMg9XyXli2meFMuaKn+UMgsClo//Th6+beeCgqweT/79BVA==} |     resolution: {integrity: sha512-Uud2IWncmAfJvRaFYzv5OHDli+FbOzxiVEQdLCKQKLyhz94PIyFC3CHcH7EDMwIn8NPtD06+PNbC/PiO0LGLtw==} | ||||||
|     dependencies: |     dependencies: | ||||||
|       vue-demi: 0.14.6(vue@3.3.4) |       vue-demi: 0.14.7(vue@3.3.4) | ||||||
|     transitivePeerDependencies: |     transitivePeerDependencies: | ||||||
|       - '@vue/composition-api' |       - '@vue/composition-api' | ||||||
|       - vue |       - vue | ||||||
| @ -6795,6 +6798,10 @@ packages: | |||||||
|     hasBin: true |     hasBin: true | ||||||
|     dev: true |     dev: true | ||||||
| 
 | 
 | ||||||
|  |   /js-base64@3.7.7: | ||||||
|  |     resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} | ||||||
|  |     dev: false | ||||||
|  | 
 | ||||||
|   /js-beautify@1.14.6: |   /js-beautify@1.14.6: | ||||||
|     resolution: {integrity: sha512-GfofQY5zDp+cuHc+gsEXKPpNw2KbPddreEo35O6jT6i0RVK6LhsoYBhq5TvK4/n74wnA0QbK8gGd+jUZwTMKJw==} |     resolution: {integrity: sha512-GfofQY5zDp+cuHc+gsEXKPpNw2KbPddreEo35O6jT6i0RVK6LhsoYBhq5TvK4/n74wnA0QbK8gGd+jUZwTMKJw==} | ||||||
|     engines: {node: '>=10'} |     engines: {node: '>=10'} | ||||||
| @ -9752,8 +9759,8 @@ packages: | |||||||
|       vue: 3.3.4 |       vue: 3.3.4 | ||||||
|     dev: false |     dev: false | ||||||
| 
 | 
 | ||||||
|   /vue-demi@0.14.6(vue@3.3.4): |   /vue-demi@0.14.7(vue@3.3.4): | ||||||
|     resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==} |     resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} | ||||||
|     engines: {node: '>=12'} |     engines: {node: '>=12'} | ||||||
|     hasBin: true |     hasBin: true | ||||||
|     requiresBuild: true |     requiresBuild: true | ||||||
|  | |||||||
| @ -2,7 +2,12 @@ | |||||||
| import { useVModel } from '@vueuse/core'; | import { useVModel } from '@vueuse/core'; | ||||||
| import { useCopy } from '@/composable/copy'; | import { useCopy } from '@/composable/copy'; | ||||||
| 
 | 
 | ||||||
| const props = defineProps<{ value: string }>(); | const props = defineProps<{ | ||||||
|  |   value: string | ||||||
|  |   multiline?: boolean | ||||||
|  |   rows?: number | string | ||||||
|  |   autosize?: boolean | ||||||
|  | }>(); | ||||||
| const emit = defineEmits(['update:value']); | const emit = defineEmits(['update:value']); | ||||||
| 
 | 
 | ||||||
| const value = useVModel(props, 'value', emit); | const value = useVModel(props, 'value', emit); | ||||||
| @ -11,7 +16,12 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : 'Copy to cli | |||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|   <c-input-text v-model:value="value"> |   <c-input-text | ||||||
|  |     v-model:value="value" | ||||||
|  |     :multiline="multiline" | ||||||
|  |     :rows="rows" | ||||||
|  |     :autosize="autosize" | ||||||
|  |   > | ||||||
|     <template #suffix> |     <template #suffix> | ||||||
|       <c-tooltip :tooltip="tooltipText"> |       <c-tooltip :tooltip="tooltipText"> | ||||||
|         <c-button circle variant="text" size="small" @click="copy()"> |         <c-button circle variant="text" size="small" @click="copy()"> | ||||||
|  | |||||||
| @ -1,8 +1,13 @@ | |||||||
| import { extension as getExtensionFromMime } from 'mime-types'; | import { extension as getExtensionFromMimeType, extension as getMimeTypeFromExtension } from 'mime-types'; | ||||||
| import type { Ref } from 'vue'; | import type { Ref } from 'vue'; | ||||||
| import _ from 'lodash'; | import _ from 'lodash'; | ||||||
| 
 | 
 | ||||||
| export { getMimeTypeFromBase64, useDownloadFileFromBase64 }; | export { | ||||||
|  |   getMimeTypeFromBase64, | ||||||
|  |   getMimeTypeFromExtension, getExtensionFromMimeType, | ||||||
|  |   useDownloadFileFromBase64, useDownloadFileFromBase64Refs, | ||||||
|  |   previewImageFromBase64, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| const commonMimeTypesSignatures = { | const commonMimeTypesSignatures = { | ||||||
|   'JVBERi0': 'application/pdf', |   'JVBERi0': 'application/pdf', | ||||||
| @ -36,30 +41,78 @@ function getFileExtensionFromMimeType({ | |||||||
|   defaultExtension?: string |   defaultExtension?: string | ||||||
| }) { | }) { | ||||||
|   if (mimeType) { |   if (mimeType) { | ||||||
|     return getExtensionFromMime(mimeType) ?? defaultExtension; |     return getExtensionFromMimeType(mimeType) ?? defaultExtension; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   return defaultExtension; |   return defaultExtension; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function useDownloadFileFromBase64({ source, filename }: { source: Ref<string>; filename?: string }) { | function downloadFromBase64({ sourceValue, filename, extension, fileMimeType }: | ||||||
|   return { | { sourceValue: string; filename?: string; extension?: string; fileMimeType?: string }) { | ||||||
|     download() { |   if (sourceValue === '') { | ||||||
|       if (source.value === '') { |  | ||||||
|     throw new Error('Base64 string is empty'); |     throw new Error('Base64 string is empty'); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|       const { mimeType } = getMimeTypeFromBase64({ base64String: source.value }); |   const defaultExtension = extension ?? 'txt'; | ||||||
|       const base64String = mimeType |   const { mimeType } = getMimeTypeFromBase64({ base64String: sourceValue }); | ||||||
|         ? source.value |   let base64String = sourceValue; | ||||||
|         : `data:text/plain;base64,${source.value}`; |   if (!mimeType) { | ||||||
|  |     const targetMimeType = fileMimeType ?? getMimeTypeFromExtension(defaultExtension); | ||||||
|  |     base64String = `data:${targetMimeType};base64,${sourceValue}`; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|       const cleanFileName = filename ?? `file.${getFileExtensionFromMimeType({ mimeType })}`; |   const cleanExtension = extension ?? getFileExtensionFromMimeType( | ||||||
|  |     { mimeType, defaultExtension }); | ||||||
|  |   let cleanFileName = filename ?? `file.${cleanExtension}`; | ||||||
|  |   if (extension && !cleanFileName.endsWith(`.${extension}`)) { | ||||||
|  |     cleanFileName = `${cleanFileName}.${cleanExtension}`; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   const a = document.createElement('a'); |   const a = document.createElement('a'); | ||||||
|   a.href = base64String; |   a.href = base64String; | ||||||
|   a.download = cleanFileName; |   a.download = cleanFileName; | ||||||
|   a.click(); |   a.click(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function useDownloadFileFromBase64( | ||||||
|  |   { source, filename, extension, fileMimeType }: | ||||||
|  |   { source: Ref<string>; filename?: string; extension?: string; fileMimeType?: string }) { | ||||||
|  |   return { | ||||||
|  |     download() { | ||||||
|  |       downloadFromBase64({ sourceValue: source.value, filename, extension, fileMimeType }); | ||||||
|     }, |     }, | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | function useDownloadFileFromBase64Refs( | ||||||
|  |   { source, filename, extension }: | ||||||
|  |   { source: Ref<string>; filename?: Ref<string>; extension?: Ref<string> }) { | ||||||
|  |   return { | ||||||
|  |     download() { | ||||||
|  |       downloadFromBase64({ sourceValue: source.value, filename: filename?.value, extension: extension?.value }); | ||||||
|  |     }, | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function previewImageFromBase64(base64String: string): HTMLImageElement { | ||||||
|  |   if (base64String === '') { | ||||||
|  |     throw new Error('Base64 string is empty'); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const img = document.createElement('img'); | ||||||
|  |   img.src = base64String; | ||||||
|  | 
 | ||||||
|  |   const container = document.createElement('div'); | ||||||
|  |   container.appendChild(img); | ||||||
|  | 
 | ||||||
|  |   const previewContainer = document.getElementById('previewContainer'); | ||||||
|  |   if (previewContainer) { | ||||||
|  |     previewContainer.innerHTML = ''; | ||||||
|  |     previewContainer.appendChild(container); | ||||||
|  |   } | ||||||
|  |   else { | ||||||
|  |     throw new Error('Preview container element not found'); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return img; | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										354
									
								
								src/tools/certificate-key-parser/certificate-key-parser.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										354
									
								
								src/tools/certificate-key-parser/certificate-key-parser.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,354 @@ | |||||||
|  | <script setup lang="ts"> | ||||||
|  | import { Buffer } from 'node:buffer'; | ||||||
|  | import { | ||||||
|  |   parseCertificate, parseFingerprint, | ||||||
|  |   parseKey, | ||||||
|  |   parsePrivateKey, | ||||||
|  |   parseSignature, | ||||||
|  | } from 'sshpk'; | ||||||
|  | import type { | ||||||
|  |   AlgorithmType, | ||||||
|  |   Certificate, | ||||||
|  |   CertificateFormat, | ||||||
|  |   Fingerprint, | ||||||
|  |   Key, | ||||||
|  |   PrivateKey, Signature, SignatureFormatType, | ||||||
|  | } from 'sshpk'; | ||||||
|  | import { Base64 } from 'js-base64'; | ||||||
|  | import { useDownloadFileFromBase64 } from '@/composable/downloadBase64'; | ||||||
|  | 
 | ||||||
|  | function buf2Hex(buffer: ArrayBuffer) { // buffer is an ArrayBuffer | ||||||
|  |   return [...new Uint8Array(buffer)] | ||||||
|  |     .map(x => x.toString(16).padStart(2, '0')) | ||||||
|  |     .join(''); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const inputKeyOrCertificate = ref(''); | ||||||
|  | const passphrase = ref(''); | ||||||
|  | const fileInput = ref() as Ref<Buffer>; | ||||||
|  | 
 | ||||||
|  | async function onUpload(file: File) { | ||||||
|  |   if (file) { | ||||||
|  |     fileInput.value = Buffer.from(await file.arrayBuffer()); | ||||||
|  |     inputKeyOrCertificate.value = ''; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const certificateX509DER = ref(''); | ||||||
|  | const { download: downloadX509DER } = useDownloadFileFromBase64( | ||||||
|  |   { | ||||||
|  |     source: certificateX509DER, | ||||||
|  |     extension: 'der', | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  | function downloadX509DERFile() { | ||||||
|  |   if (certificateX509DER.value === '') { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   try { | ||||||
|  |     downloadX509DER(); | ||||||
|  |   } | ||||||
|  |   catch (_) { | ||||||
|  |     // | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | interface LabelValue { | ||||||
|  |   label: string | ||||||
|  |   value: string | ||||||
|  |   multiline?: boolean | ||||||
|  | } | ||||||
|  | const parsedSections = computed<LabelValue[]>(() => { | ||||||
|  |   try { | ||||||
|  |     certificateX509DER.value = ''; | ||||||
|  |     const onErrorReturnErrorMessage = (func: () => any) => { | ||||||
|  |       try { | ||||||
|  |         return func(); | ||||||
|  |       } | ||||||
|  |       catch (e: any) { | ||||||
|  |         return e.toString(); | ||||||
|  |       } | ||||||
|  |     }; | ||||||
|  |     const canParse = (value: string | Buffer, parseFunction: (value: string | Buffer) => any) => { | ||||||
|  |       try { | ||||||
|  |         return parseFunction(value); | ||||||
|  |       } | ||||||
|  |       catch { | ||||||
|  |         return null; | ||||||
|  |       } | ||||||
|  |     }; | ||||||
|  |     const inputKeyOrCertificateValue | ||||||
|  |       = inputKeyOrCertificate.value !== '' | ||||||
|  |         ? inputKeyOrCertificate.value | ||||||
|  |         : fileInput.value; | ||||||
|  |     const publicKey = canParse(inputKeyOrCertificateValue, parseKey) as Key; | ||||||
|  |     if (publicKey) { | ||||||
|  |       return [ | ||||||
|  |         { | ||||||
|  |           label: 'Type: ', | ||||||
|  |           value: 'Public Key', | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Key Type: ', | ||||||
|  |           value: publicKey.type, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Size: ', | ||||||
|  |           value: publicKey.size, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Comment: ', | ||||||
|  |           value: publicKey.comment, | ||||||
|  |           multiline: true, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Curve: ', | ||||||
|  |           value: publicKey.curve ?? 'none', | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Fingerprint (sha256): ', | ||||||
|  |           value: onErrorReturnErrorMessage(() => publicKey.fingerprint('sha256')), | ||||||
|  |           multiline: true, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Fingerprint (sha512): ', | ||||||
|  |           value: onErrorReturnErrorMessage(() => publicKey.fingerprint('sha512')), | ||||||
|  |           multiline: true, | ||||||
|  |         }, | ||||||
|  |       ] as LabelValue[]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const privateKey = canParse(inputKeyOrCertificateValue, | ||||||
|  |       value => parsePrivateKey(value, 'auto', { passphrase: passphrase.value })) as PrivateKey; | ||||||
|  |     if (privateKey) { | ||||||
|  |       return [ | ||||||
|  |         { | ||||||
|  |           label: 'Type: ', | ||||||
|  |           value: 'Private Key', | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Key Type: ', | ||||||
|  |           value: privateKey.type, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Size: ', | ||||||
|  |           value: privateKey.size, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Comment: ', | ||||||
|  |           value: privateKey.comment, | ||||||
|  |           multiline: true, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Curve: ', | ||||||
|  |           value: privateKey.curve, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Fingerprint (sha256): ', | ||||||
|  |           value: onErrorReturnErrorMessage(() => privateKey.fingerprint('sha256')), | ||||||
|  |           multiline: true, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Fingerprint (sha512): ', | ||||||
|  |           value: onErrorReturnErrorMessage(() => privateKey.fingerprint('sha512')), | ||||||
|  |           multiline: true, | ||||||
|  |         }, | ||||||
|  |       ] as LabelValue[]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const cert = canParse(inputKeyOrCertificateValue, (value) => { | ||||||
|  |       for (const format of ['openssh', 'pem', 'x509']) { | ||||||
|  |         try { | ||||||
|  |           return parseCertificate(value, format as CertificateFormat); | ||||||
|  |         } | ||||||
|  |         catch {} | ||||||
|  |       } | ||||||
|  |       return null; | ||||||
|  |     }) as Certificate; | ||||||
|  |     if (cert) { | ||||||
|  |       try { | ||||||
|  |         certificateX509DER.value = Base64.fromUint8Array(cert.toBuffer('x509')); | ||||||
|  |       } | ||||||
|  |       catch {} | ||||||
|  | 
 | ||||||
|  |       return [ | ||||||
|  |         { | ||||||
|  |           label: 'Type: ', | ||||||
|  |           value: 'Certificate', | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Subjects: ', | ||||||
|  |           value: cert.subjects.map(s => s.toString()).join('\n'), | ||||||
|  |           multiline: true, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Issuer: ', | ||||||
|  |           value: cert.issuer.toString(), | ||||||
|  |           multiline: true, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Subject Key: ', | ||||||
|  |           value: onErrorReturnErrorMessage(() => cert.subjectKey?.toString('ssh')), | ||||||
|  |           multiline: true, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Subject Key Type: ', | ||||||
|  |           value: cert.subjectKey?.type, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Subject Size: ', | ||||||
|  |           value: cert.subjectKey?.size, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Subject Comment: ', | ||||||
|  |           value: cert.subjectKey?.comment, | ||||||
|  |           multiline: true, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Subject Curve: ', | ||||||
|  |           value: cert.subjectKey?.curve ?? 'none', | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Issuer Key: ', | ||||||
|  |           value: onErrorReturnErrorMessage(() => cert.issuerKey?.toString('ssh')), | ||||||
|  |           multiline: true, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Serial: ', | ||||||
|  |           value: buf2Hex(cert.serial), | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Purposes: ', | ||||||
|  |           value: cert.purposes?.join(', '), | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Extensions: ', | ||||||
|  |           value: JSON.stringify(cert.getExtensions(), null, 2), | ||||||
|  |           multiline: true, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Fingerprint (sha256): ', | ||||||
|  |           value: onErrorReturnErrorMessage(() => cert.fingerprint('sha256')), | ||||||
|  |           multiline: true, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Fingerprint (sha512): ', | ||||||
|  |           value: onErrorReturnErrorMessage(() => cert.fingerprint('sha512')), | ||||||
|  |           multiline: true, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Certificate (pem): ', | ||||||
|  |           value: onErrorReturnErrorMessage(() => cert.toString('pem')), | ||||||
|  |           multiline: true, | ||||||
|  |         }, | ||||||
|  |       ] as LabelValue[]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const fingerprint = canParse(inputKeyOrCertificateValue, value => parseFingerprint(value.toString())) as Fingerprint; | ||||||
|  |     if (fingerprint) { | ||||||
|  |       return [ | ||||||
|  |         { | ||||||
|  |           label: 'Type: ', | ||||||
|  |           value: 'Fingerprint', | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Fingerprint (hex): ', | ||||||
|  |           value: fingerprint.toString('hex'), | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Fingerprint (base64): ', | ||||||
|  |           value: fingerprint.toString('base64'), | ||||||
|  |         }, | ||||||
|  |       ] as LabelValue[]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const signature = canParse(inputKeyOrCertificateValue, (value) => { | ||||||
|  |       // | ||||||
|  |       for (const algo of ['dsa', 'rsa', 'ecdsa', 'ed25519']) { | ||||||
|  |         for (const format of ['asn1', 'ssh', 'raw']) { | ||||||
|  |           try { | ||||||
|  |             return parseSignature(value, algo as AlgorithmType, format as SignatureFormatType); | ||||||
|  |           } | ||||||
|  |           catch {} | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       return null; | ||||||
|  |     }) as Signature; | ||||||
|  |     if (signature) { | ||||||
|  |       return [ | ||||||
|  |         { | ||||||
|  |           label: 'Type: ', | ||||||
|  |           value: 'Signature', | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Fingerprint (asn1): ', | ||||||
|  |           value: signature.toString('asn1'), | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Fingerprint (ssh): ', | ||||||
|  |           value: signature.toString('ssh'), | ||||||
|  |         }, | ||||||
|  |       ] as LabelValue[]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return [ | ||||||
|  |       { | ||||||
|  |         label: 'Type: ', | ||||||
|  |         value: 'Unknown format or invalid passphrase', | ||||||
|  |       }]; | ||||||
|  |   } | ||||||
|  |   catch (e: any) { | ||||||
|  |     return [ | ||||||
|  |       { | ||||||
|  |         label: 'Error: ', | ||||||
|  |         value: e.toString(), | ||||||
|  |       }] as LabelValue[]; | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <c-card> | ||||||
|  |       <c-file-upload title="Drag and drop a Certificate file here, or click to select a Certificate file" @file-upload="onUpload" /> | ||||||
|  |       <p>-OR-</p> | ||||||
|  |       <c-input-text | ||||||
|  |         v-model:value="inputKeyOrCertificate" | ||||||
|  |         label="Paste your Public Key / Private Key / Signature / Fingerprint / Certificate:" | ||||||
|  |         placeholder="Your Public Key / Private Key / Signature / Fingerprint / Certificate..." | ||||||
|  |         multiline | ||||||
|  |         rows="8" | ||||||
|  |       /> | ||||||
|  |     </c-card> | ||||||
|  | 
 | ||||||
|  |     <c-input-text | ||||||
|  |       v-model:value="passphrase" | ||||||
|  |       label="Passphrase (for encrypted keys):" | ||||||
|  |       placeholder="Passphrase (for encrypted keys)..." | ||||||
|  |       type="password" | ||||||
|  |     /> | ||||||
|  | 
 | ||||||
|  |     <n-divider /> | ||||||
|  | 
 | ||||||
|  |     <input-copyable | ||||||
|  |       v-for="{ label, value, multiline } of parsedSections" | ||||||
|  |       :key="label" | ||||||
|  |       :label="label" | ||||||
|  |       label-position="left" | ||||||
|  |       label-width="100px" | ||||||
|  |       label-align="right" | ||||||
|  | 
 | ||||||
|  |       autosize mb-2 | ||||||
|  |       :multiline="multiline" | ||||||
|  |       :value="value" | ||||||
|  |       placeholder="Not Set" | ||||||
|  |     /> | ||||||
|  | 
 | ||||||
|  |     <div v-if="certificateX509DER !== ''" flex justify-center> | ||||||
|  |       <c-button @click="downloadX509DERFile()"> | ||||||
|  |         Download X509 DER certificate | ||||||
|  |       </c-button> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
							
								
								
									
										12
									
								
								src/tools/certificate-key-parser/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/tools/certificate-key-parser/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | import { FileCertificate } from '@vicons/tabler'; | ||||||
|  | import { defineTool } from '../tool'; | ||||||
|  | 
 | ||||||
|  | export const tool = defineTool({ | ||||||
|  |   name: 'Certificate/Key parser', | ||||||
|  |   path: '/certificate-key-parser', | ||||||
|  |   description: 'Parse Key and Certificate', | ||||||
|  |   keywords: ['certificate', 'key', 'parser'], | ||||||
|  |   component: () => import('./certificate-key-parser.vue'), | ||||||
|  |   icon: FileCertificate, | ||||||
|  |   createdAt: new Date('2024-02-22'), | ||||||
|  | }); | ||||||
| @ -2,6 +2,7 @@ import { tool as base64FileConverter } from './base64-file-converter'; | |||||||
| import { tool as base64StringConverter } from './base64-string-converter'; | import { tool as base64StringConverter } from './base64-string-converter'; | ||||||
| import { tool as basicAuthGenerator } from './basic-auth-generator'; | import { tool as basicAuthGenerator } from './basic-auth-generator'; | ||||||
| import { tool as textToUnicode } from './text-to-unicode'; | import { tool as textToUnicode } from './text-to-unicode'; | ||||||
|  | import { tool as certificateKeyParser } from './certificate-key-parser'; | ||||||
| import { tool as pdfSignatureChecker } from './pdf-signature-checker'; | import { tool as pdfSignatureChecker } from './pdf-signature-checker'; | ||||||
| import { tool as numeronymGenerator } from './numeronym-generator'; | import { tool as numeronymGenerator } from './numeronym-generator'; | ||||||
| import { tool as macAddressGenerator } from './mac-address-generator'; | import { tool as macAddressGenerator } from './mac-address-generator'; | ||||||
| @ -81,7 +82,20 @@ import { tool as yamlViewer } from './yaml-viewer'; | |||||||
| export const toolsByCategory: ToolCategory[] = [ | export const toolsByCategory: ToolCategory[] = [ | ||||||
|   { |   { | ||||||
|     name: 'Crypto', |     name: 'Crypto', | ||||||
|     components: [tokenGenerator, hashText, bcrypt, uuidGenerator, ulidGenerator, cypher, bip39, hmacGenerator, rsaKeyPairGenerator, passwordStrengthAnalyser, pdfSignatureChecker], |     components: [ | ||||||
|  |       tokenGenerator, | ||||||
|  |       hashText, | ||||||
|  |       bcrypt, | ||||||
|  |       uuidGenerator, | ||||||
|  |       ulidGenerator, | ||||||
|  |       cypher, | ||||||
|  |       bip39, | ||||||
|  |       hmacGenerator, | ||||||
|  |       rsaKeyPairGenerator, | ||||||
|  |       certificateKeyParser, | ||||||
|  |       passwordStrengthAnalyser, | ||||||
|  |       pdfSignatureChecker, | ||||||
|  |     ], | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     name: 'Converter', |     name: 'Converter', | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user