or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

http-tunneling.mdindex.mdkey-management.mdport-forwarding.mdsftp-operations.mdssh-agents.mdssh-client.mdssh-server.md

key-management.mddocs/

0

# Key Management

1

2

Comprehensive SSH key parsing, generation, and management utilities supporting RSA, ECDSA, and Ed25519 keys with multiple formats and encryption options.

3

4

## Capabilities

5

6

### Key Parsing

7

8

Parse SSH and OpenSSH private and public keys from various formats.

9

10

```javascript { .api }

11

/**

12

* Parse SSH/OpenSSH private or public key

13

* Supports multiple key formats: OpenSSH, PEM, PKCS#1, PKCS#8

14

* @param keyData - Key data as string or Buffer

15

* @param passphrase - Optional passphrase for encrypted keys

16

* @returns Parsed key object or Error if parsing fails

17

*/

18

function parseKey(keyData: string | Buffer, passphrase?: string): ParsedKey | Error;

19

```

20

21

**Key Parsing Examples:**

22

23

```javascript

24

const { utils } = require('ssh2');

25

const fs = require('fs');

26

27

// Parse OpenSSH private key

28

const privateKey = fs.readFileSync('/path/to/id_rsa', 'utf8');

29

const parsedPrivate = utils.parseKey(privateKey, 'passphrase');

30

31

if (parsedPrivate instanceof Error) {

32

console.error('Failed to parse private key:', parsedPrivate.message);

33

} else {

34

console.log('Private key type:', parsedPrivate.type);

35

console.log('Private key comment:', parsedPrivate.comment);

36

}

37

38

// Parse OpenSSH public key

39

const publicKey = fs.readFileSync('/path/to/id_rsa.pub', 'utf8');

40

const parsedPublic = utils.parseKey(publicKey);

41

42

if (parsedPublic instanceof Error) {

43

console.error('Failed to parse public key:', parsedPublic.message);

44

} else {

45

console.log('Public key type:', parsedPublic.type);

46

console.log('Public key fingerprint:', parsedPublic.getPublicSSH().toString('base64'));

47

}

48

49

// Parse PEM format key

50

const pemKey = fs.readFileSync('/path/to/key.pem', 'utf8');

51

const parsedPem = utils.parseKey(pemKey);

52

53

// Parse encrypted key

54

const encryptedKey = fs.readFileSync('/path/to/encrypted_key', 'utf8');

55

const parsedEncrypted = utils.parseKey(encryptedKey, 'my-secret-passphrase');

56

```

57

58

### Key Generation

59

60

Generate new SSH key pairs with various algorithms and options.

61

62

```javascript { .api }

63

/**

64

* Generate SSH key pair asynchronously

65

* @param keyType - Key type: 'rsa', 'ecdsa', or 'ed25519'

66

* @param options - Key generation options

67

* @param callback - Callback receiving generated key pair

68

*/

69

function generateKeyPair(keyType: string, options: KeyGenOptions, callback: KeyGenCallback): void;

70

function generateKeyPair(keyType: string, callback: KeyGenCallback): void;

71

72

/**

73

* Generate SSH key pair synchronously

74

* @param keyType - Key type: 'rsa', 'ecdsa', or 'ed25519'

75

* @param options - Key generation options

76

* @returns Generated key pair

77

*/

78

function generateKeyPairSync(keyType: string, options?: KeyGenOptions): KeyPair;

79

80

interface KeyGenOptions {

81

bits?: number; // Key size (RSA: 2048-4096, ECDSA: 256/384/521)

82

comment?: string; // Key comment

83

format?: string; // Output format ('new' for OpenSSH, 'old' for PEM)

84

passphrase?: string; // Private key encryption passphrase

85

cipher?: string; // Encryption cipher for passphrase protection

86

rounds?: number; // KDF rounds for passphrase (default: 16)

87

}

88

89

interface KeyPair {

90

private: string; // Private key in requested format

91

public: string; // Public key in OpenSSH format

92

}

93

94

type KeyGenCallback = (err: Error | null, keyPair?: KeyPair) => void;

95

```

96

97

**Key Generation Examples:**

98

99

```javascript

100

const { generateKeyPair, generateKeyPairSync } = require('ssh2').utils;

101

102

// Generate RSA key pair asynchronously

103

generateKeyPair('rsa', {

104

bits: 2048,

105

comment: 'user@hostname',

106

format: 'new',

107

passphrase: 'secure-passphrase'

108

}, (err, keyPair) => {

109

if (err) {

110

console.error('Key generation failed:', err.message);

111

return;

112

}

113

114

console.log('Private key:');

115

console.log(keyPair.private);

116

console.log('\nPublic key:');

117

console.log(keyPair.public);

118

119

// Save keys to files

120

require('fs').writeFileSync('id_rsa', keyPair.private, { mode: 0o600 });

121

require('fs').writeFileSync('id_rsa.pub', keyPair.public);

122

});

123

124

// Generate ECDSA key pair synchronously

125

try {

126

const ecdsaKeys = generateKeyPairSync('ecdsa', {

127

bits: 256,

128

comment: 'ECDSA key for production'

129

});

130

131

console.log('ECDSA key generated successfully');

132

console.log('Public key:', ecdsaKeys.public);

133

} catch (err) {

134

console.error('ECDSA generation failed:', err.message);

135

}

136

137

// Generate Ed25519 key pair (most secure)

138

generateKeyPair('ed25519', {

139

comment: 'Ed25519 key for SSH access',

140

format: 'new'

141

}, (err, keyPair) => {

142

if (err) throw err;

143

144

console.log('Ed25519 key pair generated');

145

146

// Ed25519 keys are always the same size, no bits option needed

147

console.log('Private key length:', keyPair.private.length);

148

console.log('Public key:', keyPair.public);

149

});

150

151

// Generate encrypted RSA key with custom cipher

152

generateKeyPair('rsa', {

153

bits: 4096,

154

comment: 'High-security RSA key',

155

passphrase: 'very-secure-password',

156

cipher: 'aes256-ctr',

157

rounds: 32

158

}, (err, keyPair) => {

159

if (err) throw err;

160

161

console.log('Encrypted 4096-bit RSA key generated');

162

});

163

```

164

165

### ParsedKey Object

166

167

The parsed key object provides methods for key operations and format conversion.

168

169

```javascript { .api }

170

interface ParsedKey {

171

// Key properties

172

type: string; // Key type: 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ssh-ed25519', etc.

173

comment?: string; // Key comment (if present)

174

public: Buffer; // Public key data

175

private?: Buffer; // Private key data (if available)

176

177

// Key format conversion methods

178

getPrivatePEM(): string;

179

getPublicPEM(): string;

180

getPublicSSH(): Buffer;

181

182

// Cryptographic operations

183

sign(data: Buffer, hashAlgo?: string): Buffer | Error;

184

verify(data: Buffer, signature: Buffer, hashAlgo?: string): boolean | Error;

185

186

// Key equality checking

187

equals(otherKey: ParsedKey): boolean;

188

}

189

```

190

191

**ParsedKey Usage Examples:**

192

193

```javascript

194

const { utils } = require('ssh2');

195

const fs = require('fs');

196

197

// Load and parse a private key

198

const keyData = fs.readFileSync('id_rsa', 'utf8');

199

const parsedKey = utils.parseKey(keyData, 'passphrase');

200

201

if (parsedKey instanceof Error) {

202

console.error('Key parsing failed:', parsedKey.message);

203

} else {

204

console.log('Key type:', parsedKey.type);

205

console.log('Has private key:', !!parsedKey.private);

206

console.log('Comment:', parsedKey.comment);

207

208

// Convert to different formats

209

console.log('\nPEM format:');

210

console.log(parsedKey.getPrivatePEM());

211

212

console.log('\nOpenSSH public key format:');

213

console.log(parsedKey.getPublicSSH().toString());

214

215

// Sign data

216

const data = Buffer.from('Hello, SSH world!');

217

const signature = parsedKey.sign(data);

218

219

if (signature instanceof Error) {

220

console.error('Signing failed:', signature.message);

221

} else {

222

console.log('Signature:', signature.toString('base64'));

223

224

// Verify signature

225

const verified = parsedKey.verify(data, signature);

226

console.log('Signature verified:', verified);

227

}

228

229

// Compare keys

230

const publicKeyData = fs.readFileSync('id_rsa.pub', 'utf8');

231

const publicKey = utils.parseKey(publicKeyData);

232

233

if (publicKey instanceof Error) {

234

console.error('Public key parsing failed');

235

} else {

236

console.log('Keys match:', parsedKey.equals(publicKey));

237

}

238

}

239

```

240

241

### Key Format Support

242

243

Support for multiple SSH and cryptographic key formats.

244

245

#### Supported Key Types

246

247

