int main(int argc, char **argv)
{
	bool verbose = false;
	
	int arg;
	while ((arg = getopt(argc, argv, "vh")) != -1) {
		switch (arg) {
			case 'v':
				verbose = true;
				break;
			case 'h':
				usage(argv);
		}
	}
	if(optind != argc) {
		usage(argv);
	}
	
	printNoDialog();
	
	/* initial setup */
	verboseDisp(verbose, "deleting keychain");
	unlink(KEYCHAIN_NAME);
	
	verboseDisp(verbose, "creating keychain");
	SecKeychainRef kcRef = NULL;
	OSStatus ortn = SecKeychainCreate(KEYCHAIN_NAME, 
		strlen(KEYCHAIN_PWD), KEYCHAIN_PWD,
		false, NULL, &kcRef);
	if(ortn) {
		cssmPerror("SecKeychainCreate", ortn);
		exit(1);
	}

	/* 
	 * 1. Generate key pair with cleartext public key.
	 *    Ensure we can use the public key when keychain is locked with no
	 *    user interaction.  
	 */
	 
	/* generate key pair, cleartext public key */
	verboseDisp(verbose, "creating key pair, cleartext public key");
	SecKeyRef pubKeyRef = NULL;
	SecKeyRef privKeyRef = NULL;
	if(genKeyPair(false, kcRef, &pubKeyRef, &privKeyRef)) {
		exit(1);
	}
	
	/* Use generated cleartext public key with locked keychain */
	verboseDisp(verbose, "locking keychain, exporting public key");
	SecKeychainLock(kcRef);
	CFDataRef exportData = NULL;
	ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData);
	if(ortn) {
		cssmPerror("SecKeychainCreate", ortn);
		exit(1);
	}
	CFRelease(exportData);
	
	verboseDisp(verbose, "locking keychain, encrypting with public key");
	SecKeychainLock(kcRef);
	if(pubKeyEncrypt(pubKeyRef)) {
		exit(1);
	}

	/* reset */
	verboseDisp(verbose, "deleting keys");
	ortn = SecKeychainItemDelete((SecKeychainItemRef)pubKeyRef);
	if(ortn) {
		cssmPerror("SecKeychainItemDelete", ortn);
		exit(1);
	}
	ortn = SecKeychainItemDelete((SecKeychainItemRef)privKeyRef);
	if(ortn) {
		cssmPerror("SecKeychainItemDelete", ortn);
		exit(1);
	}
	CFRelease(pubKeyRef);
	CFRelease(privKeyRef);

	/* 
	 * 2. Generate key pair with encrypted public key.
	 *    Ensure that user interaction is required when we use the public key 
	 *    when keychain is locked.
	 */

	verboseDisp(verbose, "programmatically unlocking keychain");
	ortn = SecKeychainUnlock(kcRef, strlen(KEYCHAIN_PWD), KEYCHAIN_PWD, TRUE);
	if(ortn) {
		cssmPerror("SecKeychainItemDelete", ortn);
		exit(1);
	}
	
	/* generate key pair, encrypted public key */
	verboseDisp(verbose, "creating key pair, encrypted public key");
	if(genKeyPair(true, kcRef, &pubKeyRef, &privKeyRef)) {
		exit(1);
	}

	/* Use generated encrypted public key with locked keychain */
	verboseDisp(verbose, "locking keychain, exporting public key");
	SecKeychainLock(kcRef);
	printExpectDialog();
	ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData);
	if(ortn) {
		cssmPerror("SecKeychainCreate", ortn);
		exit(1);
	}
	/* we'll use that exported blob later to test import */
	if(!didGetDialog()) {
		exit(1);
	}
	
	verboseDisp(verbose, "locking keychain, encrypting with public key");
	SecKeychainLock(kcRef);
	printExpectDialog();
	if(pubKeyEncrypt(pubKeyRef)) {
		exit(1);
	}
	if(!didGetDialog()) {
		exit(1);
	}

	/* reset */
	printNoDialog();
	verboseDisp(verbose, "locking keychain");
	SecKeychainLock(kcRef);
	verboseDisp(verbose, "deleting keys");
	ortn = SecKeychainItemDelete((SecKeychainItemRef)pubKeyRef);
	if(ortn) {
		cssmPerror("SecKeychainItemDelete", ortn);
		exit(1);
	}
	ortn = SecKeychainItemDelete((SecKeychainItemRef)privKeyRef);
	if(ortn) {
		cssmPerror("SecKeychainItemDelete", ortn);
		exit(1);
	}
	CFRelease(pubKeyRef);
	CFRelease(privKeyRef);

	/* 
	 * 3. Import public key, storing in cleartext. Ensure that the import
	 *    doesn't require unlock, and ensure we can use the public key 
	 *    when keychain is locked with no user interaction.  
	 */

	printNoDialog();
	verboseDisp(verbose, "locking keychain");
	SecKeychainLock(kcRef);

	/* import public key - default is in the clear */
	verboseDisp(verbose, "importing public key, store in the clear (default)");
	CFArrayRef outArray = NULL;
	SecExternalFormat format = kSecFormatOpenSSL;
	SecExternalItemType type = kSecItemTypePublicKey;
	ortn = SecKeychainItemImport(exportData, 
		NULL, &format, &type,
		0, NULL,
		kcRef, &outArray);
	if(ortn) {
		cssmPerror("SecKeychainItemImport", ortn);
		exit(1);
	}
	CFRelease(exportData);
	if(CFArrayGetCount(outArray) != 1) {
		printf("***Unexpected outArray size (%ld) after import\n",
			(long)CFArrayGetCount(outArray));
		exit(1);
	}
	pubKeyRef = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0);
	if(CFGetTypeID(pubKeyRef) != SecKeyGetTypeID()) {
		printf("***Unexpected item type after import\n");
		exit(1);
	}
	
	/* Use imported cleartext public key with locked keychain */
	verboseDisp(verbose, "locking keychain, exporting public key");
	SecKeychainLock(kcRef);
	exportData = NULL;
	ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData);
	if(ortn) {
		cssmPerror("SecKeychainItemExport", ortn);
		exit(1);
	}
	/* we'll use exportData again */
	
	verboseDisp(verbose, "locking keychain, encrypting with public key");
	SecKeychainLock(kcRef);
	if(pubKeyEncrypt(pubKeyRef)) {
		exit(1);
	}

	/* reset */
	verboseDisp(verbose, "deleting key");
	ortn = SecKeychainItemDelete((SecKeychainItemRef)pubKeyRef);
	if(ortn) {
		cssmPerror("SecKeychainItemDelete", ortn);
		exit(1);
	}
	CFRelease(pubKeyRef);
	
	/* 
	 * Import public key, storing in encrypted form.
	 * Ensure that user interaction is required when we use the public key 
	 * when keychain is locked.
	 */

	/* import public key, encrypted in the keychain */
	SecKeyImportExportParameters impExpParams;
	memset(&impExpParams, 0, sizeof(impExpParams));
	impExpParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
	impExpParams.keyAttributes = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE | 
								 CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT;
	verboseDisp(verbose, "importing public key, store encrypted");
	printExpectDialog();
	outArray = NULL;
	format = kSecFormatOpenSSL;
	type = kSecItemTypePublicKey;
	ortn = SecKeychainItemImport(exportData, 
		NULL, &format, &type,
		0, &impExpParams,
		kcRef, &outArray);
	if(ortn) {
		cssmPerror("SecKeychainItemImport", ortn);
		exit(1);
	}
	if(!didGetDialog()) {
		exit(1);
	}
	CFRelease(exportData);
	if(CFArrayGetCount(outArray) != 1) {
		printf("***Unexpected outArray size (%ld) after import\n",
			(long)CFArrayGetCount(outArray));
		exit(1);
	}
	pubKeyRef = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0);
	if(CFGetTypeID(pubKeyRef) != SecKeyGetTypeID()) {
		printf("***Unexpected item type after import\n");
		exit(1);
	}
					
	/* Use imported encrypted public key with locked keychain */
	verboseDisp(verbose, "locking keychain, exporting public key");
	SecKeychainLock(kcRef);
	printExpectDialog();
	ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData);
	if(ortn) {
		cssmPerror("SecKeychainItemExport", ortn);
		exit(1);
	}
	if(!didGetDialog()) {
		exit(1);
	}
	CFRelease(exportData);
	
	verboseDisp(verbose, "locking keychain, encrypting with public key");
	SecKeychainLock(kcRef);
	printExpectDialog();
	if(pubKeyEncrypt(pubKeyRef)) {
		exit(1);
	}
	if(!didGetDialog()) {
		exit(1);
	}

	SecKeychainDelete(kcRef);
	printf("...test succeeded.\n");
	return 0;
}
static int do_keychain_import(
	SecKeychainRef		kcRef,
	CFDataRef			inData,
	SecExternalFormat   externFormat,
	SecExternalItemType itemType,
	SecAccessRef		access,
	Boolean				nonExtractable,
	const char			*passphrase,
	const char			*fileName,
	char				**attrNames,
	char				**attrValues,
	unsigned			numExtendedAttributes)
{
	SecKeyImportExportParameters	keyParams;
	OSStatus		ortn;
	CFStringRef		fileStr;
	CFArrayRef		outArray = NULL;
	int				result = 0;
	int				numCerts = 0;
	int				numKeys = 0;
	int				numIdentities = 0;
	int				tryCount = 0;
	CFIndex			dex;
	CFIndex			numItems = 0;
	CFStringRef		passStr = NULL;
	CFStringRef		promptStr = NULL;
	CFStringRef		retryStr = NULL;

	/*
	 * Specify some kind of passphrase in case caller doesn't know this
	 * is a wrapped object
	 */
	memset(&keyParams, 0, sizeof(SecKeyImportExportParameters));
	keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
	if(passphrase != NULL) {
		passStr = CFStringCreateWithCString(NULL, passphrase, kCFStringEncodingASCII);
		keyParams.passphrase = passStr;
	}
	else {
		keyParams.flags = kSecKeySecurePassphrase;
	}
	if(nonExtractable) {
        // explicitly set the key attributes, omitting the CSSM_KEYATTR_EXTRACTABLE bit
        keyParams.keyAttributes = CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE;
    }
	keyParams.accessRef = access;

	fileStr = CFStringCreateWithCString(NULL, fileName, kCFStringEncodingUTF8);
	if (fileStr) {
		CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, fileStr, kCFURLPOSIXPathStyle, FALSE);
		if (fileURL) {
			CFStringRef nameStr = CFURLCopyLastPathComponent(fileURL);
			if (nameStr) {
				safe_CFRelease(&fileStr);
				fileStr = nameStr;
			}
			safe_CFRelease(&fileURL);
		}
	}
	promptStr = CFStringCreateWithFormat(NULL, NULL, KC_IMPORT_KEY_PASSWORD_MESSAGE, fileStr);
	retryStr = CFStringCreateWithFormat(NULL, NULL, KC_IMPORT_KEY_PASSWORD_RETRYMESSAGE, fileStr);

	while (TRUE)
	{
		keyParams.alertPrompt = (tryCount == 0) ? promptStr : retryStr;

		ortn = SecKeychainItemImport(inData,
									 fileStr,
									 &externFormat,
									 &itemType,
									 0,		/* flags not used (yet) */
									 &keyParams,
									 kcRef,
									 &outArray);

		if(ortn) {
			if (ortn == errSecPkcs12VerifyFailure && ++tryCount < 3) {
				continue;
			}
			sec_perror("SecKeychainItemImport", ortn);
			result = 1;
			goto cleanup;
		}
		break;
	}

	/*
	 * Parse returned items & report to user
	 */
	if(outArray == NULL) {
		sec_error("No keychain items found");
		result = 1;
		goto cleanup;
	}
	numItems = CFArrayGetCount(outArray);
	for(dex=0; dex<numItems; dex++) {
		CFTypeRef item = CFArrayGetValueAtIndex(outArray, dex);
		CFTypeID itemType = CFGetTypeID(item);
		if(itemType == SecIdentityGetTypeID()) {
			numIdentities++;
		}
		else if(itemType == SecCertificateGetTypeID()) {
			numCerts++;
		}
		else if(itemType == SecKeyGetTypeID()) {
			numKeys++;
		}
		else {
			sec_error("Unexpected item type returned from SecKeychainItemImport");
			result = 1;
			goto cleanup;
		}
	}
	if(numIdentities) {
		char *str;
		if(numIdentities > 1) {
			str = "identities";
		}
		else {
			str = "identity";
		}
		fprintf(stdout, "%d %s imported.\n", numIdentities, str);
	}
	if(numKeys) {
		char *str;
		if(numKeys > 1) {
			str = "keys";
		}
		else {
			str = "key";
		}
		fprintf(stdout, "%d %s imported.\n", numKeys, str);
	}
	if(numCerts) {
		char *str;
		if(numCerts > 1) {
			str = "certificates";
		}
		else {
			str = "certificate";
		}
		fprintf(stdout, "%d %s imported.\n", numCerts, str);
	}

	/* optionally apply extended attributes */
	if(numExtendedAttributes) {
		unsigned attrDex;
		for(attrDex=0; attrDex<numExtendedAttributes; attrDex++) {
			CFStringRef attrNameStr = CFStringCreateWithCString(NULL, attrNames[attrDex],
				kCFStringEncodingASCII);
			CFDataRef attrValueData = CFDataCreate(NULL, (const UInt8 *)attrValues[attrDex],
				strlen(attrValues[attrDex]));
			for(dex=0; dex<numItems; dex++) {
				SecKeychainItemRef itemRef =
					(SecKeychainItemRef)CFArrayGetValueAtIndex(outArray, dex);
				ortn = SecKeychainItemSetExtendedAttribute(itemRef, attrNameStr, attrValueData);
				if(ortn) {
					cssmPerror("SecKeychainItemSetExtendedAttribute", ortn);
					result = 1;
					break;
				}
			}	/* for each imported item */
			CFRelease(attrNameStr);
			CFRelease(attrValueData);
			if(result) {
				break;
			}
		}	/* for each extended attribute */
	}

