首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用C语言优化MD5 OpenSSL实现预计算哈希

用C语言优化MD5 OpenSSL实现预计算哈希
EN

Code Review用户
提问于 2018-04-17 16:18:00
回答 1查看 1.8K关注 0票数 4

我试图将一段代码从Python移植到C。代码所做的是从纯文本字列表生成一个预先计算的MD5哈希列表。

在所有3种编程语言中,该代码在SHA-1和SHA-2家族中也有其变体。结构是一样的。

我最初是用Bash编写的代码,速度很慢。所以我把它移植到Python上,它的速度要快得多。现在,我已经成功地将代码移植到C上,希望它能运行得更快。但是,即使在gcc中使用-O更快的标志,代码的运行速度仍然比Python慢(执行时间的差异随着输入大小的增加而呈指数增长)。

我也对OpenSSL密码库的效率表示怀疑,但在我看来,在阅读了它们的文档之后,它们已经相对完善了。

我猜想是我在代码的C版本中实现的嵌套循环减缓了整个过程。

有什么建议来提高业绩吗?

Bash版本:

#/!bin/bash边读边做printf $line \ md5已完成

Python版本:

导入hashlib infile = 'wordlist‘outfile =open(“预算法”,"a")和open(infile,"r")作为inf: for行inf: outfile.write(hashlib.md5(line.strip().encode('utf8')).hexdigest()+'\n')

C版本:

代码语言:javascript
复制
//-----------Libraries
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/md5.h>


//------------------------------Main Function------------------------//
int main()
{

//------Define infile, outfile, file length. Define string to be read.---//
  FILE *infile, *outfile;
  char *string = NULL;
  size_t len = 0;
  ssize_t read;
//------Open File stream for read(r) and write (w). Error Handling.--//

  //Part of MD5 Hash Function (Taken out of While Loop for Optimization)
  int md5;
  unsigned char result[MD5_DIGEST_LENGTH];
  //

  infile = fopen("file.txt", "r");

  if (infile == NULL)
    exit(EXIT_FAILURE);

  outfile = fopen("MD.txt","w");
  
  if (outfile == NULL)
    exit(EXIT_FAILURE);

//-------------Read line-by-line in using a while loop.--------------//
  while ((read = getline(&string, &len, infile)) != -1) {

     string[strcspn(string, "\n")] = 0; // Remove newline '\n'

//-------------------------MD5 Hash Function-------------------------//


      MD5(string, strlen(string), result);

      //output
      for(md5 = 0; md5 < MD5_DIGEST_LENGTH; md5++)
        
      fprintf(outfile,"%02x",result[md5]); //convert the hash to hex

      fprintf(outfile,"\n"); //newline for the output file

  }

  free(string); //free string
  fclose(infile); // close file streams
  fclose(outfile);
  exit(EXIT_SUCCESS); //Program Ends
}

如果需要,我可以提供不同大小的输入的执行时间。任何帮助都将不胜感激。

EN

回答 1

Code Review用户

回答已采纳

发布于 2018-04-17 20:01:46

在提出可能的性能改进之前,我将首先回顾一下您当前的代码。

代码中的各种注释不添加信息,可以删除,例如

代码语言:javascript
复制
//------------------------------Main Function------------------------//
int main()

free(string); //free string

exit(EXIT_SUCCESS); //Program Ends

始终用大括号表示if -语句,即使if或{ }部分仅由单个语句组成。如果稍后编辑代码,这将有助于避免错误。

在使用变量的最窄范围内声明变量,而不是在函数顶部声明变量。例如

代码语言:javascript
复制
FILE *infile = fopen("file.txt", "r");
if (infile == NULL) {
    exit(EXIT_FAILURE);
}

FILE *outfile = fopen("MD.txt","w");
if (outfile == NULL) {
    exit(EXIT_FAILURE);
}

代码语言:javascript
复制
for (int md5 = 0; md5 < MD5_DIGEST_LENGTH; md5++) { ... }

在顶部声明md5不会提高性能。

一些变量名称可以改进(当然,这部分是基于意见的):

  • char *string实际上是当前行。
  • size_t len不是当前字符串的长度,而是getline()分配的缓冲区(Re)的容量。
  • 严格地说,ssize_t read不是读入缓冲区的字节数,因为它排除了NUL字符。
  • int md5不是MD5值,而是包含MD5哈希的缓冲区的索引。
  • 我会把unsigned char result[MD5_DIGEST_LENGTH]重命名为md5hash

修正编译器警告:

代码语言:javascript
复制
MD5(string, strlen(string), result);
// Passing 'char *' to parameter of type 'const unsigned char *' converts between pointers to integer types with different sign

您已经使用了不同的退出代码来表示程序的成功或失败,这是很好的。此外,在错误情况下打印一些消息(到标准错误)也是有帮助的。

根据C11标准,main的声明应是

代码语言:javascript
复制
int main(void) { /* ... */ }
int main(int argc, char *argv[]) { /* ... */ }

(例如,请参见堆栈溢出的在C和C++中main()应该返回什么? )。最后一个return语句可以省略,有一个隐式return 0

性能改进

从输入文件中读取一行后,将遍历该字符串两次:

代码语言:javascript
复制
while ((read = getline(&string, &len, infile)) != -1) {
    string[strcspn(string, "\n")] = 0; // Remove newline '\n'
    MD5(string, strlen(string), result);

    // ...
}

首先查找终止换行符,然后再确定长度。这是不必要的,因为getline()返回写入string的字符数,即换行符只能位于read - 1位置:

代码语言:javascript
复制
while ((read = getline(&string, &len, infile)) != -1) {
    // Remove trailing newline character
    if (read > 0 && string[read - 1] == '\n') {
        read -= 1;
        string[read] = 0;
    }

    MD5(string, read, result);

    // ...
}

然而,这一变化的影响取决于线的长度,我无法观察到在我的测试显着的差异。

为了找出进一步的性能瓶颈,我现在用Xcode/仪器(使用./crunch 7 7 1234567890生成的输入文件)对程序进行了分析。

这立即表明,大部分时间是在fprintf()上度过的:

可能的原因是:

  • 字符串格式设置很慢。
  • 所有stdio打印操作都是线程安全的,因此必须为每个调用提供和释放锁。

解决办法是:

  • 编写一个自定义函数,将MD5哈希转换为十六进制字符串。
  • 为整个字符串而不是单个字节调用printf,从而减少函数调用的次数。

在我的计算机( 1.2 GHz Intel Core m5 MacBook)上,这将处理上述文件的时间从24.5秒缩短到4.4秒。

把它放在一起

经过所有这些修改,我们

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/md5.h>

// Format the data as a hexadecimal string. The buffer must have
// space for `2 * length + 1` characters.
const char *hexString(unsigned char *data, size_t length, char *buffer) {
    const char *hexDigits = "0123456789abcdef";
    char *dest = buffer;
    for (size_t i = 0; i < length; i++) {
        *dest++ = hexDigits[data[i] >> 4];
        *dest++ = hexDigits[data[i] & 0x0F];
    }
    *dest = 0;
    return buffer;
}

int main(void) {
    FILE *infile = fopen("file.txt", "r");
    if (infile == NULL) {
        perror("Cannot open input file");
        exit(EXIT_FAILURE);
    }
    FILE *outfile = fopen("MD.txt","w");
    if (outfile == NULL) {
        perror("Cannot open output file");
        exit(EXIT_FAILURE);
    }

    // Read file line-by-line
    char *line = NULL;
    size_t linecap = 0;
    ssize_t lineLength;
    while ((lineLength = getline(&line, &linecap, infile)) != -1) {
        if (lineLength > 0 && line[lineLength - 1] == '\n') {
            // Remove newline character
            lineLength -= 1;
            line[lineLength] = 0;
        }

        // Compute MD5 hash
        unsigned char md5hash[MD5_DIGEST_LENGTH];
        MD5((unsigned char*)line, lineLength, md5hash);

        // Print hash as hex string
        char hexBuffer[2 * MD5_DIGEST_LENGTH + 1];
        fputs(hexString(md5hash, MD5_DIGEST_LENGTH, hexBuffer), outfile);
        fputc('\n', outfile);
    }
    free(line);

    // Close output files
    fclose(infile);
    fclose(outfile);
}

进一步建议

  • 将输入和输出文件的名称编译到程序中,使其不灵活。可能的替代方案是
    • 将文件名作为参数传递到命令行,或
    • 使您的程序从标准输入读取并写入标准输出。

  • 实现一个-h选项以显示程序的简短帮助/使用情况。
票数 8
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/192306

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档