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