1 package org.apache.archiva.checksum;
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
22 import javax.xml.bind.DatatypeConverter;
23 import java.io.BufferedReader;
24 import java.io.IOException;
25 import java.nio.MappedByteBuffer;
26 import java.nio.channels.FileChannel;
27 import java.nio.charset.StandardCharsets;
28 import java.nio.file.Files;
29 import java.nio.file.Path;
30 import java.nio.file.StandardOpenOption;
31 import java.security.MessageDigest;
32 import java.security.NoSuchAlgorithmException;
33 import java.util.HashMap;
36 import java.util.concurrent.atomic.AtomicInteger;
38 import static org.apache.archiva.checksum.ChecksumValidationException.ValidationError.*;
41 * Class for validating checksums.
43 * @author Martin Stockhammer <martin_s@apache.org>
45 public class ChecksumValidator
47 private final int NOT_INITALIZED = 0;
48 private final int INITIALIZING = 1;
49 private final int INITIALIZED = 2;
50 private AtomicInteger status = new AtomicInteger( NOT_INITALIZED );
51 private static final Map<String, String> supportedTypes = new HashMap<>( );
53 public ChecksumValidator() {
59 if (status.compareAndSet( NOT_INITALIZED, INITIALIZING ))
63 supportedTypes.put( "md5", "MD5" );
64 supportedTypes.put( "sha1", "SHA-1" );
65 supportedTypes.put( "sha-1", "SHA-1" );
66 supportedTypes.put( "sha2", "SHA-256" );
67 supportedTypes.put( "sha256", "SHA-256" );
68 supportedTypes.put( "sha-256", "SHA-256" );
69 supportedTypes.put( "sha3", "SHA-384" );
70 supportedTypes.put( "sha384", "SHA-384" );
71 supportedTypes.put( "sha-384", "SHA-384" );
72 supportedTypes.put( "sha5", "SHA-512" );
73 supportedTypes.put( "sha512", "SHA-512" );
74 supportedTypes.put( "sha-512", "SHA-512" );
77 status.set(INITIALIZED);
79 } else if ((val = status.intValue())!=INITIALIZED) {
84 Thread.currentThread().sleep(100);
85 val = status.intValue();
87 catch ( InterruptedException e )
91 } while(val!=INITIALIZED);
95 public boolean isValidChecksum(Path checksumFile) throws ChecksumValidationException
97 String fileName = checksumFile.getFileName().toString();
98 if (!Files.exists( checksumFile )) {
99 throw new ChecksumValidationException( FILE_NOT_FOUND, "Checksum file does not exist: "+checksumFile );
101 String extension = fileName.substring( fileName.lastIndexOf( '.' )+1).toLowerCase();
102 String digestType = this.supportedTypes.get(extension);
103 if (digestType==null) {
104 throw new ChecksumValidationException( INVALID_FORMAT, "The extension '"+extension+"' ist not known." );
106 Path checkFile = null;
109 MessageDigest md = MessageDigest.getInstance( digestType );
110 checkFile = getCheckFile( checksumFile );
111 byte[] computedChecksum = computeHash( checkFile, md );
112 byte[] readChecksum = readHashFile( checksumFile );
113 return md.isEqual( computedChecksum, readChecksum );
115 catch ( NoSuchAlgorithmException e )
117 throw new ChecksumValidationException( DIGEST_ERROR, "The digest is not supported "+digestType );
119 catch ( IOException e )
121 throw new ChecksumValidationException( READ_ERROR, "Error while computing the checksum of "+checkFile+": "+e.getMessage(), e);
125 private Path getCheckFile(Path checksumFile) {
126 String fileName = checksumFile.getFileName().toString();
127 String newName = fileName.substring(0, fileName.lastIndexOf('.'));
128 return checksumFile.getParent().resolve(newName);
131 public Set<String> getSupportedExtensions() {
132 return supportedTypes.keySet();
135 public byte[] computeHash(Path file, MessageDigest digest) throws IOException
138 try(FileChannel inChannel = FileChannel.open( file, StandardOpenOption.READ ))
140 MappedByteBuffer buffer = inChannel.map( FileChannel.MapMode.READ_ONLY, 0, inChannel.size( ) );
141 digest.update( buffer );
142 result = digest.digest( );
147 public byte[] computeHash(Path file, String type) throws ChecksumValidationException, NoSuchAlgorithmException, IOException
149 if (!supportedTypes.containsKey( type )) {
150 throw new ChecksumValidationException( INVALID_FORMAT );
152 return computeHash( file, MessageDigest.getInstance( supportedTypes.get(type) ) );
155 public byte[] readHashFile(Path file) throws IOException
157 StringBuilder sb = new StringBuilder( );
158 try(BufferedReader reader = Files.newBufferedReader( file, StandardCharsets.US_ASCII )){
160 while((ci = reader.read()) != -1) {
162 if (Character.isWhitespace( c )) {
168 return convertFromHex( sb.toString() );
172 protected String convertToHex(byte[] array) {
173 return DatatypeConverter.printHexBinary( array ).trim().toLowerCase();
176 protected byte[] convertFromHex(String checksum) {
177 return DatatypeConverter.parseHexBinary( checksum.trim().toLowerCase() );