This is an automated email from the ASF dual-hosted git repository. matthiasblaesing pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/netbeans.git
commit 58ded7130bee8c57c84c12457fb34badcd468939 Author: Matthias Bläsing <[email protected]> AuthorDate: Sun Nov 3 15:47:07 2019 +0100 Base certificate validation in UpdateCenter on KeyStore and not on trust to the embedded certpath Instead of trusting the provided chain embedded in a NBM, validity of a signature is now checked against a TrustStore, as is already done to establish trust. The KeyStoreProvider SPI was extended to allow to specify a level of trust. This way a module can provide trusted and/or validation certificates. --- platform/autoupdate.services/apichanges.xml | 23 ++ platform/autoupdate.services/build.xml | 28 +++ platform/autoupdate.services/manifest.mf | 2 +- .../autoupdate/services/InstallSupportImpl.java | 26 ++- .../modules/autoupdate/services/Utilities.java | 236 +++++++++++---------- .../netbeans/spi/autoupdate/KeyStoreProvider.java | 45 +++- .../autoupdate/services/VerifyFileTest.java | 29 ++- 7 files changed, 262 insertions(+), 127 deletions(-) diff --git a/platform/autoupdate.services/apichanges.xml b/platform/autoupdate.services/apichanges.xml index 9cee0f7..350a3a2 100644 --- a/platform/autoupdate.services/apichanges.xml +++ b/platform/autoupdate.services/apichanges.xml @@ -33,6 +33,29 @@ <!-- ACTUAL CHANGES BEGIN HERE: --> <changes> + <change id="keystores-for-validation"> + <api name="general"/> + <summary>KeyStoreProviders can now report which trustlevel they intent to supply</summary> + <version major="1" minor="61"/> + <date day="4" month="11" year="2019"/> + <author login="matthiasblaesing"/> + <compatibility addition="yes" binary="compatible" deletion="no" deprecation="no" semantic="compatible" source="compatible"/> + <description> + <p> + The validation of signatures of NBMs was done by checking if at least a partial certificate chain was present + and if that chain was valid (i.e. not expired, not revoked). Instead of relying on this partial check this + version bases the verification on a list of trusted certificates, similar to the existing KeyStoreProvider. + </p> + <p> + The existing KeyStoreProviders provide Certificates, that are fully trusted. The new getTrustLevel method + allows a KeyStoreProvider to provide certificate for the new, lower trusted level. In addition to the to + levels TRUST and VALIDATE, two variants: TRUST_CA and VALIDATE_CA are introduced. Certificates provided with + that level are expected to be CA certificates and they are only trusted if a `CertPathValidator` validates + the chain. + </p> + </description> + <class package="org.netbeans.api.autoupdate" name="OperationContainer"/> + </change> <change id="missing-elements"> <api name="general"/> <summary>Report parts of a feature which is not installed yet</summary> diff --git a/platform/autoupdate.services/build.xml b/platform/autoupdate.services/build.xml index 971c989..33f8799 100644 --- a/platform/autoupdate.services/build.xml +++ b/platform/autoupdate.services/build.xml @@ -52,6 +52,20 @@ <target name="netbeans-extra" depends="jar-updater"/> <target name="do-unit-test-build" depends="projectized.do-unit-test-build"> + <property name="validation-store" value="${build.test.unit.classes.dir}/org/netbeans/api/autoupdate/data/test-validate-keystore.jks" /> + <delete file="${validation-store}" /> + <genkey + keystore="${validation-store}" + alias="demo-key" storepass="password" + dname="CN=Demo Key, OU=NetBeans, O=Apache.org, C=US" + /> + <property name="unvalidation-store" value="${build.test.unit.classes.dir}/org/netbeans/api/autoupdate/data/test-unvalidate-keystore.jks" /> + <delete file="${unvalidation-store}" /> + <genkey + keystore="${unvalidation-store}" + alias="demo-key-unvalidated" storepass="password" + dname="CN=Demo Key (unvalidated), OU=NetBeans, O=Apache.org, C=US" + /> <touch file="${build.dir}/Dummy.class" /> <touch file="${build.dir}/Dummy2.class" /> <jar destfile="${build.test.unit.classes.dir}/org/netbeans/api/autoupdate/data/dummy-signed.jar"> @@ -83,6 +97,20 @@ update="true"> <zipfileset prefix="dummy/" file="${build.dir}/Dummy2.class" /> </zip> + <jar destfile="${build.test.unit.classes.dir}/org/netbeans/api/autoupdate/data/dummy-validated.jar"> + <zipfileset prefix="dummy/" file="${build.dir}/Dummy.class"/> + </jar> + <signjar jar="${build.test.unit.classes.dir}/org/netbeans/api/autoupdate/data/dummy-validated.jar" + keystore="${validation-store}" + storepass="password" + alias="demo-key" /> + <jar destfile="${build.test.unit.classes.dir}/org/netbeans/api/autoupdate/data/dummy-unvalidated.jar"> + <zipfileset prefix="dummy/" file="${build.dir}/Dummy.class"/> + </jar> + <signjar jar="${build.test.unit.classes.dir}/org/netbeans/api/autoupdate/data/dummy-unvalidated.jar" + keystore="${unvalidation-store}" + storepass="password" + alias="demo-key-unvalidated" /> </target> </project> diff --git a/platform/autoupdate.services/manifest.mf b/platform/autoupdate.services/manifest.mf index 0948f17..f664430 100644 --- a/platform/autoupdate.services/manifest.mf +++ b/platform/autoupdate.services/manifest.mf @@ -1,7 +1,7 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.modules.autoupdate.services OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/autoupdate/services/resources/Bundle.properties -OpenIDE-Module-Specification-Version: 1.60 +OpenIDE-Module-Specification-Version: 1.61 OpenIDE-Module-Layer: org/netbeans/modules/autoupdate/services/resources/layer.xml AutoUpdate-Show-In-Client: false AutoUpdate-Essential-Module: true diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java index 43e7f84..5db31b9 100644 --- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java +++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java @@ -28,10 +28,13 @@ import java.net.SocketTimeoutException; import java.net.URL; import java.net.URLConnection; import java.net.UnknownHostException; +import java.security.CodeSigner; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.cert.CertPath; import java.security.cert.Certificate; +import java.security.cert.TrustAnchor; +import java.security.cert.X509Certificate; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; @@ -52,6 +55,7 @@ import org.netbeans.modules.autoupdate.updateprovider.ModuleItem; import org.netbeans.modules.autoupdate.updateprovider.NetworkAccess; import org.netbeans.modules.autoupdate.updateprovider.NetworkAccess.Task; import org.netbeans.modules.autoupdate.updateprovider.UpdateItemImpl; +import org.netbeans.spi.autoupdate.KeyStoreProvider; import org.netbeans.spi.autoupdate.UpdateItem; import org.netbeans.updater.ModuleDeactivator; import org.netbeans.updater.ModuleUpdater; @@ -1056,9 +1060,21 @@ public class InstallSupportImpl { try { // get trusted certificates Set<Certificate> trustedCerts = new HashSet<> (); - for (KeyStore ks : Utilities.getKeyStore ()) { + Set<Certificate> validationCerts = new HashSet<>(); + Set<TrustAnchor> trustedCACerts = new HashSet<>(); + Set<TrustAnchor> validationCACerts = new HashSet<>(); + for (KeyStore ks : Utilities.getKeyStore (KeyStoreProvider.TrustLevel.TRUST)) { trustedCerts.addAll(Utilities.getCertificates(ks)); } + for (KeyStore ks : Utilities.getKeyStore (KeyStoreProvider.TrustLevel.VALIDATE)) { + validationCerts.addAll(Utilities.getCertificates(ks)); + } + for (KeyStore ks : Utilities.getKeyStore (KeyStoreProvider.TrustLevel.TRUST_CA)) { + trustedCACerts.addAll(Utilities.getTrustAnchor(ks)); + } + for (KeyStore ks : Utilities.getKeyStore (KeyStoreProvider.TrustLevel.VALIDATE_CA)) { + validationCACerts.addAll(Utilities.getTrustAnchor(ks)); + } // load user certificates KeyStore ks = Utilities.loadKeyStore (); if (ks != null) { @@ -1073,7 +1089,7 @@ public class InstallSupportImpl { UpdateElementImpl impl = Trampoline.API.impl(el); try { - Collection<CertPath> nbmCerts = Utilities.getNbmCertificates(nbmFile); + Collection<CodeSigner> nbmCerts = Utilities.getNbmCertificates(nbmFile); if(nbmCerts == null) { res = Utilities.N_A; } else if (nbmCerts.isEmpty()) { @@ -1083,15 +1099,15 @@ public class InstallSupportImpl { // choose the certpath, that has the highest trust level // TRUSTED -> SIGNATURE_VERIFIED -> SIGNATURE_UNVERIFIED // or comes first - for(CertPath cp: nbmCerts) { - String localRes = Utilities.verifyCertificates(cp, trustedCerts); + for(CodeSigner cs: nbmCerts) { + String localRes = Utilities.verifyCertificates(cs, trustedCerts, trustedCACerts, validationCerts, validationCACerts); // If there is no previous result or if the local // verification yielded a better result than the // previous result, replace it if (res == null || VERIFICATION_RESULT_COMPARATOR.compare(res, localRes) > 0) { res = localRes; - certs.put(el, (List<Certificate>) cp.getCertificates()); + certs.put(el, (List<Certificate>) cs.getSignerCertPath().getCertificates()); } } } diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/Utilities.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/Utilities.java index fcd3139..fdef5f2 100644 --- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/Utilities.java +++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/Utilities.java @@ -27,16 +27,13 @@ import java.security.InvalidAlgorithmParameterException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; -import java.security.Principal; -import java.security.Security; -import java.security.cert.CertPath; import java.security.cert.CertPathValidator; import java.security.cert.CertPathValidatorException; import java.security.cert.Certificate; import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.PKIXCertPathValidatorResult; import java.security.cert.PKIXParameters; +import java.security.cert.PKIXRevocationChecker; +import java.security.cert.PKIXRevocationChecker.Option; import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.text.ParseException; @@ -60,6 +57,7 @@ import org.netbeans.modules.autoupdate.updateprovider.DummyModuleInfo; import org.netbeans.modules.autoupdate.updateprovider.InstalledModuleProvider; import org.netbeans.modules.autoupdate.updateprovider.UpdateItemImpl; import org.netbeans.spi.autoupdate.KeyStoreProvider; +import org.netbeans.spi.autoupdate.KeyStoreProvider.TrustLevel; import org.netbeans.spi.autoupdate.UpdateItem; import org.netbeans.updater.ModuleDeactivator; import org.netbeans.updater.ModuleUpdater; @@ -104,28 +102,38 @@ public class Utilities { private static final String USER_KS_KEY = "userKS"; private static final String USER_KS_FILE_NAME = "user.ks"; private static final String KS_USER_PASSWORD = "open4user"; - private static Lookup.Result<KeyStoreProvider> result; + private static Lookup.Result<KeyStoreProvider> keyStoreLookupResult; + private static Map<TrustLevel,List<KeyStore>> keystoreCache = Collections.synchronizedMap(new HashMap<>()); private static final Logger err = Logger.getLogger(Utilities.class.getName ()); - public static Collection<KeyStore> getKeyStore () { - if (result == null) { - result = Lookup.getDefault ().lookupResult (KeyStoreProvider.class); - result.addLookupListener (new KeyStoreProviderListener ()); - } - Collection<? extends KeyStoreProvider> c = result.allInstances (); - if (c == null || c.isEmpty ()) { - return Collections.emptyList (); + public static Collection<KeyStore> getKeyStore (TrustLevel trustLevel) { + if (keyStoreLookupResult == null) { + keyStoreLookupResult = Lookup.getDefault().lookupResult(KeyStoreProvider.class); + keyStoreLookupResult.addLookupListener(new KeyStoreProviderListener()); } - List<KeyStore> kss = new ArrayList<>(); - for (KeyStoreProvider provider : c) { - KeyStore ks = provider.getKeyStore (); - if (ks != null) { - kss.add (ks); + List<KeyStore> result = keystoreCache.get(trustLevel); + + if (result == null) { + Collection<? extends KeyStoreProvider> c = keyStoreLookupResult.allInstances(); + if (c == null || c.isEmpty()) { + return Collections.emptyList(); + } + List<KeyStore> kss = new ArrayList<>(); + + for (KeyStoreProvider provider : c) { + KeyStore ks = provider.getKeyStore(); + if (ks != null) { + kss.add(ks); + } } + + result = Collections.unmodifiableList(kss); + + keystoreCache.put(trustLevel, result); } - return kss; + return result; } /** @@ -134,103 +142,80 @@ public class Utilities { * @param trustedCertificates * @return */ - public static String verifyCertificates(CertPath archiveCertPath, Collection<? extends Certificate> trustedCertificates) { + public static String verifyCertificates(CodeSigner archiveCertPath, + Collection<Certificate> trustedCertificates, + Set<TrustAnchor> trustedCACertificates, + Collection<Certificate> validateCertificates, + Set<TrustAnchor> validationCACertificates) { assert archiveCertPath != null; - List<? extends Certificate> archiveCertificates = archiveCertPath.getCertificates(); - if (!archiveCertificates.isEmpty()) { - Collection<Certificate> c = new HashSet<>(trustedCertificates); - c.retainAll(archiveCertificates); - if (c.isEmpty()) { - Map<Principal, X509Certificate> certSubjectsMap = new HashMap<>(); - Set<Principal> certIssuersSet = new HashSet<>(); - for (Certificate cert : archiveCertificates) { - if (cert != null) { - X509Certificate x509Cert = (X509Certificate) cert; - certSubjectsMap.put(x509Cert.getSubjectDN(), x509Cert); - if (x509Cert.getIssuerDN() != null) { - certIssuersSet.add(x509Cert.getIssuerDN()); - } - } - } - Map<X509Certificate, X509Certificate> candidates = new HashMap<>(); - for (Principal p : certSubjectsMap.keySet()) { - // cert chain may not be ordered - trust anchor could before certificate itself - if (certIssuersSet.contains(p)) { - continue; - } - X509Certificate cert = certSubjectsMap.get(p); + List<? extends Certificate> archiveCertificates = archiveCertPath.getSignerCertPath().getCertificates(); - Principal tap = cert.getIssuerDN(); - if (tap != null) { - X509Certificate tempTrustAnchor = certSubjectsMap.get(tap); - if (tempTrustAnchor != null) { - candidates.put(cert, tempTrustAnchor); - } - } - } + if(archiveCertificates.isEmpty()) { + return UNSIGNED; + } - // TRUSTED = 2 - // SIGNATURE_VERIFIED = 1 - // SIGNATURE_UNVERIFIED = 0 - int res = 0; - for (X509Certificate cert : candidates.keySet()) { - X509Certificate trustCert = candidates.get(cert); - PKIXCertPathValidatorResult validResult = null; - try { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - List certList = new ArrayList(); - certList.add(cert); - CertPath cp = cf.generateCertPath(certList); - TrustAnchor trustAnchor = new TrustAnchor(trustCert, null); - PKIXParameters params = new PKIXParameters(Collections.singleton(trustAnchor)); - params.setRevocationEnabled(true); - Security.setProperty("ocsp.enable", "true"); - System.setProperty("com.sun.security.enableCRLDP", "true"); // CRL fallback - CertPathValidator cpv = CertPathValidator.getInstance("PKIX"); - validResult = (PKIXCertPathValidatorResult) cpv.validate(cp, params); - } catch (CertificateException | InvalidAlgorithmParameterException | NoSuchAlgorithmException ex) { - // CertificateException - Should not get here - "X.509" is proper certificate type - // InvalidAlgorithmParameterException - Should not get here - trustAnchor cannot be null -> collection cannot be empty - // NoSuchAlgorithmException - Should not get here - "PKIX" is proper algorythm - err.log(Level.SEVERE, "Certificate verification failed - " + ex.getMessage(), ex); - //SIGNATURE_UNVERIFIED - result = 0; - } catch (CertPathValidatorException ex) { - // CertPath cannot be validated - err.log(Level.INFO, "Cannot validate certificate path - " + ex.getMessage(), ex); - //SIGNATURE_UNVERIFIED - result = 0; - } catch (SecurityException ex) { - // When jar/nbm correctly signed, but content modified - err.log(Level.INFO, "The content of the jar/nbm has been modified - " + ex.getMessage(), ex); - return MODIFIED; - } + // Case 1: We have direct trust into one of the certificates of the + // certificate chain + if(isChainTrusted(archiveCertificates, trustedCertificates)) { + return TRUSTED; + } - if (validResult != null) { - String certDNName = cert.getSubjectDN().getName(); - if (certDNName.contains("CN=\"Oracle America, Inc.\"") - && (certDNName.contains("OU=Software Engineering") || certDNName.contains("OU=Code Signing Bureau"))) { - res = 2; - break; - } else { - res = 1; - } - } - } + // Case 2: We trust the CA, that issued a certificate - do the + // normal CertPathValidation + if(verifyCACertificatePath(trustedCACertificates, archiveCertPath)) { + return TRUSTED; + } - switch (res) { - case 2: - return TRUSTED; - case 1: - return SIGNATURE_VERIFIED; - default: - return SIGNATURE_UNVERIFIED; - } - } else { - // signed by trusted certificate stored in user's keystore od ide.ks - return TRUSTED; + // Case 3: We have a list of certificates, that we directly mark as + // valid + if(isChainTrusted(archiveCertificates, validateCertificates)) { + return SIGNATURE_VERIFIED; + } + + // Case 4: We trust the CA to do validation + if (verifyCACertificatePath(validationCACertificates, archiveCertPath)) { + return SIGNATURE_VERIFIED; + } + + // Case 5: File is not signed + return SIGNATURE_UNVERIFIED; + } + + private static boolean verifyCACertificatePath(Set<TrustAnchor> trustedCACertificates, CodeSigner archiveCertPath) { + if(trustedCACertificates.isEmpty()) { + return false; + } + try { + CertPathValidator cpv = CertPathValidator.getInstance("PKIX"); + PKIXParameters verificationParameters = new PKIXParameters(trustedCACertificates); + PKIXRevocationChecker rc = (PKIXRevocationChecker) cpv.getRevocationChecker(); + rc.setOptions(EnumSet.of(Option.SOFT_FAIL)); + verificationParameters.addCertPathChecker(rc); + if (archiveCertPath.getTimestamp() != null) { + cpv.validate(archiveCertPath.getSignerCertPath(), verificationParameters); + verificationParameters.setDate(archiveCertPath.getTimestamp().getTimestamp()); } + // validate raises a CertPathValidatorException if validation failed + cpv.validate(archiveCertPath.getSignerCertPath(), verificationParameters); + return true; + } catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException ex) { + // InvalidAlgorithmParameterException - Should not get here - trustAnchor cannot be null -> collection cannot be empty + // NoSuchAlgorithmException - Should not get here - "PKIX" is proper algorythm + err.log(Level.SEVERE, "Certificate verification failed - " + ex.getMessage(), ex); + //SIGNATURE_UNVERIFIED - result = 0; + } catch (CertPathValidatorException ex) { + // CertPath cannot be validated + err.log(Level.INFO, "Cannot validate certificate path - " + ex.getMessage(), ex); + //SIGNATURE_UNVERIFIED - result = 0; } - return UNSIGNED; + return false; + } + + private static boolean isChainTrusted(Collection<? extends Certificate> archiveCertificates, Collection<? extends Certificate> trustedCertificates) { + Collection<Certificate> c = new HashSet(trustedCertificates); + c.retainAll(archiveCertificates); + return ! c.isEmpty(); } /** @@ -270,7 +255,7 @@ public class Utilities { } public static Collection<Certificate> getCertificates (KeyStore keyStore) throws KeyStoreException { - Set<Certificate> certs = new HashSet<Certificate> (); + Set<Certificate> certs = new HashSet<> (); for (String alias: Collections.list (keyStore.aliases ())) { Certificate[] certificateChain = keyStore.getCertificateChain(alias); if (certificateChain != null) { @@ -281,18 +266,37 @@ public class Utilities { return certs; } + public static Collection<TrustAnchor> getTrustAnchor (KeyStore keyStore) throws KeyStoreException { + Set<TrustAnchor> certs = new HashSet<> (); + for (String alias: Collections.list (keyStore.aliases ())) { + Certificate[] certificateChain = keyStore.getCertificateChain(alias); + if (certificateChain != null) { + for(Certificate cert: certificateChain) { + if(cert instanceof X509Certificate) { + certs.add(new TrustAnchor((X509Certificate) cert, null)); + } + } + } + Certificate aliasCert = keyStore.getCertificate(alias); + if(aliasCert instanceof X509Certificate) { + certs.add(new TrustAnchor((X509Certificate) aliasCert, null)); + } + } + return certs; + } + /** * Get the certpaths that were used to sign the NBM content. * * @param nbmFile - * @return collection of CertPaths, that were used to sign the non-signature + * @return collection of CodeSigners, that were used to sign the non-signature * entries of the NBM * @throws IOException * @throws SecurityException if JAR was tampered with or if the certificate * chains are not consistent */ - public static Collection<CertPath> getNbmCertificates (File nbmFile) throws IOException, SecurityException { - Set<CertPath> certs = null; + public static Collection<CodeSigner> getNbmCertificates (File nbmFile) throws IOException, SecurityException { + Set<CodeSigner> certs = null; // Empty means only the MANIFEST.MF is present - special cased to be in // line with established behaviour @@ -317,11 +321,11 @@ public class Utilities { if(! entry.getName().equals("META-INF/MANIFEST.MF")) { empty = false; } - Set<CertPath> entryCerts = new HashSet<>(); + Set<CodeSigner> entryCerts = new HashSet<>(); CodeSigner[] codeSigners = entry.getCodeSigners(); if (codeSigners != null) { for (CodeSigner cs : entry.getCodeSigners()) { - entryCerts.add(cs.getSignerCertPath()); + entryCerts.add(cs); } } if(certs == null) { @@ -362,7 +366,7 @@ public class Utilities { @Override public void resultChanged (LookupEvent ev) { - result = null; + keystoreCache.clear(); } } diff --git a/platform/autoupdate.services/src/org/netbeans/spi/autoupdate/KeyStoreProvider.java b/platform/autoupdate.services/src/org/netbeans/spi/autoupdate/KeyStoreProvider.java index 5f2b3ad..e94fb1d 100644 --- a/platform/autoupdate.services/src/org/netbeans/spi/autoupdate/KeyStoreProvider.java +++ b/platform/autoupdate.services/src/org/netbeans/spi/autoupdate/KeyStoreProvider.java @@ -29,9 +29,52 @@ import java.security.KeyStore; * @author Jiri Rechtacek */ public interface KeyStoreProvider { - + /** + * TrustLevel describes the level of trust, that a {@link KeyStoreProvider} + * assigns to the provided keystore. + * + * @since 1.61 + */ + public enum TrustLevel { + /** + * Unlimited trust - modules signed with certificates in this store + * will be installed without further user requests. This level is by + * default used for the update centers of the IDE itself. + */ + TRUST, + /** + * Unlimited trust - modules signed with certificates in this store + * will be installed without further user requests. This level is by + * default used for the update centers of the IDE itself. It differes + * from {@link TRUST} in that, these certificates are subject to a + * {@code CertPathValidator} + */ + TRUST_CA, + /** + * Plugins signed with certificates from this store will show up as + * "Signed and valid". + */ + VALIDATE, + /** + * Plugins signed with certificates created by these certificates + * will show up as "Signed and valid". While certificates provided by + * {@link VALIDATE} not subject to PKIX checking, these certificates + * are run through a {@code CertPathValidator}. + */ + VALIDATE_CA + } + /** * @return KeyStore */ public KeyStore getKeyStore (); + + /** + * @return TrustLevel that is provided by the keystore this + * {@link KeyStoreProvider} provides + * @since 1.61 + */ + default TrustLevel getTrustLevel() { + return TrustLevel.TRUST; + } } diff --git a/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/VerifyFileTest.java b/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/VerifyFileTest.java index b55e48f..7813bc5 100644 --- a/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/VerifyFileTest.java +++ b/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/VerifyFileTest.java @@ -25,11 +25,15 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; +import java.security.CodeSigner; import java.security.KeyStore; import java.security.KeyStoreException; -import java.security.cert.CertPath; import java.security.cert.Certificate; +import java.security.cert.TrustAnchor; import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.netbeans.api.autoupdate.TestUtils; @@ -42,6 +46,7 @@ public class VerifyFileTest extends NbTestCase { private static final Logger LOG = Logger.getLogger(VerifyFileTest.class.getName()); private KeyStore ks; + private KeyStore validateKs; public VerifyFileTest(String testName) { super(testName); @@ -51,9 +56,14 @@ public class VerifyFileTest extends NbTestCase { protected void setUp() throws Exception { URL urlToKS = TestUtils.class.getResource("data/test-keystore.jks"); assertNotNull(urlToKS); + URL urlToValidateKS = TestUtils.class.getResource("data/test-validate-keystore.jks"); + assertNotNull(urlToValidateKS); File ksFile = org.openide.util.Utilities.toFile(urlToKS.toURI()); assertTrue(ksFile.exists()); + File validateKsFile = org.openide.util.Utilities.toFile(urlToValidateKS.toURI()); + assertTrue(validateKsFile.exists()); ks = getKeyStore(ksFile, "password"); + validateKs = getKeyStore(validateKsFile, "password"); } private String doVerification(String path) throws URISyntaxException, IOException, KeyStoreException { @@ -63,8 +73,11 @@ public class VerifyFileTest extends NbTestCase { assertTrue(jar.exists()); String res = null; try { - Collection<CertPath> nbmCerts = Utilities.getNbmCertificates(jar); + Collection<CodeSigner> nbmCerts = Utilities.getNbmCertificates(jar); Collection<Certificate> trustedCerts = Utilities.getCertificates(ks); + Set<TrustAnchor> trustedCACerts = Collections.EMPTY_SET; + Collection<Certificate> validationAnchors = new HashSet<>(Utilities.getCertificates(validateKs)); + Set<TrustAnchor> validationCACerts = Collections.EMPTY_SET; if (nbmCerts == null) { res = Utilities.N_A; } else if (nbmCerts.isEmpty()) { @@ -74,8 +87,8 @@ public class VerifyFileTest extends NbTestCase { // choose the certpath, that has the highest trust level // TRUSTED -> SIGNATURE_VERIFIED -> SIGNATURE_UNVERIFIED -> UNSIGNED // or comes first - for (CertPath cp : nbmCerts) { - String localRes = Utilities.verifyCertificates(cp, trustedCerts); + for (CodeSigner cp : nbmCerts) { + String localRes = Utilities.verifyCertificates(cp, trustedCerts, trustedCACerts, validationAnchors, validationCACerts); if (res == null || VERIFICATION_RESULT_COMPARATOR.compare(res, localRes) > 0) { res = localRes; @@ -113,6 +126,14 @@ public class VerifyFileTest extends NbTestCase { assertEquals(Utilities.TRUSTED, doVerification("data/dummy-signed-twice.jar")); } + public void testValidatedSigned() throws MalformedURLException, URISyntaxException, IOException, KeyStoreException { + assertEquals(Utilities.SIGNATURE_VERIFIED, doVerification("data/dummy-validated.jar")); + } + + public void testUnvalidatedSigned() throws MalformedURLException, URISyntaxException, IOException, KeyStoreException { + assertEquals(Utilities.SIGNATURE_UNVERIFIED, doVerification("data/dummy-unvalidated.jar")); + } + public void testUnsignedPartiallySigned() throws MalformedURLException, URISyntaxException, IOException, KeyStoreException { assertEquals(Utilities.MODIFIED, doVerification("data/dummy-partial-signed.jar")); } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected] For further information about the NetBeans mailing lists, visit: https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists
