Skip to content

Commit

Permalink
feat(new tool): SSL Certificate Converter
Browse files Browse the repository at this point in the history
Fix #1245
Handle JKS, PEM, DER, P12 as input
Handle PEM and DER as output
  • Loading branch information
sharevb committed Sep 22, 2024
1 parent f5c4ab1 commit 1a241d8
Show file tree
Hide file tree
Showing 6 changed files with 316 additions and 1 deletion.
11 changes: 10 additions & 1 deletion src/tools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,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 emailNormalizer } from './email-normalizer';
import { tool as sslCertConverter } from './ssl-cert-converter';

import { tool as asciiTextDrawer } from './ascii-text-drawer';

Expand Down Expand Up @@ -164,7 +165,15 @@ export const toolsByCategory: ToolCategory[] = [
},
{
name: 'Network',
components: [ipv4SubnetCalculator, ipv4AddressConverter, ipv4RangeExpander, macAddressLookup, macAddressGenerator, ipv6UlaGenerator],
components: [
ipv4SubnetCalculator,
ipv4AddressConverter,
ipv4RangeExpander,
macAddressLookup,
macAddressGenerator,
ipv6UlaGenerator,
sslCertConverter,
],
},
{
name: 'Math',
Expand Down
12 changes: 12 additions & 0 deletions src/tools/ssl-cert-converter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ShieldChevron } from '@vicons/tabler';
import { defineTool } from '../tool';

export const tool = defineTool({
name: 'SSL Certificate converter',
path: '/ssl-cert-converter',
description: 'Convert SSL Certificate from different formats',
keywords: ['ssl', 'certificate', 'pem', 'der', 'jks', 'converter'],
component: () => import('./ssl-cert-converter.vue'),
icon: ShieldChevron,
createdAt: new Date('2024-08-15'),
});
15 changes: 15 additions & 0 deletions src/tools/ssl-cert-converter/ssl-cert-converter.e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { test, expect } from '@playwright/test';

Check failure on line 1 in src/tools/ssl-cert-converter/ssl-cert-converter.e2e.spec.ts

View workflow job for this annotation

GitHub Actions / ci

Member 'expect' of the import declaration should be sorted alphabetically

test.describe('Tool - Ssl cert converter', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/ssl-cert-converter');
});

test('Has correct title', async ({ page }) => {
await expect(page).toHaveTitle('Ssl cert converter - IT Tools');
});

test('', async ({ page }) => {

Check warning on line 12 in src/tools/ssl-cert-converter/ssl-cert-converter.e2e.spec.ts

View workflow job for this annotation

GitHub Actions / ci

'page' is defined but never used. Allowed unused args must match /^_/u

});
});

Check failure on line 15 in src/tools/ssl-cert-converter/ssl-cert-converter.e2e.spec.ts

View workflow job for this annotation

GitHub Actions / ci

Newline required at end of file but not found
48 changes: 48 additions & 0 deletions src/tools/ssl-cert-converter/ssl-cert-converter.service.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Buffer } from 'node:buffer';
import { describe, expect, it } from 'vitest';
import { convertCertificate } from './ssl-cert-converter.service';

