Open yebin-yu opened 5 months ago
etc/ssh/sshd_config
HostKey /root/.ssh/id_sm2
PermitRootLogin yes
PubkeyAuthentication yes
AuthorizedKeysFile /authorized_keys
X11Forwarding yes
Subsystem sftp /usr/lib/ssh/sftp-server
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL
MACs hmac-sm3,umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1
HostKeyAlgorithms sm2,rsa-sha2-512,rsa-sha2-256,ssh-rsa,ecdsa-sha2-nistp256,ssh-ed25519
KexAlgorithms sm2-sm3,curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256
Ciphers sm4-ctr,chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
PubkeyAcceptedKeyTypes sm2
FingerprintHash sm3
PubkeyAcceptedAlgorithms sm2
PasswordAuthentication no
AllowUsers *
LOGLEVEL DEBUG3
这里配置的公钥存放文件为/authorized_keys
, /authorized_keys
在测试中的文件内容如下,内容为ukey的公钥
sm2 AAAAA3NtMgAAAANzbTIAAABBBKN4ft4dC8ThwwicmuITfLEjt1lOSuLdhZuJDh/5VGUhmB4sY1+3KVcoqgW8yxjECbcb9S7sImq8kVIVP4OPy84= root@yebinyu
{sshd路径}sshd -p {端口号} -D -d -oHostKeyAlgorithms=sm2 -oMacs=hmac-sm3 -oKexAlgorithms=sm2-sm3 -oCiphers=sm4-ctr -f /etc/ssh/sshd_config_sm2
可以不加 -f /etc/ssh/sshd_config_sm2
,默认的配置文件路径为 /etc/ssh/sshd_confg
例如原始公钥
sm2 AAAAA3NtMgAAAANzbTIAAABBBKN4ft4dC8ThwwicmuITfLEjt1lOSuLdhZuJDh/5VGUhmB4sY1+3KVcoqgW8yxjECbcb9S7sImq8kVIVP4OPy84= root@yebinyu
经过转换函数(python环境下执行)
import base64
base64.b64decode("AAAAA3NtMgAAAANzbTIAAABBBKN4ft4dC8ThwwicmuITfLEjt1lOSuLdhZuJDh/5VGUhmB4sY1+3KVcoqgW8yxjECbcb9S7sImq8kVIVP4OPy84=").hex()[-130:]
得到转换后的结果,这个就是需要存到设备上的:
04a3787ede1d0bc4e1c3089c9ae2137cb123b7594e4ae2dd859b890e1ff9546521981e2c635fb7295728aa05bccb18c409b71bf52eec226abc9152153f838fcbce
[~R108]dis c c ssh
#
stelnet server enable
snetconf server enable
ssh server rsa-key min-length 3072
ssh user netconf
ssh user netconf authentication-type password-sm2
ssh user netconf assign sm2-key ukeytest
ssh user netconf service-type all
ssh server-source -i Ethernet0/0/0
undo ssh server-source all-interface
undo ssh ipv6 server-source all-interface
ssh server assign sm2-host-key sm2kp
ssh server ip-block disable
ssh authorization-type default aaa
#
ssh server cipher sm4_cbc sm4_gcm sm4_ctr
ssh server hmac sm3
ssh server key-exchange sm2_kep sm2_sm3
#
ssh server publickey sm2 sm2-sm3
#
ssh server dh-exchange min-len 3072
#
ssh client publickey sm2 sm2-sm3
#
ssh client cipher sm4_cbc sm4_gcm sm4_ctr
ssh client hmac sm3
ssh client key-exchange sm2_kep sm2_sm3
sshkey
格式(底层其实是转换成openssl中的EVP_KEY
)openssh 在发起建联消息前,会遍历配置中给的私钥和certificate列表,如果找到了就读取信息存入 struct sshkey
中,再后续公钥测试的时候会遍历每一个私钥和certificate,使用私钥去生成公钥去测试。
所以这里首先得修改 /root/.ssh/config
,加入sm2的私钥 -- IdentityFile ~/.ssh/id_sm2
。
改这个配置后,如果配置文件是id_sm2
,同时这个文件不存在,就会走入ukey的流程
if ((f = fopen(filename, "r")) == NULL) {
if (strcmp(filename, "/root/.ssh/id_sm2") == 0) {
// 初始化kp
*kp = sshkey_new(KEY_SM2);
(*(*kp)).type = KEY_SM2;
(*(*kp)).ecdsa_nid = NID_sm2;
if (ukey_to_sshkey(*kp) != 0) {
return 1;
}
return 0;
}
return SSH_ERR_SYSTEM_ERROR;
}
static int
init_ukey_container_and_dev()
{
ULONG ulRslt = SAR_OK;
// Get dev name and connect dev
char szDevName[256] = {0};
ULONG ulDevNameLen = 256;
ulRslt = SKF_EnumDev(TRUE, szDevName, &ulDevNameLen);
printf("szDevName: %s \n", szDevName);
if (ulRslt != SAR_OK) {
debug("SKF_EnumDev ERROR \n");
return SSH_ERR_UKEY_ERROR;
}
ulRslt = SKF_ConnectDev(szDevName, &hdev);
if (ulRslt != SAR_OK) {
debug("SKF_ConnectDev ERROR \n");
return SSH_ERR_UKEY_ERROR;
}
// Get application and connect application
char appName[256] = {0};
ULONG appnameLen = 256;
ulRslt = SKF_EnumApplication(hdev, appName, &appnameLen);
printf("appName: %s\n", appName);
if (ulRslt != SAR_OK) {
debug("SKF_EnumApplication ERROR \n");
return SSH_ERR_UKEY_ERROR;
}
HANDLE happ;
ulRslt = SKF_OpenApplication(hdev, appName, &happ);
if (ulRslt != SAR_OK) {
debug("SKF_OpenApplication ERROR \n");
return SSH_ERR_UKEY_ERROR;
}
// Check pin
char pinStr[32];
ULONG retryCntMax = 3;
ULONG currentRetryCnt = 0;
printf("UKEY pin:");
scanf("%s", pinStr);
ulRslt = SKF_VerifyPIN(happ, USER_TYPE, pinStr, &retryCntMax);
while (ulRslt != SAR_OK && currentRetryCnt++ < retryCntMax) {
sleep(2); // 防止暴力破解,等待两秒
printf("UKEY pin again:");
scanf("%s", pinStr);
ulRslt = SKF_VerifyPIN(happ, USER_TYPE, pinStr, &retryCntMax);
}
if (ulRslt != SAR_OK) {
debug("SKF_VerifyPIN ERROR \n");
return SSH_ERR_UKEY_ERROR;
}
// Get container name and connect container
char containerName[256] = {0};
ULONG containerNameLen = 256;
ulRslt = SKF_EnumContainer(happ, containerName, &containerNameLen);
printf("containerName: %s\n", containerName);
if (ulRslt != SAR_OK) {
debug("SKF_EnumContainer ERROR \n");
return SSH_ERR_UKEY_ERROR;
}
ulRslt = SKF_OpenContainer(happ, containerName, &hcontainer);
if (ulRslt != SAR_OK) {
debug("SKF_OpenContainer ERROR \n");
return SSH_ERR_UKEY_ERROR;
}
BYTE buf[sizeof(ECCPUBLICKEYBLOB)] = {0};
ULONG bufLen = sizeof(buf);
ulRslt = SKF_ExportPublicKey(hcontainer, TRUE, buf, &bufLen);
if (ulRslt != SAR_OK) {
debug("SKF_ExportPublicKey ERROR \n");
return SSH_ERR_UKEY_ERROR;
}
blob = malloc(sizeof(ECCPUBLICKEYBLOB));
if (blob == NULL) {
debug("malloc blob failed \n");
return SSH_ERR_UKEY_ERROR;
}
memcpy(blob, buf, sizeof(ECCPUBLICKEYBLOB));
return SAR_OK;
}
sm2.pub
static int
saveAndGetSm2Pub(ECCPUBLICKEYBLOB *blob)
{
// blob to pubkey string
BYTE *x = blob->XCoordinate + 32;
BYTE *y = blob->YCoordinate + 32;
const int headLen = 19;
const int xLen = 32;
const int yLen = 32;
BYTE *head_char = malloc(headLen);
// 00000003736d3200000003736d3200000041: always
// 04: uncompressed
hexToByte("00000003736d3200000003736d320000004104", 38, head_char);
BYTE *allBytes = NULL;
if ((allBytes = malloc(headLen + 32 + 32)) == NULL) {
debug("allBytes malloc ERROR \n");
return 1;
}
memcpy(allBytes, head_char, headLen);
memcpy(allBytes + headLen, x, xLen);
memcpy(allBytes + headLen + xLen, y, yLen);
size_t pubLen = ((headLen + xLen + yLen + 2) / 3) * 4 + 1;
char *pubStr = malloc(pubLen);
if (b64_ntop(allBytes, headLen + xLen + yLen, pubStr, pubLen) == -1) {
debug("b64_ntop ERcROR \n");
return 1;
}
FILE *fp = fopen("sm2.pub", "wb");
fwrite("sm2 ", sizeof(char), 4, fp);
fwrite(pubStr, sizeof(char), pubLen - 1, fp);
fwrite(" root@yebinyu", sizeof(char), 13, fp);
fclose(fp);
free(allBytes);
free(pubStr);
return SAR_OK;
}
sm2.pub
中读取公钥string // read pubkey info from sm2.pub
FILE *fp = fopen("sm2.pub", "r");
if (fp == NULL) {
printf("Error opening file: sm2.pub\n");
return 1;
}
char *buffer = NULL;
size_t buffer_size = 0;
char c;
while ((c = fgetc(fp)) != EOF) {
buffer_size++;
buffer = realloc(buffer, buffer_size);
if (buffer == NULL) {
printf("Error allocating memory\n");
fclose(fp);
return 1;
}
buffer[buffer_size - 1] = c;
}
buffer[buffer_size] = '\0';
printf("File contents:\n%s\n", buffer);
fclose(fp);
extern const struct sshkey_impl sshkey_sm2_impl;
static void
sshkey_free_contents(struct sshkey *k)
{
const struct sshkey_impl *impl;
if (k == NULL)
return;
if ((impl = &sshkey_sm2_impl) != NULL &&
impl->funcs->cleanup != NULL)
impl->funcs->cleanup(k);
freezero(k->shielded_private, k->shielded_len);
freezero(k->shield_prekey, k->shield_prekey_len);
}
int
sshkey_read2(struct sshkey *ret, char **cpp)
{
struct sshkey *k;
char *cp, *blobcopy;
size_t space;
int r = -1;
struct sshbuf *blob;
if (ret == NULL)
return SSH_ERR_INVALID_ARGUMENT;
cp = *cpp;
space = strcspn(cp, " \t");
if (space == strlen(cp))
return SSH_ERR_INVALID_FORMAT;
// skip whitespace
for (cp += space; *cp == ' ' || *cp == '\t'; cp++)
;
if (*cp == '\0')
return SSH_ERR_INVALID_FORMAT;
if ((blob = sshbuf_new()) == NULL)
return SSH_ERR_ALLOC_FAIL;
// find end of keyblob and decode
space = strcspn(cp, " \t");
if ((blobcopy = strndup(cp, space)) == NULL) {
sshbuf_free(blob);
return SSH_ERR_ALLOC_FAIL;
}
if ((r = sshbuf_b64tod(blob, blobcopy)) != 0) {
free(blobcopy);
sshbuf_free(blob);
return r;
}
free(blobcopy);
if ((r = sshkey_fromb(blob, &k)) != 0) {
sshbuf_free(blob); // Py84= failed here
return r;
}
sshbuf_free(blob);
// skip whitespace and leave cp at start of comment
for (cp += space; *cp == ' ' || *cp == '\t'; cp++)
;
// Fill in ret from parsed key
sshkey_free_contents(ret);
*ret = *k;
freezero(k, sizeof(*k));
*cpp = cp;
return 0;
}
if (id->key != NULL &&
(id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT))) {
sign_key = id->key;
is_agent = 1;
} else if (strcmp(id->filename, "/root/.ssh/id_sm2") == 0 && (f = fopen("/root/.ssh/id_sm2", "r")) == NULL) {
sm2_retry_pin:
sign_key = id->key;
if ((r = ssh_sm2_sign_sm2(sign_key, sigp, lenp, data, datalen, sign_key)) != 0) {
goto out;
}
debug("ssh_sm2_sign_sm2 return : %d \n", r);
if ((r = sshkey_check_sigtype(*sigp, *lenp, alg)) != 0) {
debug_fr(r, "sshkey_check_sigtype");
goto out;
}
/* success */
r = 0;
goto out;
} else {
if (f != NULL) {
fclose(f);
}
/* Load the private key from the file. */
if ((prv = load_identity_file(id)) == NULL)
return SSH_ERR_KEY_NOT_FOUND;
if (id->key != NULL && !sshkey_equal_public(prv, id->key)) {
error_f("private key %s contents do not match public",
id->filename);
r = SSH_ERR_KEY_NOT_FOUND;
goto out;
}
sign_key = prv;
}
static int
ssh_sm2_sign_sm2(struct sshkey *key,
u_char **sigp, size_t *lenp,
const u_char *data, size_t datalen,
struct sshkey *sign_key)
{
int r = 0;
int len = 0;
struct sshbuf *b = NULL;
int ret = SSH_ERR_INTERNAL_ERROR;
u_char *sig = NULL;
size_t slen = 0;
if (lenp != NULL)
*lenp = 0;
if (sigp != NULL)
*sigp = NULL;
// get signed data
if ((ret = ukey_get_sig(data, datalen, &sig, &slen, sign_key)) != 0) {
printf("ERROR: ukey_get_sig failed! \n");
goto out;
}
printf("====== sig len: %zu \n", slen);
// save sign data to b
if ((b = sshbuf_new()) == NULL) {
printf("ERROR: sshbuf_new failed! \n");
ret = SSH_ERR_ALLOC_FAIL;
goto out;
}
if ((r = sshbuf_put_cstring(b, "sm2")) != 0 ||
(r = sshbuf_put_string(b, sig, slen)) != 0) {
printf("ERROR: sshbuf_put_string or sshbuf_put_cstring failed! \n");
goto out;
}
// save sign data to b, and than copy to ·sigp·
len = sshbuf_len(b);
if (sigp != NULL) {
if ((*sigp = malloc(len)) == NULL) {
printf("ERROR: *sigp = malloc(len) failed! \n");
ret = SSH_ERR_ALLOC_FAIL;
goto out;
}
memcpy(*sigp, sshbuf_ptr(b), len);
}
if (lenp != NULL)
*lenp = len;
ret = 0;
out:
if (sig != NULL) {
explicit_bzero(sig, slen);
OPENSSL_free(sig);
}
sshbuf_free(b);
return ret;
}
1234567812345678
,哈希完后用ukey做签名void SM2_gmSig2OpensslSig(ECCSIGNATUREBLOB *sigBlob, unsigned char **sig, size_t *len)
{
BIGNUM *rB=NULL;
BIGNUM *sB=NULL;
BYTE *r = sigBlob->r+32;
BYTE *s = sigBlob->s+32;
rB = BN_bin2bn(r, 32, NULL);
if(rB == NULL) {
return;
}
sB = BN_bin2bn(s, 32, NULL);
if(sB == NULL) {
return;
}
ECDSA_SIG *ecdsaSig = ECDSA_SIG_new();
if (ecdsaSig == NULL) {
return;
}
if(ECDSA_SIG_set0(ecdsaSig, rB, sB) != 1) {
return;
}
unsigned char* sig_temp = NULL;
int sigLen = i2d_ECDSA_SIG(ecdsaSig, sig);;
debug("sig: %s", *sig);
*len = sigLen;
}
extern unsigned char *sm2_id;
static int
ukey_get_sig(u_char *data, size_t datalen, u_char **sig, size_t *slen, struct sshkey *sign_key)
{
ULONG ulRslt = SAR_OK;
unsigned char hashRslt[32] = {0};
int hashLen = 32;
HANDLE phHash = NULL;
ulRslt = SKF_DigestInit(hdev, SGD_SM3, blob, sm2_id, 16, &phHash);
if (ulRslt != SAR_OK) {
debug("ulRsltSKF_DigestInit:%d", ulRslt);
return 1;
}
ulRslt = SKF_Digest(phHash, data, datalen, hashRslt, &hashLen);
if (ulRslt != SAR_OK) {
debug("SKF_Digest:%d", ulRslt);
return 1;
}
// sign
ECCSIGNATUREBLOB stSign = {0};
ulRslt = SKF_ECCSignData(hcontainer, hashRslt, hashLen, &stSign);
if (ulRslt != SAR_OK) {
debug("SKF_ECCSignData ERROR \n");
return 1;
}
SM2_gmSig2OpensslSig(&stSign, sig, slen);
return 0;
}
openssh 客户端配置
/etc/ssh/ssh_config
/root/.ssh/config
建立连接
注意事项
第一次插入ukey的时候,不知道ukey的公钥,不过没关系,还是用上面建立连接的命令尝试去建联,输入ukey的pin码后,会在当前目录下生成一个
sm2.pub
文件,里面就是base64转码后的公钥内容