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 8e4916ec3d16ce8d38c8939bd84897e34f55ab44 Author: Matthias Bläsing <[email protected]> AuthorDate: Sun Nov 10 17:51:35 2019 +0100 Add option to provide a message digest in update center The idea is, that an update center can provide message digests/hashes for the modules it provides. This way, the NetBeans Platform can verify a download of a module by checking the hash, even if the download happens over an insecure link (http). If a download does not yield the same digest, installation will be blocked, just as if certification validation failed with a SecurityException. --- .../libsrc/org/netbeans/updater/XMLUtil.java | 2 + .../updater/resources/autoupdate-catalog-2_8.dtd | 96 +++++++++++++ .../autoupdate/services/InstallSupportImpl.java | 29 +++- .../autoupdate/services/MessageDigestChecker.java | 152 +++++++++++++++++++++ .../autoupdate/services/UpdateElementImpl.java | 16 ++- .../updateprovider/AutoupdateCatalogParser.java | 25 +++- .../updateprovider/MessageDigestValue.java | 79 +++++++++++ .../autoupdate/updateprovider/UpdateItemImpl.java | 11 ++ .../services/MessageDigestCheckerTest.java | 48 +++++++ 9 files changed, 450 insertions(+), 8 deletions(-) diff --git a/platform/autoupdate.services/libsrc/org/netbeans/updater/XMLUtil.java b/platform/autoupdate.services/libsrc/org/netbeans/updater/XMLUtil.java index 7ce7088..b0e7560 100644 --- a/platform/autoupdate.services/libsrc/org/netbeans/updater/XMLUtil.java +++ b/platform/autoupdate.services/libsrc/org/netbeans/updater/XMLUtil.java @@ -183,6 +183,8 @@ public final class XMLUtil extends Object { return new InputSource(XMLUtil.class.getResource("resources/autoupdate-catalog-2_7.dtd").toString()); // NOI18N } else if ("-//NetBeans//DTD Autoupdate Module Info 2.7//EN".equals(publicID)) { // NOI18N return new InputSource(XMLUtil.class.getResource("resources/autoupdate-info-2_7.dtd").toString()); // NOI18N + } else if ("-//NetBeans//DTD Autoupdate Catalog 2.8//EN".equals(publicID)) { // NOI18N + return new InputSource(XMLUtil.class.getResource("resources/autoupdate-catalog-2_8.dtd").toString()); // NOI18N } else { if (systemID.endsWith(".dtd")) { // NOI18N return new InputSource(new ByteArrayInputStream(new byte[0])); diff --git a/platform/autoupdate.services/libsrc/org/netbeans/updater/resources/autoupdate-catalog-2_8.dtd b/platform/autoupdate.services/libsrc/org/netbeans/updater/resources/autoupdate-catalog-2_8.dtd new file mode 100644 index 0000000..074e636 --- /dev/null +++ b/platform/autoupdate.services/libsrc/org/netbeans/updater/resources/autoupdate-catalog-2_8.dtd @@ -0,0 +1,96 @@ +<!-- + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + +--> +<!-- -//NetBeans//DTD Autoupdate Catalog 2.8//EN --> +<!-- XML representation of Autoupdate Modules/Updates Catalog --> + +<!ELEMENT module_updates ((notification?, content_description?, (module_group|module)*, license*)|error)> +<!ATTLIST module_updates timestamp CDATA #REQUIRED> + +<!ELEMENT module_group ((module_group|module)*)> +<!ATTLIST module_group name CDATA #REQUIRED> + +<!ELEMENT notification (#PCDATA)> +<!ATTLIST notification url CDATA #IMPLIED> + +<!ELEMENT content_description (#PCDATA)> +<!ATTLIST content_description url CDATA #IMPLIED> + +<!ELEMENT module (description?, module_notification?, external_package*, (manifest | l10n), message_digest* )> +<!ATTLIST module codenamebase CDATA #REQUIRED + homepage CDATA #IMPLIED + distribution CDATA #REQUIRED + license CDATA #IMPLIED + downloadsize CDATA #REQUIRED + needsrestart (true|false) #IMPLIED + moduleauthor CDATA #IMPLIED + releasedate CDATA #IMPLIED + global (true|false) #IMPLIED + targetcluster CDATA #IMPLIED + preferredupdate (true|false) #IMPLIED + eager (true|false) #IMPLIED + autoload (true|false) #IMPLIED> + +<!ELEMENT description (#PCDATA)> + +<!ELEMENT module_notification (#PCDATA)> + +<!ELEMENT external_package EMPTY> +<!ATTLIST external_package + name CDATA #REQUIRED + target_name CDATA #REQUIRED + start_url CDATA #REQUIRED + description CDATA #IMPLIED> + +<!ELEMENT manifest EMPTY> +<!ATTLIST manifest OpenIDE-Module CDATA #REQUIRED + OpenIDE-Module-Name CDATA #REQUIRED + OpenIDE-Module-Specification-Version CDATA #REQUIRED + OpenIDE-Module-Implementation-Version CDATA #IMPLIED + OpenIDE-Module-Module-Dependencies CDATA #IMPLIED + OpenIDE-Module-Package-Dependencies CDATA #IMPLIED + OpenIDE-Module-Java-Dependencies CDATA #IMPLIED + OpenIDE-Module-IDE-Dependencies CDATA #IMPLIED + OpenIDE-Module-Short-Description CDATA #IMPLIED + OpenIDE-Module-Long-Description CDATA #IMPLIED + OpenIDE-Module-Display-Category CDATA #IMPLIED + OpenIDE-Module-Provides CDATA #IMPLIED + OpenIDE-Module-Requires CDATA #IMPLIED + OpenIDE-Module-Recommends CDATA #IMPLIED + OpenIDE-Module-Needs CDATA #IMPLIED + AutoUpdate-Show-In-Client (true|false) #IMPLIED + AutoUpdate-Essential-Module (true|false) #IMPLIED + OpenIDE-Module-Fragment-Host CDATA #IMPLIED> + +<!ELEMENT l10n EMPTY> +<!ATTLIST l10n langcode CDATA #IMPLIED + brandingcode CDATA #IMPLIED + module_spec_version CDATA #IMPLIED + module_major_version CDATA #IMPLIED + OpenIDE-Module-Name CDATA #IMPLIED + OpenIDE-Module-Long-Description CDATA #IMPLIED> + +<!ELEMENT license (#PCDATA)> +<!ATTLIST license name CDATA #REQUIRED + url CDATA #IMPLIED> + +<!ELEMENT message_digest EMPTY> +<!ATTLIST message_digest algorithm CDATA #REQUIRED + value CDATA #REQUIRED> \ No newline at end of file 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 28263fc..7a37748 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 @@ -31,10 +31,8 @@ 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; @@ -51,6 +49,7 @@ import org.netbeans.api.autoupdate.OperationContainer.OperationInfo; import org.netbeans.api.autoupdate.OperationSupport.Restarter; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.modules.autoupdate.updateprovider.AutoupdateInfoParser; +import org.netbeans.modules.autoupdate.updateprovider.MessageDigestValue; import org.netbeans.modules.autoupdate.updateprovider.ModuleItem; import org.netbeans.modules.autoupdate.updateprovider.NetworkAccess; import org.netbeans.modules.autoupdate.updateprovider.NetworkAccess.Task; @@ -1017,7 +1016,7 @@ public class InstallSupportImpl { // + ") of is equal to estimatedSize (" + estimatedSize + ")."; if (estimatedSize != increment) { LOG.log (Level.FINEST, "Increment (" + increment + ") of is not equal to estimatedSize (" + estimatedSize + ")."); - } + } } catch (IOException ioe) { LOG.log (Level.INFO, "Writing content of URL " + source + " failed.", ioe); } finally { @@ -1029,6 +1028,7 @@ public class InstallSupportImpl { LOG.log (Level.INFO, ioe.getMessage (), ioe); } } + if (contentLength != -1 && increment != contentLength) { if(canceled) { LOG.log(Level.INFO, "Download of " + source + " was cancelled"); @@ -1121,6 +1121,29 @@ public class InstallSupportImpl { res = Utilities.MODIFIED; } + { + MessageDigestChecker mdChecker = new MessageDigestChecker(impl.getMessageDigests()); + byte[] buffer = new byte[102400]; + int read; + try(FileInputStream fis = new FileInputStream(nbmFile)) { + while((read = fis.read(buffer)) > 0) { + mdChecker.update(buffer, 0, read); + } + } + if(!mdChecker.validate()) { + for (String algorithm : mdChecker.getFailingHashes()) { + LOG.log(Level.INFO, + "Failed to validate message digest for ''{0}'' expected ''{1}'' got ''{2}''", + new Object[]{ + nbmFile.getAbsolutePath(), + mdChecker.getExpectedHashAsString(algorithm), + mdChecker.getCalculatedHashAsString(algorithm) + }); + } + res = Utilities.MODIFIED; + } + } + if (res != null) { switch (res) { case Utilities.MODIFIED: diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/MessageDigestChecker.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/MessageDigestChecker.java new file mode 100644 index 0000000..53d3eaf --- /dev/null +++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/MessageDigestChecker.java @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.autoupdate.services; + +import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.netbeans.modules.autoupdate.updateprovider.MessageDigestValue; + +public class MessageDigestChecker { + + private static final Logger LOG = Logger.getLogger(MessageDigestChecker.class.getName()); + + private final Map<String,MessageDigest> messageDigest = new HashMap<>(); + private final Map<String,byte[]> exptectedResult = new HashMap<>(); + private final Map<String,byte[]> calculatedResult = new HashMap<>(); + private Boolean overallValid = null; + + public MessageDigestChecker(Collection<MessageDigestValue> referenceHashes) { + if (referenceHashes != null) { + for (MessageDigestValue h : referenceHashes) { + try { + MessageDigest md = MessageDigest.getInstance(h.getAlgorithm()); + messageDigest.put(h.getAlgorithm(), md); + exptectedResult.put(h.getAlgorithm(), hexDecode(h.getValue())); + } catch (NoSuchAlgorithmException ex) { + LOG.log(Level.FINE, "Unsupported Hash Algorithm: {0}", h.getAlgorithm()); + } + } + } + } + + public void update(byte[] data) { + ensureValidateNotCalled(); + for(MessageDigest md: messageDigest.values()) { + md.update(data); + } + } + + public void update(byte[] data, int offset, int len) { + ensureValidateNotCalled(); + for(MessageDigest md: messageDigest.values()) { + md.update(data, offset, len); + } + } + + public void update(byte data) { + ensureValidateNotCalled(); + for(MessageDigest md: messageDigest.values()) { + md.update(data); + } + } + + private void ensureValidateNotCalled() throws IllegalStateException { + if(overallValid != null) { + throw new IllegalStateException("update must not be called after validate is invoked"); + } + } + + private void ensureValidateCalled() throws IllegalStateException { + if(overallValid == null) { + throw new IllegalStateException("This method must not be called before validate is invoked"); + } + } + + public boolean isDigestAvailable() { + return ! messageDigest.isEmpty(); + } + + public boolean validate() throws IOException { + if (overallValid == null) { + overallValid = true; + for (Entry<String, MessageDigest> e : messageDigest.entrySet()) { + String algorithm = e.getKey(); + calculatedResult.put(algorithm, e.getValue().digest()); + boolean localValid = Arrays.equals( + exptectedResult.get(algorithm), + calculatedResult.get(algorithm)); + overallValid &= localValid; + } + } + return overallValid; + } + + public List<String> getFailingHashes() { + ensureValidateCalled(); + List<String> result = new ArrayList<>(); + for(String algorithm: messageDigest.keySet()) { + if(! Arrays.equals( + exptectedResult.get(algorithm), + calculatedResult.get(algorithm))) { + result.add(algorithm); + } + } + return result; + } + + public String getExpectedHashAsString(String algorithm) { + ensureValidateCalled(); + return hexEncode(exptectedResult.get(algorithm)); + } + + public String getCalculatedHashAsString(String algorithm) { + ensureValidateCalled(); + return hexEncode(calculatedResult.get(algorithm)); + } + + public static String hexEncode(byte[] input) { + StringBuilder sb = new StringBuilder(input.length * 2); + for(byte b: input) { + sb.append(Character.forDigit((b & 0xF0) >> 4, 16)); + sb.append(Character.forDigit((b & 0x0F), 16)); + } + return sb.toString(); + } + + public static byte[] hexDecode(String input) { + int length = input.length() / 2; + byte[] result = new byte[length]; + for(int i = 0; i < length; i++) { + int b = Character.digit(input.charAt(i * 2), 16) << 4; + b |= Character.digit(input.charAt(i * 2 + 1), 16); + result[i] = (byte) (b & 0xFF); + } + return result; + } +} diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateElementImpl.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateElementImpl.java index 7cc580c..d2ba5dd 100644 --- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateElementImpl.java +++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateElementImpl.java @@ -19,10 +19,12 @@ package org.netbeans.modules.autoupdate.services; +import java.util.ArrayList; import java.util.List; import org.netbeans.api.autoupdate.UpdateElement; import org.netbeans.api.autoupdate.UpdateManager; import org.netbeans.api.autoupdate.UpdateUnit; +import org.netbeans.modules.autoupdate.updateprovider.MessageDigestValue; import org.netbeans.modules.autoupdate.updateprovider.InstallInfo; import org.netbeans.modules.autoupdate.updateprovider.UpdateItemImpl; import org.openide.modules.ModuleInfo; @@ -35,8 +37,13 @@ import org.openide.modules.SpecificationVersion; public abstract class UpdateElementImpl extends Object { private UpdateUnit unit; private UpdateElement element; + private List<MessageDigestValue> messageDigests = new ArrayList<>(); - public UpdateElementImpl (UpdateItemImpl item, String providerName) {} + public UpdateElementImpl (UpdateItemImpl item, String providerName) { + if(item.getMessageDigests() != null) { + messageDigests.addAll(item.getMessageDigests()); + } + } public UpdateUnit getUpdateUnit () { return unit; @@ -99,4 +106,11 @@ public abstract class UpdateElementImpl extends Object { // XXX: try to rid of this public abstract InstallInfo getInstallInfo (); + public List<MessageDigestValue> getMessageDigests() { + return messageDigests; + } + + public void setMessageDigests(List<MessageDigestValue> messageDigests) { + this.messageDigests = messageDigests; + } } diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogParser.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogParser.java index d95e1b4..a6d737f 100644 --- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogParser.java +++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogParser.java @@ -34,7 +34,6 @@ import java.util.logging.Logger; import java.util.zip.GZIPInputStream; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; -import org.netbeans.Module; import org.netbeans.modules.autoupdate.services.Trampoline; import org.netbeans.modules.autoupdate.services.UpdateLicenseImpl; import org.netbeans.modules.autoupdate.services.Utilities; @@ -52,6 +51,7 @@ public class AutoupdateCatalogParser extends DefaultHandler { private final AutoupdateCatalogProvider provider; private final EntityResolver entityResolver; private final URI baseUri; + private final List<MessageDigestValue> messageDigestsBuffer = new ArrayList<>(); private AutoupdateCatalogParser (Map<String, UpdateItem> items, AutoupdateCatalogProvider provider, URI base) { this.items = items; @@ -111,7 +111,7 @@ public class AutoupdateCatalogParser extends DefaultHandler { private static enum ELEMENTS { module_updates, module_group, notification, content_description, module, description, - module_notification, external_package, manifest, l10n, license + module_notification, external_package, manifest, l10n, license, message_digest } private static final String MODULE_UPDATES_ATTR_TIMESTAMP = "timestamp"; // NOI18N @@ -149,7 +149,10 @@ public class AutoupdateCatalogParser extends DefaultHandler { private static final String L10N_ATTR_MODULE_MAJOR_VERSION = "module_major_version"; // NOI18N private static final String L10N_ATTR_LOCALIZED_MODULE_NAME = "OpenIDE-Module-Name"; // NOI18N private static final String L10N_ATTR_LOCALIZED_MODULE_DESCRIPTION = "OpenIDE-Module-Long-Description"; // NOI18N - + + private static final String MESSAGE_DIGEST_ATTR_ALGORITHM = "algorithm"; // NOI18N + private static final String MESSAGE_DIGEST_ATTR_VALUE = "value"; // NOI18N + private static String GZIP_EXTENSION = ".gz"; // NOI18N private static String XML_EXTENSION = ".xml"; // NOI18N private static String GZIP_MIME_TYPE = "application/x-gzip"; // NOI18N @@ -359,6 +362,8 @@ public class AutoupdateCatalogParser extends DefaultHandler { currentLicense.pop (); break; + case message_digest: + break; default: ERR.warning ("Unknown element " + qName); } @@ -454,6 +459,16 @@ public class AutoupdateCatalogParser extends DefaultHandler { map.put(attributes.getValue (LICENSE_ATTR_NAME), attributes.getValue (LICENSE_ATTR_URL)); currentLicense.push (map); break; + case message_digest: + ModuleDescriptor desc2 = currentModule.peek (); + // At this point the manifest element must have been seen + UpdateItem ui = items.get (desc2.getId ()); + UpdateItemImpl uiImpl = Trampoline.SPI.impl (ui); + uiImpl.getMessageDigests().add(new MessageDigestValue( + attributes.getValue(MESSAGE_DIGEST_ATTR_ALGORITHM), + attributes.getValue(MESSAGE_DIGEST_ATTR_VALUE) + )); + break; default: ERR.warning ("Unknown element " + qName); } @@ -511,6 +526,8 @@ public class AutoupdateCatalogParser extends DefaultHandler { private String catalogDate; private String fragmentHost; + + private List<MessageDigestValue> hashes; private static ModuleDescriptor md = null; @@ -608,7 +625,7 @@ public class AutoupdateCatalogParser extends DefaultHandler { return res; } - private void cleanUp (){ + public void cleanUp (){ this.specVersion = null; this.mf = null; this.notification = null; diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/MessageDigestValue.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/MessageDigestValue.java new file mode 100644 index 0000000..1351ef9 --- /dev/null +++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/MessageDigestValue.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.autoupdate.updateprovider; + +import java.util.Objects; + +public class MessageDigestValue { + private String algorithm; + private String value; + + public MessageDigestValue() { + } + + public MessageDigestValue(String algorithm, String value) { + this.algorithm = algorithm; + this.value = value; + } + + public String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 53 * hash + Objects.hashCode(this.algorithm); + hash = 53 * hash + Objects.hashCode(this.value); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final MessageDigestValue other = (MessageDigestValue) obj; + if (!Objects.equals(this.algorithm, other.algorithm)) { + return false; + } + if (!Objects.equals(this.value, other.value)) { + return false; + } + return true; + } +} diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/UpdateItemImpl.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/UpdateItemImpl.java index 0f639dd..bb4497c 100644 --- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/UpdateItemImpl.java +++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/UpdateItemImpl.java @@ -19,6 +19,8 @@ package org.netbeans.modules.autoupdate.updateprovider; +import java.util.ArrayList; +import java.util.List; import org.netbeans.modules.autoupdate.services.UpdateLicenseImpl; import org.netbeans.spi.autoupdate.UpdateItem; @@ -28,6 +30,7 @@ import org.netbeans.spi.autoupdate.UpdateItem; */ public abstract class UpdateItemImpl { private UpdateItem originalUpdateItem; + private List<MessageDigestValue> messageDigests = new ArrayList<>(); /** Creates a new instance of UpdateItemImpl */ UpdateItemImpl () { @@ -64,4 +67,12 @@ public abstract class UpdateItemImpl { public String getFragmentHost() { return null; } + + public List<MessageDigestValue> getMessageDigests() { + return messageDigests; + } + + public void setMessageDigests(List<MessageDigestValue> messageDigests) { + this.messageDigests = new ArrayList<>(messageDigests); + } } diff --git a/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/MessageDigestCheckerTest.java b/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/MessageDigestCheckerTest.java new file mode 100644 index 0000000..79af4ab --- /dev/null +++ b/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/MessageDigestCheckerTest.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.autoupdate.services; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import org.junit.Test; +import static org.junit.Assert.*; + +public class MessageDigestCheckerTest { + + public MessageDigestCheckerTest() { + } + + @Test + public void testHexEncode() throws NoSuchAlgorithmException { + MessageDigest sha512 = MessageDigest.getInstance("SHA-512"); + byte[] hash = sha512.digest(new byte[] {}); + assertEquals( + "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", + MessageDigestChecker.hexEncode(hash)); + } + + @Test + public void testHexDecode() throws NoSuchAlgorithmException { + MessageDigest sha512 = MessageDigest.getInstance("SHA-512"); + byte[] hash = sha512.digest(new byte[] {}); + assertArrayEquals( + hash, + MessageDigestChecker.hexDecode("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")); + } +} --------------------------------------------------------------------- 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
