最近在使用mosquitto做项目,使用TLS安全连接,并且客户端证书需要密码,在使用mosquitto_pub/mosquitto_sub命令是终端出输入证书密码的提示:Enter PEM pass phrase:
为了解决手动输入密码的问题我搜索此打印找到如下函数
int PEM_def_callback(char *buf, int num, int rwflag, void *userdata)
{
int i, min_len;
const char *prompt;
/* We assume that the user passes a default password as userdata */
if (userdata) {
i = strlen(userdata);
i = (i > num) ? num : i;
memcpy(buf, userdata, i);
return i;
}
prompt = EVP_get_pw_prompt();
if (prompt == NULL)
prompt = "1.1.1 Enter PEM pass phrase:";
/*
* rwflag == 0 means decryption
* rwflag == 1 means encryption
*
* We assume that for encryption, we want a minimum length, while for
* decryption, we cannot know any minimum length, so we assume zero.
*/
min_len = rwflag ? MIN_LENGTH : 0;
i = EVP_read_pw_string_min(buf, min_len, num, prompt, rwflag);
if (i != 0) {
PEMerr(PEM_F_PEM_DEF_CALLBACK, PEM_R_PROBLEMS_GETTING_PASSWORD);
memset(buf, 0, (unsigned int)num);
return -1;
}
return strlen(buf);
}
EVP_get_pw_prompt()获取预置密码,如果没有预置密码则进入EVP_read_pw_string_min函数由用户手动输入,顺藤摸瓜又找到了设置预置密码的函数
/* should be init to zeros. */
static char prompt_string[80];
void EVP_set_pw_prompt(const char *prompt)
{
if (prompt == NULL)
prompt_string[0] = '\0';
else {
strncpy(prompt_string, prompt, 79);
prompt_string[79] = '\0';
}
}
看到此处豁然开朗,如果预置密码,调用EVP_set_pw_prompt函数即可提前设置密码,但是我调用此函数之后再运行mosquitto_pub/mosquitto_sub命令,终端只是把预置的密码回显到终端,并不会继续执行。再敲个回车,直接挂死。头疼十分钟。
于是又想找到在哪里获取手动输入的密码,找了半天看不懂i = EVP_read_pw_string_min(buf, min_len, num, prompt, rwflag)函数,里面好多函数找不到哪里定义,放弃之。于是想是不是EVP_read_pw_string_min函数对预置密码处理有问题,当前使用openssl为1.1.1,到了之后的新版本是不是解决这个bug了?于是又编译了openssl3.0.0,依然是这个样子,于是尝试将代码改成如下形式,不使用自带的EVP_read_pw_string_min函数,如果有预置密码直接拷贝到buf中,试了一下果然成功。
int PEM_def_callback(char *buf, int num, int rwflag, void *userdata)
{
int i, min_len;
const char *prompt;
/* We assume that the user passes a default password as userdata */
if (userdata) {
i = strlen(userdata);
i = (i > num) ? num : i;
memcpy(buf, userdata, i);
return i;
}
prompt = EVP_get_pw_prompt();
if (prompt == NULL){
prompt = "Enter PEM pass phrase:";
/*
* rwflag == 0 means decryption
* rwflag == 1 means encryption
*
* We assume that for encryption, we want a minimum length, while for
* decryption, we cannot know any minimum length, so we assume zero.
*/
min_len = rwflag ? MIN_LENGTH : 0;
i = EVP_read_pw_string_min(buf, min_len, num, prompt, rwflag);
if (i != 0) {
PEMerr(PEM_F_PEM_DEF_CALLBACK, PEM_R_PROBLEMS_GETTING_PASSWORD);
memset(buf, 0, (unsigned int)num);
return -1;
}
}else
{
strncpy(buf, prompt, num);
buf[num-1] = '\0';
}
return strlen(buf);
}
后来又在github问了这个问题给出的回复是我的方法不对。。。。。
正确的方法应该是设置PEM密码回调函数
int pem_passwd_cb(char *buf, int size, int rwflag, void *password)
{
strncpy(buf, "123456", size);
buf[size - 1] = '\0';
return(strlen(buf));
}
int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg)
{
......
#ifdef WITH_TLS
if(cfg->cafile || cfg->capath){
rc = mosquitto_tls_set(mosq, cfg->cafile, cfg->capath, cfg->certfile, cfg->keyfile, pem_passwd_cb);
......
}