2012-03-17 50 views
6

背景:如何在C/C++/Objective-C中計算X.509證書的SHA-1指紋?

我正在寫一個客戶端實用程序,它能夠使用SSL/TLS連接到遠程服務器。客戶端使用OpenSSL執行SSL/TLS事務,並且希望允許用戶指定用於簽署服務器證書的授權CA證書(在使用自簽名證書或專用CA設置的情況下)。我計劃使用證書的指紋,通用名稱和有效日期,以便用戶快速查看客戶端用於驗證服務器的證書。

問:

如何計算存儲使用C/C++/Objective-C的一個PEM文件中的X509證書的SHA1哈希/指紋?

經過幾天的搜索和實驗,我找到了解決方案,並將其作爲答案發布,但我歡迎更好或更正確的解決方案。

回答

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中稍短。它需要下面的擴展到NSData/NSString,儘管爲了使格式化接近Netscape,OSX或Windows。

- (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]; 
} 
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