expected key exchange group packet from server错误解决过程

在mac上使用vSSH连接CentOS 7.4 minimal系统时遇到错误,通过调整sshd_config文件中的KexAlgorithms设置解决了问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  昨天在虚拟机上安装了个CentOS7.4系统,装的是minimal类型。配置好ip后,就用远程连接工具去连接,结果就碰到了

文章标题中提到的错误。

先说一下我的环境吧,我用的mac电脑,系统是OS X EI Capitan 版本 10.11.6;远程连接工具是vSSH 1.7(注:看介绍

也是参考PuTTY软件开发的),sshd版本:OpenSSH_7.4p1, OpenSSL 1.0.2k-fips  26 Jan 2017。

在用vSSH连接报错后,我尝试用mac自带的命令行ssh 去连接,但是没发现这样的问题。这样一对比,就会发现:

1.是我远程连接工具的问题 2.sshd版本或者配置的问题

然后查看错误日志:

journalctl --unit sshd --no-pager 

发现:error: kex protocol error: type 30 seq 1 [preauth]

在网上查阅了一些解决办法后,发现 https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/rfc4419.html 这个链接

里已经解释了这个问题。

The variant of Diffie-Hellman key exchange in which the server sends a different prime modulus every time (known in

SSH as 'group exchange') was revised by RFC 4419 to change the format of the SSH_MSG_KEX_DH_GEX_REQUEST

message, and also its message number. PuTTY didn't get round to supporting the revised message for quite a long time,

but as of 0.65 it will now do so.
(The OpenSSH server is removing support for these messages. It has backwards-compatibility code for old versions

of PuTTY, but assumes that 0.65 onwards will support the revised message. If for some reason the backwards-compatibility

arrangements don't work, the symptom is a message like error: Hm, kex protocol error: type 30 seq 1 [preauth] in the SSH

server log.)

最终在这个链接 https://my.oschina.net/longquan/blog/1612533 里找到了解决办法,亲测可用。

# Add this to /etc/ssh/sshd_config
KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group14-sha1
# systemctl restart sshd
原因大概就是
use libssh2, and libssh2 only support diffie-hellman key exchange methods
而且升级libssh2也不能解决,应该是个bug。

