11/*
2- * Copyright (c) 2011, 2021 , Oracle and/or its affiliates. All rights reserved.
2+ * Copyright (c) 2011, 2022 , Oracle and/or its affiliates. All rights reserved.
33 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44 *
55 * This code is free software; you can redistribute it and/or modify it
@@ -68,6 +68,25 @@ static class TrustedCertEntry {
6868
6969 Certificate cert ;
7070 long certRef ; // SecCertificateRef for this key
71+
72+ // Each KeyStore.TrustedCertificateEntry have 2 attributes:
73+ // 1. "trustSettings" -> trustSettings.toString()
74+ // 2. "2.16.840.1.113894.746875.1.1" -> trustedKeyUsageValue
75+ // The 1st one is mainly for debugging use. The 2nd one is similar
76+ // to the attribute with the same key in a PKCS12KeyStore.
77+
78+ // The SecTrustSettingsCopyTrustSettings() output for this certificate
79+ // inside the KeyChain in its original array of CFDictionaryRef objects
80+ // structure with values dumped as strings. For each trust, an extra
81+ // entry "SecPolicyOid" is added whose value is the OID for this trust.
82+ // The extra entries are used to construct trustedKeyUsageValue.
83+ List <Map <String , String >> trustSettings ;
84+
85+ // One or more OIDs defined in http://oidref.com/1.2.840.113635.100.1.
86+ // It can also be "2.5.29.37.0" for a self-signed certificate with
87+ // an empty trust settings. This value is never empty. When there are
88+ // multiple OID values, it takes the form of "[1.1.1, 1.1.2]".
89+ String trustedKeyUsageValue ;
7190 };
7291
7392 /**
@@ -300,6 +319,35 @@ public Certificate engineGetCertificate(String alias) {
300319 }
301320 }
302321
322+ private record LocalAttr (String name , String value )
323+ implements KeyStore .Entry .Attribute {
324+
325+ @ Override
326+ public String getName () {
327+ return name ;
328+ }
329+
330+ @ Override
331+ public String getValue () {
332+ return value ;
333+ }
334+ }
335+
336+ @ Override
337+ public KeyStore .Entry engineGetEntry (String alias , KeyStore .ProtectionParameter protParam )
338+ throws KeyStoreException , NoSuchAlgorithmException , UnrecoverableEntryException {
339+ if (engineIsCertificateEntry (alias )) {
340+ Object entry = entries .get (alias .toLowerCase ());
341+ if (entry instanceof TrustedCertEntry tEntry ) {
342+ return new KeyStore .TrustedCertificateEntry (
343+ tEntry .cert , Set .of (
344+ new LocalAttr (KnownOIDs .ORACLE_TrustedKeyUsage .value (), tEntry .trustedKeyUsageValue ),
345+ new LocalAttr ("trustSettings" , tEntry .trustSettings .toString ())));
346+ }
347+ }
348+ return super .engineGetEntry (alias , protParam );
349+ }
350+
303351 /**
304352 * Returns the creation date of the entry identified by the given alias.
305353 *
@@ -453,55 +501,12 @@ public void engineSetKeyEntry(String alias, byte[] key,
453501 }
454502
455503 /**
456- * Assigns the given certificate to the given alias.
457- *
458- * <p>If the given alias already exists in this keystore and identifies a
459- * <i>trusted certificate entry</i>, the certificate associated with it is
460- * overridden by the given certificate.
461- *
462- * @param alias the alias name
463- * @param cert the certificate
464- *
465- * @exception KeyStoreException if the given alias already exists and does
466- * not identify a <i>trusted certificate entry</i>, or this operation
467- * fails for some other reason.
504+ * Adding trusted certificate entry is not supported.
468505 */
469506 public void engineSetCertificateEntry (String alias , Certificate cert )
470- throws KeyStoreException
471- {
472- permissionCheck ();
473-
474- synchronized (entries ) {
475-
476- Object entry = entries .get (alias .toLowerCase ());
477- if ((entry != null ) && (entry instanceof KeyEntry )) {
478- throw new KeyStoreException
479- ("Cannot overwrite key entry with certificate" );
480- }
481-
482- // This will be slow, but necessary. Enumerate the values and then see if the cert matches the one in the trusted cert entry.
483- // Security framework doesn't support the same certificate twice in a keychain.
484- Collection <Object > allValues = entries .values ();
485-
486- for (Object value : allValues ) {
487- if (value instanceof TrustedCertEntry ) {
488- TrustedCertEntry tce = (TrustedCertEntry )value ;
489- if (tce .cert .equals (cert )) {
490- throw new KeyStoreException ("Keychain does not support mulitple copies of same certificate." );
491- }
492- }
493- }
494-
495- TrustedCertEntry trustedCertEntry = new TrustedCertEntry ();
496- trustedCertEntry .cert = cert ;
497- trustedCertEntry .date = new Date ();
498- String lowerAlias = alias .toLowerCase ();
499- if (entries .get (lowerAlias ) != null ) {
500- deletedEntries .put (lowerAlias , entries .get (lowerAlias ));
501- }
502- entries .put (lowerAlias , trustedCertEntry );
503- addedEntries .put (lowerAlias , trustedCertEntry );
504- }
507+ throws KeyStoreException {
508+ throw new KeyStoreException ("Cannot set trusted certificate entry." +
509+ " Use the macOS \" security add-trusted-cert\" command instead." );
505510 }
506511
507512 /**
@@ -680,10 +685,7 @@ public void engineStore(OutputStream stream, char[] password)
680685 String alias = e .nextElement ();
681686 Object entry = addedEntries .get (alias );
682687 if (entry instanceof TrustedCertEntry ) {
683- TrustedCertEntry tce = (TrustedCertEntry )entry ;
684- Certificate certElem ;
685- certElem = tce .cert ;
686- tce .certRef = addCertificateToKeychain (alias , certElem );
688+ // Cannot set trusted certificate entry
687689 } else {
688690 KeyEntry keyEntry = (KeyEntry )entry ;
689691
@@ -778,9 +780,28 @@ public void engineLoad(InputStream stream, char[] password)
778780 private native void _scanKeychain ();
779781
780782 /**
781- * Callback method from _scanKeychain. If a trusted certificate is found, this method will be called.
783+ * Callback method from _scanKeychain. If a trusted certificate is found,
784+ * this method will be called.
785+ *
786+ * inputTrust is a list of strings in groups. Each group contains key/value
787+ * pairs for one trust setting and ends with a null. Thus the size of the
788+ * whole list is (2 * s_1 + 1) + (2 * s_2 + 1) + ... + (2 * s_n + 1),
789+ * where s_i is the size of mapping for the i'th trust setting,
790+ * and n is the number of trust settings. Ex:
791+ *
792+ * key1 for trust1
793+ * value1 for trust1
794+ * ..
795+ * null (end of trust1)
796+ * key1 for trust2
797+ * value1 for trust2
798+ * ...
799+ * null (end of trust2)
800+ * ...
801+ * null (end if trust_n)
782802 */
783- private void createTrustedCertEntry (String alias , long keychainItemRef , long creationDate , byte [] derStream ) {
803+ private void createTrustedCertEntry (String alias , List <String > inputTrust ,
804+ long keychainItemRef , long creationDate , byte [] derStream ) {
784805 TrustedCertEntry tce = new TrustedCertEntry ();
785806
786807 try {
@@ -791,6 +812,69 @@ private void createTrustedCertEntry(String alias, long keychainItemRef, long cre
791812 tce .cert = cert ;
792813 tce .certRef = keychainItemRef ;
793814
815+ tce .trustSettings = new ArrayList <>();
816+ Map <String ,String > tmpMap = new LinkedHashMap <>();
817+ for (int i = 0 ; i < inputTrust .size (); i ++) {
818+ if (inputTrust .get (i ) == null ) {
819+ tce .trustSettings .add (tmpMap );
820+ if (i < inputTrust .size () - 1 ) {
821+ // Prepare an empty map for the next trust setting.
822+ // Do not just clear(), must be a new object.
823+ // Only create if not at end of list.
824+ tmpMap = new LinkedHashMap <>();
825+ }
826+ } else {
827+ tmpMap .put (inputTrust .get (i ), inputTrust .get (i +1 ));
828+ i ++;
829+ }
830+ }
831+
832+ boolean isSelfSigned ;
833+ try {
834+ cert .verify (cert .getPublicKey ());
835+ isSelfSigned = true ;
836+ } catch (Exception e ) {
837+ isSelfSigned = false ;
838+ }
839+ if (tce .trustSettings .isEmpty ()) {
840+ if (isSelfSigned ) {
841+ // If a self-signed certificate has an empty trust settings,
842+ // trust it for all purposes
843+ tce .trustedKeyUsageValue = KnownOIDs .anyExtendedKeyUsage .value ();
844+ } else {
845+ // Otherwise, return immediately. The certificate is not
846+ // added into entries.
847+ return ;
848+ }
849+ } else {
850+ List <String > values = new ArrayList <>();
851+ for (var oneTrust : tce .trustSettings ) {
852+ var result = oneTrust .get ("kSecTrustSettingsResult" );
853+ // https://developer.apple.com/documentation/security/sectrustsettingsresult?language=objc
854+ // 1 = kSecTrustSettingsResultTrustRoot, 2 = kSecTrustSettingsResultTrustAsRoot
855+ // If missing, a default value of kSecTrustSettingsResultTrustRoot is assumed
856+ // for self-signed certificates (see doc for SecTrustSettingsCopyTrustSettings).
857+ // Note that the same SecPolicyOid can appear in multiple trust settings
858+ // for different kSecTrustSettingsAllowedError and/or kSecTrustSettingsPolicyString.
859+ if ((result == null && isSelfSigned )
860+ || "1" .equals (result ) || "2" .equals (result )) {
861+ // When no kSecTrustSettingsPolicy, it means everything
862+ String oid = oneTrust .getOrDefault ("SecPolicyOid" ,
863+ KnownOIDs .anyExtendedKeyUsage .value ());
864+ if (!values .contains (oid )) {
865+ values .add (oid );
866+ }
867+ }
868+ }
869+ if (values .isEmpty ()) {
870+ return ;
871+ }
872+ if (values .size () == 1 ) {
873+ tce .trustedKeyUsageValue = values .get (0 );
874+ } else {
875+ tce .trustedKeyUsageValue = values .toString ();
876+ }
877+ }
794878 // Make a creation date.
795879 if (creationDate != 0 )
796880 tce .date = new Date (creationDate );
0 commit comments