idTool.cpp   [plain text]


/*
 * Examine and test a keychain's identity
 */
#include <Security/Security.h>
#include <stdlib.h>
#include <stdio.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include <security_cdsa_utils/cuPrintCert.h>
#include <utilLib/common.h>
#include <utilLib/cspwrap.h>
#include <clAppUtils/clutils.h>

typedef enum {
	KC_Nop,
	KC_GetInfo,
	KC_LockKC,
	KC_UnlockKC,
	KC_SignVfy,
	KC_KeyCertInfo
} KcOp;

static void usage(char **argv)
{
	printf("Usage: %s keychain|- cmd [options]\n", argv[0]);
	printf("Command:\n");
	printf("  i  get KC info\n");
	printf("  k  get key and cert info\n");
	printf("  l  lock\n");
	printf("  u  unlock\n");
	printf("  s  sign and verify\n");
	printf("Options:\n");
	printf("  p=passphrase\n");
	printf("Specifying '-' for keychain means NULL, default\n");
	exit(1);
}

static void showError(
	OSStatus ortn,
	const char *msg)
{
	const char *errStr = NULL;
	switch(ortn) {
		case errSecItemNotFound:
			errStr = "errSecItemNotFound"; break;
		case errSecNoSuchKeychain:
			errStr = "errSecNoSuchKeychain"; break;
		case errSecNotAvailable:
			errStr = "errSecNotAvailable"; break;
		/* more? */
		default:
			if(ortn < (CSSM_BASE_ERROR + 
					(CSSM_ERRORCODE_MODULE_EXTENT * 8))) {
				/* assume CSSM error */
				errStr = cssmErrToStr(ortn);
			}
			break;

	}
	if(errStr) {
		printf("***Error on %s: %s\n", msg, errStr);
	}
	else {
		printf("***Error on %s: %d(d)\n", msg, (int)ortn);
	}
}

static void printDataAsHex(
	const CSSM_DATA *d,
	unsigned maxToPrint = 0)		// optional, 0 means print it all
{
	unsigned i;
	bool more = false;
	uint32 len = d->Length;
	uint8 *cp = d->Data;
	
	if((maxToPrint != 0) && (len > maxToPrint)) {
		len = maxToPrint;
		more = true;
	}	
	for(i=0; i<len; i++) {
		printf("%02X ", ((unsigned char *)cp)[i]);
	}
	if(more) {
		printf("...\n");
	}
	else {
		printf("\n");
	}
}

static void printKeyHeader(
	const CSSM_KEYHEADER &hdr)
{
	printf("   Algorithm       : ");
	switch(hdr.AlgorithmId) {
		case CSSM_ALGID_RSA:
			printf("RSA\n");
			break;
		case CSSM_ALGID_DSA:
			printf("DSA\n");
			break;
		case CSSM_ALGID_FEE:
			printf("FEE\n");
			break;
		case CSSM_ALGID_DH:
			printf("Diffie-Hellman\n");
			break;
		default:
			printf("Unknown(%u(d), 0x%x)\n", (unsigned)hdr.AlgorithmId, 
				(unsigned)hdr.AlgorithmId);
	}
	printf("   Key Size        : %u bits\n", 
		(unsigned)hdr.LogicalKeySizeInBits);
	printf("   Key Use         : ");
	CSSM_KEYUSE usage = hdr.KeyUsage;
	if(usage & CSSM_KEYUSE_ANY) {
		printf("CSSM_KEYUSE_ANY ");
	}
	if(usage & CSSM_KEYUSE_ENCRYPT) {
		printf("CSSM_KEYUSE_ENCRYPT ");
	}
	if(usage & CSSM_KEYUSE_DECRYPT) {
		printf("CSSM_KEYUSE_DECRYPT ");
	}
	if(usage & CSSM_KEYUSE_SIGN) {
		printf("CSSM_KEYUSE_SIGN ");
	}
	if(usage & CSSM_KEYUSE_VERIFY) {
		printf("CSSM_KEYUSE_VERIFY ");
	}
	if(usage & CSSM_KEYUSE_SIGN_RECOVER) {
		printf("CSSM_KEYUSE_SIGN_RECOVER ");
	}
	if(usage & CSSM_KEYUSE_VERIFY_RECOVER) {
		printf("CSSM_KEYUSE_VERIFY_RECOVER ");
	}
	if(usage & CSSM_KEYUSE_WRAP) {
		printf("CSSM_KEYUSE_WRAP ");
	}
	if(usage & CSSM_KEYUSE_UNWRAP) {
		printf("CSSM_KEYUSE_UNWRAP ");
	}
	if(usage & CSSM_KEYUSE_DERIVE) {
		printf("CSSM_KEYUSE_DERIVE ");
	}
	printf("\n");

}