cleanup:
	safe_CFRelease(&fileStr);
	safe_CFRelease(&outArray);
	safe_CFRelease(&passStr);
	safe_CFRelease(&promptStr);
	safe_CFRelease(&retryStr);

	return result;
}
OSStatus SecCertificateRequestGetResult(
        SecCertificateRequestRef certRequestRef,
        SecKeychainRef keychain,
        sint32 *estimatedTime,
        SecCertificateRef *certificateRef)
{
	BEGIN_SECAPI

	CssmData certData;
	*certificateRef = NULL;
	CertificateRequest::required(certRequestRef)->getResult(estimatedTime, certData);
	if(certData.data() != NULL) {
		/*
		 * Convert to SecCertifcateRef, optionally import. 
		 */
		CFDataRef cfCert = CFDataCreate(NULL, (UInt8 *)certData.data(), certData.Length);
		SecExternalItemType itemType = kSecItemTypeCertificate;
		CFArrayRef outItems = NULL;
		bool freeKcRef = false;
		OSStatus ortn;
		
		if(keychain == NULL) {
			/* 
			 * Unlike most Sec* calls, if the keychain argument to SecKeychainItemImport()
			 * is NULL, the item is not imported to the default keychain. At our
			 * interface, however, a NULL keychain means "import to the default
			 * keychain". 
			 */
			ortn = SecKeychainCopyDefault(&keychain);
			if(ortn) {
				certReqDbg("GetResult: SecKeychainCopyDefault failure");
				/* oh well, there's nothing we can do about this */
			}
			else {
				freeKcRef = true;
			}
		}
		ortn = SecKeychainItemImport(cfCert, NULL,
			NULL,			// format, don't care
			&itemType,
			0,				// flags
			NULL,			// keyParams
			keychain,		// optional, like ours
			&outItems);
		CFRelease(cfCert);
		if(freeKcRef) {
			CFRelease(keychain);
		}
		if(ortn) {
			certReqDbg("SecCertificateRequestGetResult: SecKeychainItemImport failure");
			MacOSError::throwMe(ortn);
		}
		CFIndex numItems = CFArrayGetCount(outItems);
		switch(numItems) {
			case 0:
				certReqDbg("SecCertificateRequestGetResult: import zero items");
				MacOSError::throwMe(errSecInternalComponent);
			default:
				certReqDbg("SecCertificateRequestGetResult: import %d items", 
					(int)numItems);
				/* but drop thru anyway, take the first one */
			case 1:
				SecCertificateRef certRef = 
					(SecCertificateRef)(CFArrayGetValueAtIndex(outItems, 0));
				if(CFGetTypeID(certRef) != SecCertificateGetTypeID()) {
					certReqDbg("SecCertificateRequestGetResult: bad type");
				}
				else {
					CFRetain(certRef);
					*certificateRef = certRef;
				}
		}
		CFRelease(outItems);
	}	
	END_SECAPI
}