CertBlobUtility Source

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