const formatsData = [
{
title: 'JKS',
input: Buffer.from(
'/u3+7QAAAAIAAAABAAAAAQAGamtzLWpzAAABeDENGOMAAAB/MH0wDgYKKwYBBAEqAhEBAQUABGstagMT9RkL6lTrGvx2untoFmXM13xjQjMKfxFU/iQHuk3Y44LeB5oP9/e8KEe6nK1NTQhaTRrKyMZGJhs5Oro+TLowYerbBiJJ2DKyBTVjMDCZj8f29hOXpxQpIVv6IEAlFJwL3TQNydxjdgAAAAEABVguNTA5AAAB5jCCAeIwggGHoAMCAQICBCljjXAwDAYIKoZIzj0EAwIFADBmMQ8wDQYDVQQGEwZqa3MtanMxDzANBgNVBAgTBmprcy1qczEPMA0GA1UEBxMGamtzLWpzMQ8wDQYDVQQKEwZsZW5jaHYxDzANBgNVBAsTBmprcy1qczEPMA0GA1UEAxMGamtzLWpzMB4XDTIxMDMxNDE0MDQwNVoXDTIxMDYxMjE0MDQwNVowZjEPMA0GA1UEBhMGamtzLWpzMQ8wDQYDVQQIEwZqa3MtanMxDzANBgNVBAcTBmprcy1qczEPMA0GA1UEChMGbGVuY2h2MQ8wDQYDVQQLEwZqa3MtanMxDzANBgNVBAMTBmprcy1qczBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEXhUAIulQWBEFCRBqPl7t/FKuI6hzWYpdBpwWZSRiDkp9A0xeIJyHazINyyx2xIDyCvR1vrqIhMuFxJIWxp8DmjITAfMB0GA1UdDgQWBBRpec0VpSfJmWjyzDtiUWk5difuTjAMBggqhkjOPQQDAgUAA0cAMEQCIE2hdtgyJZgO+gGZrCBxSgQ7G/uRugeIDGBR5X9oY2rAAiBgRfCUsTPr5NPeTfuS854/koMCTYrvLEcwcRGD4uBuNe3vG6EIUGVgYuXdiR4aycUoOcEb',
'base64'),
pass: 'password',
convertedCount: 1,
},
{
title: 'PEM',
input: Buffer.from(
'QmFnIEF0dHJpYnV0ZXMKICAgIGxvY2FsS2V5SUQ6IENGIDVDIDAxIEU4IEQyIDYzIERGIEM4IEZFIDMyIERCIEI0IDI4IDJEIDc2IEYzIEQwIDVEIDUyIEQ4IApzdWJqZWN0PS9DPVVTL1NUPUNhbGlmb3JuaWEvTD1TYW4gRnJhbmNpc2NvL089QmFkU1NML0NOPUJhZFNTTCBDbGllbnQgQ2VydGlmaWNhdGUKaXNzdWVyPS9DPVVTL1NUPUNhbGlmb3JuaWEvTD1TYW4gRnJhbmNpc2NvL089QmFkU1NML0NOPUJhZFNTTCBDbGllbnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkKLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVuVENDQW9XZ0F3SUJBZ0lKQU0xSHg0SkoxT2dwTUEwR0NTcUdTSWIzRFFFQkN3VUFNSDR4Q3pBSkJnTlYKQkFZVEFsVlRNUk13RVFZRFZRUUlEQXBEWVd4cFptOXlibWxoTVJZd0ZBWURWUVFIREExVFlXNGdSbkpoYm1OcApjMk52TVE4d0RRWURWUVFLREFaQ1lXUlRVMHd4TVRBdkJnTlZCQU1NS0VKaFpGTlRUQ0JEYkdsbGJuUWdVbTl2CmRDQkRaWEowYVdacFkyRjBaU0JCZFhSb2IzSnBkSGt3SGhjTk1qUXdPREl3TVRZeU5EUXpXaGNOTWpZd09ESXcKTVRZeU5EUXpXakJ2TVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVXTUJRRwpBMVVFQnd3TlUyRnVJRVp5WVc1amFYTmpiekVQTUEwR0ExVUVDZ3dHUW1Ga1UxTk1NU0l3SUFZRFZRUUREQmxDCllXUlRVMHdnUTJ4cFpXNTBJRU5sY25ScFptbGpZWFJsTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEEKTUlJQkNnS0NBUUVBeHpkZkVlc2VUcy9ydWtqbHk2TVNMSE0rUmgwZW5BM0FpNE1qMnNkbDMxeDNTYlBvZW4wOAp1dFZoalBtbHhJVWRraU1HNCtmZmU3TitKdERMRzc1Q2F4WnA5Q3h5dFg3a3l3b29SQkpzUm5RaG1RUGNhOE1SCldBSkJJeit3L0wrM0FGa1RJcVdCZnlUKzFWTzhUVktQa0VwR2RMRG92Wk9telpBQVNpOS9zaitqNmdNN0FhQ2kKRGVaVGYyRVM2NmFiQTVwT3A2MFE2T0Vkd2cvdkNVSmZhcmhLRHBpOXRqM1A2cVRveTlZNERpQlVoT2N0NE1HOAp3NVh3bUtBQytWZm04dGI3dE1pVW9VMHl2S0tPY0w2WVhCWHhCMmtQY09ZeFlOb2JYYXZmVkJFZHdTcmpRN2kvCnMzbzZoa0dRbG05RjdKUEV1VmdibC9KZHdhNjRPWUlxalFJREFRQUJveTB3S3pBSkJnTlZIUk1FQWpBQU1CRUcKQ1dDR1NBR0crRUlCQVFRRUF3SUhnREFMQmdOVkhROEVCQU1DQmVBd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dJQgpBSDlhU2NjMGNQQ2VtUEt3Yk9IY1B1VThON2pINjJoMkxqOGJyZWhlMjZZcXN3dFZYWUN1ZmtLRzFJQkFhNk1XCkxTOHhWVEV0b1ZjOHAwUVZDL3AzTDY1YjB4RG5pUlNCMFArWC9FVWFDNXIzUEE1SmQzVHgzakdBRzgxYjNETVUKMk83L3Jma2ZyZ1hCRUNWQlpRcm1ObVcvWnJpYXI5dmEwKzQwZGFPMTZLUVRRK2tHTDIzdldTWFNKQllJc1FsYgpyU2Y1QWp6eFpXY1Y1aXpUWUZLVjZwWmFsYjNibk8wRmptN2FMWVJZdndqWFhMR2pSb0JYd2xJaVpXRzNWVGJ1CnA5UWlBNEtyS2FvQ2x1bTRCakxtQkVlL1dnK1NVeXdTd2sydkFxUXViVUVtN1l3MmFiL1F3eSttUmIrRTVpaHcKcEhkOUxCaWlGLzBlbVNVbGVJY1NzUWpIend0WWNnQ0M5ZktpYUE1eEhkUXdLY3hOQVRDalZIUUtuMTJpQi92cgpwcVAxRjVHUGhUWk0zWWJ6ZG1wcDdlV0cva2NQRkREQk5CS2xzaXJsR3VRUkVPZ0QweW9pdTg5TG83Q01LV29kClBYbTZrMWxNWS9uRXR0R3VFZm5HR1ZvUUF2SFMvWU5pL3QwVFQ0SlJTMlZBbmZJMGV5K01nVmRCdDVETDMxd3QKa1hIWHh4M2QwV3JmZ0UxSlNvd0NNQ3E4ek1yZGo1UC9vQ0dqYmtsdFJtNnJ2UHZsTmtmRkt0eU42KzV2R0pKMgpWR1BxNnpnTkhmRXVOc1BDRWJ5MExwWm1NSEh6RlhuWjB1d3h6ZnBvVnZqcktYbEFQc1UreGd0K3ZNRzRaZ2VqCjlLTjBmdktnWkZlWWNkZVdsdzR3YThUc0ZadVZVeG1oa3ZsYVJQUlkzenNjCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KQmFnIEF0dHJpYnV0ZXMKICAgIGxvY2FsS2V5SUQ6IENGIDVDIDAxIEU4IEQyIDYzIERGIEM4IEZFIDMyIERCIEI0IDI4IDJEIDc2IEYzIEQwIDVEIDUyIEQ4IApLZXkgQXR0cmlidXRlczogPE5vIEF0dHJpYnV0ZXM+Ci0tLS0tQkVHSU4gRU5DUllQVEVEIFBSSVZBVEUgS0VZLS0tLS0KTUlJRkRqQkFCZ2txaGtpRzl3MEJCUTB3TXpBYkJna3Foa2lHOXcwQkJRd3dEZ1FJcjh6aStFNGwyTElDQWdnQQpNQlFHQ0NxR1NJYjNEUU1IQkFoSEpQUStQVHd3eHdTQ0JNaXVObWlCSGErQmIyWVdGUW1WUzZRYmJBMnlEZFROCmtSamp5K3locG5QSW1UU0plb28xY000UWJRL3owR2c5cUdxZDhNeThKMmtIcFQvNVRWK2VsOHBrbkRtYXYxdG0KMTMwMGlzOXdYd3ZxOUFlTWhSbXJlaFMyeldLYmJDSmk2YkNZczQ2NkZSRy8vRWM5eWZhcUN4blpVVzNhTGlXeApnV2xMeHQyRFRhN1hiTEU2Q1hWaEo4UUNnWGJQcVRRWTYxM1V3KzlRY2xod2t6dXZXYWNwQk5jU2N4dEZhTWZtCkp3UmxWZEdJdUxReDRXckFtUUxjU3J0RlhjMGJwZ1ljRjhqaEsxSTVUaElGNFFCWERlS2FGZFVDbG80NW9XNWIKb0djNXpwd1oraVA3eW96d2c5TnpySTYyam5FMXZBdEtnOWlsQzN0czR4MU9MMktsRXFycUJzbTRLU3c0ZTB6dgpTWUJJRUJuTEFsL0NlK0NLUGZ2TnpKTXpWVHV2Y0MrRGNOUXlmZ2N5bWlVZ2N1SHlkdFFGRi81cU55bjhIT2d2CnoxYm1qR0FBaHJaTHNyajdVWVA3a1FGMzliUlNiZy9Wa1dOa0Z5V2p2REJMc05iMTBXVy8xZEg1ZmJ3YzU4TmcKOTZrT2d3RFJWYWZuVEhtaDI5TzB6QXVMMUFsK0xPUEpnb3JGN0hHYzJneG5vVXEvYk1RREx6MFlSUHJ3eEVnbgpHNnRObTI3THgrZkdvaUdpMnFFUXRWVGErMGl2em1XS3pQbTUxOUU0VU5SR2ZhZGQrdjMrcXBKY3UyR1ZOb0E0Cmdvc2RNbUpYL3kvTkVXSk5kWndHTWpOaGluQW5obDdkL3ZJeVR4aHlvQVhLNExVRkdhWU01L1FYMkFzVjB2ZFUKMTJZWDhnd2ZKMVBYYXhqRGx2ZXp6dTErZTVHbTF5MjI1a0NKU1BNaWpaNE1uMzJnV2lFY0QvN3Vod2dQWkROTQo2clozaWl3K01aaDhqQkQveTBxdTNIdHV4ck0vclRTeERkZVFIb0QydWdnK0lZK0VhMmxsWjI0Sms2Ri9tOG5vCkY2Z3JDODdneEVXdzZIVW8xcE4zVVRzU2l4WCtxaG42eVUzYUJuRWpya0hwQ2ZqOEtNUXV4dXR4cWZDZk1jWmIKaVJpcXIvTmpkYjZ5dUk4OW52OGxVcHRHYjZMZUVEOTc2MGNyNjVhMGh3Ky9rcWZSd0VzUlhRbndYNzEyWHRIRgpVVWlTcmRVd1pGOGdBUFF0dWxmR0tiekllelFVaG9meHFLdUg3NDBtZzFsWmcrNzc2aGtOUGMzOVNNYkJVOEZ5CkYwTllORmhmdnJjbUdIY0Q2b0grZHJvUDJlODhQVXBDU0lHQ0lscm16eHJSOE0rRVR1d1FCOFIyeVFrM1NsN0QKTkh4cXk5RU1KUldVT0JpdmovYjk5QUFkTjE5eWRrZG94VXhOMFlSbWVRMXJGelpvU25OMFBUTjJNSWUvT2lOUApYeEhJZkZIckpSR1NPZm4wU0E0WDdqWHAwZllvUnFMVkJOOGN4bmJLNG5Ma2tob3pVc3BPUGtNVjBONEpLNDBBCkFmc05QTkUyWk1XYmNPbThoZ0ROMk8rVTl5c1NpTnB6akpoOURTWm5OOGR0d1V6NW1WVllaTm1hMExUbXpBME0KMmxKb1JlRGpJUFNaYWE0Q2tWcHhQK29OT1hCN0FMOW1BTnFxc0F4TjhiSjg1UUlSaXRDSEw5YW5oa1lsWGQyNwp1Sld6RWhJbGh6MjM4ZVlmL09KOUNQVkhoMVdmZHZlRndUM0Z4ZXZYaVNlcnVCT1M4UXp3QXJ5eGVxL0l4VmNqCnRSV25OQ2VVQTlqa3BJRktRd05CZ294K2tzU3BNN2t3eXd6dHFNV2F3aHNlSUNCMk9MTDBpaksyVVFaeHp4bTYKQnkvUmt0Qlg0RzI5Wk0wU05uOThNaXdKdW9TMHFvRlg4bVc2MExMWWdtMzZpazR1NTJvWm1CdzNScDVhZGF6cQo4UXREeEs2WDB0bVRUYVVteXB1SmIwUEdRN202c0dVc2h5SnNUTWd3Yi8zREljUFRZaENSRVVOeVVMdlk2MmhSCko5Q3c2aURGRVMzSDNjcjQ1QlRLZHJ3K1R1amFRRWdOUUJpMXNRVTFWUlVmVm5LNURhUjNkU2tEMTBxWmR5Q0EKV0JFPQotLS0tLUVORCBFTkNSWVBURUQgUFJJVkFURSBLRVktLS0tLQo=',
'base64').toString('binary'),

pass: '',
convertedCount: 1,
},
{
title: 'DER',
input: Buffer.from(
'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=',
'base64').toString('binary'),
pass: '',
convertedCount: 1,
},
{
title: 'P12',
input: Buffer.from(
'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',
'base64').toString('binary'),
pass: 'chef$123',
convertedCount: 1,
},
];

