Putty key format.

From: Sascha Schwarz (saschaschwarz_at_yahoo.de)
Date: 05/30/03


Date: Fri, 30 May 2003 22:06:35 +0200

Hi,

i have the need to generate openssh keys and convert
them via scripts (Perl?) into the putty format on a unix box.
Therefor i found a piece of code at www.anchor.net.au/code/openssh2putty.c

But this handles only RSA keys. so i added a few lines to make it work with
DSA keys.

But i have a problem with the encryption of the private part. Only the 1st
part of the private line is ok.
What i know they make a sha1 hash of the private blob and attach it to the
private blob, instead of
outpadding with 0.

I donŽt see the problem, maybe someone has time and interrest to take a look
at it.

MfG/best regards

Sascha Schwarz

Here is th code:

/*

* Author: Tatu Ylonen <ylo@cs.hut.fi>

* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland

* All rights reserved

* Adds an identity to the authentication server, or removes an identity.

*

* As far as I am concerned, the code I have written for this software

* can be used freely for any purpose. Any derived versions of this

* software must be clearly marked as such, and if the derived work is

* incompatible with the protocol description in the RFC file, it must be

* called by a name other than "ssh" or "Secure Shell".

*

* SSH2 implementation,

* Copyright (c) 2000 Markus Friedl. All rights reserved.

*

* Redistribution and use in source and binary forms, with or without

* modification, are permitted provided that the following conditions

* are met:

* 1. Redistributions of source code must retain the above copyright

* notice, this list of conditions and the following disclaimer.

* 2. Redistributions in binary form must reproduce the above copyright

* notice, this list of conditions and the following disclaimer in the

* documentation and/or other materials provided with the distribution.

*

* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR

* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES

* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.

* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,

* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT

* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF

* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

/* Hacked up ssh-add.c code to convert an OpenSSH key into a format

* suitable for the Windows SSH client "putty"

*

* egalanos-putty@anchor.net.au

*

* To compile you will need the OpenSSH source code. I have tested this

* using OpenSSH 2.9p2 (on a ix86 Red Hat GNU/Linux box) and Putty 0.52 on

* Windows 98

*

* Requires key to use encryption. A small change to the code could get

* around this...

*

* Simply place in the same directory as the code, then edit Makefile.in

* to add some lines to compile openssh2putty.c (copy the lines for
ssh-add.c)

* (re)run ./configure

* make openssh2putty

* ./openssh2putty keyfilename

*

*/

/*

* Added API changes to compile with openssh 3.4p1 from Frederic Krueger

* ysrthgrathe AT NOSPAM PLEASE spunkyworld DOT com

*/

#include "includes.h"

RCSID("$OpenBSD: ssh-add.c,v 1.36 2001/04/18 21:57:42 markus Exp $");

#include <openssl/evp.h>

#include <openssl/hmac.h>

#include "ssh.h"

#include "rsa.h"

#include "log.h"

#include "xmalloc.h"

#include "key.h"

#include "authfile.h"

#include "pathnames.h"

#include "readpass.h"

#include "uuencode.h"

#include "cipher.h"

#ifdef HAVE___PROGNAME

extern char *__progname;

#else

char *__progname;

#endif

static char *pass = NULL;

static Key *private;

static char *comment = NULL;

static void

clear_pass(void)

{

if (pass) {

memset(pass, 0, strlen(pass));

xfree(pass);

pass = NULL;

}

}

static void

add_file(const char *filename)

{

struct stat st;

char msg[1024];

if (stat(filename, &st) < 0) {

perror(filename);

exit(1);

}

/* At first, try empty passphrase */

private = key_load_private(filename, "", &comment);

if (comment == NULL)

comment = xstrdup(filename);

if (private == NULL) {

/* clear passphrase since it did not work */

clear_pass();

printf("Need passphrase for %.200s\n", filename);

snprintf(msg, sizeof msg, "Enter passphrase for %.200s ",

comment);

for (;;) {

pass = read_passphrase(msg, 1);

if (strcmp(pass, "") == 0) {

clear_pass();

xfree(comment);

return;

}

private = key_load_private(filename, pass, &comment);

if (private != NULL)

break;

clear_pass();

strlcpy(msg, "Bad passphrase, try again ", sizeof msg);

}

}

}

// binary strings with 4 octet length header when dumped

typedef struct {

int length;

unsigned char data[1];

} bstring;

bstring *

asciiz_to_bstring(const char *s)

{

unsigned l;

bstring *b;

l = strlen(s);

b = (bstring *)xmalloc(4 + l);

b->length = l;

memcpy(&b->data, s, l);

return b;

}

bstring *

mem_to_bstring(const char *p, unsigned long length)

{

bstring *b;

b = (bstring *)xmalloc(4 + length);

b->length = length;

memcpy(&b->data, p, length);

return b;

}

int

bstring_size(bstring *b)

{

return b->length + 4;

}

void

bstring_dump(unsigned char *dest, bstring *b)

