1 package org.bouncycastle.tsp.cms; 2 3 import java.io.IOException; 4 import java.io.OutputStream; 5 6 import org.bouncycastle.asn1.ASN1Encoding; 7 import org.bouncycastle.asn1.ASN1ObjectIdentifier; 8 import org.bouncycastle.asn1.cms.AttributeTable; 9 import org.bouncycastle.asn1.cms.ContentInfo; 10 import org.bouncycastle.asn1.cms.Evidence; 11 import org.bouncycastle.asn1.cms.TimeStampAndCRL; 12 import org.bouncycastle.asn1.cms.TimeStampedData; 13 import org.bouncycastle.asn1.cms.TimeStampedDataParser; 14 import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 15 import org.bouncycastle.cms.CMSException; 16 import org.bouncycastle.operator.DigestCalculator; 17 import org.bouncycastle.operator.DigestCalculatorProvider; 18 import org.bouncycastle.operator.OperatorCreationException; 19 import org.bouncycastle.tsp.TSPException; 20 import org.bouncycastle.tsp.TimeStampToken; 21 import org.bouncycastle.tsp.TimeStampTokenInfo; 22 import org.bouncycastle.util.Arrays; 23 24 class TimeStampDataUtil 25 { 26 private final TimeStampAndCRL[] timeStamps; 27 28 private final MetaDataUtil metaDataUtil; 29 TimeStampDataUtil(TimeStampedData timeStampedData)30 TimeStampDataUtil(TimeStampedData timeStampedData) 31 { 32 this.metaDataUtil = new MetaDataUtil(timeStampedData.getMetaData()); 33 34 Evidence evidence = timeStampedData.getTemporalEvidence(); 35 this.timeStamps = evidence.getTstEvidence().toTimeStampAndCRLArray(); 36 } 37 TimeStampDataUtil(TimeStampedDataParser timeStampedData)38 TimeStampDataUtil(TimeStampedDataParser timeStampedData) 39 throws IOException 40 { 41 this.metaDataUtil = new MetaDataUtil(timeStampedData.getMetaData()); 42 43 Evidence evidence = timeStampedData.getTemporalEvidence(); 44 this.timeStamps = evidence.getTstEvidence().toTimeStampAndCRLArray(); 45 } 46 getTimeStampToken(TimeStampAndCRL timeStampAndCRL)47 TimeStampToken getTimeStampToken(TimeStampAndCRL timeStampAndCRL) 48 throws CMSException 49 { 50 ContentInfo timeStampToken = timeStampAndCRL.getTimeStampToken(); 51 52 try 53 { 54 TimeStampToken token = new TimeStampToken(timeStampToken); 55 return token; 56 } 57 catch (IOException e) 58 { 59 throw new CMSException("unable to parse token data: " + e.getMessage(), e); 60 } 61 catch (TSPException e) 62 { 63 if (e.getCause() instanceof CMSException) 64 { 65 throw (CMSException)e.getCause(); 66 } 67 68 throw new CMSException("token data invalid: " + e.getMessage(), e); 69 } 70 catch (IllegalArgumentException e) 71 { 72 throw new CMSException("token data invalid: " + e.getMessage(), e); 73 } 74 } 75 initialiseMessageImprintDigestCalculator(DigestCalculator calculator)76 void initialiseMessageImprintDigestCalculator(DigestCalculator calculator) 77 throws CMSException 78 { 79 metaDataUtil.initialiseMessageImprintDigestCalculator(calculator); 80 } 81 getMessageImprintDigestCalculator(DigestCalculatorProvider calculatorProvider)82 DigestCalculator getMessageImprintDigestCalculator(DigestCalculatorProvider calculatorProvider) 83 throws OperatorCreationException 84 { 85 TimeStampToken token; 86 87 try 88 { 89 token = this.getTimeStampToken(timeStamps[0]); 90 91 TimeStampTokenInfo info = token.getTimeStampInfo(); 92 ASN1ObjectIdentifier algOID = info.getMessageImprintAlgOID(); 93 94 DigestCalculator calc = calculatorProvider.get(new AlgorithmIdentifier(algOID)); 95 96 initialiseMessageImprintDigestCalculator(calc); 97 98 return calc; 99 } 100 catch (CMSException e) 101 { 102 throw new OperatorCreationException("unable to extract algorithm ID: " + e.getMessage(), e); 103 } 104 } 105 getTimeStampTokens()106 TimeStampToken[] getTimeStampTokens() 107 throws CMSException 108 { 109 TimeStampToken[] tokens = new TimeStampToken[timeStamps.length]; 110 for (int i = 0; i < timeStamps.length; i++) 111 { 112 tokens[i] = this.getTimeStampToken(timeStamps[i]); 113 } 114 115 return tokens; 116 } 117 getTimeStamps()118 TimeStampAndCRL[] getTimeStamps() 119 { 120 return timeStamps; 121 } 122 calculateNextHash(DigestCalculator calculator)123 byte[] calculateNextHash(DigestCalculator calculator) 124 throws CMSException 125 { 126 TimeStampAndCRL tspToken = timeStamps[timeStamps.length - 1]; 127 128 OutputStream out = calculator.getOutputStream(); 129 130 try 131 { 132 out.write(tspToken.getEncoded(ASN1Encoding.DER)); 133 134 out.close(); 135 136 return calculator.getDigest(); 137 } 138 catch (IOException e) 139 { 140 throw new CMSException("exception calculating hash: " + e.getMessage(), e); 141 } 142 } 143 144 /** 145 * Validate the digests present in the TimeStampTokens contained in the CMSTimeStampedData. 146 */ validate(DigestCalculatorProvider calculatorProvider, byte[] dataDigest)147 void validate(DigestCalculatorProvider calculatorProvider, byte[] dataDigest) 148 throws ImprintDigestInvalidException, CMSException 149 { 150 byte[] currentDigest = dataDigest; 151 152 for (int i = 0; i < timeStamps.length; i++) 153 { 154 try 155 { 156 TimeStampToken token = this.getTimeStampToken(timeStamps[i]); 157 if (i > 0) 158 { 159 TimeStampTokenInfo info = token.getTimeStampInfo(); 160 DigestCalculator calculator = calculatorProvider.get(info.getHashAlgorithm()); 161 162 calculator.getOutputStream().write(timeStamps[i - 1].getEncoded(ASN1Encoding.DER)); 163 164 currentDigest = calculator.getDigest(); 165 } 166 167 this.compareDigest(token, currentDigest); 168 } 169 catch (IOException e) 170 { 171 throw new CMSException("exception calculating hash: " + e.getMessage(), e); 172 } 173 catch (OperatorCreationException e) 174 { 175 throw new CMSException("cannot create digest: " + e.getMessage(), e); 176 } 177 } 178 } 179 validate(DigestCalculatorProvider calculatorProvider, byte[] dataDigest, TimeStampToken timeStampToken)180 void validate(DigestCalculatorProvider calculatorProvider, byte[] dataDigest, TimeStampToken timeStampToken) 181 throws ImprintDigestInvalidException, CMSException 182 { 183 byte[] currentDigest = dataDigest; 184 byte[] encToken; 185 186 try 187 { 188 encToken = timeStampToken.getEncoded(); 189 } 190 catch (IOException e) 191 { 192 throw new CMSException("exception encoding timeStampToken: " + e.getMessage(), e); 193 } 194 195 for (int i = 0; i < timeStamps.length; i++) 196 { 197 try 198 { 199 TimeStampToken token = this.getTimeStampToken(timeStamps[i]); 200 if (i > 0) 201 { 202 TimeStampTokenInfo info = token.getTimeStampInfo(); 203 DigestCalculator calculator = calculatorProvider.get(info.getHashAlgorithm()); 204 205 calculator.getOutputStream().write(timeStamps[i - 1].getEncoded(ASN1Encoding.DER)); 206 207 currentDigest = calculator.getDigest(); 208 } 209 210 this.compareDigest(token, currentDigest); 211 212 if (Arrays.areEqual(token.getEncoded(), encToken)) 213 { 214 return; 215 } 216 } 217 catch (IOException e) 218 { 219 throw new CMSException("exception calculating hash: " + e.getMessage(), e); 220 } 221 catch (OperatorCreationException e) 222 { 223 throw new CMSException("cannot create digest: " + e.getMessage(), e); 224 } 225 } 226 227 throw new ImprintDigestInvalidException("passed in token not associated with timestamps present", timeStampToken); 228 } 229 compareDigest(TimeStampToken timeStampToken, byte[] digest)230 private void compareDigest(TimeStampToken timeStampToken, byte[] digest) 231 throws ImprintDigestInvalidException 232 { 233 TimeStampTokenInfo info = timeStampToken.getTimeStampInfo(); 234 byte[] tsrMessageDigest = info.getMessageImprintDigest(); 235 236 if (!Arrays.areEqual(digest, tsrMessageDigest)) 237 { 238 throw new ImprintDigestInvalidException("hash calculated is different from MessageImprintDigest found in TimeStampToken", timeStampToken); 239 } 240 } 241 getFileName()242 String getFileName() 243 { 244 return metaDataUtil.getFileName(); 245 } 246 getMediaType()247 String getMediaType() 248 { 249 return metaDataUtil.getMediaType(); 250 } 251 getOtherMetaData()252 AttributeTable getOtherMetaData() 253 { 254 return new AttributeTable(metaDataUtil.getOtherMetaData()); 255 } 256 } 257