```javascript { .api }

248

// RSA keys

249

type: 'ssh-rsa'

250

251

// ECDSA keys

252

type: 'ecdsa-sha2-nistp256' // NIST P-256

253

type: 'ecdsa-sha2-nistp384' // NIST P-384

254

type: 'ecdsa-sha2-nistp521' // NIST P-521

255

256

// Ed25519 keys

257

type: 'ssh-ed25519'

258

259

// Legacy DSA keys (deprecated)

260

type: 'ssh-dss'

261

```

262

263

#### Supported Input Formats

264

265

- **OpenSSH**: Native OpenSSH private/public key format

266

- **PEM**: Privacy-Enhanced Mail format (PKCS#1, PKCS#8)

267

- **RFC4716**: SSH.com public key format

268

- **Legacy**: Various legacy formats

269

270

```javascript

271

// Examples of supported formats

272

273

// OpenSSH private key

274

const opensshPrivate = `-----BEGIN OPENSSH PRIVATE KEY-----

275

b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn

276

...

277

-----END OPENSSH PRIVATE KEY-----`;

278

279

// OpenSSH public key

280

const opensshPublic = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC... user@hostname`;

281

282

// PEM private key

283

const pemPrivate = `-----BEGIN RSA PRIVATE KEY-----

284

MIIEpAIBAAKCAQEA5j5j5j5j5j5j5j5j5j5j5j5j5j5j5j5j5j5j5j5j5j5j5j5j5j5j

285

...

286

-----END RSA PRIVATE KEY-----`;

287

288

// All can be parsed with utils.parseKey()

289

const key1 = utils.parseKey(opensshPrivate);

290

const key2 = utils.parseKey(opensshPublic);

291

const key3 = utils.parseKey(pemPrivate);

292

```

293

294

### Advanced Key Operations

295

296

Advanced operations for key management and cryptographic functions.

297

298

```javascript { .api }

299

/**

300

* Check if object is a parsed key

301

* @param obj - Object to check

302

* @returns true if object is ParsedKey

303

*/

304

function isParsedKey(obj: any): obj is ParsedKey;

305

306

/**

307

* Generate key fingerprint

308

* @param key - Parsed key object

309

* @param hashAlgo - Hash algorithm ('md5', 'sha1', 'sha256')

310

* @returns Key fingerprint string

311

*/

312

function generateFingerprint(key: ParsedKey, hashAlgo?: string): string;

313

```

314

315

**Advanced Operations Examples:**

316

317

```javascript

318

const { utils } = require('ssh2');

319

const crypto = require('crypto');

320

321

// Key validation and fingerprinting

322

function analyzeKey(keyPath, passphrase) {

323

const keyData = require('fs').readFileSync(keyPath, 'utf8');

324

const key = utils.parseKey(keyData, passphrase);

325

326

if (key instanceof Error) {

327

console.error('Invalid key:', key.message);

328

return;

329

}

330

331

console.log('Key Analysis:');

332

console.log(' Type:', key.type);

333

console.log(' Comment:', key.comment || '(no comment)');

334

console.log(' Has private key:', !!key.private);

335

336

// Generate fingerprints

337

const publicSSH = key.getPublicSSH();

338

const md5Hash = crypto.createHash('md5').update(publicSSH).digest('hex');

339

const sha256Hash = crypto.createHash('sha256').update(publicSSH).digest('base64');

340

341

console.log(' MD5 fingerprint:', md5Hash.replace(/(.{2})/g, '$1:').slice(0, -1));

342

console.log(' SHA256 fingerprint:', sha256Hash);

343

344

// Key strength analysis

345

if (key.type === 'ssh-rsa') {

346

const keySize = key.public.length * 8; // Rough estimate

347

console.log(' Estimated key size:', keySize, 'bits');

348

349

if (keySize < 2048) {

350

console.log(' WARNING: RSA key size may be too small for security');

351

}

352

}

353

354

return key;

355

}

356

357

// Key conversion utility

358

function convertKeyFormat(inputPath, outputPath, targetFormat, passphrase) {

359

const keyData = require('fs').readFileSync(inputPath, 'utf8');

360

const key = utils.parseKey(keyData, passphrase);

361

362

if (key instanceof Error) {

363

throw new Error(`Failed to parse key: ${key.message}`);

364

}

365

366

let output;

367

switch (targetFormat) {

368

case 'openssh-private':

369

output = key.getPrivatePEM(); // Note: This gets PEM, OpenSSH format not directly available

370

break;

371

case 'openssh-public':

372

output = key.getPublicSSH().toString();

373

break;

374

case 'pem-private':

375

output = key.getPrivatePEM();

376

break;

377

case 'pem-public':

378

output = key.getPublicPEM();

379

break;

380

default:

381

throw new Error('Unsupported target format');

382

}

383

384

require('fs').writeFileSync(outputPath, output, { mode: 0o600 });

385

console.log(`Converted key saved to ${outputPath}`);

386

}

387

388

// Usage examples

389

analyzeKey('id_rsa', 'passphrase');

390

convertKeyFormat('id_rsa', 'id_rsa.pem', 'pem-private', 'passphrase');

391

```

392

393

### Key Validation and Security

394

395

Utilities for validating key security and best practices.

396

397

```javascript

398

// Key security analyzer

399

class KeySecurityAnalyzer {

400

static analyzeKey(key) {

401

const analysis = {

402

type: key.type,

403

secure: true,

404

warnings: [],

405

recommendations: []

406

};

407

408

switch (key.type) {

409

case 'ssh-rsa':

410

// RSA key analysis

411

const rsaBits = this.estimateRSAKeySize(key);

412

analysis.keySize = rsaBits;

413

414

if (rsaBits < 2048) {

415

analysis.secure = false;

416

analysis.warnings.push('RSA key size is less than 2048 bits');

417

analysis.recommendations.push('Generate a new RSA key with at least 2048 bits');

418

} else if (rsaBits < 3072) {

419

analysis.warnings.push('RSA key size is less than 3072 bits (recommended for long-term use)');

420

}

421

break;

422

423

case 'ecdsa-sha2-nistp256':

424

analysis.keySize = 256;

425

analysis.recommendations.push('Consider Ed25519 for better performance and security');

426

break;

427

428

case 'ecdsa-sha2-nistp384':

429

analysis.keySize = 384;

430

break;

431

432

case 'ecdsa-sha2-nistp521':

433

analysis.keySize = 521;

434

break;

435

436

case 'ssh-ed25519':

437

analysis.keySize = 256;

438

analysis.recommendations.push('Ed25519 is currently the most secure and efficient option');

439

break;

440

441

case 'ssh-dss':

442

analysis.secure = false;

443

analysis.warnings.push('DSA keys are deprecated and insecure');

444

analysis.recommendations.push('Replace with RSA, ECDSA, or Ed25519 key');

445

break;

446

447

default:

448

analysis.warnings.push('Unknown or unsupported key type');

449

}

450

451

return analysis;

452

}

453

454

static estimateRSAKeySize(key) {

455

// This is a rough estimation - actual implementation would need to parse the key structure

456

const publicKeyLen = key.public.length;

457

return Math.floor(publicKeyLen * 4); // Rough approximation

458

}

459

460

static checkKeyAge(keyPath) {

461

const stats = require('fs').statSync(keyPath);

462

const ageInDays = (Date.now() - stats.mtime.getTime()) / (1000 * 60 * 60 * 24);

463

464

if (ageInDays > 365 * 2) {

465

return {

466

age: ageInDays,

467

warning: 'Key is older than 2 years, consider rotation'

468

};

469

}

470

471

return { age: ageInDays };

472

}

473

}

474

475

// Usage

476

const key = utils.parseKey(keyData);

477

if (!(key instanceof Error)) {

478

const analysis = KeySecurityAnalyzer.analyzeKey(key);

479

console.log('Security Analysis:', analysis);

480

481

const ageInfo = KeySecurityAnalyzer.checkKeyAge('id_rsa');

482

console.log('Key Age:', Math.floor(ageInfo.age), 'days');

483

if (ageInfo.warning) {

484

console.log('Age Warning:', ageInfo.warning);

485

}

486

}

487

```

488

489

## Cipher Support

490

491

Available ciphers for key encryption when generating keys with passphrases.

492

493

```javascript { .api }

494

// Supported ciphers for key encryption

495

const SUPPORTED_CIPHERS = [

496

'aes128-cbc',

497

'aes128-ctr',

498

'aes192-cbc',

499

'aes192-ctr',

500

'aes256-cbc',

501

'aes256-ctr',

502

'aes128-gcm@openssh.com',

503

'aes256-gcm@openssh.com'

504

];

505

506

// Generate key with specific cipher

507

generateKeyPair('rsa', {

508

bits: 2048,

509

passphrase: 'secure-password',

510

cipher: 'aes256-ctr',

511

rounds: 32

512

}, (err, keyPair) => {

513

if (err) throw err;

514

console.log('Key generated with AES-256-CTR encryption');

515

});

516

```

517

518

## Error Handling

519

520

Common errors and how to handle them when working with keys.

521

522

```javascript

523

const { utils } = require('ssh2');

524

525

function safeParseKey(keyData, passphrase) {

526

try {

527

const key = utils.parseKey(keyData, passphrase);

528

529

if (key instanceof Error) {

530

switch (key.message) {

531

case 'Invalid key format':

532

console.error('The key file is not in a recognized format');

533

break;

534

case 'Invalid passphrase':

535

console.error('The passphrase provided is incorrect');

536

break;

537

case 'Encrypted key without passphrase':

538

console.error('This key is encrypted and requires a passphrase');

539

break;

540

default:

541

console.error('Key parsing error:', key.message);

542

}

543

return null;

544

}

545

546

return key;

547

} catch (err) {

548

console.error('Unexpected error parsing key:', err.message);

549

return null;

550

}

551

}

552

553

function safeGenerateKey(type, options, callback) {

554

generateKeyPair(type, options, (err, keyPair) => {

555

if (err) {

556

if (err.message.includes('Invalid key type')) {

557

console.error('Unsupported key type:', type);

558

} else if (err.message.includes('Invalid bits')) {

559

console.error('Invalid key size for', type);

560

} else {

561

console.error('Key generation failed:', err.message);

562

}

563

callback(err);

564

} else {

565

callback(null, keyPair);

566

}

567

});

568

}

569

```

570

571

## Best Practices

572

573

### Key Generation Recommendations

574

575

```javascript

576

// Recommended key generation settings

577

const RECOMMENDED_SETTINGS = {

578

rsa: {

579

bits: 3072, // Good balance of security and performance

580

cipher: 'aes256-ctr',

581

rounds: 16

582

},

583

ecdsa: {

584

bits: 384, // NIST P-384 provides good security

585

cipher: 'aes256-ctr',

586

rounds: 16

587

},

588

ed25519: {

589

// Ed25519 has fixed parameters, only cipher and rounds apply

590

cipher: 'aes256-ctr',

591

rounds: 16

592

}

593

};

594

595

function generateSecureKey(type, comment, passphrase) {

596

const options = {

597

...RECOMMENDED_SETTINGS[type],

598

comment: comment || `${type.toUpperCase()} key generated ${new Date().toISOString()}`,

599

format: 'new',

600

passphrase: passphrase

601

};

602

603

return new Promise((resolve, reject) => {

604

generateKeyPair(type, options, (err, keyPair) => {

605

if (err) reject(err);

606

else resolve(keyPair);

607

});

608

});

609

}

610

611

// Usage

612

generateSecureKey('ed25519', 'Production server key', 'strong-passphrase')

613

.then(keyPair => {

614

console.log('Secure Ed25519 key generated');

615

// Save keys securely

616

})

617

.catch(err => {

618

console.error('Key generation failed:', err);

619

});

620

```

621

622

### Key Storage Security

623

624

```javascript

625

const fs = require('fs');

626

const path = require('path');

627

628

class SecureKeyStorage {

629

static saveKeyPair(keyPair, baseName, directory = '.ssh') {

630

const privateKeyPath = path.join(directory, baseName);

631

const publicKeyPath = `${privateKeyPath}.pub`;

632

633

// Ensure directory exists

634

if (!fs.existsSync(directory)) {

635

fs.mkdirSync(directory, { mode: 0o700 });

636

}

637

638

// Save private key with restrictive permissions

639

fs.writeFileSync(privateKeyPath, keyPair.private, { mode: 0o600 });

640

641

// Save public key with standard permissions

642

fs.writeFileSync(publicKeyPath, keyPair.public, { mode: 0o644 });

643

644

console.log(`Key pair saved:`);

645

console.log(` Private: ${privateKeyPath} (600)`);

646

console.log(` Public: ${publicKeyPath} (644)`);

647

648

return { privateKeyPath, publicKeyPath };

649

}

650

651

static loadKeyPair(baseName, directory = '.ssh', passphrase) {

652

const privateKeyPath = path.join(directory, baseName);

653

const publicKeyPath = `${privateKeyPath}.pub`;

654

655

const privateKeyData = fs.readFileSync(privateKeyPath, 'utf8');

656

const publicKeyData = fs.readFileSync(publicKeyPath, 'utf8');

657

658

const privateKey = utils.parseKey(privateKeyData, passphrase);

659

const publicKey = utils.parseKey(publicKeyData);

660

661

if (privateKey instanceof Error) {

662

throw new Error(`Failed to load private key: ${privateKey.message}`);

663

}

664

665

if (publicKey instanceof Error) {

666

throw new Error(`Failed to load public key: ${publicKey.message}`);

667

}

668

669

return { privateKey, publicKey };

670

}

671

}

672

```