Putty key format.
From: Sascha Schwarz (saschaschwarz_at_yahoo.de)
Date: 05/30/03
- Next message: Lawrence DčOliveiro: "Re: Endless sftp ls listings on Tru64"
- Previous message: Darren Tucker: "Re: Endless sftp ls listings on Tru64"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] [ attachment ]
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);
}
- Next message: Lawrence DčOliveiro: "Re: Endless sftp ls listings on Tru64"
- Previous message: Darren Tucker: "Re: Endless sftp ls listings on Tru64"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] [ attachment ]
Relevant Pages
|