From e3ec10b826a9cb2b723693a46b7e469167714312 Mon Sep 17 00:00:00 2001 From: Daan Hoogland Date: Fri, 12 Jan 2018 17:17:11 +0100 Subject: [PATCH] fr16 checksum in java instead of script to be more platform independent, hopefully --- .../DirectTemplateDownloaderImpl.java | 41 +++----- .../utils/security/DigestHelper.java | 99 +++++++++++++++++++ 2 files changed, 111 insertions(+), 29 deletions(-) create mode 100644 utils/src/org/apache/cloudstack/utils/security/DigestHelper.java diff --git a/agent/src/com/cloud/agent/direct/download/DirectTemplateDownloaderImpl.java b/agent/src/com/cloud/agent/direct/download/DirectTemplateDownloaderImpl.java index 708a52cb1ff..f9963615e4b 100644 --- a/agent/src/com/cloud/agent/direct/download/DirectTemplateDownloaderImpl.java +++ b/agent/src/com/cloud/agent/direct/download/DirectTemplateDownloaderImpl.java @@ -20,10 +20,13 @@ package com.cloud.agent.direct.download; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.Script; -import org.apache.cloudstack.utils.security.ChecksumValue; +import org.apache.cloudstack.utils.security.DigestHelper; import org.apache.commons.lang.StringUtils; import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; import java.util.UUID; public abstract class DirectTemplateDownloaderImpl implements DirectTemplateDownloader { @@ -149,37 +152,17 @@ public abstract class DirectTemplateDownloaderImpl implements DirectTemplateDown return new DirectTemplateInformation(installPath, size, checksum); } - /** - * Return checksum command from algorithm - */ - private String getChecksumCommandFromAlgorithm(String algorithm) { - if (algorithm.equalsIgnoreCase("MD5")) { - return "md5sum"; - } else if (algorithm.equalsIgnoreCase("SHA-1")) { - return "sha1sum"; - } else if (algorithm.equalsIgnoreCase("SHA-224")) { - return "sha224sum"; - } else if (algorithm.equalsIgnoreCase("SHA-256")) { - return "sha256sum"; - } else if (algorithm.equalsIgnoreCase("SHA-384")) { - return "sha384sum"; - } else if (algorithm.equalsIgnoreCase("SHA-512")) { - return "sha512sum"; - } else { - throw new CloudRuntimeException("Unknown checksum algorithm: " + algorithm); - } - } - @Override public boolean validateChecksum() { if (StringUtils.isNotBlank(checksum)) { - ChecksumValue providedChecksum = new ChecksumValue(checksum); - String algorithm = providedChecksum.getAlgorithm(); - String checksumCommand = "echo '%s %s' | %s -c --quiet"; - String cmd = String.format(checksumCommand, providedChecksum.getChecksum(), downloadedFilePath, getChecksumCommandFromAlgorithm(algorithm)); - int result = Script.runSimpleBashScriptForExitValue(cmd); - return result == 0; + try { + return DigestHelper.check(checksum, new FileInputStream(downloadedFilePath)); + } catch (IOException e) { + throw new CloudRuntimeException("could not check sum for file: " + downloadedFilePath); + } catch (NoSuchAlgorithmException e) { + throw new CloudRuntimeException("Unknown checksum algorithm: " + checksum, e); + } } return true; } -} \ No newline at end of file +} diff --git a/utils/src/org/apache/cloudstack/utils/security/DigestHelper.java b/utils/src/org/apache/cloudstack/utils/security/DigestHelper.java new file mode 100644 index 00000000000..12a117ee2d7 --- /dev/null +++ b/utils/src/org/apache/cloudstack/utils/security/DigestHelper.java @@ -0,0 +1,99 @@ +// +// 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.apache.cloudstack.utils.security; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Map; + +public class DigestHelper { + + public static ChecksumValue digest(String algorithm, InputStream is) throws NoSuchAlgorithmException, IOException { + MessageDigest digest = MessageDigest.getInstance(algorithm); + ChecksumValue checksum = null; + byte[] buffer = new byte[8192]; + int read = 0; + while ((read = is.read(buffer)) > 0) { + digest.update(buffer, 0, read); + } + byte[] md5sum = digest.digest(); + // TODO make sure this is valid for all types of checksums !?! + BigInteger bigInt = new BigInteger(1, md5sum); + checksum = new ChecksumValue(digest.getAlgorithm(), getPaddedDigestString(digest,bigInt)); + return checksum; + } + + public static boolean check(String checksum, InputStream is) throws IOException, NoSuchAlgorithmException { + ChecksumValue toCheckAgainst = new ChecksumValue(checksum); + String algorithm = toCheckAgainst.getAlgorithm(); + ChecksumValue result = digest(algorithm,is); + return result.equals(toCheckAgainst); + } + + public static String getPaddedDigest(String algorithm, String inputString) throws NoSuchAlgorithmException { + MessageDigest digest = MessageDigest.getInstance(algorithm); + String checksum; + digest.reset(); + BigInteger pwInt = new BigInteger(1, digest.digest(inputString.getBytes())); + return getPaddedDigestString(digest, pwInt); + } + + private static String getPaddedDigestString(MessageDigest digest, BigInteger pwInt) { + String checksum; + String pwStr = pwInt.toString(16); + // we have half byte string representation, so + int padding = 2*digest.getDigestLength() - pwStr.length(); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < padding; i++) { + sb.append('0'); // make sure the MD5 password is 32 digits long + } + sb.append(pwStr); + checksum = sb.toString(); + return checksum; + } + + static final Map paddingLengths = creatPaddingLengths(); + + private static final Map creatPaddingLengths() { + Map map = new HashMap<>(); + map.put("MD5", 32); + map.put("SHA-1", 40); + map.put("SHA-224", 56); + map.put("SHA-256", 64); + map.put("SHA-384", 96); + map.put("SHA-512", 128); + return map; + } + + public static boolean isAlgorithmSupported(String checksum) { + ChecksumValue toCheckAgainst = new ChecksumValue(checksum); + String algorithm = toCheckAgainst.getAlgorithm(); + try { + MessageDigest.getInstance(algorithm); + } catch (NoSuchAlgorithmException e) { + return false; + } + return true; + } +}