The CertBlob Utility source of the CertBlob class.
#import "CertBlobUtility.h" #import <CommonCrypto/CommonDigest.h> bool getAsn1LengthBytes( int iLengthVal, // (IN) value to be encoded unsigned char* pbOut, // (IN/OUT) buffer to be populated with the encoding or NULL to get sizing information int *iOutLen // (IN/OUT) if pbOut != NULL, size of pbOut buffer in allocated bytes. Is set to the number // of bytes required/written in the encoding on return. ); bool makeCertBlob( unsigned char* pbCert, // Certificate to be encoded in the CertBlob int iCertLen, // Length in bytes of pbCert unsigned char* pbSig, // Signature to be encoded in the CertBlob int iSigLen, // Length in bytes of pbSig unsigned char byteAlgorithm, // Algorithm constant to be encoded in the CertBlob unsigned char* pbOut, // (IN/OUT) buffer to be populated with the encoding or NULL to get sizing information int *iOutLen // (IN/OUT) if pbOut != NULL, size of pbOut buffer in allocated bytes. Is set to the number // of bytes required/written in the encoding on return. ); bool getAsn1LengthBytes( int iLengthVal, // (IN) value to be encoded unsigned char* pbOut, // (IN/OUT) buffer to be populated with the encoding or NULL to get sizing information int *iOutLen // (IN/OUT) if pbOut != NULL, size of pbOut buffer in allocated bytes. Is set to the number // of bytes required/written in the encoding on return. ) { // simple short form length if ( iLengthVal < 0x80 ) { if ( ( pbOut != NULL ) && ( *iOutLen < 1 ) ) return false; *iOutLen = 1; if ( pbOut != NULL ) *pbOut = (unsigned char) iLengthVal; return true; } // if we got here, we need long form, because the short form doesn't fit in a single byte // count the number of bytes in iVal int iTmp = iLengthVal; int iCount = 0; iTmp = iLengthVal; unsigned char byteLast = 0; while ( iTmp != 0 ) { iCount++; byteLast = (unsigned char) ( iTmp & 0xFF ); iTmp >>= 8; } // case where caller wants to know how to size buffer if ( NULL == pbOut ) { *iOutLen = iCount + 1; // +1 for the length byte return true; } if ( *iOutLen < iCount + 1 ) return false; *iOutLen = iCount + 1; // +1 for the length byte // Create an array with the count of bytes, followed by the iVal bytes // Setting the top bit of the count indicates that this is a count with the value to follow, not the actual integer value pbOut[ 0 ] = (unsigned char) ( iCount | 0x80 ); // count iTmp = iLengthVal; while ( iTmp != 0 ) { unsigned char b = (unsigned char) ( iTmp & 0xFF ); iTmp >>= 8; pbOut[ iCount-- ] = b; } return true; } // makeCertBlob "C" function used by SSOCertManager makeCertBlob method below /* * Returns a buffer containing an ASN.1 encoding for a CertBlob. * Upon return, pbOut will be filled with the result and * iOutLen will contain the number of bytes written. If this * function is called with NULL as the pbOut pointer, it will * populate iOutLen without writing anything. The expected usage * is to call with pbOut==NULL to size the buffer, allocate the buffer, * then call it again with the newly allocated buffer. * * Return value of false is if pbOut!=NULL and the passed in iOutLen * is less than the required number of bytes to write the result. * */ bool makeCertBlob( unsigned char* pbCert, // Certificate to be encoded in the CertBlob int iCertLen, // Length in bytes of pbCert unsigned char* pbSig, // Signature to be encoded in the CertBlob int iSigLen, // Length in bytes of pbSig unsigned char byteAlgorithm, // Algorithm constant to be encoded in the CertBlob unsigned char* pbOut, // (IN/OUT) buffer to be populated with the encoding or NULL to get sizing information int *iOutLen // (IN/OUT) if pbOut != NULL, size of pbOut buffer in allocated bytes. Is set to the number // of bytes required/written in the encoding on return. ) { int iCertLenLen, iSigLenLen; int iAlgorithmLen = 2; // get number of bytes in length descriptors if ( !getAsn1LengthBytes( iCertLen, NULL, &iCertLenLen ) ) return false; if ( !getAsn1LengthBytes( iSigLen, NULL, &iSigLenLen ) ) return false; // calculate size of content of sequence int iSeqLen = 1 + // type code for OCTET STRING iCertLenLen + // length bytes for Certificate iCertLen + // data bytes for Certificate 1 + // type code for OCTET STRING iSigLenLen + // length bytes for Signature iSigLen + // data bytes for Signature 1 + // type code for INTEGER iAlgorithmLen; // data bytes for algorithm (assumed to be an integer that fits in a single byte) // now calculate size of outer sequence int iSeqLenLen; if ( !getAsn1LengthBytes( iSeqLen, NULL, &iSeqLenLen ) ) return false; int iTotalLen = 1 + // type code for SEQUENCE iSeqLenLen + // length bytes for Sequence iSeqLen; // data bytes for Sequence if ( NULL == pbOut ) { // caller is just asking for required buffer size *iOutLen = iTotalLen; return true; } // test whether buffer is large enough if ( *iOutLen < iTotalLen ) return false; // write everything to the buffer int iCurIdx = 0; // header bytes for wrapping sequence pbOut[ iCurIdx++ ] = (unsigned char) 0x30; // type code for SEQUENCE if ( !getAsn1LengthBytes( iSeqLen, pbOut + iCurIdx, &iSeqLenLen ) ) // length bytes for Sequence return false; iCurIdx += iSeqLenLen; // first element of sequence -> certificate pbOut[ iCurIdx++ ] = (unsigned char) 0x04; // type code for OCTET STRING if ( !getAsn1LengthBytes( iCertLen, pbOut + iCurIdx, &iCertLenLen ) ) // length bytes for Certificate return false; iCurIdx += iCertLenLen; memcpy( pbOut + iCurIdx, pbCert, iCertLen ); // bytes for Certificate iCurIdx += iCertLen; // second element of sequence -> signature pbOut[ iCurIdx++ ] = (unsigned char) 0x04; // type code for OCTET STRING if ( !getAsn1LengthBytes( iSigLen, pbOut + iCurIdx, &iSigLenLen ) ) // length bytes for Certificate return false; iCurIdx += iSigLenLen; memcpy( pbOut + iCurIdx, pbSig, iSigLen ); // bytes for Certificate iCurIdx += iSigLen; // third element of sequence -> algorithm pbOut[ iCurIdx++ ] = (unsigned char) 0x02; // type code for INTEGER pbOut[ iCurIdx++ ] = (unsigned char) 0x01; // length bytes for value (assume 1) pbOut[ iCurIdx++ ] = byteAlgorithm; // algorithm constant return true; } @implementation CertBlobUtility + (NSString*)md5sum:(NSData*)certData { CC_MD5_CTX md5; CC_MD5_Init(&md5); CC_MD5_Update(&md5, [certData bytes], [certData length]); unsigned char digest[CC_MD5_DIGEST_LENGTH]; CC_MD5_Final(digest, &md5); NSString* s = [NSString stringWithFormat: @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7], digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]]; return s; } + (NSString*)sha1:(NSData*)certData { unsigned char sha1Buffer[CC_SHA1_DIGEST_LENGTH]; CC_SHA1(certData.bytes, certData.length, sha1Buffer); NSMutableString *fingerprint = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 3]; for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; ++i) [fingerprint appendFormat:@"%02x ",sha1Buffer[i]]; return [fingerprint stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; } // SSOCertManager makeCertBlob: used by getCertBlob: API below // Makes a certBlob from given certificate and private key and returns it + (NSData *)makeCertBlob:(SecCertificateRef)certificate andPrivateKey:(SecKeyRef)privateKey { NSData *sigData; NSData *certData; CFDataRef certCFData = SecCertificateCopyData(certificate); unsigned char certDigest[CC_SHA1_DIGEST_LENGTH]; CC_SHA1( CFDataGetBytePtr(certCFData), CFDataGetLength(certCFData), certDigest ); certData = [NSData dataWithBytes:CFDataGetBytePtr(certCFData) length:CFDataGetLength(certCFData)]; size_t sigLen = 1024; uint8_t sigBuf[sigLen]; // Encrypt the digest of the certificate with private key OSStatus err = SecKeyRawSign(privateKey, kSecPaddingPKCS1, certDigest, CC_SHA1_DIGEST_LENGTH, //data.bytes, data.length, sigBuf, &sigLen); if (err == noErr) { sigData = [NSData dataWithBytes:sigBuf length:sigLen]; } if ( certCFData != NULL ) CFRelease(certCFData); if ( ( certData == nil ) || ( sigData == nil ) ) return nil; int iLength = 0; if ( ( !makeCertBlob( (unsigned char *)[certData bytes], [certData length], (unsigned char *)[sigData bytes], [sigData length], 1, NULL, &iLength ) ) || ( iLength == 0 ) ) return nil; unsigned char* pBuf = (unsigned char*)malloc(iLength); if ( !makeCertBlob( (unsigned char *)[certData bytes], [certData length], (unsigned char *)[sigData bytes], [sigData length], 1, pBuf, &iLength ) ) { free( pBuf ); return nil; } NSData* certBlob = [NSData dataWithBytes:pBuf length:iLength]; free( pBuf ); return certBlob; } @end