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