📄 import.c
字号:
/*
* Code for PuTTY to import and export private key files in other
* SSH clients' formats.
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include "putty.h"
#include "ssh.h"
#include "misc.h"
#define PUT_32BIT(cp, value) do { \
(cp)[3] = (unsigned char)(value); \
(cp)[2] = (unsigned char)((value) >> 8); \
(cp)[1] = (unsigned char)((value) >> 16); \
(cp)[0] = (unsigned char)((value) >> 24); } while (0)
#define GET_32BIT(cp) \
(((unsigned long)(unsigned char)(cp)[0] << 24) | \
((unsigned long)(unsigned char)(cp)[1] << 16) | \
((unsigned long)(unsigned char)(cp)[2] << 8) | \
((unsigned long)(unsigned char)(cp)[3]))
int openssh_encrypted(const Filename *filename);
struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase);
int openssh_write(const Filename *filename, struct ssh2_userkey *key,
char *passphrase);
int sshcom_encrypted(const Filename *filename, char **comment);
struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase);
int sshcom_write(const Filename *filename, struct ssh2_userkey *key,
char *passphrase);
/*
* Given a key type, determine whether we know how to import it.
*/
int import_possible(int type)
{
if (type == SSH_KEYTYPE_OPENSSH)
return 1;
if (type == SSH_KEYTYPE_SSHCOM)
return 1;
return 0;
}
/*
* Given a key type, determine what native key type
* (SSH_KEYTYPE_SSH1 or SSH_KEYTYPE_SSH2) it will come out as once
* we've imported it.
*/
int import_target_type(int type)
{
/*
* There are no known foreign SSH1 key formats.
*/
return SSH_KEYTYPE_SSH2;
}
/*
* Determine whether a foreign key is encrypted.
*/
int import_encrypted(const Filename *filename, int type, char **comment)
{
if (type == SSH_KEYTYPE_OPENSSH) {
/* OpenSSH doesn't do key comments */
*comment = dupstr(filename_to_str(filename));
return openssh_encrypted(filename);
}
if (type == SSH_KEYTYPE_SSHCOM) {
return sshcom_encrypted(filename, comment);
}
return 0;
}
/*
* Import an SSH1 key.
*/
int import_ssh1(const Filename *filename, int type,
struct RSAKey *key, char *passphrase)
{
return 0;
}
/*
* Import an SSH2 key.
*/
struct ssh2_userkey *import_ssh2(const Filename *filename, int type,
char *passphrase)
{
if (type == SSH_KEYTYPE_OPENSSH)
return openssh_read(filename, passphrase);
if (type == SSH_KEYTYPE_SSHCOM)
return sshcom_read(filename, passphrase);
return NULL;
}
/*
* Export an SSH1 key.
*/
int export_ssh1(const Filename *filename, int type, struct RSAKey *key,
char *passphrase)
{
return 0;
}
/*
* Export an SSH2 key.
*/
int export_ssh2(const Filename *filename, int type,
struct ssh2_userkey *key, char *passphrase)
{
if (type == SSH_KEYTYPE_OPENSSH)
return openssh_write(filename, key, passphrase);
if (type == SSH_KEYTYPE_SSHCOM)
return sshcom_write(filename, key, passphrase);
return 0;
}
/* ----------------------------------------------------------------------
* Helper routines. (The base64 ones are defined in sshpubk.c.)
*/
#define isbase64(c) ( ((c) >= 'A' && (c) <= 'Z') || \
((c) >= 'a' && (c) <= 'z') || \
((c) >= '0' && (c) <= '9') || \
(c) == '+' || (c) == '/' || (c) == '=' \
)
/*
* Read an ASN.1/BER identifier and length pair.
*
* Flags are a combination of the #defines listed below.
*
* Returns -1 if unsuccessful; otherwise returns the number of
* bytes used out of the source data.
*/
/* ASN.1 tag classes. */
#define ASN1_CLASS_UNIVERSAL (0 << 6)
#define ASN1_CLASS_APPLICATION (1 << 6)
#define ASN1_CLASS_CONTEXT_SPECIFIC (2 << 6)
#define ASN1_CLASS_PRIVATE (3 << 6)
#define ASN1_CLASS_MASK (3 << 6)
/* Primitive versus constructed bit. */
#define ASN1_CONSTRUCTED (1 << 5)
static int ber_read_id_len(void *source, int sourcelen,
int *id, int *length, int *flags)
{
unsigned char *p = (unsigned char *) source;
if (sourcelen == 0)
return -1;
*flags = (*p & 0xE0);
if ((*p & 0x1F) == 0x1F) {
*id = 0;
while (*p & 0x80) {
p++, sourcelen--;
if (sourcelen == 0)
return -1;
*id = (*id << 7) | (*p & 0x7F);
}
p++, sourcelen--;
} else {
*id = *p & 0x1F;
p++, sourcelen--;
}
if (sourcelen == 0)
return -1;
if (*p & 0x80) {
int n = *p & 0x7F;
p++, sourcelen--;
if (sourcelen < n)
return -1;
*length = 0;
while (n--)
*length = (*length << 8) | (*p++);
sourcelen -= n;
} else {
*length = *p;
p++, sourcelen--;
}
return p - (unsigned char *) source;
}
/*
* Write an ASN.1/BER identifier and length pair. Returns the
* number of bytes consumed. Assumes dest contains enough space.
* Will avoid writing anything if dest is NULL, but still return
* amount of space required.
*/
static int ber_write_id_len(void *dest, int id, int length, int flags)
{
unsigned char *d = (unsigned char *)dest;
int len = 0;
if (id <= 30) {
/*
* Identifier is one byte.
*/
len++;
if (d) *d++ = id | flags;
} else {
int n;
/*
* Identifier is multiple bytes: the first byte is 11111
* plus the flags, and subsequent bytes encode the value of
* the identifier, 7 bits at a time, with the top bit of
* each byte 1 except the last one which is 0.
*/
len++;
if (d) *d++ = 0x1F | flags;
for (n = 1; (id >> (7*n)) > 0; n++)
continue; /* count the bytes */
while (n--) {
len++;
if (d) *d++ = (n ? 0x80 : 0) | ((id >> (7*n)) & 0x7F);
}
}
if (length < 128) {
/*
* Length is one byte.
*/
len++;
if (d) *d++ = length;
} else {
int n;
/*
* Length is multiple bytes. The first is 0x80 plus the
* number of subsequent bytes, and the subsequent bytes
* encode the actual length.
*/
for (n = 1; (length >> (8*n)) > 0; n++)
continue; /* count the bytes */
len++;
if (d) *d++ = 0x80 | n;
while (n--) {
len++;
if (d) *d++ = (length >> (8*n)) & 0xFF;
}
}
return len;
}
static int put_string(void *target, void *data, int len)
{
unsigned char *d = (unsigned char *)target;
PUT_32BIT(d, len);
memcpy(d+4, data, len);
return len+4;
}
static int put_mp(void *target, void *data, int len)
{
unsigned char *d = (unsigned char *)target;
unsigned char *i = (unsigned char *)data;
if (*i & 0x80) {
PUT_32BIT(d, len+1);
d[4] = 0;
memcpy(d+5, data, len);
return len+5;
} else {
PUT_32BIT(d, len);
memcpy(d+4, data, len);
return len+4;
}
}
/* Simple structure to point to an mp-int within a blob. */
struct mpint_pos { void *start; int bytes; };
static int ssh2_read_mpint(void *data, int len, struct mpint_pos *ret)
{
int bytes;
unsigned char *d = (unsigned char *) data;
if (len < 4)
goto error;
bytes = GET_32BIT(d);
if (len < 4+bytes)
goto error;
ret->start = d + 4;
ret->bytes = bytes;
return bytes+4;
error:
ret->start = NULL;
ret->bytes = -1;
return len; /* ensure further calls fail as well */
}
/* ----------------------------------------------------------------------
* Code to read and write OpenSSH private keys.
*/
enum { OSSH_DSA, OSSH_RSA };
struct openssh_key {
int type;
int encrypted;
char iv[32];
unsigned char *keyblob;
int keyblob_len, keyblob_size;
};
static struct openssh_key *load_openssh_key(const Filename *filename)
{
struct openssh_key *ret;
FILE *fp;
char buffer[256];
char *errmsg, *p;
int headers_done;
char base64_bit[4];
int base64_chars = 0;
ret = snew(struct openssh_key);
ret->keyblob = NULL;
ret->keyblob_len = ret->keyblob_size = 0;
ret->encrypted = 0;
memset(ret->iv, 0, sizeof(ret->iv));
fp = f_open(*filename, "r");
if (!fp) {
errmsg = "Unable to open key file";
goto error;
}
if (!fgets(buffer, sizeof(buffer), fp) ||
0 != strncmp(buffer, "-----BEGIN ", 11) ||
0 != strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n")) {
errmsg = "File does not begin with OpenSSH key header";
goto error;
}
if (!strcmp(buffer, "-----BEGIN RSA PRIVATE KEY-----\n"))
ret->type = OSSH_RSA;
else if (!strcmp(buffer, "-----BEGIN DSA PRIVATE KEY-----\n"))
ret->type = OSSH_DSA;
else {
errmsg = "Unrecognised key type";
goto error;
}
headers_done = 0;
while (1) {
if (!fgets(buffer, sizeof(buffer), fp)) {
errmsg = "Unexpected end of file";
goto error;
}
if (0 == strncmp(buffer, "-----END ", 9) &&
0 == strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n"))
break; /* done */
if ((p = strchr(buffer, ':')) != NULL) {
if (headers_done) {
errmsg = "Header found in body of key data";
goto error;
}
*p++ = '\0';
while (*p && isspace((unsigned char)*p)) p++;
if (!strcmp(buffer, "Proc-Type")) {
if (p[0] != '4' || p[1] != ',') {
errmsg = "Proc-Type is not 4 (only 4 is supported)";
goto error;
}
p += 2;
if (!strcmp(p, "ENCRYPTED\n"))
ret->encrypted = 1;
} else if (!strcmp(buffer, "DEK-Info")) {
int i, j;
if (strncmp(p, "DES-EDE3-CBC,", 13)) {
errmsg = "Ciphers other than DES-EDE3-CBC not supported";
goto error;
}
p += 13;
for (i = 0; i < 8; i++) {
if (1 != sscanf(p, "%2x", &j))
break;
ret->iv[i] = j;
p += 2;
}
if (i < 8) {
errmsg = "Expected 16-digit iv in DEK-Info";
goto error;
}
}
} else {
headers_done = 1;
p = buffer;
while (isbase64(*p)) {
base64_bit[base64_chars++] = *p;
if (base64_chars == 4) {
unsigned char out[3];
int len;
base64_chars = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -