背景:
我正在编写一个能够使用SSL / TLS连接到远程服务器的客户端实用程序。客户端使用OpenSSL执行SSL / TLS事务,我希望允许用户指定用于签署服务器证书的授权CA Certs(在自签名证书或私有CA设置的情况下)。我计划使用证书的指纹,通用名称和有效日期,以便用户快速查看客户端用于验证服务器的证书。
问题:
如何使用C / C ++ / Objective-C计算存储在PEM文件中的X509证书的SHA1哈希/指纹?
经过数天的搜索和实验,我找到了一个解决方案并将其作为答案发布,但我欢迎更好或更正确的解决方案。
答案 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];
}