212 行
6.6 KiB
C
212 行
6.6 KiB
C
/* paze-keygen —— SSH 密钥生成工具 (平替 ssh-keygen)
|
|
*
|
|
* 用法:
|
|
* paze-keygen [-t ed25519|rsa|ecdsa] [-b bits] [-f keyfile] [-C comment]
|
|
* paze-keygen -y -f keyfile 从私钥打印公钥
|
|
* paze-keygen -l -f keyfile 打印公钥指纹
|
|
*
|
|
* 生成 OpenSSH 新格式私钥 ("openssh-key-v1", PEM 包装,未加密)
|
|
* 与 .pub 公钥文件,并显示 SHA256 指纹。
|
|
*/
|
|
#include "paze/ssh_keys.h"
|
|
#include "paze/encoding.h"
|
|
#include "paze/hash.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#ifdef _WIN32
|
|
#include <io.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#define CHMOD_0600(f) _chmod((f), _S_IREAD | _S_IWRITE)
|
|
#else
|
|
#include <sys/stat.h>
|
|
#define CHMOD_0600(f) chmod((f), 0600)
|
|
#endif
|
|
|
|
static void sshdir_path(char *buf, size_t sz, const char *rel) {
|
|
const char *home = NULL;
|
|
#ifdef _WIN32
|
|
home = getenv("USERPROFILE");
|
|
if (!home) home = getenv("HOME");
|
|
#else
|
|
home = getenv("HOME");
|
|
#endif
|
|
if (!home) home = ".";
|
|
if (rel)
|
|
snprintf(buf, sz, "%s/.ssh/%s", home, rel);
|
|
else
|
|
snprintf(buf, sz, "%s/.ssh", home);
|
|
}
|
|
|
|
/* 读取整个文件到 malloc 缓冲 */
|
|
static uint8_t *read_file(const char *path, size_t *len) {
|
|
FILE *f = fopen(path, "rb");
|
|
if (!f) return NULL;
|
|
fseek(f, 0, SEEK_END);
|
|
long sz = ftell(f);
|
|
rewind(f);
|
|
if (sz <= 0 || sz > 16 * 1024 * 1024) { fclose(f); return NULL; }
|
|
uint8_t *buf = (uint8_t *)malloc((size_t)sz);
|
|
if (!buf) { fclose(f); return NULL; }
|
|
if (fread(buf, 1, (size_t)sz, f) != (size_t)sz) {
|
|
free(buf); fclose(f); return NULL;
|
|
}
|
|
fclose(f);
|
|
*len = (size_t)sz;
|
|
return buf;
|
|
}
|
|
|
|
/* SHA256 指纹 "SHA256:xxxx"(无 base64 padding) */
|
|
static void print_fingerprint(const uint8_t *blob, size_t blen) {
|
|
uint8_t h[32];
|
|
paze_sha256(blob, blen, h);
|
|
char b64[64];
|
|
paze_base64_encode(h, 32, b64);
|
|
size_t n = strlen(b64);
|
|
while (n > 0 && b64[n - 1] == '=') b64[--n] = '\0';
|
|
printf("SHA256:%s", b64);
|
|
}
|
|
|
|
/* 从 pubblob 提取 keytype */
|
|
static void key_type_name(const uint8_t *blob, size_t blen, char *out, size_t outsz) {
|
|
if (blen >= 4) {
|
|
uint32_t kl = 0;
|
|
kl = ((uint32_t)blob[0] << 24) | ((uint32_t)blob[1] << 16) |
|
|
((uint32_t)blob[2] << 8) | blob[3];
|
|
if (4 + kl <= blen && kl < outsz) {
|
|
memcpy(out, blob + 4, kl);
|
|
out[kl] = '\0';
|
|
return;
|
|
}
|
|
}
|
|
snprintf(out, outsz, "unknown");
|
|
}
|
|
|
|
int pssh_cmd_keygen(int argc, char **argv) {
|
|
if (argc < 2) {
|
|
fprintf(stderr,
|
|
"用法: pssh keygen [选项]\n"
|
|
" -t <type> 密钥类型 ed25519|rsa|ecdsa (默认 ed25519)\n"
|
|
" -b <bits> 密钥位数 (rsa 默认 3072,ecdsa 默认 256)\n"
|
|
" -f <file> 输出文件 (默认 ~/.ssh/id_<type>)\n"
|
|
" -C <text> 密钥注释\n"
|
|
" -y 从私钥导出公钥\n"
|
|
" -l 列出私钥指纹\n");
|
|
return 1;
|
|
}
|
|
const char *type = "ed25519";
|
|
const char *outfile = NULL;
|
|
const char *comment = NULL;
|
|
int bits = 0;
|
|
int do_y = 0, do_l = 0;
|
|
|
|
for (int i = 1; i < argc; i++) {
|
|
if ((strcmp(argv[i], "-t") == 0) && i + 1 < argc) type = argv[++i];
|
|
else if ((strcmp(argv[i], "-b") == 0) && i + 1 < argc) bits = atoi(argv[++i]);
|
|
else if ((strcmp(argv[i], "-f") == 0) && i + 1 < argc) outfile = argv[++i];
|
|
else if ((strcmp(argv[i], "-C") == 0) && i + 1 < argc) comment = argv[++i];
|
|
else if (strcmp(argv[i], "-y") == 0) do_y = 1;
|
|
else if (strcmp(argv[i], "-l") == 0) do_l = 1;
|
|
else if (strcmp(argv[i], "-E") == 0 && i + 1 < argc) i++; /* 忽略 fingerprint 算法选项 */
|
|
}
|
|
|
|
char keypath[1024];
|
|
if (!outfile) {
|
|
char rel[128];
|
|
snprintf(rel, sizeof(rel), "id_%s", type);
|
|
sshdir_path(keypath, sizeof(keypath), rel);
|
|
outfile = keypath;
|
|
}
|
|
|
|
/* -y / -l:从私钥文件导出 */
|
|
if (do_y || do_l) {
|
|
size_t len = 0;
|
|
uint8_t *data = read_file(outfile, &len);
|
|
if (!data) {
|
|
fprintf(stderr, "paze-keygen: cannot read %s\n", outfile);
|
|
return 1;
|
|
}
|
|
ssh_privkey_t *k = NULL;
|
|
if (ssh_privkey_parse(data, len, &k) != 0) {
|
|
fprintf(stderr, "paze-keygen: unsupported or corrupt key file %s\n", outfile);
|
|
free(data);
|
|
return 1;
|
|
}
|
|
size_t blen = 0;
|
|
const uint8_t *blob = ssh_privkey_pubblob(k, &blen);
|
|
if (do_y) {
|
|
char b64[8192];
|
|
paze_base64_encode(blob, blen, b64);
|
|
printf("%s %s\n", ssh_privkey_alg(k), b64);
|
|
} else {
|
|
char kt[128];
|
|
key_type_name(blob, blen, kt, sizeof(kt));
|
|
print_fingerprint(blob, blen);
|
|
printf(" %s (%s)\n", kt, ssh_privkey_alg(k));
|
|
}
|
|
ssh_privkey_free(k);
|
|
free(data);
|
|
return 0;
|
|
}
|
|
|
|
/* 生成密钥 */
|
|
ssh_privkey_t *k = NULL;
|
|
if (ssh_privkey_generate(type, bits, &k) != 0) {
|
|
fprintf(stderr, "paze-keygen: unsupported key type '%s' (ed25519/rsa/ecdsa)\n", type);
|
|
return 1;
|
|
}
|
|
|
|
if (!comment) {
|
|
char def[256];
|
|
const char *u = getenv("USER");
|
|
if (!u) u = getenv("USERNAME");
|
|
const char *h = getenv("COMPUTERNAME");
|
|
if (!h) h = getenv("HOSTNAME");
|
|
snprintf(def, sizeof(def), "%s@%s", u ? u : "paze", h ? h : "localhost");
|
|
comment = def;
|
|
}
|
|
|
|
/* 写私钥文件 (openssh-key-v1 PEM) */
|
|
char *pem = ssh_privkey_to_pem(k);
|
|
if (!pem) {
|
|
fprintf(stderr, "paze-keygen: serialize failed\n");
|
|
ssh_privkey_free(k);
|
|
return 1;
|
|
}
|
|
FILE *f = fopen(outfile, "wb");
|
|
if (!f) {
|
|
fprintf(stderr, "paze-keygen: cannot write %s\n", outfile);
|
|
free(pem);
|
|
ssh_privkey_free(k);
|
|
return 1;
|
|
}
|
|
fwrite(pem, 1, strlen(pem), f);
|
|
fclose(f);
|
|
CHMOD_0600(outfile);
|
|
free(pem);
|
|
|
|
/* 写公钥文件 outfile.pub */
|
|
size_t blen = 0;
|
|
const uint8_t *blob = ssh_privkey_pubblob(k, &blen);
|
|
char b64[8192];
|
|
paze_base64_encode(blob, blen, b64);
|
|
char pubpath[1100];
|
|
snprintf(pubpath, sizeof(pubpath), "%s.pub", outfile);
|
|
f = fopen(pubpath, "wb");
|
|
if (f) {
|
|
fprintf(f, "%s %s %s\n", ssh_privkey_alg(k), b64, comment);
|
|
fclose(f);
|
|
}
|
|
|
|
printf("Your identification has been saved in %s\n", outfile);
|
|
printf("Your public key has been saved in %s\n", pubpath);
|
|
printf("The key fingerprint is:\n");
|
|
print_fingerprint(blob, blen);
|
|
printf(" %s@%s\n", comment ? comment : "", outfile);
|
|
|
|
ssh_privkey_free(k);
|
|
return 0;
|
|
}
|