⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sshpubk.c

📁 一个支持FTP,SFTP的客户端程序
💻 C
📖 第 1 页 / 共 3 页
字号:
 *
 *    string "ssh-rsa"
 *    mpint  exponent
 *    mpint  modulus
 *
 * Next, there is a line saying "Private-Lines: " plus a number N,
 * and then N lines containing the (potentially encrypted) private
 * part of the key. For the key type "ssh-rsa", this will be
 * composed of
 *
 *    mpint  private_exponent
 *    mpint  p                  (the larger of the two primes)
 *    mpint  q                  (the smaller prime)
 *    mpint  iqmp               (the inverse of q modulo p)
 *    data   padding            (to reach a multiple of the cipher block size)
 *
 * And for "ssh-dss", it will be composed of
 *
 *    mpint  x                  (the private key parameter)
 *  [ string hash   20-byte hash of mpints p || q || g   only in old format ]
 * 
 * Finally, there is a line saying "Private-MAC: " plus a hex
 * representation of a HMAC-SHA-1 of:
 *
 *    string  name of algorithm ("ssh-dss", "ssh-rsa")
 *    string  encryption type
 *    string  comment
 *    string  public-blob
 *    string  private-plaintext (the plaintext version of the
 *                               private part, including the final
 *                               padding)
 * 
 * The key to the MAC is itself a SHA-1 hash of:
 * 
 *    data    "putty-private-key-file-mac-key"
 *    data    passphrase
 *
 * (An empty passphrase is used for unencrypted keys.)
 *
 * If the key is encrypted, the encryption key is derived from the
 * passphrase by means of a succession of SHA-1 hashes. Each hash
 * is the hash of:
 *
 *    uint32  sequence-number
 *    data    passphrase
 *
 * where the sequence-number increases from zero. As many of these
 * hashes are used as necessary.
 *
 * For backwards compatibility with snapshots between 0.51 and
 * 0.52, we also support the older key file format, which begins
 * with "PuTTY-User-Key-File-1" (version number differs). In this
 * format the Private-MAC: field only covers the private-plaintext
 * field and nothing else (and without the 4-byte string length on
 * the front too). Moreover, for RSA keys the Private-MAC: field
 * can be replaced with a Private-Hash: field which is a plain
 * SHA-1 hash instead of an HMAC. This is not allowable in DSA
 * keys. (Yes, the old format was a mess. Guess why it changed :-)
 */

static int read_header(FILE * fp, char *header)
{
    int len = 39;
    int c;

    while (len > 0) {
	c = fgetc(fp);
	if (c == '\n' || c == '\r' || c == EOF)
	    return 0;		       /* failure */
	if (c == ':') {
	    c = fgetc(fp);
	    if (c != ' ')
		return 0;
	    *header = '\0';
	    return 1;		       /* success! */
	}
	if (len == 0)
	    return 0;		       /* failure */
	*header++ = c;
	len--;
    }
    return 0;			       /* failure */
}

static char *read_body(FILE * fp)
{
    char *text;
    int len;
    int size;
    int c;

    size = 128;
    text = snewn(size, char);
    len = 0;
    text[len] = '\0';

    while (1) {
	c = fgetc(fp);
	if (c == '\r' || c == '\n') {
	    c = fgetc(fp);
	    if (c != '\r' && c != '\n' && c != EOF)
		ungetc(c, fp);
	    return text;
	}
	if (c == EOF) {
	    sfree(text);
	    return NULL;
	}
	if (len + 1 > size) {
	    size += 128;
	    text = sresize(text, size, char);
	}
	text[len++] = c;
	text[len] = '\0';
    }
}