{

dest[3] = (b->length) & 255;

dest[2] = (b->length >> 8) & 255;

dest[1] = (b->length >> 16) & 255;

dest[0] = (b->length >> 24) & 255;;

memcpy(dest + 4, &b->data, b->length);

}

char *

bstring_value(bstring *b)

{

static unsigned char * buffer = NULL;

static int bufsize = 0;

if (b == NULL && buffer)

{

memset(buffer, 0, bufsize);

xfree(buffer);

buffer = NULL;

bufsize = 0;

return NULL;

}

if (bufsize < bstring_size(b))

{

if (buffer != NULL)

xfree(buffer);

buffer = xmalloc(bstring_size(b));

bufsize = bstring_size(b);

}

bstring_dump(buffer, b);

return buffer;

}

void

bstring_free(bstring *b)

{

xfree(b);

}

void

SHA_Simple(void *p, int len, unsigned char *output)

{

SHA_CTX sha;

SHA_Init(&sha);

SHA_Update(&sha, p, len);

SHA_Final(output, &sha);

}

write_putty_key(Key *pubkey, const char * cipher_name, const char
*passphrase,

const char *comment, FILE *f)

{

unsigned char *public_blob = NULL;

unsigned int public_blob_len;

unsigned char *private_blob = NULL;

unsigned int private_blob_len;

fprintf(f, "PuTTY-User-Key-File-2: %s\r\n", key_ssh_name(pubkey));

fprintf(f, "Encryption: %s\r\n", cipher_name);

fprintf(f, "Comment: %s\r\n", comment);

// print out public key

{

unsigned int len, n;

unsigned char *uu, *p;

unsigned int s1, s2, s3, s4 ,s5;

bstring * key_type;

key_type = asciiz_to_bstring(key_ssh_name(pubkey));

switch (pubkey->type) {

case KEY_RSA:

s1 = bstring_size(key_type);

s2 = BN_bn2mpi(pubkey->rsa->e, NULL);

s3 = BN_bn2mpi(pubkey->rsa->n, NULL);

public_blob_len = len = s1 + s2 + s3;

public_blob = p = xmalloc(len);

bstring_dump(p, key_type); p += s1;

BN_bn2mpi(pubkey->rsa->e, p); p += s2;

BN_bn2mpi(pubkey->rsa->n, p);

bstring_free(key_type);

break;

case KEY_DSA:

s1 = bstring_size(key_type);

s2 = BN_bn2mpi(pubkey->dsa->p, NULL);

s3 = BN_bn2mpi(pubkey->dsa->q, NULL);

s4 = BN_bn2mpi(pubkey->dsa->g, NULL);

s5 = BN_bn2mpi(pubkey->dsa->pub_key, NULL);

public_blob_len = len = s1 + s2 + s3 +s4 +s5;

public_blob = p = xmalloc(len);

bstring_dump(p, key_type); p += s1;

BN_bn2mpi(pubkey->dsa->p, p); p += s2;

BN_bn2mpi(pubkey->dsa->q, p); p += s3;

BN_bn2mpi(pubkey->dsa->g, p); p += s4;

BN_bn2mpi(pubkey->dsa->pub_key, p);

bstring_free(key_type);

break;

default:

fprintf(stderr, "Unknown key type\n");

return 0;

}

// Print out public key in base64

// at most 64 base64 characters per line.

uu = xmalloc(2*len);

n = uuencode(public_blob, len, uu, 2*len);

if (n == -1)

{

fprintf(stderr, "size error\n");

exit(1);

}

printf("Public-Lines: %d\r\n", (n + 63)/64 );

p = uu;

while ( n > 0 )

{

int s = n > 64 ? 64 : n;

fprintf(f, "%.*s", s, p);

fprintf(f, "\r\n");

if (n >= 64)

n -= 64;

else

n = 0;

p += 64;

}

xfree(uu);

}

// print out private key part

{

unsigned char *data, *data2, *uu, *p;

unsigned char *priv_mac;

unsigned int len, n;

unsigned int s1, s2, s3, s4;

switch (pubkey->type) {

case KEY_RSA:

s1 = BN_bn2mpi(pubkey->rsa->d, NULL);

s2 = BN_bn2mpi(pubkey->rsa->p, NULL);

s3 = BN_bn2mpi(pubkey->rsa->q, NULL);

s4 = BN_bn2mpi(pubkey->rsa->iqmp, NULL);

len = s1 + s2 + s3 + s4;

// pad to 16 bytes for cipher block size.

private_blob_len = len = (len + 15) & ~15;

private_blob = data = xmalloc(len);

BN_bn2mpi(pubkey->rsa->d, data); data += s1;

BN_bn2mpi(pubkey->rsa->p, data); data += s2;

BN_bn2mpi(pubkey->rsa->q, data); data += s3;

BN_bn2mpi(pubkey->rsa->iqmp, data); data += s4;

/* zero out padding */

memset(data, 0, len - s1 - s2 - s3 - s4);

data = private_blob;

break;

case KEY_DSA:

s1 = BN_bn2mpi(pubkey->dsa->priv_key, NULL);

len = s1;

// pad to 16 bytes for cipher block size.

private_blob_len = len = (len + 15) & ~15;

private_blob = data = xmalloc(len);

BN_bn2mpi(pubkey->dsa->priv_key, data);

SHA_Simple(data2, s1, priv_mac);data += s1;

memcpy ( data , priv_mac, len - s1);

/* zero out padding */

//memset(data, 0, len -s1);

data = private_blob;

break;

default:

fprintf(stderr, "Unknown key type\n");

return 0;

}

// encrypt the private key data

{

unsigned char cipher_key[40];

// Create the key

{

SHA_CTX c;

SHA1_Init(&c);

SHA1_Update(&c, "\0\0\0\0", 4);

SHA1_Update(&c, passphrase, strlen(passphrase));

SHA1_Final(cipher_key, &c);

SHA1_Init(&c);

SHA1_Update(&c, "\0\0\0\1", 4);

SHA1_Update(&c, passphrase, strlen(passphrase));

SHA1_Final(cipher_key + 20, &c);

}

// Actually encrypt it

{

unsigned char iv[16];

unsigned char *ciphertext, *plaintext;

Cipher *cipher;

CipherContext cc;

cipher = cipher_by_name(cipher_name);

if (cipher == NULL)

{

fprintf(stderr, "cipher error\n");

exit(1);

}

memset(iv, 0, sizeof iv);

plaintext = private_blob;

ciphertext = xmalloc(private_blob_len);

cipher_init(&cc, cipher, cipher_key, 32, iv,

sizeof iv, 1); // 1 = encrypt, 0 = decrypt (hsp)

cipher_crypt(&cc, ciphertext, plaintext, private_blob_len);

memset(&cc, 0, sizeof cc);

memset(cipher_key, 0, sizeof cipher_key);

data = ciphertext;

}

}

uu = xmalloc(2*len);

n = uuencode(data, len, uu, 2*len);

// at most 64 base64 characters per line.

printf("Private-Lines: %d\r\n", (n + 63)/64 );

p = uu;

while ( n > 0 )

{

int s = n > 64 ? 64 : n;

fprintf(f, "%.*s", s, p);

fprintf(f, "\r\n");

if (n >= 64)

n -= 64;

else

n = 0;

p += 64;

}

memset(uu, 0, 2*len);

xfree(uu);

}

// Write HMAC out.

{

unsigned char hmac_key[20];

unsigned char hmac[EVP_MAX_MD_SIZE];

int len;

// work out HMAC key

{

char header[] = "putty-private-key-file-mac-key";

SHA_CTX c;

SHA1_Init(&c);

SHA1_Update(&c, header, sizeof(header) - 1);

SHA1_Update(&c, passphrase, strlen(passphrase));

SHA1_Final(hmac_key, &c);

}

// do HMAC

{

bstring *b;

HMAC_CTX hc;

int i;

HMAC_Init(&hc, hmac_key, sizeof hmac_key, EVP_sha1());

b = asciiz_to_bstring(key_ssh_name(pubkey));

HMAC_Update(&hc, bstring_value(b), bstring_size(b));

bstring_free(b);

b = asciiz_to_bstring(cipher_name);

HMAC_Update(&hc, bstring_value(b), bstring_size(b));

bstring_free(b);

b = asciiz_to_bstring(comment);

HMAC_Update(&hc, bstring_value(b), bstring_size(b));

bstring_free(b);

b = mem_to_bstring(public_blob, public_blob_len);

HMAC_Update(&hc, bstring_value(b), bstring_size(b));

bstring_free(b);

b = mem_to_bstring(private_blob, private_blob_len);

HMAC_Update(&hc, bstring_value(b), bstring_size(b));

bstring_free(b);

bstring_value(NULL);

len = sizeof hmac;

HMAC_Final(&hc, hmac, &len);

HMAC_cleanup(&hc);

memset(hmac_key, 0, sizeof hmac_key);

fprintf(f, "Private-MAC: ");

for (i = 0; i < len; i++)

{

fprintf(f, "%02x", hmac[i]);

}

fprintf(f, "\r\n");

}

}

memset(private_blob, 0, private_blob_len);

xfree(private_blob);

xfree(public_blob);

}

int

main(int argc, char **argv)

{

__progname = get_progname(argv[0]);

init_rng();

SSLeay_add_all_algorithms();

if (argc != 2)

{

fprintf(stderr, "Usage: %s filename\n", __progname);

exit(1);

}

add_file(argv[1]);

if (private && 0)

{

char *fp;

fp = key_fingerprint(private, SSH_FP_MD5, SSH_FP_HEX);

printf("%d %s (%s)\n",

key_size(private), fp, key_type(private));

xfree(fp);

if (!key_write(private, stdout))

fprintf(stderr, "key_write failed");

}

if (private)

{

write_putty_key(private, "aes256-cbc", pass, comment, stdout);

}

clear_pass();

key_free(private);

xfree(comment);

exit(0);

}



Relevant Pages