static OSStatus getIdentity(
	SecKeychainRef kcRef,
	CSSM_KEYUSE keyUse,
	SecIdentityRef &idRef)
{
	SecIdentitySearchRef srchRef = nil;
	OSStatus ortn = SecIdentitySearchCreate(kcRef, keyUse, &srchRef);
	if(ortn) {
		showError(ortn, "SecIdentitySearchCreate");
		return ortn;
	}
	ortn = SecIdentitySearchCopyNext(srchRef, &idRef);
	if(ortn) {
		showError(ortn, "SecIdentitySearchCopyNext");
		return ortn;
	}
	if(CFGetTypeID(idRef) != SecIdentityGetTypeID()) {
		printf("SecIdentitySearchCopyNext CFTypeID failure!\n");
		return paramErr;
	}
	return noErr;
}

static OSStatus getKeyCertInfo(
	SecCertificateRef certRef,
	SecKeyRef keyRef,
	CSSM_KEY_PTR cssmKey,
	CSSM_CSP_HANDLE cspHand)
{
	/* display the private key */
	if(cssmKey == NULL) {
		printf("   ***malformed CSSM_KEY\n");
	}
	else {
		printf("Private Key        :\n");
		printKeyHeader(cssmKey->KeyHeader);
		printf("   Key Blob        : ");
		printDataAsHex(&cssmKey->KeyData, 8);
	}

	/* and the cert */
	CSSM_DATA certData;
	OSStatus ortn = SecCertificateGetData(certRef, &certData);
	if(ortn) {
		showError(ortn, "SecCertificateGetData");
		return ortn;
	}
	printf("\nCertificate        :\n");
	printCert((unsigned char *)certData.Data, (unsigned)certData.Length,
		CSSM_TRUE);
	return noErr;
}

#define SIG_ALG		CSSM_ALGID_SHA1WithRSA

static OSStatus signVfy(
	SecCertificateRef certRef,
	SecKeyRef keyRef,
	CSSM_KEY_PTR cssmKey,
	CSSM_CSP_HANDLE cspHand)
{
	uint8 someData[] = {0,1,2,3,4,5,6,7,8};
	CSSM_DATA ptext = {sizeof(someData), someData};
	CSSM_DATA sig = {0, NULL};
	CSSM_RETURN crtn;
	
	/* sign with CSPDL */
	crtn = cspSign(cspHand, SIG_ALG, cssmKey, &ptext, &sig);
	if(crtn) {
		printf("Error signing with private key\n");
		return crtn;
	}
	
	/* attach to CL */
	CSSM_CL_HANDLE clHand = clStartup();
	if(clHand == 0) {
		printf("***Error attaching to CL\n");
		return ioErr;
	}
	
	/* get the public key from the cert */
	CSSM_DATA certData;
	OSStatus ortn = SecCertificateGetData(certRef, &certData);
	if(ortn) {
		showError(ortn, "SecCertificateGetData");
		return ortn;
	}
	CSSM_KEY_PTR pubKey = NULL;
	crtn = CSSM_CL_CertGetKeyInfo(clHand, &certData, &pubKey);
	if(crtn) {
		printError("CSSM_CL_CertGetKeyInfo", crtn);
		return crtn;
	}
	
	/* attach to raw CSP */
	CSSM_CSP_HANDLE rawCspHand = cspStartup();
	if(rawCspHand == 0) {
		printf("***Error attaching to raw CSP\n");
		return ioErr;
	}
	
	/* verify with raw CSP and raw public key */
	crtn = cspSigVerify(rawCspHand, SIG_ALG, pubKey, &ptext, 
		&sig, CSSM_OK);
	if(crtn) {
		printf("Error verifying with public key\n");
		return crtn;
	}
	
	/* free everything */
	CSSM_ModuleDetach(rawCspHand);
	CSSM_ModuleDetach(clHand);
	printf("...sign with private key, vfy with cert OK\n");
	return noErr;
}

/* get cert and private key (in Sec and CSSM form) from identity */
static OSStatus getKeyCert(
	SecIdentityRef 		idRef,
	SecCertificateRef	&certRef,		// RETURNED
	SecKeyRef			&keyRef,		// private key, RETURNED
	CSSM_KEY_PTR		&cssmKey)		// private key, RETURNED
{
	OSStatus ortn = SecIdentityCopyCertificate(idRef, &certRef);
	if(ortn) {
		showError(ortn, "SecIdentityCopyCertificate");
		return ortn;
	}
	ortn = SecIdentityCopyPrivateKey(idRef, &keyRef);
	if(ortn) {
		showError(ortn, "SecIdentityCopyPrivateKey");
		return ortn;
	}
	ortn = SecKeyGetCSSMKey(keyRef, (const CSSM_KEY **)&cssmKey);
	if(ortn) {
		showError(ortn, "SecKeyGetCSSMKey");
	}
	return ortn;	
}

