Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/macosx/native_NOTIOS/apple/security/KeystoreImpl.m
38829 views
/*1* Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#import "apple_security_KeychainStore.h"2627#import <Security/Security.h>28#import <Security/SecImportExport.h>29#import <CoreServices/CoreServices.h> // (for require() macros)30#import <JavaNativeFoundation/JavaNativeFoundation.h>313233static JNF_CLASS_CACHE(jc_KeychainStore, "apple/security/KeychainStore");34static JNF_MEMBER_CACHE(jm_createTrustedCertEntry, jc_KeychainStore, "createTrustedCertEntry", "(Ljava/lang/String;JJ[B)V");35static JNF_MEMBER_CACHE(jm_createKeyEntry, jc_KeychainStore, "createKeyEntry", "(Ljava/lang/String;JJ[J[[B)V");3637static jstring getLabelFromItem(JNIEnv *env, SecKeychainItemRef inItem)38{39OSStatus status;40jstring returnValue = NULL;41char *attribCString = NULL;4243SecKeychainAttribute itemAttrs[] = { { kSecLabelItemAttr, 0, NULL } };44SecKeychainAttributeList attrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs };4546status = SecKeychainItemCopyContent(inItem, NULL, &attrList, NULL, NULL);4748if(status) {49cssmPerror("getLabelFromItem: SecKeychainItemCopyContent", status);50goto errOut;51}5253attribCString = malloc(itemAttrs[0].length + 1);54strncpy(attribCString, itemAttrs[0].data, itemAttrs[0].length);55attribCString[itemAttrs[0].length] = '\0';56returnValue = (*env)->NewStringUTF(env, attribCString);5758errOut:59SecKeychainItemFreeContent(&attrList, NULL);60if (attribCString) free(attribCString);61return returnValue;62}6364static jlong getModDateFromItem(JNIEnv *env, SecKeychainItemRef inItem)65{66OSStatus status;67SecKeychainAttribute itemAttrs[] = { { kSecModDateItemAttr, 0, NULL } };68SecKeychainAttributeList attrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs };69jlong returnValue = 0;7071status = SecKeychainItemCopyContent(inItem, NULL, &attrList, NULL, NULL);7273if(status) {74// This is almost always missing, so don't dump an error.75// cssmPerror("getModDateFromItem: SecKeychainItemCopyContent", status);76goto errOut;77}7879memcpy(&returnValue, itemAttrs[0].data, itemAttrs[0].length);8081errOut:82SecKeychainItemFreeContent(&attrList, NULL);83return returnValue;84}8586static void setLabelForItem(NSString *inLabel, SecKeychainItemRef inItem)87{88OSStatus status;89const char *labelCString = [inLabel UTF8String];9091// Set up attribute vector (each attribute consists of {tag, length, pointer}):92SecKeychainAttribute attrs[] = {93{ kSecLabelItemAttr, strlen(labelCString), (void *)labelCString }94};9596const SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };9798// Not changing data here, just attributes.99status = SecKeychainItemModifyContent(inItem, &attributes, 0, NULL);100101if(status) {102cssmPerror("setLabelForItem: SecKeychainItemModifyContent", status);103}104}105106/*107* Given a SecIdentityRef, do our best to construct a complete, ordered, and108* verified cert chain, returning the result in a CFArrayRef. The result is109* can be passed back to Java as a chain for a private key.110*/111static OSStatus completeCertChain(112SecIdentityRef identity,113SecCertificateRef trustedAnchor, // optional additional trusted anchor114bool includeRoot, // include the root in outArray115CFArrayRef *outArray) // created and RETURNED116{117SecTrustRef secTrust = NULL;118SecPolicyRef policy = NULL;119SecPolicySearchRef policySearch = NULL;120SecTrustResultType secTrustResult;121CSSM_TP_APPLE_EVIDENCE_INFO *dummyEv; // not used122CFArrayRef certChain = NULL; // constructed chain, CERTS ONLY123CFMutableArrayRef subjCerts; // passed to SecTrust124CFMutableArrayRef certArray; // returned array starting with125// identity126CFIndex numResCerts;127CFIndex dex;128OSStatus ortn;129SecCertificateRef certRef;130131/* First element in out array is the SecIdentity */132certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);133CFArrayAppendValue(certArray, identity);134135/* the single element in certs-to-be-evaluated comes from the identity */136ortn = SecIdentityCopyCertificate(identity, &certRef);137if(ortn) {138/* should never happen */139cssmPerror("SecIdentityCopyCertificate", ortn);140return ortn;141}142143/*144* Now use SecTrust to get a complete cert chain, using all of the145* user's keychains to look for intermediate certs.146* NOTE this does NOT handle root certs which are not in the system147* root cert DB.148*/149subjCerts = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks);150CFArraySetValueAtIndex(subjCerts, 0, certRef);151152/* the array owns the subject cert ref now */153CFRelease(certRef);154155/* Get a SecPolicyRef for generic X509 cert chain verification */156ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,157&CSSMOID_APPLE_X509_BASIC,158NULL, // value159&policySearch);160if(ortn) {161/* should never happen */162cssmPerror("SecPolicySearchCreate", ortn);163goto errOut;164}165ortn = SecPolicySearchCopyNext(policySearch, &policy);166if(ortn) {167/* should never happen */168cssmPerror("SecPolicySearchCopyNext", ortn);169goto errOut;170}171172/* build a SecTrustRef for specified policy and certs */173ortn = SecTrustCreateWithCertificates(subjCerts,174policy, &secTrust);175if(ortn) {176cssmPerror("SecTrustCreateWithCertificates", ortn);177goto errOut;178}179180if(trustedAnchor) {181/*182* Tell SecTrust to trust this one in addition to the current183* trusted system-wide anchors.184*/185CFMutableArrayRef newAnchors;186CFArrayRef currAnchors;187188ortn = SecTrustCopyAnchorCertificates(&currAnchors);189if(ortn) {190/* should never happen */191cssmPerror("SecTrustCopyAnchorCertificates", ortn);192goto errOut;193}194newAnchors = CFArrayCreateMutableCopy(NULL,195CFArrayGetCount(currAnchors) + 1,196currAnchors);197CFRelease(currAnchors);198CFArrayAppendValue(newAnchors, trustedAnchor);199ortn = SecTrustSetAnchorCertificates(secTrust, newAnchors);200CFRelease(newAnchors);201if(ortn) {202cssmPerror("SecTrustSetAnchorCertificates", ortn);203goto errOut;204}205}206207/* evaluate: GO */208ortn = SecTrustEvaluate(secTrust, &secTrustResult);209if(ortn) {210cssmPerror("SecTrustEvaluate", ortn);211goto errOut;212}213switch(secTrustResult) {214case kSecTrustResultUnspecified:215/* cert chain valid, no special UserTrust assignments; drop thru */216case kSecTrustResultProceed:217/* cert chain valid AND user explicitly trusts this */218break;219default:220/*221* Cert chain construction failed.222* Just go with the single subject cert we were given; maybe the223* peer can complete the chain.224*/225ortn = noErr;226goto errOut;227}228229/* get resulting constructed cert chain */230ortn = SecTrustGetResult(secTrust, &secTrustResult, &certChain, &dummyEv);231if(ortn) {232cssmPerror("SecTrustEvaluate", ortn);233goto errOut;234}235236/*237* Copy certs from constructed chain to our result array, skipping238* the leaf (which is already there, as a SecIdentityRef) and possibly239* a root.240*/241numResCerts = CFArrayGetCount(certChain);242if(numResCerts < 1) {243/*244* Can't happen: If chain doesn't verify to a root, we'd245* have bailed after SecTrustEvaluate().246*/247ortn = noErr;248goto errOut;249}250if(!includeRoot) {251/* skip the last (root) cert) */252numResCerts--;253}254for(dex=1; dex<numResCerts; dex++) {255certRef = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, dex);256CFArrayAppendValue(certArray, certRef);257}258errOut:259/* clean up */260if(secTrust) {261CFRelease(secTrust);262}263if(subjCerts) {264CFRelease(subjCerts);265}266if(policy) {267CFRelease(policy);268}269if(policySearch) {270CFRelease(policySearch);271}272*outArray = certArray;273return ortn;274}275276static void addIdentitiesToKeystore(JNIEnv *env, jobject keyStore)277{278// Search the user keychain list for all identities. Identities are a certificate/private key association that279// can be chosen for a purpose such as signing or an SSL connection.280SecIdentitySearchRef identitySearch = NULL;281OSStatus err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_ANY, &identitySearch);282SecIdentityRef theIdentity = NULL;283OSErr searchResult = noErr;284285do {286searchResult = SecIdentitySearchCopyNext(identitySearch, &theIdentity);287288if (searchResult == noErr) {289// Get the cert from the identity, then generate a chain.290SecCertificateRef certificate;291SecIdentityCopyCertificate(theIdentity, &certificate);292CFArrayRef certChain = NULL;293294// *** Should do something with this error...295err = completeCertChain(theIdentity, NULL, TRUE, &certChain);296297CFIndex i, certCount = CFArrayGetCount(certChain);298299// Make a java array of certificate data from the chain.300jclass byteArrayClass = (*env)->FindClass(env, "[B");301if (byteArrayClass == NULL) {302goto errOut;303}304jobjectArray javaCertArray = (*env)->NewObjectArray(env, certCount, byteArrayClass, NULL);305// Cleanup first then check for a NULL return code306(*env)->DeleteLocalRef(env, byteArrayClass);307if (javaCertArray == NULL) {308goto errOut;309}310311// And, make an array of the certificate refs.312jlongArray certRefArray = (*env)->NewLongArray(env, certCount);313if (certRefArray == NULL) {314goto errOut;315}316317SecCertificateRef currCertRef = NULL;318319for (i = 0; i < certCount; i++) {320CSSM_DATA currCertData;321322if (i == 0)323currCertRef = certificate;324else325currCertRef = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, i);326327bzero(&currCertData, sizeof(CSSM_DATA));328err = SecCertificateGetData(currCertRef, &currCertData);329jbyteArray encodedCertData = (*env)->NewByteArray(env, currCertData.Length);330if (encodedCertData == NULL) {331goto errOut;332}333(*env)->SetByteArrayRegion(env, encodedCertData, 0, currCertData.Length, (jbyte *)currCertData.Data);334(*env)->SetObjectArrayElement(env, javaCertArray, i, encodedCertData);335jlong certRefElement = ptr_to_jlong(currCertRef);336(*env)->SetLongArrayRegion(env, certRefArray, i, 1, &certRefElement);337}338339// Get the private key. When needed we'll export the data from it later.340SecKeyRef privateKeyRef;341err = SecIdentityCopyPrivateKey(theIdentity, &privateKeyRef);342343// Find the label. It's a 'blob', but we interpret as characters.344jstring alias = getLabelFromItem(env, (SecKeychainItemRef)certificate);345if (alias == NULL) {346goto errOut;347}348349// Find the creation date.350jlong creationDate = getModDateFromItem(env, (SecKeychainItemRef)certificate);351352// Call back to the Java object to create Java objects corresponding to this security object.353jlong nativeKeyRef = ptr_to_jlong(privateKeyRef);354JNFCallVoidMethod(env, keyStore, jm_createKeyEntry, alias, creationDate, nativeKeyRef, certRefArray, javaCertArray);355}356} while (searchResult == noErr);357358errOut:359if (identitySearch != NULL) {360CFRelease(identitySearch);361}362}363364static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore)365{366// Search the user keychain list for all X509 certificates.367SecKeychainSearchRef keychainItemSearch = NULL;368OSStatus err = SecKeychainSearchCreateFromAttributes(NULL, kSecCertificateItemClass, NULL, &keychainItemSearch);369SecKeychainItemRef theItem = NULL;370OSErr searchResult = noErr;371372do {373searchResult = SecKeychainSearchCopyNext(keychainItemSearch, &theItem);374375if (searchResult == noErr) {376// Make a byte array with the DER-encoded contents of the certificate.377SecCertificateRef certRef = (SecCertificateRef)theItem;378CSSM_DATA currCertificate;379err = SecCertificateGetData(certRef, &currCertificate);380jbyteArray certData = (*env)->NewByteArray(env, currCertificate.Length);381if (certData == NULL) {382goto errOut;383}384(*env)->SetByteArrayRegion(env, certData, 0, currCertificate.Length, (jbyte *)currCertificate.Data);385386// Find the label. It's a 'blob', but we interpret as characters.387jstring alias = getLabelFromItem(env, theItem);388if (alias == NULL) {389goto errOut;390}391392// Find the creation date.393jlong creationDate = getModDateFromItem(env, theItem);394395// Call back to the Java object to create Java objects corresponding to this security object.396jlong nativeRef = ptr_to_jlong(certRef);397JNFCallVoidMethod(env, keyStore, jm_createTrustedCertEntry, alias, nativeRef, creationDate, certData);398}399} while (searchResult == noErr);400401errOut:402if (keychainItemSearch != NULL) {403CFRelease(keychainItemSearch);404}405}406407/*408* Class: apple_security_KeychainStore409* Method: _getEncodedKeyData410* Signature: (J)[B411*/412JNIEXPORT jbyteArray JNICALL Java_apple_security_KeychainStore__1getEncodedKeyData413(JNIEnv *env, jobject this, jlong keyRefLong, jcharArray passwordObj)414{415SecKeyRef keyRef = (SecKeyRef)jlong_to_ptr(keyRefLong);416SecKeyImportExportParameters paramBlock;417OSStatus err = noErr;418CFDataRef exportedData = NULL;419jbyteArray returnValue = NULL;420CFStringRef passwordStrRef = NULL;421422jsize passwordLen = 0;423jchar *passwordChars = NULL;424425if (passwordObj) {426passwordLen = (*env)->GetArrayLength(env, passwordObj);427428if (passwordLen > 0) {429passwordChars = (*env)->GetCharArrayElements(env, passwordObj, NULL);430if (passwordChars == NULL) {431goto errOut;432}433434passwordStrRef = CFStringCreateWithCharactersNoCopy(NULL, passwordChars, passwordLen, kCFAllocatorNull);435if (passwordStrRef == NULL) {436goto errOut;437}438}439}440441paramBlock.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;442// Note that setting the flags field **requires** you to pass in a password of some kind. The keychain will not prompt you.443paramBlock.flags = 0;444paramBlock.passphrase = passwordStrRef;445paramBlock.alertTitle = NULL;446paramBlock.alertPrompt = NULL;447paramBlock.accessRef = NULL;448paramBlock.keyUsage = CSSM_KEYUSE_ANY;449paramBlock.keyAttributes = CSSM_KEYATTR_RETURN_DEFAULT;450451err = SecKeychainItemExport(keyRef, kSecFormatPKCS12, 0, ¶mBlock, &exportedData);452453if (err == noErr) {454CFIndex size = CFDataGetLength(exportedData);455returnValue = (*env)->NewByteArray(env, size);456if (returnValue == NULL) {457goto errOut;458}459(*env)->SetByteArrayRegion(env, returnValue, 0, size, (jbyte *)CFDataGetBytePtr(exportedData));460}461462errOut:463if (exportedData) CFRelease(exportedData);464if (passwordStrRef) CFRelease(passwordStrRef);465if (passwordChars) {466// clear the password and release467memset(passwordChars, 0, passwordLen);468(*env)->ReleaseCharArrayElements(env, passwordObj, passwordChars,469JNI_ABORT);470}471return returnValue;472}473474475/*476* Class: apple_security_KeychainStore477* Method: _scanKeychain478* Signature: ()V479*/480JNIEXPORT void JNICALL Java_apple_security_KeychainStore__1scanKeychain481(JNIEnv *env, jobject this)482{483// Look for 'identities' -- private key and certificate chain pairs -- and add those.484// Search for these first, because a certificate that's found here as part of an identity will show up485// again later as a certificate.486addIdentitiesToKeystore(env, this);487488// Scan current keychain for trusted certificates.489addCertificatesToKeystore(env, this);490491}492493/*494* Class: apple_security_KeychainStore495* Method: _addItemToKeychain496* Signature: (Ljava/lang/String;[B)I497*/498JNIEXPORT jlong JNICALL Java_apple_security_KeychainStore__1addItemToKeychain499(JNIEnv *env, jobject this, jstring alias, jboolean isCertificate, jbyteArray rawDataObj, jcharArray passwordObj)500{501OSStatus err;502jlong returnValue = 0;503504JNF_COCOA_ENTER(env);505506jsize dataSize = (*env)->GetArrayLength(env, rawDataObj);507jbyte *rawData = (*env)->GetByteArrayElements(env, rawDataObj, NULL);508if (rawData == NULL) {509goto errOut;510}511512CFDataRef cfDataToImport = CFDataCreate(kCFAllocatorDefault, (UInt8 *)rawData, dataSize);513CFArrayRef createdItems = NULL;514515SecKeychainRef defaultKeychain = NULL;516SecKeychainCopyDefault(&defaultKeychain);517518SecExternalItemType dataType = (isCertificate == JNI_TRUE ? kSecFormatX509Cert : kSecFormatWrappedPKCS8);519520// Convert the password obj into a CFStringRef that the keychain importer can use for encryption.521SecKeyImportExportParameters paramBlock;522CFStringRef passwordStrRef = NULL;523524jsize passwordLen = 0;525jchar *passwordChars = NULL;526527if (passwordObj) {528passwordLen = (*env)->GetArrayLength(env, passwordObj);529530if (passwordLen > 0) {531passwordChars = (*env)->GetCharArrayElements(env, passwordObj, NULL);532if (passwordChars == NULL) {533goto errOut;534}535536passwordStrRef = CFStringCreateWithCharactersNoCopy(NULL, passwordChars, passwordLen, kCFAllocatorNull);537if (passwordStrRef == NULL) {538goto errOut;539}540}541}542543paramBlock.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;544// Note that setting the flags field **requires** you to pass in a password of some kind. The keychain will not prompt you.545paramBlock.flags = 0;546paramBlock.passphrase = passwordStrRef;547paramBlock.alertTitle = NULL;548paramBlock.alertPrompt = NULL;549paramBlock.accessRef = NULL;550paramBlock.keyUsage = CSSM_KEYUSE_ANY;551paramBlock.keyAttributes = CSSM_KEYATTR_RETURN_DEFAULT;552553err = SecKeychainItemImport(cfDataToImport, NULL, &dataType, NULL,5540, ¶mBlock, defaultKeychain, &createdItems);555556if (err == noErr) {557SecKeychainItemRef anItem = (SecKeychainItemRef)CFArrayGetValueAtIndex(createdItems, 0);558559// Don't bother labeling keys. They become part of an identity, and are not an accessible part of the keychain.560if (CFGetTypeID(anItem) == SecCertificateGetTypeID()) {561setLabelForItem(JNFJavaToNSString(env, alias), anItem);562}563564// Retain the item, since it will be released once when the array holding it gets released.565CFRetain(anItem);566returnValue = ptr_to_jlong(anItem);567} else {568cssmPerror("_addItemToKeychain: SecKeychainItemImport", err);569}570571(*env)->ReleaseByteArrayElements(env, rawDataObj, rawData, JNI_ABORT);572573if (createdItems != NULL) {574CFRelease(createdItems);575}576577errOut:578if (passwordStrRef) CFRelease(passwordStrRef);579if (passwordChars) {580// clear the password and release581memset(passwordChars, 0, passwordLen);582(*env)->ReleaseCharArrayElements(env, passwordObj, passwordChars,583JNI_ABORT);584}585586JNF_COCOA_EXIT(env);587588return returnValue;589}590591/*592* Class: apple_security_KeychainStore593* Method: _removeItemFromKeychain594* Signature: (J)I595*/596JNIEXPORT jint JNICALL Java_apple_security_KeychainStore__1removeItemFromKeychain597(JNIEnv *env, jobject this, jlong keychainItem)598{599SecKeychainItemRef itemToRemove = jlong_to_ptr(keychainItem);600return SecKeychainItemDelete(itemToRemove);601}602603/*604* Class: apple_security_KeychainStore605* Method: _releaseKeychainItemRef606* Signature: (J)V607*/608JNIEXPORT void JNICALL Java_apple_security_KeychainStore__1releaseKeychainItemRef609(JNIEnv *env, jobject this, jlong keychainItem)610{611SecKeychainItemRef itemToFree = jlong_to_ptr(keychainItem);612CFRelease(itemToFree);613}614615616