describe('ssl-cert-converter', () => {
for (const format of formatsData) {
const { input, pass, convertedCount, title } = format;
it(`Convert '${title}' format to correct values`, () => {
expect(convertCertificate(input, pass)).toHaveLength(convertedCount);
});
}
});
119 changes: 119 additions & 0 deletions src/tools/ssl-cert-converter/ssl-cert-converter.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import type { Buffer } from 'node:buffer';
import {
parseCertificate,
} from 'sshpk';

import type {
Certificate,
CertificateFormat,
} from 'sshpk';

import * as forge from 'node-forge';
import jks from 'jks-js';

function convertPKCS12ToPem(p12base64: forge.Bytes | forge.util.ByteBuffer, password: string) {
const p12Asn1 = forge.asn1.fromDer(p12base64, false);
const p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, false, password);

const pemKey = getKeyFromP12(p12);
const { pemCertificate, commonName } = getCertificateFromP12(p12);

return { pemKey, pemCertificate, commonName };
}

function getKeyFromP12(p12: forge.pkcs12.Pkcs12Pfx) {
const keyData = p12.getBags({ bagType: forge.pki.oids.pkcs8ShroudedKeyBag });
let pkcs8Key = keyData[forge.pki.oids.pkcs8ShroudedKeyBag]![0];

if (typeof pkcs8Key === 'undefined') {
pkcs8Key = keyData[forge.pki.oids.keyBag]![0];
}

if (typeof pkcs8Key === 'undefined') {
throw new TypeError('Unable to get private key.');
}

return forge.pki.privateKeyToPem(pkcs8Key.key!);
}

