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.
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 @@
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.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.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 @@
// + ") 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 @@
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 @@
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 @@
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 @@
// 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.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 @@
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 @@
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 @@
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 @@
currentLicense.pop ();
break;
+ case message_digest:
+ break;
default:
ERR.warning ("Unknown element " + qName);
}
@@ -454,6 +459,16 @@
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 @@
private String catalogDate;
private String fragmentHost;
+
+ private List<MessageDigestValue> hashes;
private static ModuleDescriptor md = null;
@@ -608,7 +625,7 @@
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 @@
*/
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 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"));
+ }
+}