Skip to content

Commit 80895b0

Browse files
committed
Merge branch 'feat/ssl-converter' into chore/all-my-stuffs
# Conflicts: # package.json # pnpm-lock.yaml # src/tools/index.ts # vite.config.ts
2 parents 0c8bb63 + e62ca22 commit 80895b0

File tree

8 files changed

+317
-1
lines changed

8 files changed

+317
-1
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@
151151
"ip-bigint": "^8.2.0",
152152
"jq-wasm": "^0.0.9",
153153
"ip-regex": "^5.0.0",
154+
"jks-js": "^1.1.3",
154155
"js-base64": "^3.7.6",
155156
"image-in-browser": "^3.1.0",
156157
"js-base64": "^3.7.7",
@@ -217,6 +218,7 @@
217218
"svg2png-wasm": "^1.4.1",
218219
"svg-to-url": "^4.0.0",
219220
"tesseract.js": "^5.0.4",
221+
"sshpk": "^1.18.0",
220222
"ua-parser-js": "^1.0.35",
221223
"ulid": "^2.3.0",
222224
"unicode-emoji-json": "^0.4.0",

pnpm-lock.yaml

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/tools/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { tool as pinCodeGenerator } from './pin-code-generator';
3636
import { tool as punycodeConverter } from './punycode-converter';
3737
import { tool as rsaEcdsaSigning } from './rsa-ecdsa-signing';
3838
import { tool as smartRawConverter } from './smart-raw-converter';
39+
import { tool as sslCertConverter } from './ssl-cert-converter';
3940

