文件
PazeSSH/apps/keygen/main.c
T

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;
}