function getCertificateFromP12(p12: any) {
const certData = p12.getBags({ bagType: forge.pki.oids.certBag });
const certificate = certData[forge.pki.oids.certBag][0];

const pemCertificate = forge.pki.certificateToPem(certificate.cert);
const commonName = certificate.cert.subject.attributes[0].value;
return { pemCertificate, commonName };
}

export function convertCertificate(
inputKeyOrCertificateValue: string | Buffer,
password: string) {
const canParse = (value: any, parseFunction: (value: any) => any) => {
try {
return parseFunction(value);
}
catch (e: any) {
// console.log(e);
return null;
}
};

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) {
return [{
alias: '#default',
key: null,
der: canParse(cert, c => c.toBuffer('x509')),
pem: cert.toString('pem'),
}];
}

const pkcs12 = canParse(inputKeyOrCertificateValue, (value) => {
return convertPKCS12ToPem(forge.util.createBuffer(value, 'raw'), password);
});
if (pkcs12) {
return [{
alias: pkcs12.commonName,
key: pkcs12.pemKey,
der: canParse(pkcs12.pemCertificate, pemCert => parseCertificate(pemCert, 'pem').toBuffer('x509')),
pem: pkcs12.pemCertificate,
}];
}

const parsedJKS = canParse(inputKeyOrCertificateValue, (value) => {
return jks.toPem(
value,
password,
);
});
if (parsedJKS) {
return Object.entries(parsedJKS).map(([k, v]) => {
if (typeof v === 'string') {
return {
alias: k,
key: null,
der: canParse(v, pemCert => parseCertificate(pemCert, 'pem').toBuffer('x509')),
pem: v,
};
}
const { cert, key } = v as { cert: string; key: string };
return {
alias: k,
key,
der: canParse(cert, pemCert => parseCertificate(pemCert, 'pem').toBuffer('x509')),
pem: cert,
};
});
}

return null;
}
Loading

0 comments on commit 1a241d8

Please sign in to comment.