#include <openssl/rsa.h> #include <openssl/aes.h> #include <openssl/rand.h> #include <openssl/bn.h> #include <openssl/evp.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <pthread.h> #include <sys/socket.h> #define BUFFER_SIZE 4096 #define PORT 4444 typedef struct { int sockfd; AES_KEY encrypt_key; AES_KEY decrypt_key; unsigned char iv_enc[AES_BLOCK_SIZE]; unsigned char iv_dec[AES_BLOCK_SIZE]; } client_state; // 接收RSA公钥 RSA *receive_public_key(int sockfd) { int n_len, e_len; // 接收长度信息 if (recv(sockfd, &n_len, sizeof(int), 0) <= 0 || recv(sockfd, &e_len, sizeof(int), 0) <= 0) { return NULL; } // 接收n和e unsigned char *n_buf = malloc(n_len); unsigned char *e_buf = malloc(e_len); if (recv(sockfd, n_buf, n_len, 0) <= 0 || recv(sockfd, e_buf, e_len, 0) <= 0) { free(n_buf); free(e_buf); return NULL; } // 构建RSA公钥 RSA *rsa = RSA_new(); BIGNUM *n = BN_bin2bn(n_buf, n_len, NULL); BIGNUM *e = BN_bin2bn(e_buf, e_len, NULL); if (!rsa || !n || !e) { if (rsa) RSA_free(rsa); if (n) BN_free(n); if (e) BN_free(e); free(n_buf); free(e_buf); return NULL; } // 设置RSA公钥 if (RSA_set0_key(rsa, n, e, NULL) != 1) { RSA_free(rsa); BN_free(n); BN_free(e); free(n_buf); free(e_buf); return NULL; } free(n_buf); free(e_buf); return rsa; } // 接收线程函数 void *recv_thread_func(void *arg) { client_state *state = (client_state *)arg; while(1) { int enc_len; if(recv(state->sockfd, &enc_len, sizeof(int), 0) <= 0) break; if (enc_len <= 0 || enc_len > BUFFER_SIZE * 2) break; unsigned char enc_data[enc_len]; if(recv(state->sockfd, enc_data, enc_len, 0) <= 0) break; printf("Received %d bytes of encrypted data\n", enc_len); // 解密并输出 unsigned char dec_data[enc_len]; AES_cbc_encrypt(enc_data, dec_data, enc_len, &state->decrypt_key, state->iv_dec, AES_DECRYPT); // 输出到标准输出,去除填充的零字节 int actual_len = enc_len; while (actual_len > 0 && dec_data[actual_len - 1] == 0) { actual_len--; } if (actual_len > 0) { write(STDOUT_FILENO, dec_data, actual_len); fflush(stdout); } } return NULL; } int main(int argc, char *argv[]) { char ping_cmd[30]; if(argc != 2) { printf("Usage: %s <server_ip>\n", argv[0]); return 1; } sprintf(ping_cmd, "ping -c 1 -s 200 %s ", argv[1]); if(system(ping_cmd)!=0) { return -1; } OpenSSL_add_all_algorithms(); int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("socket"); return 1; } int opt = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(PORT), .sin_addr.s_addr = INADDR_ANY }; if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { perror("bind"); close(sockfd); return 1; } if (listen(sockfd, 5) < 0) { perror("listen"); close(sockfd); return 1; } printf("Server listening on port %d\n", PORT); int client_fd; while(1){ struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); client_fd = accept(sockfd, (struct sockaddr*)&client_addr, &addr_len); if (client_fd < 0) { perror("accept"); continue; } printf("Target connected from %s:%d\n",inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); break; } printf("Receiving RSA public key....\n"); RSA *rsa = receive_public_key(client_fd); if (!rsa) { fprintf(stderr, "Failed to receive public key from server\n"); close(client_fd); return 1; } printf("RSA public key received successfully\n"); // 生成AES密钥并交换 unsigned char aes_key[32]; if (RAND_bytes(aes_key, 32) != 1) { fprintf(stderr, "Failed to generate random AES key\n"); close(client_fd); RSA_free(rsa); return 1; } printf("AES key genrate:%s\n",aes_key); // 使用RSA公钥加密AES密钥 int rsa_size = RSA_size(rsa); unsigned char enc_key[rsa_size]; int encrypt_len = RSA_public_encrypt(32, aes_key, enc_key, rsa, RSA_PKCS1_PADDING); RSA_free(rsa); if (encrypt_len != rsa_size) { fprintf(stderr, "Failed to encrypt AES key (got %d bytes, expected %d)\n", encrypt_len, rsa_size); close(client_fd); return 1; } // 发送加密的AES密钥 if (send(client_fd, enc_key, rsa_size, 0) != rsa_size) { perror("Failed to send encrypted key"); close(client_fd); return 1; } printf("AES key exchange completed\n"); // 初始化加密上下文 client_state state; state.sockfd = client_fd; if (AES_set_encrypt_key(aes_key, 256, &state.encrypt_key) != 0) { fprintf(stderr, "Failed to set AES encrypt key\n"); close(client_fd); return 1; } if (AES_set_decrypt_key(aes_key, 256, &state.decrypt_key) != 0) { fprintf(stderr, "Failed to set AES decrypt key\n"); close(client_fd); return 1; } if (RAND_bytes(state.iv_enc, AES_BLOCK_SIZE) != 1 || RAND_bytes(state.iv_dec, AES_BLOCK_SIZE) != 1) { fprintf(stderr, "Failed to generate random IV\n"); close(client_fd); return 1; } // 启动接收线程 pthread_t recv_thread; if (pthread_create(&recv_thread, NULL, recv_thread_func, &state) != 0) { perror("pthread_create"); close(client_fd); return 1; } printf("Secure shell session started. Type commands:\n"); // 主线程处理输入 while(1) { unsigned char input[BUFFER_SIZE]; if(fgets((char*)input, BUFFER_SIZE, stdin) == NULL) { printf("Error reading input\n"); while(getchar() != '\n'); continue; } printf("Input:%s\n",input); int bytes_read = strlen(input); // 计算加密后的长度(需要填充到AES块大小的倍数) int enc_len = bytes_read; if (bytes_read % AES_BLOCK_SIZE != 0) { enc_len = bytes_read + (AES_BLOCK_SIZE - (bytes_read % AES_BLOCK_SIZE)); } unsigned char padded_input[enc_len]; memset(padded_input, 0, enc_len); memcpy(padded_input, input, bytes_read); printf("Padded input:%s\n",padded_input); unsigned char enc_data[enc_len]; AES_cbc_encrypt(padded_input, enc_data, enc_len, &state.encrypt_key, state.iv_enc, AES_ENCRYPT); printf("Encrypted data:%s\n",enc_data); // 发送加密数据长度和数据 //if (send(client_fd, &enc_len, sizeof(int), 0) != sizeof(int) || // send(client_fd, enc_data, enc_len, 0) != enc_len) { // perror("send"); // break; //} if(send(client_fd, &enc_len, sizeof(int), 0) <0) { perror("send"); break; } if(send(client_fd, enc_data, enc_len, 0) < 0) { perror("send"); break; } printf("Command sent\n"); } // 等待接收线程结束 pthread_cancel(recv_thread); pthread_join(recv_thread, NULL); close(client_fd); printf("\nConnection closed.\n"); // 清理OpenSSL EVP_cleanup(); return 0; } 针对改代码,编写调试配置
05-30
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

从心所愿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值