alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (KeystoreImpl.m)

This example Java source code file (KeystoreImpl.m) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

cfarrayref, cfindex, cfrelease, jnicall, jnienv, jniexport, null, osstatus, seccertificateref, seckeychainattribute, seckeychainitemcopycontent, seckeychainitemref, seckeyref, sectrustevaluate

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

 

new blog posts

 

Copyright 1998-2021 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.