Created
December 17, 2017 08:39
-
-
Save Glamdring/f4b5b73e4de5b4f6bcdb7151c3fb6c0d to your computer and use it in GitHub Desktop.
Validating trusted timestamps
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class TimestampValidationService { | |
public boolean validate(String hash, String encodedTimestampToken) { | |
try { | |
byte[] tokenBytes = Base64.getDecoder().decode(encodedTimestampToken); | |
CMSSignedData signedData = new CMSSignedData(tokenBytes); | |
TimeStampToken token = new TimeStampToken(signedData); | |
Optional<X509CertificateHolder> certHolder = getCertificateHolder(signedData); | |
BcRSASignerInfoVerifierBuilder verifierBuilder = new BcRSASignerInfoVerifierBuilder( | |
new DefaultCMSSignatureAlgorithmNameGenerator(), | |
new DefaultSignatureAlgorithmIdentifierFinder(), | |
new DefaultDigestAlgorithmIdentifierFinder(), new BcDigestCalculatorProvider()); | |
if (certHolder.isPresent()) { | |
token.validate(verifierBuilder.build(certHolder.get())); | |
return validateContentHash(hash, token.getTimeStampInfo()); | |
} else { | |
// do not verify the certificate, but verify everything else | |
boolean result = token.isSignatureValid(verifierBuilder.build(dummyCertificate)); | |
if (result) { | |
return validateContentHash(timestampGroupHash, token.getTimeStampInfo()); | |
} else { | |
return false; | |
} | |
} | |
} catch (Exception ex) { | |
logger.error("Failed to validate timestamp", ex); | |
return false; | |
} | |
} | |
public TimestampInfo getTimestampInfo(String encodedTimestampToken) { | |
try { | |
byte[] tokenBytes = Base64.getDecoder().decode(encodedTimestampToken); | |
CMSSignedData signedData = new CMSSignedData(tokenBytes); | |
TimeStampToken token = new TimeStampToken(signedData); | |
TimeStampTokenInfo rawInfo = token.getTimeStampInfo(); | |
TimestampInfo result = new TimestampInfo(); | |
result.setAccuracy(rawInfo.getGenTimeAccuracy() != null ? rawInfo.getGenTimeAccuracy().toString() : null); | |
result.setTime(rawInfo.getGenTime() != null ? rawInfo.getGenTime().getTime() : 0); | |
result.setPolicy(rawInfo.getPolicy() != null ? rawInfo.getPolicy().getId() : null); | |
result.setSerialNumber(rawInfo.getSerialNumber()); | |
result.setTsa(rawInfo.getTsa() != null ? rawInfo.getTsa().toString() : null); | |
result.setEncoded(encodedTimestampToken); | |
return result; | |
} catch (Exception e) { | |
throw new IllegalStateException("Failed to parse token", e); | |
} | |
} | |
/** | |
* We need to validate that the hash that has been timestamped is actually the one that we expect. | |
*/ | |
private boolean validateContentHash(String expectedData, TimeStampTokenInfo info) throws Exception { | |
byte[] contentDigest = info.getMessageImprintDigest(); | |
return Arrays.equals(Base64.getUrlDecoder().decode(expectedData), contentDigest); | |
} | |
@SuppressWarnings("unchecked") | |
private Optional<X509CertificateHolder> getCertificateHolder(CMSSignedData signedData) throws IOException { | |
CollectionStore<X509CertificateHolder> store = (CollectionStore<X509CertificateHolder>) signedData | |
.getCertificates(); | |
Iterator<X509CertificateHolder> iterator = store.iterator(); | |
if (iterator.hasNext()) { | |
return Optional.of(store.iterator().next()); | |
} else { | |
logger.debug("No certificate found in signed data, probably the TSA did not provide it"); | |
return Optional.empty(); | |
} | |
} | |
/** | |
* Always-valid dummy certificate holder for cases when no certificate was | |
* provided by TSA, but we need to pass some certificate to the BC | |
* validation classes | |
* | |
*/ | |
private static final class DummyCertificate extends X509CertificateHolder { | |
public DummyCertificate(Certificate cert) throws IOException { | |
super(cert); | |
} | |
@Override | |
public boolean isValidOn(Date date) { | |
return true; | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment