1 /*
   2  * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #import "apple_security_KeychainStore.h"
  27 #import "jni_util.h"
  28 
  29 #import <Security/Security.h>
  30 #import <Security/SecImportExport.h>
  31 #import <CoreServices/CoreServices.h>  // (for require() macros)
  32 #import <JavaNativeFoundation/JavaNativeFoundation.h>
  33 
  34 
  35 static JNF_CLASS_CACHE(jc_KeychainStore, "apple/security/KeychainStore");
  36 static JNF_MEMBER_CACHE(jm_createTrustedCertEntry, jc_KeychainStore, "createTrustedCertEntry", "(Ljava/lang/String;JJ[B)V");
  37 static JNF_MEMBER_CACHE(jm_createKeyEntry, jc_KeychainStore, "createKeyEntry", "(Ljava/lang/String;JJ[J[[B)V");
  38 
  39 static jstring getLabelFromItem(JNIEnv *env, SecKeychainItemRef inItem)
  40 {
  41     OSStatus status;
  42     jstring returnValue = NULL;
  43     char *attribCString = NULL;
  44 
  45     SecKeychainAttribute itemAttrs[] = { { kSecLabelItemAttr, 0, NULL } };
  46     SecKeychainAttributeList attrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs };
  47 
  48     status = SecKeychainItemCopyContent(inItem, NULL, &attrList, NULL, NULL);
  49 
  50     if(status) {
  51         cssmPerror("getLabelFromItem: SecKeychainItemCopyContent", status);
  52         goto errOut;
  53     }
  54 
  55     attribCString = malloc(itemAttrs[0].length + 1);
  56     if (attribCString == NULL) {
  57         JNU_ThrowOutOfMemoryError(env, "native heap");
  58         goto errOut;
  59     }
  60 
  61     strncpy(attribCString, itemAttrs[0].data, itemAttrs[0].length);
  62     attribCString[itemAttrs[0].length] = '\0';
  63     returnValue = (*env)->NewStringUTF(env, attribCString);
  64 
  65 errOut:
  66     SecKeychainItemFreeContent(&attrList, NULL);
  67     if (attribCString) free(attribCString);
  68     return returnValue;
  69 }
  70 
  71 static jlong getModDateFromItem(JNIEnv *env, SecKeychainItemRef inItem)
  72 {
  73     OSStatus status;
  74     SecKeychainAttribute itemAttrs[] = { { kSecModDateItemAttr, 0, NULL } };
  75     SecKeychainAttributeList attrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs };
  76     jlong returnValue = 0;
  77 
  78     status = SecKeychainItemCopyContent(inItem, NULL, &attrList, NULL, NULL);
  79 
  80     if(status) {
  81         // This is almost always missing, so don't dump an error.
  82         // cssmPerror("getModDateFromItem: SecKeychainItemCopyContent", status);
  83         goto errOut;
  84     }
  85 
  86     memcpy(&returnValue, itemAttrs[0].data, itemAttrs[0].length);
  87 
  88 errOut:
  89     SecKeychainItemFreeContent(&attrList, NULL);
  90     return returnValue;
  91 }
  92 
  93 static void setLabelForItem(NSString *inLabel, SecKeychainItemRef inItem)
  94 {
  95     OSStatus status;
  96     const char *labelCString = [inLabel UTF8String];
  97 
  98     // Set up attribute vector (each attribute consists of {tag, length, pointer}):
  99     SecKeychainAttribute attrs[] = {
 100         { kSecLabelItemAttr, strlen(labelCString), (void *)labelCString }
 101     };
 102 
 103     const SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
 104 
 105     // Not changing data here, just attributes.
 106     status = SecKeychainItemModifyContent(inItem, &attributes, 0, NULL);
 107 
 108     if(status) {
 109         cssmPerror("setLabelForItem: SecKeychainItemModifyContent", status);
 110     }
 111 }
 112 
 113 /*
 114  * Given a SecIdentityRef, do our best to construct a complete, ordered, and
 115  * verified cert chain, returning the result in a CFArrayRef. The result is
 116  * can be passed back to Java as a chain for a private key.
 117  */
 118 static OSStatus completeCertChain(
 119                                      SecIdentityRef         identity,
 120                                      SecCertificateRef    trustedAnchor,    // optional additional trusted anchor
 121                                      bool                 includeRoot,     // include the root in outArray
 122                                      CFArrayRef            *outArray)        // created and RETURNED
 123 {
 124     SecTrustRef                    secTrust = NULL;
 125     SecPolicyRef                policy = NULL;
 126     SecPolicySearchRef            policySearch = NULL;
 127     SecTrustResultType            secTrustResult;
 128     CSSM_TP_APPLE_EVIDENCE_INFO *dummyEv;            // not used
 129     CFArrayRef                    certChain = NULL;   // constructed chain, CERTS ONLY
 130     CFMutableArrayRef             subjCerts;            // passed to SecTrust
 131     CFMutableArrayRef             certArray;            // returned array starting with
 132                                                     //   identity
 133     CFIndex                     numResCerts;
 134     CFIndex                     dex;
 135     OSStatus                     ortn;
 136       SecCertificateRef             certRef;
 137 
 138     /* First element in out array is the SecIdentity */
 139     certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
 140     CFArrayAppendValue(certArray, identity);
 141 
 142     /* the single element in certs-to-be-evaluated comes from the identity */
 143        ortn = SecIdentityCopyCertificate(identity, &certRef);
 144     if(ortn) {
 145         /* should never happen */
 146         cssmPerror("SecIdentityCopyCertificate", ortn);
 147         return ortn;
 148     }
 149 
 150     /*
 151      * Now use SecTrust to get a complete cert chain, using all of the
 152      * user's keychains to look for intermediate certs.
 153      * NOTE this does NOT handle root certs which are not in the system
 154      * root cert DB.
 155      */
 156     subjCerts = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks);
 157     CFArraySetValueAtIndex(subjCerts, 0, certRef);
 158 
 159     /* the array owns the subject cert ref now */
 160     CFRelease(certRef);
 161 
 162     /* Get a SecPolicyRef for generic X509 cert chain verification */
 163     ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,
 164                                  &CSSMOID_APPLE_X509_BASIC,
 165                                  NULL,                // value
 166                                  &policySearch);
 167     if(ortn) {
 168         /* should never happen */
 169         cssmPerror("SecPolicySearchCreate", ortn);
 170         goto errOut;
 171     }
 172     ortn = SecPolicySearchCopyNext(policySearch, &policy);
 173     if(ortn) {
 174         /* should never happen */
 175         cssmPerror("SecPolicySearchCopyNext", ortn);
 176         goto errOut;
 177     }
 178 
 179     /* build a SecTrustRef for specified policy and certs */
 180     ortn = SecTrustCreateWithCertificates(subjCerts,
 181                                           policy, &secTrust);
 182     if(ortn) {
 183         cssmPerror("SecTrustCreateWithCertificates", ortn);
 184         goto errOut;
 185     }
 186 
 187     if(trustedAnchor) {
 188         /*
 189         * Tell SecTrust to trust this one in addition to the current
 190          * trusted system-wide anchors.
 191          */
 192         CFMutableArrayRef newAnchors;
 193         CFArrayRef currAnchors;
 194 
 195         ortn = SecTrustCopyAnchorCertificates(&currAnchors);
 196         if(ortn) {
 197             /* should never happen */
 198             cssmPerror("SecTrustCopyAnchorCertificates", ortn);
 199             goto errOut;
 200         }
 201         newAnchors = CFArrayCreateMutableCopy(NULL,
 202                                               CFArrayGetCount(currAnchors) + 1,
 203                                               currAnchors);
 204         CFRelease(currAnchors);
 205         CFArrayAppendValue(newAnchors, trustedAnchor);
 206         ortn = SecTrustSetAnchorCertificates(secTrust, newAnchors);
 207         CFRelease(newAnchors);
 208         if(ortn) {
 209             cssmPerror("SecTrustSetAnchorCertificates", ortn);
 210             goto errOut;
 211         }
 212     }
 213 
 214     /* evaluate: GO */
 215     ortn = SecTrustEvaluate(secTrust, &secTrustResult);
 216     if(ortn) {
 217         cssmPerror("SecTrustEvaluate", ortn);
 218         goto errOut;
 219     }
 220     switch(secTrustResult) {
 221         case kSecTrustResultUnspecified:
 222             /* cert chain valid, no special UserTrust assignments; drop thru */
 223         case kSecTrustResultProceed:
 224             /* cert chain valid AND user explicitly trusts this */
 225             break;
 226         default:
 227             /*
 228              * Cert chain construction failed.
 229              * Just go with the single subject cert we were given; maybe the
 230              * peer can complete the chain.
 231              */
 232             ortn = noErr;
 233             goto errOut;
 234     }
 235 
 236     /* get resulting constructed cert chain */
 237     ortn = SecTrustGetResult(secTrust, &secTrustResult, &certChain, &dummyEv);
 238     if(ortn) {
 239         cssmPerror("SecTrustEvaluate", ortn);
 240         goto errOut;
 241     }
 242 
 243     /*
 244      * Copy certs from constructed chain to our result array, skipping
 245      * the leaf (which is already there, as a SecIdentityRef) and possibly
 246      * a root.
 247      */
 248     numResCerts = CFArrayGetCount(certChain);
 249     if(numResCerts < 1) {
 250         /*
 251          * Can't happen: If chain doesn't verify to a root, we'd
 252          * have bailed after SecTrustEvaluate().
 253          */
 254         ortn = noErr;
 255         goto errOut;
 256     }
 257     if(!includeRoot) {
 258         /* skip the last (root) cert) */
 259         numResCerts--;
 260     }
 261     for(dex=1; dex<numResCerts; dex++) {
 262         certRef = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, dex);
 263         CFArrayAppendValue(certArray, certRef);
 264     }
 265 errOut:
 266         /* clean up */
 267         if(secTrust) {
 268             CFRelease(secTrust);
 269         }
 270     if(subjCerts) {
 271         CFRelease(subjCerts);
 272     }
 273     if(policy) {
 274         CFRelease(policy);
 275     }
 276     if(policySearch) {
 277         CFRelease(policySearch);
 278     }
 279     *outArray = certArray;
 280     return ortn;
 281 }
 282 
 283 static void addIdentitiesToKeystore(JNIEnv *env, jobject keyStore)
 284 {
 285     // Search the user keychain list for all identities. Identities are a certificate/private key association that
 286     // can be chosen for a purpose such as signing or an SSL connection.
 287     SecIdentitySearchRef identitySearch = NULL;
 288     // Pass 0 if you want all identities returned by this search
 289     OSStatus err = SecIdentitySearchCreate(NULL, 0, &identitySearch);
 290     SecIdentityRef theIdentity = NULL;
 291     OSErr searchResult = noErr;
 292 
 293     do {
 294         searchResult = SecIdentitySearchCopyNext(identitySearch, &theIdentity);
 295 
 296         if (searchResult == noErr) {
 297             // Get the cert from the identity, then generate a chain.
 298             SecCertificateRef certificate;
 299             SecIdentityCopyCertificate(theIdentity, &certificate);
 300             CFArrayRef certChain = NULL;
 301 
 302             // *** Should do something with this error...
 303             err = completeCertChain(theIdentity, NULL, TRUE, &certChain);
 304 
 305             CFIndex i, certCount = CFArrayGetCount(certChain);
 306 
 307             // Make a java array of certificate data from the chain.
 308             jclass byteArrayClass = (*env)->FindClass(env, "[B");
 309             if (byteArrayClass == NULL) {
 310                 goto errOut;
 311             }
 312             jobjectArray javaCertArray = (*env)->NewObjectArray(env, certCount, byteArrayClass, NULL);
 313             // Cleanup first then check for a NULL return code
 314             (*env)->DeleteLocalRef(env, byteArrayClass);
 315             if (javaCertArray == NULL) {
 316                 goto errOut;
 317             }
 318 
 319             // And, make an array of the certificate refs.
 320             jlongArray certRefArray = (*env)->NewLongArray(env, certCount);
 321             if (certRefArray == NULL) {
 322                 goto errOut;
 323             }
 324 
 325             SecCertificateRef currCertRef = NULL;
 326 
 327             for (i = 0; i < certCount; i++) {
 328                 CSSM_DATA currCertData;
 329 
 330                 if (i == 0)
 331                     currCertRef = certificate;
 332                 else
 333                     currCertRef = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, i);
 334 
 335                 bzero(&currCertData, sizeof(CSSM_DATA));
 336                 err = SecCertificateGetData(currCertRef, &currCertData);
 337                 jbyteArray encodedCertData = (*env)->NewByteArray(env, currCertData.Length);
 338                 if (encodedCertData == NULL) {
 339                     goto errOut;
 340                 }
 341                 (*env)->SetByteArrayRegion(env, encodedCertData, 0, currCertData.Length, (jbyte *)currCertData.Data);
 342                 (*env)->SetObjectArrayElement(env, javaCertArray, i, encodedCertData);
 343                 jlong certRefElement = ptr_to_jlong(currCertRef);
 344                 (*env)->SetLongArrayRegion(env, certRefArray, i, 1, &certRefElement);
 345             }
 346 
 347             // Get the private key.  When needed we'll export the data from it later.
 348             SecKeyRef privateKeyRef;
 349             err = SecIdentityCopyPrivateKey(theIdentity, &privateKeyRef);
 350 
 351             // Find the label.  It's a 'blob', but we interpret as characters.
 352             jstring alias = getLabelFromItem(env, (SecKeychainItemRef)certificate);
 353             if (alias == NULL) {
 354                 goto errOut;
 355             }
 356 
 357             // Find the creation date.
 358             jlong creationDate = getModDateFromItem(env, (SecKeychainItemRef)certificate);
 359 
 360             // Call back to the Java object to create Java objects corresponding to this security object.
 361             jlong nativeKeyRef = ptr_to_jlong(privateKeyRef);
 362             JNFCallVoidMethod(env, keyStore, jm_createKeyEntry, alias, creationDate, nativeKeyRef, certRefArray, javaCertArray);
 363         }
 364     } while (searchResult == noErr);
 365 
 366 errOut:
 367     if (identitySearch != NULL) {
 368         CFRelease(identitySearch);
 369     }
 370 }
 371 
 372 static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore)
 373 {
 374     // Search the user keychain list for all X509 certificates.
 375     SecKeychainSearchRef keychainItemSearch = NULL;
 376     OSStatus err = SecKeychainSearchCreateFromAttributes(NULL, kSecCertificateItemClass, NULL, &keychainItemSearch);
 377     SecKeychainItemRef theItem = NULL;
 378     OSErr searchResult = noErr;
 379 
 380     do {
 381         searchResult = SecKeychainSearchCopyNext(keychainItemSearch, &theItem);
 382 
 383         if (searchResult == noErr) {
 384             // Make a byte array with the DER-encoded contents of the certificate.
 385             SecCertificateRef certRef = (SecCertificateRef)theItem;
 386             CSSM_DATA currCertificate;
 387             err = SecCertificateGetData(certRef, &currCertificate);
 388             jbyteArray certData = (*env)->NewByteArray(env, currCertificate.Length);
 389             if (certData == NULL) {
 390                 goto errOut;
 391             }
 392             (*env)->SetByteArrayRegion(env, certData, 0, currCertificate.Length, (jbyte *)currCertificate.Data);
 393 
 394             // Find the label.  It's a 'blob', but we interpret as characters.
 395             jstring alias = getLabelFromItem(env, theItem);
 396             if (alias == NULL) {
 397                 goto errOut;
 398             }
 399 
 400             // Find the creation date.
 401             jlong creationDate = getModDateFromItem(env, theItem);
 402 
 403             // Call back to the Java object to create Java objects corresponding to this security object.
 404             jlong nativeRef = ptr_to_jlong(certRef);
 405             JNFCallVoidMethod(env, keyStore, jm_createTrustedCertEntry, alias, nativeRef, creationDate, certData);
 406         }
 407     } while (searchResult == noErr);
 408 
 409 errOut:
 410     if (keychainItemSearch != NULL) {
 411         CFRelease(keychainItemSearch);
 412     }
 413 }
 414 
 415 /*
 416  * Class:     apple_security_KeychainStore
 417  * Method:    _getEncodedKeyData
 418  * Signature: (J)[B
 419      */
 420 JNIEXPORT jbyteArray JNICALL Java_apple_security_KeychainStore__1getEncodedKeyData
 421 (JNIEnv *env, jobject this, jlong keyRefLong, jcharArray passwordObj)
 422 {
 423     SecKeyRef keyRef = (SecKeyRef)jlong_to_ptr(keyRefLong);
 424     SecKeyImportExportParameters paramBlock;
 425     OSStatus err = noErr;
 426     CFDataRef exportedData = NULL;
 427     jbyteArray returnValue = NULL;
 428     CFStringRef passwordStrRef = NULL;
 429 
 430     jsize passwordLen = 0;
 431     jchar *passwordChars = NULL;
 432 
 433     if (passwordObj) {
 434         passwordLen = (*env)->GetArrayLength(env, passwordObj);
 435 
 436         if (passwordLen > 0) {
 437             passwordChars = (*env)->GetCharArrayElements(env, passwordObj, NULL);
 438             if (passwordChars == NULL) {
 439                 goto errOut;
 440             }
 441 
 442             passwordStrRef = CFStringCreateWithCharactersNoCopy(NULL, passwordChars, passwordLen, kCFAllocatorNull);
 443             if (passwordStrRef == NULL) {
 444                 goto errOut;
 445             }
 446         }
 447     }
 448 
 449     paramBlock.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
 450     // Note that setting the flags field **requires** you to pass in a password of some kind.  The keychain will not prompt you.
 451     paramBlock.flags = 0;
 452     paramBlock.passphrase = passwordStrRef;
 453     paramBlock.alertTitle = NULL;
 454     paramBlock.alertPrompt = NULL;
 455     paramBlock.accessRef = NULL;
 456     paramBlock.keyUsage = CSSM_KEYUSE_ANY;
 457     paramBlock.keyAttributes = CSSM_KEYATTR_RETURN_DEFAULT;
 458 
 459     err = SecKeychainItemExport(keyRef, kSecFormatPKCS12, 0, &paramBlock, &exportedData);
 460 
 461     if (err == noErr) {
 462         CFIndex size = CFDataGetLength(exportedData);
 463         returnValue = (*env)->NewByteArray(env, size);
 464         if (returnValue == NULL) {
 465             goto errOut;
 466         }
 467         (*env)->SetByteArrayRegion(env, returnValue, 0, size, (jbyte *)CFDataGetBytePtr(exportedData));
 468     }
 469 
 470 errOut:
 471     if (exportedData) CFRelease(exportedData);
 472     if (passwordStrRef) CFRelease(passwordStrRef);
 473     if (passwordChars) {
 474         // clear the password and release
 475         memset(passwordChars, 0, passwordLen);
 476         (*env)->ReleaseCharArrayElements(env, passwordObj, passwordChars,
 477             JNI_ABORT);
 478     }
 479     return returnValue;
 480 }
 481 
 482 
 483 /*
 484  * Class:     apple_security_KeychainStore
 485  * Method:    _scanKeychain
 486  * Signature: ()V
 487  */
 488 JNIEXPORT void JNICALL Java_apple_security_KeychainStore__1scanKeychain
 489 (JNIEnv *env, jobject this)
 490 {
 491     // Look for 'identities' -- private key and certificate chain pairs -- and add those.
 492     // Search for these first, because a certificate that's found here as part of an identity will show up
 493     // again later as a certificate.
 494     addIdentitiesToKeystore(env, this);
 495 
 496     JNU_CHECK_EXCEPTION(env);
 497 
 498     // Scan current keychain for trusted certificates.
 499     addCertificatesToKeystore(env, this);
 500 
 501 }
 502 
 503 /*
 504  * Class:     apple_security_KeychainStore
 505  * Method:    _addItemToKeychain
 506  * Signature: (Ljava/lang/String;[B)I
 507 */
 508 JNIEXPORT jlong JNICALL Java_apple_security_KeychainStore__1addItemToKeychain
 509 (JNIEnv *env, jobject this, jstring alias, jboolean isCertificate, jbyteArray rawDataObj, jcharArray passwordObj)
 510 {
 511     OSStatus err;
 512     jlong returnValue = 0;
 513 
 514 JNF_COCOA_ENTER(env);
 515 
 516     jsize dataSize = (*env)->GetArrayLength(env, rawDataObj);
 517     jbyte *rawData = (*env)->GetByteArrayElements(env, rawDataObj, NULL);
 518     if (rawData == NULL) {
 519         goto errOut;
 520     }
 521 
 522     CFDataRef cfDataToImport = CFDataCreate(kCFAllocatorDefault, (UInt8 *)rawData, dataSize);
 523     CFArrayRef createdItems = NULL;
 524 
 525     SecKeychainRef defaultKeychain = NULL;
 526     SecKeychainCopyDefault(&defaultKeychain);
 527 
 528     SecExternalFormat dataFormat = (isCertificate == JNI_TRUE ? kSecFormatX509Cert : kSecFormatWrappedPKCS8);
 529 
 530     // Convert the password obj into a CFStringRef that the keychain importer can use for encryption.
 531     SecKeyImportExportParameters paramBlock;
 532     CFStringRef passwordStrRef = NULL;
 533 
 534     jsize passwordLen = 0;
 535     jchar *passwordChars = NULL;
 536 
 537     if (passwordObj) {
 538         passwordLen = (*env)->GetArrayLength(env, passwordObj);
 539 
 540         if (passwordLen > 0) {
 541             passwordChars = (*env)->GetCharArrayElements(env, passwordObj, NULL);
 542             if (passwordChars == NULL) {
 543                 goto errOut;
 544             }
 545 
 546             passwordStrRef = CFStringCreateWithCharactersNoCopy(NULL, passwordChars, passwordLen, kCFAllocatorNull);
 547             if (passwordStrRef == NULL) {
 548                 goto errOut;
 549             }
 550         }
 551     }
 552 
 553     paramBlock.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
 554     // Note that setting the flags field **requires** you to pass in a password of some kind.  The keychain will not prompt you.
 555     paramBlock.flags = 0;
 556     paramBlock.passphrase = passwordStrRef;
 557     paramBlock.alertTitle = NULL;
 558     paramBlock.alertPrompt = NULL;
 559     paramBlock.accessRef = NULL;
 560     paramBlock.keyUsage = CSSM_KEYUSE_ANY;
 561     paramBlock.keyAttributes = CSSM_KEYATTR_RETURN_DEFAULT;
 562 
 563     err = SecKeychainItemImport(cfDataToImport, NULL, &dataFormat, NULL,
 564                                 0, &paramBlock, defaultKeychain, &createdItems);
 565     if (cfDataToImport != NULL) {
 566         CFRelease(cfDataToImport);
 567     }
 568 
 569     if (err == noErr) {
 570         SecKeychainItemRef anItem = (SecKeychainItemRef)CFArrayGetValueAtIndex(createdItems, 0);
 571 
 572         // Don't bother labeling keys. They become part of an identity, and are not an accessible part of the keychain.
 573         if (CFGetTypeID(anItem) == SecCertificateGetTypeID()) {
 574             setLabelForItem(JNFJavaToNSString(env, alias), anItem);
 575         }
 576 
 577         // Retain the item, since it will be released once when the array holding it gets released.
 578         CFRetain(anItem);
 579         returnValue = ptr_to_jlong(anItem);
 580     } else {
 581         cssmPerror("_addItemToKeychain: SecKeychainItemImport", err);
 582     }
 583 
 584     if (createdItems != NULL) {
 585         CFRelease(createdItems);
 586     }
 587 
 588 errOut:
 589     if (rawData) {
 590         (*env)->ReleaseByteArrayElements(env, rawDataObj, rawData, JNI_ABORT);
 591     }
 592 
 593     if (passwordStrRef) CFRelease(passwordStrRef);
 594     if (passwordChars) {
 595         // clear the password and release
 596         memset(passwordChars, 0, passwordLen);
 597         (*env)->ReleaseCharArrayElements(env, passwordObj, passwordChars,
 598             JNI_ABORT);
 599     }
 600 
 601 JNF_COCOA_EXIT(env);
 602 
 603     return returnValue;
 604 }
 605 
 606 /*
 607  * Class:     apple_security_KeychainStore
 608  * Method:    _removeItemFromKeychain
 609  * Signature: (J)I
 610 */
 611 JNIEXPORT jint JNICALL Java_apple_security_KeychainStore__1removeItemFromKeychain
 612 (JNIEnv *env, jobject this, jlong keychainItem)
 613 {
 614     SecKeychainItemRef itemToRemove = jlong_to_ptr(keychainItem);
 615     return SecKeychainItemDelete(itemToRemove);
 616 }
 617 
 618 /*
 619  * Class:     apple_security_KeychainStore
 620  * Method:    _releaseKeychainItemRef
 621  * Signature: (J)V
 622  */
 623 JNIEXPORT void JNICALL Java_apple_security_KeychainStore__1releaseKeychainItemRef
 624 (JNIEnv *env, jobject this, jlong keychainItem)
 625 {
 626     SecKeychainItemRef itemToFree = jlong_to_ptr(keychainItem);
 627     CFRelease(itemToFree);
 628 }