]> source.dussan.org Git - archiva.git/blob
f6005dd0b300339409769bdbdc93615d2845c90b
[archiva.git] /
1 package org.apache.archiva.checksum;
2
3 /*
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
11  *
12  *  http://www.apache.org/licenses/LICENSE-2.0
13  *
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
19  * under the License.
20  */
21
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;
34 import java.util.Map;
35 import java.util.Set;
36 import java.util.concurrent.atomic.AtomicInteger;
37
38 import static org.apache.archiva.checksum.ChecksumValidationException.ValidationError.*;
39
40 /**
41  * Class for validating checksums.
42  *
43  * @author Martin Stockhammer <martin_s@apache.org>
44  */
45 public class ChecksumValidator
46 {
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<>(  );
52
53     public ChecksumValidator() {
54         init();
55     }
56
57     private void init() {
58         int val;
59         if (status.compareAndSet( NOT_INITALIZED, INITIALIZING ))
60         {
61             try
62             {
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" );
75             } finally
76             {
77                 status.set(INITIALIZED);
78             }
79         } else if ((val = status.intValue())!=INITIALIZED) {
80             do
81             {
82                 try
83                 {
84                     Thread.currentThread().sleep(100);
85                     val = status.intValue();
86                 }
87                 catch ( InterruptedException e )
88                 {
89                     // Ignore
90                 }
91             } while(val!=INITIALIZED);
92         }
93     }
94
95     public boolean isValidChecksum(Path checksumFile) throws ChecksumValidationException
96     {
97         String fileName = checksumFile.getFileName().toString();
98         if (!Files.exists( checksumFile )) {
99             throw new ChecksumValidationException( FILE_NOT_FOUND, "Checksum file does not exist: "+checksumFile );
100         }
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." );
105         }
106         Path checkFile = null;
107         try
108         {
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 );
114         }
115         catch ( NoSuchAlgorithmException e )
116         {
117             throw new ChecksumValidationException( DIGEST_ERROR, "The digest is not supported "+digestType  );
118         }
119         catch ( IOException e )
120         {
121             throw new ChecksumValidationException( READ_ERROR, "Error while computing the checksum of "+checkFile+": "+e.getMessage(), e);
122         }
123     }
124
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);
129     }
130
131     public Set<String> getSupportedExtensions() {
132         return supportedTypes.keySet();
133     }
134
135     public byte[] computeHash(Path file, MessageDigest digest) throws IOException
136     {
137         byte[] result;
138         try(FileChannel inChannel = FileChannel.open( file, StandardOpenOption.READ ))
139         {
140             MappedByteBuffer buffer = inChannel.map( FileChannel.MapMode.READ_ONLY, 0, inChannel.size( ) );
141             digest.update( buffer );
142             result = digest.digest( );
143         }
144         return result;
145     }
146
147     public byte[] computeHash(Path file, String type) throws ChecksumValidationException, NoSuchAlgorithmException, IOException
148     {
149         if (!supportedTypes.containsKey( type )) {
150             throw new ChecksumValidationException( INVALID_FORMAT );
151         }
152         return computeHash( file, MessageDigest.getInstance( supportedTypes.get(type) ) );
153     }
154
155     public byte[] readHashFile(Path file) throws IOException
156     {
157         StringBuilder sb = new StringBuilder(  );
158         try(BufferedReader reader = Files.newBufferedReader( file, StandardCharsets.US_ASCII )){
159             int ci;
160             while((ci = reader.read()) != -1) {
161                 char c = (char)ci;
162                 if (Character.isWhitespace( c )) {
163                     break;
164                 } else {
165                     sb.append(c);
166                 }
167             }
168             return convertFromHex( sb.toString() );
169         }
170     }
171
172     protected String convertToHex(byte[] array) {
173         return DatatypeConverter.printHexBinary( array ).trim().toLowerCase();
174     }
175
176     protected byte[] convertFromHex(String checksum) {
177         return DatatypeConverter.parseHexBinary( checksum.trim().toLowerCase() );
178     }
179
180
181 }