4041
import { tool as cssXpathConverter } from './css-xpath-converter';
4142
import { tool as cssSelectorsMemo } from './css-selectors-memo';
@@ -341,6 +342,7 @@ export const toolsByCategory: ToolCategory[] = [
341342
macAddressConverter,
342343
ipv6UlaGenerator,
343344
punycodeConverter,
345+
sslCertConverter,
344346
],
345347
},
346348
{

src/tools/ssl-cert-converter/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { ShieldChevron } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'SSL Certificate converter',
6+
path: '/ssl-cert-converter',
7+
description: 'Convert SSL Certificate from different formats',
8+
keywords: ['ssl', 'certificate', 'crt', 'pkcs', 'p12', 'pem', 'der', 'jks', 'converter'],
9+
component: () => import('./ssl-cert-converter.vue'),
10+
icon: ShieldChevron,
11+
createdAt: new Date('2024-08-15'),
12+
});
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Buffer } from 'node:buffer';
2+
import { describe, expect, it } from 'vitest';
3+
import { convertCertificate } from './ssl-cert-converter.service';
4+
5+
const formatsData = [
6+
{
7+
title: 'JKS',
8+
input: Buffer.from(
9+
'/u3+7QAAAAIAAAABAAAAAQAGamtzLWpzAAABeDENGOMAAAB/MH0wDgYKKwYBBAEqAhEBAQUABGstagMT9RkL6lTrGvx2untoFmXM13xjQjMKfxFU/iQHuk3Y44LeB5oP9/e8KEe6nK1NTQhaTRrKyMZGJhs5Oro+TLowYerbBiJJ2DKyBTVjMDCZj8f29hOXpxQpIVv6IEAlFJwL3TQNydxjdgAAAAEABVguNTA5AAAB5jCCAeIwggGHoAMCAQICBCljjXAwDAYIKoZIzj0EAwIFADBmMQ8wDQYDVQQGEwZqa3MtanMxDzANBgNVBAgTBmprcy1qczEPMA0GA1UEBxMGamtzLWpzMQ8wDQYDVQQKEwZsZW5jaHYxDzANBgNVBAsTBmprcy1qczEPMA0GA1UEAxMGamtzLWpzMB4XDTIxMDMxNDE0MDQwNVoXDTIxMDYxMjE0MDQwNVowZjEPMA0GA1UEBhMGamtzLWpzMQ8wDQYDVQQIEwZqa3MtanMxDzANBgNVBAcTBmprcy1qczEPMA0GA1UEChMGbGVuY2h2MQ8wDQYDVQQLEwZqa3MtanMxDzANBgNVBAMTBmprcy1qczBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEXhUAIulQWBEFCRBqPl7t/FKuI6hzWYpdBpwWZSRiDkp9A0xeIJyHazINyyx2xIDyCvR1vrqIhMuFxJIWxp8DmjITAfMB0GA1UdDgQWBBRpec0VpSfJmWjyzDtiUWk5difuTjAMBggqhkjOPQQDAgUAA0cAMEQCIE2hdtgyJZgO+gGZrCBxSgQ7G/uRugeIDGBR5X9oY2rAAiBgRfCUsTPr5NPeTfuS854/koMCTYrvLEcwcRGD4uBuNe3vG6EIUGVgYuXdiR4aycUoOcEb',
10+
'base64'),
11+
pass: 'password',
12+
convertedCount: 1,
13+
},
14+
{
15+
title: 'PEM',
16+
input: Buffer.from(
17+
'QmFnIEF0dHJpYnV0ZXMKICAgIGxvY2FsS2V5SUQ6IENGIDVDIDAxIEU4IEQyIDYzIERGIEM4IEZFIDMyIERCIEI0IDI4IDJEIDc2IEYzIEQwIDVEIDUyIEQ4IApzdWJqZWN0PS9DPVVTL1NUPUNhbGlmb3JuaWEvTD1TYW4gRnJhbmNpc2NvL089QmFkU1NML0NOPUJhZFNTTCBDbGllbnQgQ2VydGlmaWNhdGUKaXNzdWVyPS9DPVVTL1NUPUNhbGlmb3JuaWEvTD1TYW4gRnJhbmNpc2NvL089QmFkU1NML0NOPUJhZFNTTCBDbGllbnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkKLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVuVENDQW9XZ0F3SUJBZ0lKQU0xSHg0SkoxT2dwTUEwR0NTcUdTSWIzRFFFQkN3VUFNSDR4Q3pBSkJnTlYKQkFZVEFsVlRNUk13RVFZRFZRUUlEQXBEWVd4cFptOXlibWxoTVJZd0ZBWURWUVFIREExVFlXNGdSbkpoYm1OcApjMk52TVE4d0RRWURWUVFLREFaQ1lXUlRVMHd4TVRBdkJnTlZCQU1NS0VKaFpGTlRUQ0JEYkdsbGJuUWdVbTl2CmRDQkRaWEowYVdacFkyRjBaU0JCZFhSb2IzSnBkSGt3SGhjTk1qUXdPREl3TVRZeU5EUXpXaGNOTWpZd09ESXcKTVRZeU5EUXpXakJ2TVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVXTUJRRwpBMVVFQnd3TlUyRnVJRVp5WVc1amFYTmpiekVQTUEwR0ExVUVDZ3dHUW1Ga1UxTk1NU0l3SUFZRFZRUUREQmxDCllXUlRVMHdnUTJ4cFpXNTBJRU5sY25ScFptbGpZWFJsTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEEKTUlJQkNnS0NBUUVBeHpkZkVlc2VUcy9ydWtqbHk2TVNMSE0rUmgwZW5BM0FpNE1qMnNkbDMxeDNTYlBvZW4wOAp1dFZoalBtbHhJVWRraU1HNCtmZmU3TitKdERMRzc1Q2F4WnA5Q3h5dFg3a3l3b29SQkpzUm5RaG1RUGNhOE1SCldBSkJJeit3L0wrM0FGa1RJcVdCZnlUKzFWTzhUVktQa0VwR2RMRG92Wk9telpBQVNpOS9zaitqNmdNN0FhQ2kKRGVaVGYyRVM2NmFiQTVwT3A2MFE2T0Vkd2cvdkNVSmZhcmhLRHBpOXRqM1A2cVRveTlZNERpQlVoT2N0NE1HOAp3NVh3bUtBQytWZm04dGI3dE1pVW9VMHl2S0tPY0w2WVhCWHhCMmtQY09ZeFlOb2JYYXZmVkJFZHdTcmpRN2kvCnMzbzZoa0dRbG05RjdKUEV1VmdibC9KZHdhNjRPWUlxalFJREFRQUJveTB3S3pBSkJnTlZIUk1FQWpBQU1CRUcKQ1dDR1NBR0crRUlCQVFRRUF3SUhnREFMQmdOVkhROEVCQU1DQmVBd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dJQgpBSDlhU2NjMGNQQ2VtUEt3Yk9IY1B1VThON2pINjJoMkxqOGJyZWhlMjZZcXN3dFZYWUN1ZmtLRzFJQkFhNk1XCkxTOHhWVEV0b1ZjOHAwUVZDL3AzTDY1YjB4RG5pUlNCMFArWC9FVWFDNXIzUEE1SmQzVHgzakdBRzgxYjNETVUKMk83L3Jma2ZyZ1hCRUNWQlpRcm1ObVcvWnJpYXI5dmEwKzQwZGFPMTZLUVRRK2tHTDIzdldTWFNKQllJc1FsYgpyU2Y1QWp6eFpXY1Y1aXpUWUZLVjZwWmFsYjNibk8wRmptN2FMWVJZdndqWFhMR2pSb0JYd2xJaVpXRzNWVGJ1CnA5UWlBNEtyS2FvQ2x1bTRCakxtQkVlL1dnK1NVeXdTd2sydkFxUXViVUVtN1l3MmFiL1F3eSttUmIrRTVpaHcKcEhkOUxCaWlGLzBlbVNVbGVJY1NzUWpIend0WWNnQ0M5ZktpYUE1eEhkUXdLY3hOQVRDalZIUUtuMTJpQi92cgpwcVAxRjVHUGhUWk0zWWJ6ZG1wcDdlV0cva2NQRkREQk5CS2xzaXJsR3VRUkVPZ0QweW9pdTg5TG83Q01LV29kClBYbTZrMWxNWS9uRXR0R3VFZm5HR1ZvUUF2SFMvWU5pL3QwVFQ0SlJTMlZBbmZJMGV5K01nVmRCdDVETDMxd3QKa1hIWHh4M2QwV3JmZ0UxSlNvd0NNQ3E4ek1yZGo1UC9vQ0dqYmtsdFJtNnJ2UHZsTmtmRkt0eU42KzV2R0pKMgpWR1BxNnpnTkhmRXVOc1BDRWJ5MExwWm1NSEh6RlhuWjB1d3h6ZnBvVnZqcktYbEFQc1UreGd0K3ZNRzRaZ2VqCjlLTjBmdktnWkZlWWNkZVdsdzR3YThUc0ZadVZVeG1oa3ZsYVJQUlkzenNjCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KQmFnIEF0dHJpYnV0ZXMKICAgIGxvY2FsS2V5SUQ6IENGIDVDIDAxIEU4IEQyIDYzIERGIEM4IEZFIDMyIERCIEI0IDI4IDJEIDc2IEYzIEQwIDVEIDUyIEQ4IApLZXkgQXR0cmlidXRlczogPE5vIEF0dHJpYnV0ZXM+Ci0tLS0tQkVHSU4gRU5DUllQVEVEIFBSSVZBVEUgS0VZLS0tLS0KTUlJRkRqQkFCZ2txaGtpRzl3MEJCUTB3TXpBYkJna3Foa2lHOXcwQkJRd3dEZ1FJcjh6aStFNGwyTElDQWdnQQpNQlFHQ0NxR1NJYjNEUU1IQkFoSEpQUStQVHd3eHdTQ0JNaXVObWlCSGErQmIyWVdGUW1WUzZRYmJBMnlEZFROCmtSamp5K3locG5QSW1UU0plb28xY000UWJRL3owR2c5cUdxZDhNeThKMmtIcFQvNVRWK2VsOHBrbkRtYXYxdG0KMTMwMGlzOXdYd3ZxOUFlTWhSbXJlaFMyeldLYmJDSmk2YkNZczQ2NkZSRy8vRWM5eWZhcUN4blpVVzNhTGlXeApnV2xMeHQyRFRhN1hiTEU2Q1hWaEo4UUNnWGJQcVRRWTYxM1V3KzlRY2xod2t6dXZXYWNwQk5jU2N4dEZhTWZtCkp3UmxWZEdJdUxReDRXckFtUUxjU3J0RlhjMGJwZ1ljRjhqaEsxSTVUaElGNFFCWERlS2FGZFVDbG80NW9XNWIKb0djNXpwd1oraVA3eW96d2c5TnpySTYyam5FMXZBdEtnOWlsQzN0czR4MU9MMktsRXFycUJzbTRLU3c0ZTB6dgpTWUJJRUJuTEFsL0NlK0NLUGZ2TnpKTXpWVHV2Y0MrRGNOUXlmZ2N5bWlVZ2N1SHlkdFFGRi81cU55bjhIT2d2CnoxYm1qR0FBaHJaTHNyajdVWVA3a1FGMzliUlNiZy9Wa1dOa0Z5V2p2REJMc05iMTBXVy8xZEg1ZmJ3YzU4TmcKOTZrT2d3RFJWYWZuVEhtaDI5TzB6QXVMMUFsK0xPUEpnb3JGN0hHYzJneG5vVXEvYk1RREx6MFlSUHJ3eEVnbgpHNnRObTI3THgrZkdvaUdpMnFFUXRWVGErMGl2em1XS3pQbTUxOUU0VU5SR2ZhZGQrdjMrcXBKY3UyR1ZOb0E0Cmdvc2RNbUpYL3kvTkVXSk5kWndHTWpOaGluQW5obDdkL3ZJeVR4aHlvQVhLNExVRkdhWU01L1FYMkFzVjB2ZFUKMTJZWDhnd2ZKMVBYYXhqRGx2ZXp6dTErZTVHbTF5MjI1a0NKU1BNaWpaNE1uMzJnV2lFY0QvN3Vod2dQWkROTQo2clozaWl3K01aaDhqQkQveTBxdTNIdHV4ck0vclRTeERkZVFIb0QydWdnK0lZK0VhMmxsWjI0Sms2Ri9tOG5vCkY2Z3JDODdneEVXdzZIVW8xcE4zVVRzU2l4WCtxaG42eVUzYUJuRWpya0hwQ2ZqOEtNUXV4dXR4cWZDZk1jWmIKaVJpcXIvTmpkYjZ5dUk4OW52OGxVcHRHYjZMZUVEOTc2MGNyNjVhMGh3Ky9rcWZSd0VzUlhRbndYNzEyWHRIRgpVVWlTcmRVd1pGOGdBUFF0dWxmR0tiekllelFVaG9meHFLdUg3NDBtZzFsWmcrNzc2aGtOUGMzOVNNYkJVOEZ5CkYwTllORmhmdnJjbUdIY0Q2b0grZHJvUDJlODhQVXBDU0lHQ0lscm16eHJSOE0rRVR1d1FCOFIyeVFrM1NsN0QKTkh4cXk5RU1KUldVT0JpdmovYjk5QUFkTjE5eWRrZG94VXhOMFlSbWVRMXJGelpvU25OMFBUTjJNSWUvT2lOUApYeEhJZkZIckpSR1NPZm4wU0E0WDdqWHAwZllvUnFMVkJOOGN4bmJLNG5Ma2tob3pVc3BPUGtNVjBONEpLNDBBCkFmc05QTkUyWk1XYmNPbThoZ0ROMk8rVTl5c1NpTnB6akpoOURTWm5OOGR0d1V6NW1WVllaTm1hMExUbXpBME0KMmxKb1JlRGpJUFNaYWE0Q2tWcHhQK29OT1hCN0FMOW1BTnFxc0F4TjhiSjg1UUlSaXRDSEw5YW5oa1lsWGQyNwp1Sld6RWhJbGh6MjM4ZVlmL09KOUNQVkhoMVdmZHZlRndUM0Z4ZXZYaVNlcnVCT1M4UXp3QXJ5eGVxL0l4VmNqCnRSV25OQ2VVQTlqa3BJRktRd05CZ294K2tzU3BNN2t3eXd6dHFNV2F3aHNlSUNCMk9MTDBpaksyVVFaeHp4bTYKQnkvUmt0Qlg0RzI5Wk0wU05uOThNaXdKdW9TMHFvRlg4bVc2MExMWWdtMzZpazR1NTJvWm1CdzNScDVhZGF6cQo4UXREeEs2WDB0bVRUYVVteXB1SmIwUEdRN202c0dVc2h5SnNUTWd3Yi8zREljUFRZaENSRVVOeVVMdlk2MmhSCko5Q3c2aURGRVMzSDNjcjQ1QlRLZHJ3K1R1amFRRWdOUUJpMXNRVTFWUlVmVm5LNURhUjNkU2tEMTBxWmR5Q0EKV0JFPQotLS0tLUVORCBFTkNSWVBURUQgUFJJVkFURSBLRVktLS0tLQo=',
18+
'base64').toString('binary'),
19+
20+
pass: '',
21+
convertedCount: 1,
22+
},
23+
{
24+
title: 'DER',
25+
input: Buffer.from(
26+
'MIIC+zCCAeOgAwIBAgIJAOxwUeP5dw46MA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNzA1MzExNDExNTNaFw00NDEwMTYxNDExNTNaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALEPP29L/6fGvrA6wVD1kaPMS5o2O8h7AwIgnV8Fne74q/5NyMWrd/s5jAzug+yMRqLD8UbrA2s9vwKwkflKxHFZeIh1BtttTd31HmyPiE5tqp2/N2SqIm6bVb/8R4+i6CkW+K8SWk4+hQbEeAaAzNxq1oH/QKqkq1CzVsTjoLiUW2+dWw1iC0ZGefoablyICuIfBBH4OSK5KOKA8oKdOHoucYMOHXbXvsJVaNP+wJa8+qaDot45s2tliDljX06xNzOwSWSGzFbjiyEm5YuhO1VEIojQ+fe4EfToD00OrC5eTUI2BQ5eJ7FA8udjqAE86xmj9f63GxTbHvUyXx3ve8MCAwEAAaNQME4wHQYDVR0OBBYEFLW83bDG7ZWwluyWcueSk5A/hCk6MB8GA1UdIwQYMBaAFLW83bDG7ZWwluyWcueSk5A/hCk6MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAIfVtAO6VOwUQ2zp4QI698uV3ESxYIg1K6kTTWkvXkvYMlG5Wuvsan83V4zCTpclKSqePPr73eN9HkcGEOEXU+zM1eS4RfW8TDj9eLbGt0cBGzWC4gXJiMEx6Rr0VpcnjF5u9Iqty+YWQ/8MH10kTeOojuAtOmcrVe3VwB8bDx+PwHd0ZZCTWxi+qU/ST5jOEokiihaW68aHOc6IXgNRChPpI01gZVUFRevaJP5g1Yk41cSegGEPkmNO1X8ps2wrIzV28Ih5u7GiW1Gca+MwAAErzf8HsbGwZc56AXnpqKpM55T1cmfwPQe36lyUk8Cz/WzivmwsfbIlGRreK8kwCpc=',
27+
'base64').toString('binary'),
28+
pass: '',
29+
convertedCount: 1,
30+
},
31+
{
32+
title: 'P12',
33+
input: Buffer.from(
34+
'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',
35+
'base64').toString('binary'),
36+
pass: 'chef$123',
37+
convertedCount: 1,
38+
},
39+
];
40+
41+
describe('ssl-cert-converter', () => {
42+
for (const format of formatsData) {
43+
const { input, pass, convertedCount, title } = format;
44+
it(`Convert '${title}' format to correct values`, () => {
45+
expect(convertCertificate(input, pass)).toHaveLength(convertedCount);
46+
});
47+
}
48+
});
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import type { Buffer } from 'node:buffer';
2+
import {
3+
parseCertificate,
4+
} from 'sshpk';
5+
6+
import type {
7+
Certificate,
8+
CertificateFormat,
9+
} from 'sshpk';
10+
11+
import * as forge from 'node-forge';
12+
import jks from 'jks-js';
13+
14+
function convertPKCS12ToPem(p12base64: forge.Bytes | forge.util.ByteBuffer, password: string) {
15+
const p12Asn1 = forge.asn1.fromDer(p12base64, false);
16+
const p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, false, password);
17+
18+
const pemKey = getKeyFromP12(p12);
19+
const { pemCertificate, commonName } = getCertificateFromP12(p12);
20+
21+
return { pemKey, pemCertificate, commonName };
22+
}
23+
24+
function getKeyFromP12(p12: forge.pkcs12.Pkcs12Pfx) {
25+
const keyData = p12.getBags({ bagType: forge.pki.oids.pkcs8ShroudedKeyBag });
26+
let pkcs8Key = keyData[forge.pki.oids.pkcs8ShroudedKeyBag]![0];
27+
28+
if (!pkcs8Key) {
29+
pkcs8Key = keyData[forge.pki.oids.keyBag]![0];
30+
}
31+
32+
if (!pkcs8Key?.key) {
33+
throw new TypeError('Unable to get private key.');
34+
}
35+
36+
return forge.pki.privateKeyToPem(pkcs8Key.key);
37+
}
38+
39+
function getCertificateFromP12(p12: any) {
40+
const certData = p12.getBags({ bagType: forge.pki.oids.certBag });
41+
const certificate = certData[forge.pki.oids.certBag][0];
42+
43+
const pemCertificate = forge.pki.certificateToPem(certificate.cert);
44+
const commonName = certificate.cert.subject.attributes[0].value;
45+
return { pemCertificate, commonName };
46+
}
47+
48+
export function convertCertificate(
49+
inputKeyOrCertificateValue: string | Buffer,
50+
password: string) {
51+
const canParse = (value: any, parseFunction: (value: any) => any) => {
52+
try {
53+
return parseFunction(value);
54+
}
55+
catch (e: any) {
56+
// console.log(e);
57+
return null;
58+
}
59+
};
60+
61+
const cert = canParse(inputKeyOrCertificateValue, (value) => {
62+
for (const format of ['openssh', 'pem', 'x509']) {
63+
try {
64+
return parseCertificate(value, format as CertificateFormat);
65+
}
66+
catch {
67+
}
68+
}
69+
return null;
70+
}) as Certificate;
71+
if (cert) {
72+
return [{
73+
alias: '#default',
74+
key: null,
75+
der: canParse(cert, c => c.toBuffer('x509')),
76+
pem: cert.toString('pem'),
77+
}];
78+
}
79+
80+
const pkcs12 = canParse(inputKeyOrCertificateValue, (value) => {
81+
return convertPKCS12ToPem(forge.util.createBuffer(value, 'raw'), password);
82+
});
83+
if (pkcs12) {
84+
return [{
85+
alias: pkcs12.commonName,
86+
key: pkcs12.pemKey,
87+
der: canParse(pkcs12.pemCertificate, pemCert => parseCertificate(pemCert, 'pem').toBuffer('x509')),
88+
pem: pkcs12.pemCertificate,
89+
}];
90+
}
91+
92+
const parsedJKS = canParse(inputKeyOrCertificateValue, (value) => {
93+
return jks.toPem(
94+
value,
95+
password,
96+
);
97+
});
98+
if (parsedJKS) {
99+
return Object.entries(parsedJKS).map(([k, v]) => {
100+
if (typeof v === 'string') {
101+
return {
102+
alias: k,
103+
key: null,
104+
der: canParse(v, pemCert => parseCertificate(pemCert, 'pem').toBuffer('x509')),
105+
pem: v,
106+
};
107+
}
108+
const { cert, key } = v as { cert: string; key: string };
109+
return {
110+
alias: k,
111+
key,
112+
der: canParse(cert, pemCert => parseCertificate(pemCert, 'pem').toBuffer('x509')),
113+
pem: cert,
114+
};
115+
});
116+
}
117+
118+
return null;
119+
}

0 commit comments

Comments
 (0)