int main(int argc, char **argv)
{
	SecKeychainRef 		kcRef = nil;
	OSStatus 			ortn;
	int					arg;
	char				*argp;
	
	/* user-spec'd variables */
	KcOp				op = KC_Nop;
	char 				*pwd = NULL;
	char				*kcName;
	
	if(argc < 3) {
		usage(argv);
	}
	kcName = argv[1];
	if(!strcmp("-", kcName)) {
		/* null - no open */
		kcName = NULL;
	}
	switch(argv[2][0]) {
		case 'i':
			op = KC_GetInfo; break;
		case 'l':
			op = KC_LockKC; break;
		case 'u':
			op = KC_UnlockKC; break;
		case 's':
			op = KC_SignVfy; break;
		case 'k':
			op = KC_KeyCertInfo; break;
		default:
			usage(argv);
	}
	for(arg=3; arg<argc; arg++) {
		argp = argv[arg];
		switch(argp[0]) {
			case 'p':
				pwd = &argp[2];
				break;
			default:
				usage(argv);
		}
	}
	
	if(kcName != NULL) {
		ortn = SecKeychainOpen(kcName, &kcRef);
		if(ortn) {
			showError(ortn, "SecKeychainOpen");
			printf("Cannot open keychain at %s. Aborting.\n", kcName);
			exit(1);
		}
	}
	
	/* handle trivial commands right now */
	switch(op) {
		case KC_LockKC:
			ortn = SecKeychainLock(kcRef);
			if(ortn) {
				showError(ortn, "SecKeychainLock");
				exit(1);				
			}
			printf("...keychain %s locked.\n", argv[1]);
			exit(0);
			
		case KC_UnlockKC:
			if(pwd == NULL) {
				printf("***Warning: unlocking with no password\n");
			}
			ortn = SecKeychainUnlock(kcRef,
				pwd ? strlen(pwd) : 0,
				pwd,
				pwd ? true : false);		// usePassword
			if(ortn) {
				showError(ortn, "SecKeychainUnlock");
				exit(1);	
			}
			printf("...keychain %s unlocked.\n", argv[1]);
			exit(0);

		case KC_GetInfo:
		{
			SecKeychainStatus kcStat;
			ortn = SecKeychainGetStatus(kcRef, &kcStat);
			if(ortn) {
				showError(ortn, "SecKeychainGetStatus");
				exit(1);				
			}
			printf("...SecKeychainStatus = %u ( ", (unsigned)kcStat);
			if(kcStat & kSecUnlockStateStatus) {
				printf("UnlockState ");
			}
			if(kcStat & kSecReadPermStatus) {
				printf("RdPerm ");
			}
			if(kcStat & kSecWritePermStatus) {
				printf("WrPerm ");
			}
			printf(")\n");
			exit(0);
		}
		
		default:
			/* more processing below */
			break;
	}

	/* remaining cmds need an identity */
	SecIdentityRef idRef;
	ortn = getIdentity(kcRef, CSSM_KEYUSE_SIGN, idRef);
	if(ortn) {
		printf("***No identity found in keychain %s. Aborting.\n", kcName);
		exit(1);
	}
	
	/* and a CSP */
	CSSM_CSP_HANDLE cspHand;
	ortn = SecKeychainGetCSPHandle(kcRef, &cspHand);
	if(ortn) {
		showError(ortn, "SecKeychainGetCSPHandle");
		exit(1);
	}

	/* and the cert and keys */
	SecCertificateRef certRef = nil;
	SecKeyRef keyRef = nil;
	CSSM_KEY_PTR privKey = NULL;
	ortn = getKeyCert(idRef, certRef, keyRef, privKey);
	if(ortn) {
		printf("***Incomplete identity\n");
		exit(1);
	}
	
	switch(op) {
		case KC_KeyCertInfo:
			ortn = getKeyCertInfo(certRef, keyRef, privKey, cspHand);
			break;
		case KC_SignVfy:
			ortn = signVfy(certRef, keyRef, privKey, cspHand);
			break;
		default:
			printf("BRRRZAP!\n");
			exit(1);
	}
	CFRelease(idRef);
	if(kcRef) {
		CFRelease(kcRef);
	}
	return (int)ortn;
}