|
Java example source code file (KeystoreImpl.m)
The KeystoreImpl.m Java example source code/* * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #import "apple_security_KeychainStore.h" #import <Security/Security.h> #import <Security/SecImportExport.h> #import <CoreServices/CoreServices.h> // (for require() macros) #import <JavaNativeFoundation/JavaNativeFoundation.h> static JNF_CLASS_CACHE(jc_KeychainStore, "apple/security/KeychainStore"); static JNF_MEMBER_CACHE(jm_createTrustedCertEntry, jc_KeychainStore, "createTrustedCertEntry", "(Ljava/lang/String;JJ[B)V"); static JNF_MEMBER_CACHE(jm_createKeyEntry, jc_KeychainStore, "createKeyEntry", "(Ljava/lang/String;JJ[J[[B)V"); static jstring getLabelFromItem(JNIEnv *env, SecKeychainItemRef inItem) { OSStatus status; jstring returnValue = NULL; char *attribCString = NULL; SecKeychainAttribute itemAttrs[] = { { kSecLabelItemAttr, 0, NULL } }; SecKeychainAttributeList attrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs }; status = SecKeychainItemCopyContent(inItem, NULL, &attrList, NULL, NULL); if(status) { cssmPerror("getLabelFromItem: SecKeychainItemCopyContent", status); goto errOut; } attribCString = malloc(itemAttrs[0].length + 1); strncpy(attribCString, itemAttrs[0].data, itemAttrs[0].length); attribCString[itemAttrs[0].length] = '\0'; returnValue = (*env)->NewStringUTF(env, attribCString); errOut: SecKeychainItemFreeContent(&attrList, NULL); if (attribCString) free(attribCString); return returnValue; } static jlong getModDateFromItem(JNIEnv *env, SecKeychainItemRef inItem) { OSStatus status; SecKeychainAttribute itemAttrs[] = { { kSecModDateItemAttr, 0, NULL } }; SecKeychainAttributeList attrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs }; jlong returnValue = 0; status = SecKeychainItemCopyContent(inItem, NULL, &attrList, NULL, NULL); if(status) { // This is almost always missing, so don't dump an error. // cssmPerror("getModDateFromItem: SecKeychainItemCopyContent", status); goto errOut; } memcpy(&returnValue, itemAttrs[0].data, itemAttrs[0].length); errOut: SecKeychainItemFreeContent(&attrList, NULL); return returnValue; } static void setLabelForItem(NSString *inLabel, SecKeychainItemRef inItem) { OSStatus status; const char *labelCString = [inLabel UTF8String]; // Set up attribute vector (each attribute consists of {tag, length, pointer}): SecKeychainAttribute attrs[] = { { kSecLabelItemAttr, strlen(labelCString), (void *)labelCString } }; const SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs }; // Not changing data here, just attributes. status = SecKeychainItemModifyContent(inItem, &attributes, 0, NULL); if(status) { cssmPerror("setLabelForItem: SecKeychainItemModifyContent", status); } } /* * Given a SecIdentityRef, do our best to construct a complete, ordered, and * verified cert chain, returning the result in a CFArrayRef. The result is * can be passed back to Java as a chain for a private key. */ static OSStatus completeCertChain( SecIdentityRef identity, SecCertificateRef trustedAnchor, // optional additional trusted anchor bool includeRoot, // include the root in outArray CFArrayRef *outArray) // created and RETURNED { SecTrustRef secTrust = NULL; SecPolicyRef policy = NULL; SecPolicySearchRef policySearch = NULL; SecTrustResultType secTrustResult; CSSM_TP_APPLE_EVIDENCE_INFO *dummyEv; // not used CFArrayRef certChain = NULL; // constructed chain, CERTS ONLY CFMutableArrayRef subjCerts; // passed to SecTrust CFMutableArrayRef certArray; // returned array starting with // identity CFIndex numResCerts; CFIndex dex; OSStatus ortn; SecCertificateRef certRef; /* First element in out array is the SecIdentity */ certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); CFArrayAppendValue(certArray, identity); /* the single element in certs-to-be-evaluated comes from the identity */ ortn = SecIdentityCopyCertificate(identity, &certRef); if(ortn) { /* should never happen */ cssmPerror("SecIdentityCopyCertificate", ortn); return ortn; } /* * Now use SecTrust to get a complete cert chain, using all of the * user's keychains to look for intermediate certs. * NOTE this does NOT handle root certs which are not in the system * root cert DB. */ subjCerts = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks); CFArraySetValueAtIndex(subjCerts, 0, certRef); /* the array owns the subject cert ref now */ CFRelease(certRef); /* Get a SecPolicyRef for generic X509 cert chain verification */ ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_X509_BASIC, NULL, // value &policySearch); if(ortn) { /* should never happen */ cssmPerror("SecPolicySearchCreate", ortn); goto errOut; } ortn = SecPolicySearchCopyNext(policySearch, &policy); if(ortn) { /* should never happen */ cssmPerror("SecPolicySearchCopyNext", ortn); goto errOut; } /* build a SecTrustRef for specified policy and certs */ ortn = SecTrustCreateWithCertificates(subjCerts, policy, &secTrust); if(ortn) { cssmPerror("SecTrustCreateWithCertificates", ortn); goto errOut; } if(trustedAnchor) { /* * Tell SecTrust to trust this one in addition to the current * trusted system-wide anchors. */ CFMutableArrayRef newAnchors; CFArrayRef currAnchors; ortn = SecTrustCopyAnchorCertificates(&currAnchors); if(ortn) { /* should never happen */ cssmPerror("SecTrustCopyAnchorCertificates", ortn); goto errOut; } newAnchors = CFArrayCreateMutableCopy(NULL, CFArrayGetCount(currAnchors) + 1, currAnchors); CFRelease(currAnchors); CFArrayAppendValue(newAnchors, trustedAnchor); ortn = SecTrustSetAnchorCertificates(secTrust, newAnchors); CFRelease(newAnchors); if(ortn) { cssmPerror("SecTrustSetAnchorCertificates", ortn); goto errOut; } } /* evaluate: GO */ ortn = SecTrustEvaluate(secTrust, &secTrustResult); if(ortn) { cssmPerror("SecTrustEvaluate", ortn); goto errOut; } switch(secTrustResult) { case kSecTrustResultUnspecified: /* cert chain valid, no special UserTrust assignments; drop thru */ case kSecTrustResultProceed: /* cert chain valid AND user explicitly trusts this */ break; default: /* * Cert chain construction failed. * Just go with the single subject cert we were given; maybe the * peer can complete the chain. */ ortn = noErr; goto errOut; } /* get resulting constructed cert chain */ ortn = SecTrustGetResult(secTrust, &secTrustResult, &certChain, &dummyEv); if(ortn) { cssmPerror("SecTrustEvaluate", ortn); goto errOut; } /* * Copy certs from constructed chain to our result array, skipping * the leaf (which is already there, as a SecIdentityRef) and possibly * a root. */ numResCerts = CFArrayGetCount(certChain); if(numResCerts < 1) { /* * Can't happen: If chain doesn't verify to a root, we'd * have bailed after SecTrustEvaluate(). */ ortn = noErr; goto errOut; } if(!includeRoot) { /* skip the last (root) cert) */ numResCerts--; } for(dex=1; dex<numResCerts; dex++) { certRef = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, dex); CFArrayAppendValue(certArray, certRef); } errOut: /* clean up */ if(secTrust) { CFRelease(secTrust); } if(subjCerts) { CFRelease(subjCerts); } if(policy) { CFRelease(policy); } if(policySearch) { CFRelease(policySearch); } *outArray = certArray; return ortn; } static void addIdentitiesToKeystore(JNIEnv *env, jobject keyStore) { // Search the user keychain list for all identities. Identities are a certificate/private key association that // can be chosen for a purpose such as signing or an SSL connection. SecIdentitySearchRef identitySearch = NULL; OSStatus err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_ANY, &identitySearch); SecIdentityRef theIdentity = NULL; OSErr searchResult = noErr; do { searchResult = SecIdentitySearchCopyNext(identitySearch, &theIdentity); if (searchResult == noErr) { // Get the cert from the identity, then generate a chain. SecCertificateRef certificate; SecIdentityCopyCertificate(theIdentity, &certificate); CFArrayRef certChain = NULL; // *** Should do something with this error... err = completeCertChain(theIdentity, NULL, TRUE, &certChain); CFIndex i, certCount = CFArrayGetCount(certChain); // Make a java array of certificate data from the chain. jclass byteArrayClass = (*env)->FindClass(env, "[B"); jobjectArray javaCertArray = (*env)->NewObjectArray(env, certCount, byteArrayClass, NULL); (*env)->DeleteLocalRef(env, byteArrayClass); // And, make an array of the certificate refs. jlongArray certRefArray = (*env)->NewLongArray(env, certCount); SecCertificateRef currCertRef = NULL; for (i = 0; i < certCount; i++) { CSSM_DATA currCertData; if (i == 0) currCertRef = certificate; else currCertRef = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, i); bzero(&currCertData, sizeof(CSSM_DATA)); err = SecCertificateGetData(currCertRef, &currCertData); jbyteArray encodedCertData = (*env)->NewByteArray(env, currCertData.Length); (*env)->SetByteArrayRegion(env, encodedCertData, 0, currCertData.Length, (jbyte *)currCertData.Data); (*env)->SetObjectArrayElement(env, javaCertArray, i, encodedCertData); jlong certRefElement = ptr_to_jlong(currCertRef); (*env)->SetLongArrayRegion(env, certRefArray, i, 1, &certRefElement); } // Get the private key. When needed we'll export the data from it later. SecKeyRef privateKeyRef; err = SecIdentityCopyPrivateKey(theIdentity, &privateKeyRef); // Find the label. It's a 'blob', but we interpret as characters. jstring alias = getLabelFromItem(env, (SecKeychainItemRef)certificate); // Find the creation date. jlong creationDate = getModDateFromItem(env, (SecKeychainItemRef)certificate); // Call back to the Java object to create Java objects corresponding to this security object. jlong nativeKeyRef = ptr_to_jlong(privateKeyRef); JNFCallVoidMethod(env, keyStore, jm_createKeyEntry, alias, creationDate, nativeKeyRef, certRefArray, javaCertArray); } } while (searchResult == noErr); if (identitySearch != NULL) { CFRelease(identitySearch); } } static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore) { // Search the user keychain list for all X509 certificates. SecKeychainSearchRef keychainItemSearch = NULL; OSStatus err = SecKeychainSearchCreateFromAttributes(NULL, kSecCertificateItemClass, NULL, &keychainItemSearch); SecKeychainItemRef theItem = NULL; OSErr searchResult = noErr; do { searchResult = SecKeychainSearchCopyNext(keychainItemSearch, &theItem); if (searchResult == noErr) { // Make a byte array with the DER-encoded contents of the certificate. SecCertificateRef certRef = (SecCertificateRef)theItem; CSSM_DATA currCertificate; err = SecCertificateGetData(certRef, &currCertificate); jbyteArray certData = (*env)->NewByteArray(env, currCertificate.Length); (*env)->SetByteArrayRegion(env, certData, 0, currCertificate.Length, (jbyte *)currCertificate.Data); // Find the label. It's a 'blob', but we interpret as characters. jstring alias = getLabelFromItem(env, theItem); // Find the creation date. jlong creationDate = getModDateFromItem(env, theItem); // Call back to the Java object to create Java objects corresponding to this security object. jlong nativeRef = ptr_to_jlong(certRef); JNFCallVoidMethod(env, keyStore, jm_createTrustedCertEntry, alias, nativeRef, creationDate, certData); } } while (searchResult == noErr); if (keychainItemSearch != NULL) { CFRelease(keychainItemSearch); } } /* * Class: apple_security_KeychainStore * Method: _getEncodedKeyData * Signature: (J)[B */ JNIEXPORT jbyteArray JNICALL Java_apple_security_KeychainStore__1getEncodedKeyData (JNIEnv *env, jobject this, jlong keyRefLong, jcharArray passwordObj) { SecKeyRef keyRef = (SecKeyRef)jlong_to_ptr(keyRefLong); SecKeyImportExportParameters paramBlock; OSStatus err = noErr; CFDataRef exportedData = NULL; jbyteArray returnValue = NULL; CFStringRef passwordStrRef = NULL; jsize passwordLen = 0; jchar *passwordChars = NULL; if (passwordObj) { passwordLen = (*env)->GetArrayLength(env, passwordObj); if (passwordLen > 0) { passwordChars = (*env)->GetCharArrayElements(env, passwordObj, NULL); passwordStrRef = CFStringCreateWithCharacters(kCFAllocatorDefault, passwordChars, passwordLen); } } paramBlock.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; // Note that setting the flags field **requires** you to pass in a password of some kind. The keychain will not prompt you. paramBlock.flags = 0; paramBlock.passphrase = passwordStrRef; paramBlock.alertTitle = NULL; paramBlock.alertPrompt = NULL; paramBlock.accessRef = NULL; paramBlock.keyUsage = CSSM_KEYUSE_ANY; paramBlock.keyAttributes = CSSM_KEYATTR_RETURN_DEFAULT; err = SecKeychainItemExport(keyRef, kSecFormatPKCS12, 0, ¶mBlock, &exportedData); if (err == noErr) { CFIndex size = CFDataGetLength(exportedData); returnValue = (*env)->NewByteArray(env, size); (*env)->SetByteArrayRegion(env, returnValue, 0, size, (jbyte *)CFDataGetBytePtr(exportedData)); } if (exportedData) CFRelease(exportedData); if (passwordStrRef) CFRelease(passwordStrRef); return returnValue; } /* * Class: apple_security_KeychainStore * Method: _scanKeychain * Signature: ()V */ JNIEXPORT void JNICALL Java_apple_security_KeychainStore__1scanKeychain (JNIEnv *env, jobject this) { // Look for 'identities' -- private key and certificate chain pairs -- and add those. // Search for these first, because a certificate that's found here as part of an identity will show up // again later as a certificate. addIdentitiesToKeystore(env, this); // Scan current keychain for trusted certificates. addCertificatesToKeystore(env, this); } /* * Class: apple_security_KeychainStore * Method: _addItemToKeychain * Signature: (Ljava/lang/String;[B)I */ JNIEXPORT jlong JNICALL Java_apple_security_KeychainStore__1addItemToKeychain (JNIEnv *env, jobject this, jstring alias, jboolean isCertificate, jbyteArray rawDataObj, jcharArray passwordObj) { OSStatus err; jlong returnValue = 0; JNF_COCOA_ENTER(env); jsize dataSize = (*env)->GetArrayLength(env, rawDataObj); jbyte *rawData = (*env)->GetByteArrayElements(env, rawDataObj, NULL); CFDataRef cfDataToImport = CFDataCreate(kCFAllocatorDefault, (UInt8 *)rawData, dataSize); CFArrayRef createdItems = NULL; SecKeychainRef defaultKeychain = NULL; SecKeychainCopyDefault(&defaultKeychain); SecExternalItemType dataType = (isCertificate == JNI_TRUE ? kSecFormatX509Cert : kSecFormatWrappedPKCS8); // Convert the password obj into a CFStringRef that the keychain importer can use for encryption. SecKeyImportExportParameters paramBlock; CFStringRef passwordStrRef = NULL; jsize passwordLen = 0; jchar *passwordChars = NULL; if (passwordObj) { passwordLen = (*env)->GetArrayLength(env, passwordObj); passwordChars = (*env)->GetCharArrayElements(env, passwordObj, NULL); passwordStrRef = CFStringCreateWithCharacters(kCFAllocatorDefault, passwordChars, passwordLen); } paramBlock.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; // Note that setting the flags field **requires** you to pass in a password of some kind. The keychain will not prompt you. paramBlock.flags = 0; paramBlock.passphrase = passwordStrRef; paramBlock.alertTitle = NULL; paramBlock.alertPrompt = NULL; paramBlock.accessRef = NULL; paramBlock.keyUsage = CSSM_KEYUSE_ANY; paramBlock.keyAttributes = CSSM_KEYATTR_RETURN_DEFAULT; err = SecKeychainItemImport(cfDataToImport, NULL, &dataType, NULL, 0, ¶mBlock, defaultKeychain, &createdItems); if (err == noErr) { SecKeychainItemRef anItem = (SecKeychainItemRef)CFArrayGetValueAtIndex(createdItems, 0); // Don't bother labeling keys. They become part of an identity, and are not an accessible part of the keychain. if (CFGetTypeID(anItem) == SecCertificateGetTypeID()) { setLabelForItem(JNFJavaToNSString(env, alias), anItem); } // Retain the item, since it will be released once when the array holding it gets released. CFRetain(anItem); returnValue = ptr_to_jlong(anItem); } else { cssmPerror("_addItemToKeychain: SecKeychainItemImport", err); } (*env)->ReleaseByteArrayElements(env, rawDataObj, rawData, JNI_ABORT); if (createdItems != NULL) { CFRelease(createdItems); } JNF_COCOA_EXIT(env); return returnValue; } /* * Class: apple_security_KeychainStore * Method: _removeItemFromKeychain * Signature: (J)I */ JNIEXPORT jint JNICALL Java_apple_security_KeychainStore__1removeItemFromKeychain (JNIEnv *env, jobject this, jlong keychainItem) { SecKeychainItemRef itemToRemove = jlong_to_ptr(keychainItem); return SecKeychainItemDelete(itemToRemove); } /* * Class: apple_security_KeychainStore * Method: _releaseKeychainItemRef * Signature: (J)V */ JNIEXPORT void JNICALL Java_apple_security_KeychainStore__1releaseKeychainItemRef (JNIEnv *env, jobject this, jlong keychainItem) { SecKeychainItemRef itemToFree = jlong_to_ptr(keychainItem); CFRelease(itemToFree); } Other Java examples (source code examples)Here is a short list of links related to this Java KeystoreImpl.m source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.