iOS 使用OpenSSL 讀取P12證書信息

1悬嗓、OpenSSL 庫使用pod 方式引入:

pod 'OpenSSL-Universal'

2、準(zhǔn)備一個p12證書

示例:develop.p12, 密碼: 123456

3、引入相關(guān)的頭文件:

#import <openssl/pkcs12.h>
#import <openssl/err.h>
#import <openssl/pem.h>
#import <CommonCrypto/CommonDigest.h>

4舟肉、讀取p12證書相關(guān)信息:

-  (void)readP12Info {
      NSString *filePath = [[NSBundle mainBundle]     pathForResource:@"develop" ofType:@"p12"];
      NSString *password = @"123456";

      PKCS12 *p12 = NULL; //p12對像:包含證書信息和秘鑰
      X509* usrCert = NULL; //證書信息
      EVP_PKEY* pkey = NULL; //秘鑰
      STACK_OF(X509)* ca = NULL; //證書信息棧
      BIO*bio = NULL; //BIO是封裝了許多類型I/O接口細(xì)節(jié)的一種應(yīng)用接口修噪,可以和SSL連接、非加密的網(wǎng)絡(luò)連接以及文件IO進(jìn)行透明的連接路媚。 
      //BIO更多信息:https://blog.csdn.net/liao20081228/article/details/77193729/

      OpenSSL_add_all_algorithms();
      SSLeay_add_all_algorithms();
      ERR_load_crypto_strings();
    
      bio = BIO_new_file([filePath UTF8String], "r");
      p12 = d2i_PKCS12_bio(bio, NULL); //得到p12結(jié)構(gòu)
      PKCS12_parse(p12, [password UTF8String], &pkey, &usrCert, &ca); //得到x509結(jié)構(gòu)

      //序列號
      NSString *serialNumber = [self __getSerialNumberString:usrCert];
      //證書擁有者信息
      NSString *userName = [self __getUserName:usrCert];
      //頒發(fā)機(jī)構(gòu)
      NSString *issuserInfo = [self __getIssuserInfo:usrCert];
      //生效時間
      NSDate *beginDate = [self __getSignBeginDate:usrCert];
      //過期時間
      NSDate *expiryDate = [self __getExpiryDate:usrCert];
      //SHA-1
      NSString *sha1 = [self __sha1:usrCert];
      //版本號
      NSString *version = [self __getVersion:usrCert];

      BIO_free_all(bio);
      PKCS12_free(p12);
      EVP_PKEY_free(pkey);
      X509_free(usrCert);
}
//版本號
- (long)__getVersion:(X509 *)cert {
    long version = X509_get_version(cert);
    return version;
}
///序列號
- (NSString *)__getSerialNumberString:(X509*)usrCert {
    ASN1_INTEGER *serial = X509_get_serialNumber(usrCert);
    BIGNUM *bignum = ASN1_INTEGER_to_BN(serial, NULL);
    char *res = BN_bn2hex(bignum);
    NSString *serialStr = [NSString stringWithUTF8String:res];
    return serialStr;
}

///證書擁有者信息
- (NSString *)__getUserName:(X509 *)usrCert {
    char szOutCN[256]={0};
    X509_NAME *name = NULL;
    name = usrCert->cert_info->subject;
    X509_NAME_get_text_by_NID(name,NID_commonName,szOutCN,256);
    NSString *nameStr = [NSString stringWithUTF8String:szOutCN];
    return nameStr;
}

///頒發(fā)機(jī)構(gòu)
- (NSString *)__getIssuserInfo:(X509 *)usrCert {
    X509_NAME_ENTRY *name_entry;
    long Nid;
    unsigned char msginfo[1024];
    int msginfoLen;
    
    NSMutableString *issuerInfo = [[NSMutableString alloc] init];
    NSMutableString *certCN = [[NSMutableString alloc] init];
    
    X509_NAME *issuer = X509_get_issuer_name(usrCert);
    int entriesNum = sk_X509_NAME_ENTRY_num(issuer->entries);
    for (int i = 0; i < entriesNum; i++) {
        name_entry = sk_X509_NAME_ENTRY_value(issuer->entries, i);
        Nid = OBJ_obj2nid(name_entry->object);
        msginfoLen = name_entry->value->length;
        memcpy(msginfo,name_entry->value->data,msginfoLen);
        msginfo[msginfoLen]='\0';
        switch(Nid) {
            case NID_countryName://國家C
                [issuerInfo appendString:[NSString stringWithFormat:@"C=%s,",msginfo]];
                [certCN appendString:[NSString stringWithFormat:@"C=%s",msginfo]];
                self.info.country = [NSString stringWithFormat:@"%s",msginfo];
                break;
                
            case NID_stateOrProvinceName://省ST
                [issuerInfo appendString:[NSString stringWithFormat:@"ST=%s,",msginfo]];
                self.info.province = [NSString stringWithFormat:@"%s",msginfo];
                break;
                
            case NID_localityName://地區(qū)L
                [issuerInfo appendString:[NSString stringWithFormat:@"L=%s,",msginfo]];
                self.info.region = [NSString stringWithFormat:@"%s",msginfo];
                break;
                
            case NID_organizationName://組織O=
                [issuerInfo appendString:[NSString stringWithFormat:@"O=%s,",msginfo]];
                self.info.organization = [NSString stringWithFormat:@"%s",msginfo];
                break;
                
            case NID_organizationalUnitName://單位OU
                [issuerInfo appendString:[NSString stringWithFormat:@"OU=%s,",msginfo]];
                self.info.unit = [NSString stringWithFormat:@"%s",msginfo];
                break;
                
            case NID_commonName://通用名CN
                [issuerInfo appendString:[NSString stringWithFormat:@"CN=%s",msginfo]];
                self.info.commonName = [NSString stringWithFormat:@"%s",msginfo];
                break;
                
            case NID_pkcs9_emailAddress://Mail
                break;
        }
    }
    return issuerInfo;
}