int base64_decode_atom(char *atom, unsigned char *out)
{
    int vals[4];
    int i, v, len;
    unsigned word;
    char c;

    for (i = 0; i < 4; i++) {
	c = atom[i];
	if (c >= 'A' && c <= 'Z')
	    v = c - 'A';
	else if (c >= 'a' && c <= 'z')
	    v = c - 'a' + 26;
	else if (c >= '0' && c <= '9')
	    v = c - '0' + 52;
	else if (c == '+')
	    v = 62;
	else if (c == '/')
	    v = 63;
	else if (c == '=')
	    v = -1;
	else
	    return 0;		       /* invalid atom */
	vals[i] = v;
    }

    if (vals[0] == -1 || vals[1] == -1)
	return 0;
    if (vals[2] == -1 && vals[3] != -1)
	return 0;

    if (vals[3] != -1)
	len = 3;
    else if (vals[2] != -1)
	len = 2;
    else
	len = 1;

    word = ((vals[0] << 18) |
	    (vals[1] << 12) | ((vals[2] & 0x3F) << 6) | (vals[3] & 0x3F));
    out[0] = (word >> 16) & 0xFF;
    if (len > 1)
	out[1] = (word >> 8) & 0xFF;
    if (len > 2)
	out[2] = word & 0xFF;
    return len;
}

static unsigned char *read_blob(FILE * fp, int nlines, int *bloblen)
{
    unsigned char *blob;
    char *line;
    int linelen, len;
    int i, j, k;

    /* We expect at most 64 base64 characters, ie 48 real bytes, per line. */
    blob = snewn(48 * nlines, unsigned char);
    len = 0;
    for (i = 0; i < nlines; i++) {
	line = read_body(fp);
	if (!line) {
	    sfree(blob);
	    return NULL;
	}
	linelen = strlen(line);
	if (linelen % 4 != 0 || linelen > 64) {
	    sfree(blob);
	    sfree(line);
	    return NULL;
	}
	for (j = 0; j < linelen; j += 4) {
	    k = base64_decode_atom(line + j, blob + len);
	    if (!k) {
		sfree(line);
		sfree(blob);
		return NULL;
	    }
	    len += k;
	}
	sfree(line);
    }
    *bloblen = len;
    return blob;
}

/*
 * Magic error return value for when the passphrase is wrong.
 */
struct ssh2_userkey ssh2_wrong_passphrase = {
    NULL, NULL, NULL
};

const struct ssh_signkey *find_pubkey_alg(const char *name)
{
    if (!strcmp(name, "ssh-rsa"))
	return &ssh_rsa;
    else if (!strcmp(name, "ssh-dss"))
	return &ssh_dss;
    else
	return NULL;
}

struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
				       char *passphrase, const char **errorstr)
{
    FILE *fp;
    char header[40], *b, *encryption, *comment, *mac;
    const struct ssh_signkey *alg;
    struct ssh2_userkey *ret;
    int cipher, cipherblk;
    unsigned char *public_blob, *private_blob;
    int public_blob_len, private_blob_len;
    int i, is_mac, old_fmt;
    int passlen = passphrase ? strlen(passphrase) : 0;
    const char *error = NULL;

    ret = NULL;			       /* return NULL for most errors */
    encryption = comment = mac = NULL;
    public_blob = private_blob = NULL;

    fp = f_open(*filename, "rb");
    if (!fp) {
	error = "can't open file";
	goto error;
    }

    /* Read the first header line which contains the key type. */
    if (!read_header(fp, header))
	goto error;
    if (0 == strcmp(header, "PuTTY-User-Key-File-2")) {
	old_fmt = 0;
    } else if (0 == strcmp(header, "PuTTY-User-Key-File-1")) {
	/* this is an old key file; warn and then continue */
	old_keyfile_warning();
	old_fmt = 1;
    } else {
	error = "not a PuTTY SSH-2 private key";
	goto error;
    }
    error = "file format error";
    if ((b = read_body(fp)) == NULL)
	goto error;
    /* Select key algorithm structure. */
    alg = find_pubkey_alg(b);
    if (!alg) {
	sfree(b);
	goto error;
    }
    sfree(b);

    /* Read the Encryption header line. */
    if (!read_header(fp, header) || 0 != strcmp(header, "Encryption"))
	goto error;
    if ((encryption = read_body(fp)) == NULL)
	goto error;
    if (!strcmp(encryption, "aes256-cbc")) {
	cipher = 1;
	cipherblk = 16;
    } else if (!strcmp(encryption, "none")) {
	cipher = 0;
	cipherblk = 1;
    } else {
	sfree(encryption);
	goto error;
    }

    /* Read the Comment header line. */
    if (!read_header(fp, header) || 0 != strcmp(header, "Comment"))
	goto error;
    if ((comment = read_body(fp)) == NULL)
	goto error;

    /* Read the Public-Lines header line and the public blob. */
    if (!read_header(fp, header) || 0 != strcmp(header, "Public-Lines"))
	goto error;
    if ((b = read_body(fp)) == NULL)
	goto error;
    i = atoi(b);
    sfree(b);
    if ((public_blob = read_blob(fp, i, &public_blob_len)) == NULL)
	goto error;

    /* Read the Private-Lines header line and the Private blob. */
    if (!read_header(fp, header) || 0 != strcmp(header, "Private-Lines"))
	goto error;
    if ((b = read_body(fp)) == NULL)
	goto error;
    i = atoi(b);
    sfree(b);
    if ((private_blob = read_blob(fp, i, &private_blob_len)) == NULL)
	goto error;

    /* Read the Private-MAC or Private-Hash header line. */
    if (!read_header(fp, header))
	goto error;
    if (0 == strcmp(header, "Private-MAC")) {
	if ((mac = read_body(fp)) == NULL)
	    goto error;
	is_mac = 1;
    } else if (0 == strcmp(header, "Private-Hash") &&
			   alg == &ssh_rsa && old_fmt) {
	if ((mac = read_body(fp)) == NULL)
	    goto error;
	is_mac = 0;
    } else
	goto error;

    fclose(fp);
    fp = NULL;

    /*
     * Decrypt the private blob.
     */
    if (cipher) {
	unsigned char key[40];
	SHA_State s;

	if (!passphrase)
	    goto error;
	if (private_blob_len % cipherblk)
	    goto error;

	SHA_Init(&s);
	SHA_Bytes(&s, "\0\0\0\0", 4);
	SHA_Bytes(&s, passphrase, passlen);
	SHA_Final(&s, key + 0);
	SHA_Init(&s);
	SHA_Bytes(&s, "\0\0\0\1", 4);
	SHA_Bytes(&s, passphrase, passlen);
	SHA_Final(&s, key + 20);
	aes256_decrypt_pubkey(key, private_blob, private_blob_len);
    }

    /*
     * Verify the MAC.
     */
    {
	char realmac[41];
	unsigned char binary[20];
	unsigned char *macdata;
	int maclen;
	int free_macdata;

	if (old_fmt) {
	    /* MAC (or hash) only covers the private blob. */
	    macdata = private_blob;
	    maclen = private_blob_len;
	    free_macdata = 0;
	} else {
	    unsigned char *p;
	    int namelen = strlen(alg->name);
	    int enclen = strlen(encryption);
	    int commlen = strlen(comment);
	    maclen = (4 + namelen +
		      4 + enclen +
		      4 + commlen +
		      4 + public_blob_len +
		      4 + private_blob_len);
	    macdata = snewn(maclen, unsigned char);
	    p = macdata;
#define DO_STR(s,len) PUT_32BIT(p,(len));memcpy(p+4,(s),(len));p+=4+(len)
	    DO_STR(alg->name, namelen);
	    DO_STR(encryption, enclen);
	    DO_STR(comment, commlen);
	    DO_STR(public_blob, public_blob_len);
	    DO_STR(private_blob, private_blob_len);

	    free_macdata = 1;
	}

	if (is_mac) {
	    SHA_State s;
	    unsigned char mackey[20];
	    char header[] = "putty-private-key-file-mac-key";

	    SHA_Init(&s);
	    SHA_Bytes(&s, header, sizeof(header)-1);
	    if (cipher && passphrase)
		SHA_Bytes(&s, passphrase, passlen);
	    SHA_Final(&s, mackey);

	    hmac_sha1_simple(mackey, 20, macdata, maclen, binary);

	    memset(mackey, 0, sizeof(mackey));
	    memset(&s, 0, sizeof(s));
	} else {
	    SHA_Simple(macdata, maclen, binary);
	}

	if (free_macdata) {
	    memset(macdata, 0, maclen);
	    sfree(macdata);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -