如何在C / C ++ / Objective-C中计算X.509证书SHA1指纹?

时间:2012-03-17 11:17:13

标签: c openssl x509

背景:

我正在编写一个能够使用SSL / TLS连接到远程服务器的客户端实用程序。客户端使用OpenSSL执行SSL / TLS事务,我希望允许用户指定用于签署服务器证书的授权CA Certs(在自签名证书或私有CA设置的情况下)。我计划使用证书的指纹,通用名称和有效日期,以便用户快速查看客户端用于验证服务器的证书。

问题:

如何使用C / C ++ / Objective-C计算存储在PEM文件中的X509证书的SHA1哈希/指纹?

经过数天的搜索和实验,我找到了一个解决方案并将其作为答案发布,但我欢迎更好或更正确的解决方案。

2 个答案:

答案 0 :(得分:11)

这是我使用OpenSSL库找到的解决方案。我发布了有关堆栈溢出的问题和答案,希望能够为其他人节省麻烦和时间来自行解决问题。

#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/x509.h>
#include <openssl/bio.h>


int main(int argc, char * argv[])
{
   struct stat           sb;
   unsigned char       * buff;
   int                   fd;
   ssize_t               len;
   BIO                 * bio;
   X509                * x;
   unsigned              err;
   int                   pos;
   char                  errmsg[1024];
   const EVP_MD        * digest;
   unsigned char         md[EVP_MAX_MD_SIZE];
   unsigned int          n;

   // checks arguments
   if (argc != 2)
   {
      fprintf(stderr, "Usage: peminfo <pemfile>\n");
      return(1);
   };

   // checks file
   if ((stat(argv[1], &sb)) == -1)
   {
      perror("peminfo: stat()");
      return(1);
   };
   len = (sb.st_size * 2);

   // allocates memory
   if (!(buff = malloc(len)))
   {
      fprintf(stderr, "peminfo: out of virtual memory\n");
      return(1);
   };

   // opens file for reading
   if ((fd = open(argv[1], O_RDONLY)) == -1)
   {
      perror("peminfo: open()");
      free(buff);
      return(1);
   };

   // reads file
   if ((len = read(fd, buff, len)) == -1)
   {
      perror("peminfo: read()");
      free(buff);
      return(1);
   };

   // closes file
   close(fd);

   // initialize OpenSSL
   SSL_load_error_strings();
   SSL_library_init();

   // creates BIO buffer
   bio = BIO_new_mem_buf(buff, len);

   // decodes buffer
   if (!(x = PEM_read_bio_X509(bio, NULL, 0L, NULL)))
   {
      while((err = ERR_get_error()))
      {
         errmsg[1023] = '\0';
         ERR_error_string_n(err, errmsg, 1023);
         fprintf(stderr, "peminfo: %s\n", errmsg);
      };
      BIO_free(bio);
      free(buff);
      return(1);
   };

   // prints x509 info
   printf("name:      %s\n",    x->name);
   printf("serial:    ");
   printf("%02X", x->cert_info->serialNumber->data[0]);
   for(pos = 1; pos < x->cert_info->serialNumber->length; pos++)
      printf(":%02X", x->cert_info->serialNumber->data[pos]);
   printf("\n");

   // calculate & print fingerprint
   digest = EVP_get_digestbyname("sha1");
   X509_digest(x, digest, md, &n);
   printf("Fingerprint: ");
   for(pos = 0; pos < 19; pos++)
      printf("%02x:", md[pos]);
   printf("%02x\n", md[19]);

   // frees memory
   BIO_free(bio);
   free(buff);

   return(0);
}

以下是上述程序的编译和输出:

$ cc -pedantic -W -Wall -Werror -O2  -Wno-deprecated -o peminfo  peminfo.c \
> -lcrypto -lssl
$ ./peminfo /usr/local/etc/openldap/keys/ca-certs.pem 
serial:      98:61:EB:C4:F2:C9:59:72
Fingerprint: 1d:59:d3:d4:4f:c9:e3:dc:f3:d7:66:b0:b8:7e:87:0b:01:73:c2:7e

以下是openssl实用程序的输出:

$ openssl x509 -noout -in /usr/local/etc/openldap/keys/ca-certs.pem \
> -fingerprint -serial
SHA1 Fingerprint=1D:59:D3:D4:4F:C9:E3:DC:F3:D7:66:B0:B8:7E:87:0B:01:73:C2:7E
serial=9861EBC4F2C95972

答案 1 :(得分:5)

我在下面发现产生与上面相同的输出:

+(NSData *)sha1:(SecCertificateRef) cert {
    // fingerprint is over canonical DER rep.
    CFDataRef data = SecCertificateCopyData(cert);
    NSData * out = [[NSData dataWithBytes:CFDataGetBytePtr(data) length:CFDataGetLength(data)] sha1Digest];
    CFRelease(data);
    return out;
}

在目标C中略短。虽然要使格式接近Netscape,OSX或Windows,但它需要NSData / NSString的以下扩展。

- (NSData *)md5Digest
{
    unsigned char result[CC_MD5_DIGEST_LENGTH];

    CC_MD5([self bytes], (CC_LONG)[self length], result);
    return [NSData dataWithBytes:result length:CC_MD5_DIGEST_LENGTH];
}

- (NSData *)sha1Digest
{
    unsigned char result[CC_SHA1_DIGEST_LENGTH];

    CC_SHA1([self bytes], (CC_LONG)[self length], result);
    return [NSData dataWithBytes:result length:CC_SHA1_DIGEST_LENGTH];
}

- (NSString *)hexStringValue
{
    NSMutableString *stringBuffer = [NSMutableString stringWithCapacity:([self length] * 2)];

    const unsigned char *dataBuffer = [self bytes];
    int i;

    for (i = 0; i < [self length]; ++i)
    {
        [stringBuffer appendFormat:@"%02lx", (unsigned long)dataBuffer[i]];
    }

    return [stringBuffer copy];
}


- (NSString *)hexColonSeperatedStringValue
{
    return [self hexColonSeperatedStringValueWithCapitals:YES];
}

- (NSString *)hexColonSeperatedStringValueWithCapitals:(BOOL)capitalize {
    NSMutableString *stringBuffer = [NSMutableString stringWithCapacity:([self length] * 3)];

    const unsigned char *dataBuffer = [self bytes];
    NSString * format = capitalize ? @"%02X" : @"%02x";
    int i;

    for (i = 0; i < [self length]; ++i)
    {
        if (i) 
            [stringBuffer appendString:@":"];
        [stringBuffer appendFormat:format, (unsigned long)dataBuffer[i]];
    }

    return [stringBuffer copy];
}