///開始頒發(fā)的時間
- (NSDate *)__getSignBeginDate:(X509 *)cert {
    ASN1_TIME *start = NULL;
    time_t ttStart = {0};
    // 頒發(fā)時間
    start = X509_get_notBefore(cert);
    ttStart = [self __skf_ext_ASN1_GetTimeT:start];
    // 格林威治時間與北京時間相差八小時黄琼,所以加八小時。
    ttStart = ttStart + 8 * 60 * 60;
    NSDate *startDate = [NSDate dateWithTimeIntervalSince1970:ttStart];
    return startDate;
}

///有效時間
- (NSDate *)__getExpiryDate:(X509 *)cert {
    ASN1_TIME *end = NULL;
    time_t ttEnd = {0};
    
    // 過期時間
    end = X509_get_notAfter(cert);
    ttEnd = [self __skf_ext_ASN1_GetTimeT:end];
    ttEnd = ttEnd + 8 * 60 * 60;
    NSDate *endDate = [NSDate dateWithTimeIntervalSince1970:ttEnd];
    return endDate;
}

///格林威治時間轉(zhuǎn)本地時間
- (time_t)__skf_ext_ASN1_GetTimeT:(ASN1_TIME *)time {
    struct tm t;
    const char* str = (const char*) time->data;
    size_t i = 0;
    
    memset(&t, 0, sizeof(t));
    
    if (time->type == V_ASN1_UTCTIME) {/* two digit year */
        t.tm_year = (str[i++] - '0') * 10;
        t.tm_year += (str[i++] - '0');
        if (t.tm_year < 70)
            t.tm_year += 100;
    } else if (time->type == V_ASN1_GENERALIZEDTIME) {/* four digit year */
        t.tm_year = (str[i++] - '0') * 1000;
        t.tm_year+= (str[i++] - '0') * 100;
        t.tm_year+= (str[i++] - '0') * 10;
        t.tm_year+= (str[i++] - '0');
        t.tm_year -= 1900;
    }
    t.tm_mon  = (str[i++] - '0') * 10;
    t.tm_mon += (str[i++] - '0') - 1; // -1 since January is 0 not 1.
    t.tm_mday = (str[i++] - '0') * 10;
    t.tm_mday+= (str[i++] - '0');
    t.tm_hour = (str[i++] - '0') * 10;
    t.tm_hour+= (str[i++] - '0');
    t.tm_min  = (str[i++] - '0') * 10;
    t.tm_min += (str[i++] - '0');
    t.tm_sec  = (str[i++] - '0') * 10;
    t.tm_sec += (str[i++] - '0');
    
    /* Note: we did not adjust the time based on time zone information */
    return mktime(&t);
}
//指紋信息
- (NSString*) __sha1:(X509 *)cert {
    PKCS12_SAFEBAG *safeBag = PKCS12_x5092certbag(cert);
    NSData *certData = [NSData dataWithBytes: safeBag->value.bag->value.x509cert->data   length:safeBag->value.bag->value.x509cert->length];
    
    unsigned char sha1Buffer[CC_SHA1_DIGEST_LENGTH];
    CC_SHA1(certData.bytes, (unsigned int)certData.length, sha1Buffer);
    NSMutableString *fingerprint = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
    for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; ++i){
        [fingerprint appendFormat:@"%02x ",sha1Buffer[i]];
    }
    NSString *fingerString = [fingerprint stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
    fingerString = [fingerString stringByReplacingOccurrencesOfString:@" " withString:@""];
    PKCS12_SAFEBAG_free(safeBag);
    return fingerString.uppercaseString;
}

備注:想要獲取更多信息可以去X509對象中獲取整慎。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末脏款,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子裤园,更是在濱河造成了極大的恐慌撤师,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拧揽,死亡現(xiàn)場離奇詭異剃盾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)淤袜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門痒谴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人铡羡,你說我怎么就攤上這事积蔚。” “怎么了烦周?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵尽爆,是天一觀的道長。 經(jīng)常有香客問我论矾,道長教翩,這世上最難降的妖魔是什么杆勇? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任贪壳,我火速辦了婚禮,結(jié)果婚禮上蚜退,老公的妹妹穿的比我還像新娘闰靴。我一直安慰自己,他們只是感情好钻注,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布蚂且。 她就那樣靜靜地躺著,像睡著了一般幅恋。 火紅的嫁衣襯著肌膚如雪杏死。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機(jī)與錄音淑翼,去河邊找鬼腐巢。 笑死,一個胖子當(dāng)著我的面吹牛玄括,可吹牛的內(nèi)容都是我干的冯丙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼遭京,長吁一口氣:“原來是場噩夢啊……” “哼胃惜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起哪雕,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤船殉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后斯嚎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捺弦,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年孝扛,在試婚紗的時候發(fā)現(xiàn)自己被綠了列吼。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡苦始,死狀恐怖寞钥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情陌选,我是刑警寧澤理郑,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站咨油,受9級特大地震影響您炉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜役电,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一赚爵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧法瑟,春花似錦冀膝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至酥夭,卻和暖如春赐纱,著一層夾襖步出監(jiān)牢的瞬間脊奋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工疙描, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留狂魔,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓淫痰,卻偏偏與公主長得像最楷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子待错,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評論 2 354