Code Review Request JDK-8129988 JSSE should create a single instance of the cacerts KeyStore
Xuelei Fan
xuelei.fan at oracle.com
Wed Jan 4 18:38:06 UTC 2017
Updated to use synchronized method. Most of the time, the synchronized
only perform the read access to the variables. The impact on performance
should be acceptable.
http://cr.openjdk.java.net/~xuelei/8129988/webrev.03/
Only the TrustStoreManager.java implementation get updated in this webrev.
Thanks,
Xuelei
On 12/23/2016 7:52 AM, Sean Mullan wrote:
> On 12/22/16 2:52 PM, Xuelei Fan wrote:
>> updated: http://cr.openjdk.java.net/~xuelei/8129988/webrev.02/
>
> I think there are still some race conditions. For example:
>
> 264 TrustStoreDescriptor temporaryDesc = this.descriptor;
> 265 KeyStore cachedKeyStore = ksRef.get();
> 266 if (descriptor.equals(temporaryDesc) && (cachedKeyStore
> != null)) {
> 267 return cachedKeyStore;
> 268 }
>
> There is no locking here.
>
> Maybe that's ok based on your explanation below, but it seems a bit
> fragile and could lead to problems that are hard to debug. Have you
> looked at the AtomicReference class? You could define a new class
> containing the descriptor, keystore, and certs and wrap that in an
> AtomicReference and then use the methods on that class to update it.
> Might be worth exploring that a bit more.
>
> --Sean
>
>
>> On 12/22/2016 9:32 AM, Sean Mullan wrote:
>>> On 12/20/16 3:21 PM, Xuelei Fan wrote:
>>>
>>>>> 213 if (storePassword != null &&
>>>>> !storePassword.isEmpty()) {
>>>>> 214 MessageDigest md =
>>>>> JsseJce.getMessageDigest("SHA-256");
>>>>> 215 result = 31 * result +
>>>>> 216 Arrays.hashCode(md.digest(storePassword.getBytes()));
>>>>> 217 }
>>>>>
>>>>> Why are you hashing the password here? Are you afraid this could be
>>>>> leaked or guessed somehow?
>>>> Yes. The hash code of the password part can be computed. I was
>>>> wondering the String.hashCode() may not have sufficient strength.
>>>>
>>>>> I would just leave the password out of the
>>>>> hashcode and equals. It doesn't matter, it's still the same file,
>>>>> right?
>>>>> I'm not sure if the type or provider matter either. Don't you just
>>>>> care
>>>>> about the name of the file and the modification time?
>>>>>
>>>> For file type key store, the file and the modification time should be
>>>> sufficient. But for non-file (PKCS11) key store, the provider and
>>>> password may be sensible. The basic idea is that, if one of the system
>>>> property get updated, the key store should be reloaded. Checking every
>>>> property update makes the code more straightforward.
>>>
>>> But the main focus of this performance issue is for the cacerts file,
>>> which is not PKCS11. So I would not use the password and other
>>> non-relevant or security-sensitive attributes. A hash of the password
>>> isn't sufficient against dictionary-type attacks, for example.
>>>
>> I see your point. The password hash code block is removed.
>>
>>>>> 268 if ((temporaryDesc != null) &&
>>>>>
>>>>> Why would a null descriptor ever be ok? Shouldn't you just let this
>>>>> throw NPE? Same comment on line 301.
>>>>>
>>>> The temporaryDesc is initialized as null. A singleton service
>>>> (TrustStoreManage.tam) is used and lazy loaded. Null means the
>>>> descriptor has not been assigned.
>>>
>>> I think there are thread-safeness issues in the TrustStoreManager class.
>>> You are not synchronizing when you read so looks like there can be
>>> various race conditions. For example, this.descriptor and this.ksRef can
>>> be updated by another thread in the middle of this code
>>>
>>> 267 TrustStoreDescriptor temporaryDesc = this.descriptor;
>>> 268 if ((temporaryDesc != null) &&
>>> 269 temporaryDesc.equals(descriptor)) {
>>> 270 KeyStore ks = ksRef.get();
>>> 271 if (ks != null) {
>>> 272 return ks;
>>> 273 }
>>> 274 }
>>>
>>> Maybe that doesn't really matter, but I'm not sure -- have you thought
>>> about it?
>>>
>> I thought about the issue. But I really missed to the double check
>> idiom. Updated.
>>
>> For performance consideration, I'm trying to mitigate the impact of
>> synchronization. Once the key store get loaded, there is a strong
>> reference, and it can be used safely. If another thread is trying to
>> modify the descriptor and key store, this thread will use the existing
>> key store, and another thread can use the new key store. If two threads
>> try to modify the key store for the same descriptor, I added the double
>> check idiom so that the first thread will complete the update and the
>> 2nd thread will use the 1st thread updated key store. If two threads
>> try to modify the key store for different descriptor, each will get a
>> different key store and the 2nd thread will reset the final key store
>> for future use.
>>
>> In general, applications would not modify the system properties. So the
>> use of the synchronized block should be very rare. It benefits the
>> performance in multiple threading computation environment.
>>
>> Xuelei
>>
>>> --Sean
More information about the security-dev
mailing list