/** * Checks if the {@code TimeStampToken} matches the signed data. * * @param data the array of {@code byte} representing the timestamped data * @return true if the data is verified by the TimeStampToken */ public boolean matchData(final byte[] data) { try { messageImprintData = data != null; final TimeStampTokenInfo timeStampInfo = timeStamp.getTimeStampInfo(); final ASN1ObjectIdentifier hashAlgorithm = timeStampInfo.getHashAlgorithm().getAlgorithm(); final DigestAlgorithm digestAlgorithm = DigestAlgorithm.forOID(hashAlgorithm.getId()); final byte[] computedDigest = DSSUtils.digest(digestAlgorithm, data); final byte[] timestampDigest = timeStampInfo.getMessageImprintDigest(); messageImprintIntact = Arrays.equals(computedDigest, timestampDigest); if (!messageImprintIntact) { logger.error( "Computed digest ({}) on the extracted data from the document : {}", digestAlgorithm, Hex.encodeHexString(computedDigest)); logger.error("Digest present in TimestampToken: {}", Hex.encodeHexString(timestampDigest)); logger.error( "Digest in TimestampToken matches digest of extracted data from document: {}", messageImprintIntact); } } catch (DSSException e) { messageImprintIntact = false; signedDataMessage = "Timestamp digest problem: " + e.getMessage(); } return messageImprintIntact; }
@Override public boolean isSignedBy(final CertificateToken issuerToken) { if (this.issuerToken != null) { return this.issuerToken.equals(issuerToken); } final TimestampValidation timestampValidation = validateTimestampToken(timeStamp, issuerToken); final TimestampValidity timestampValidity = timestampValidation.getValidity(); signatureInvalidityReason = timestampValidity.name(); signatureValid = timestampValidation.isValid(); if (signatureValid) { this.issuerToken = issuerToken; issuerX500Principal = issuerToken.getSubjectX500Principal(); final String algorithm = issuerToken.getPublicKey().getAlgorithm(); final EncryptionAlgorithm encryptionAlgorithm = EncryptionAlgorithm.forName(algorithm); final AlgorithmIdentifier hashAlgorithm = timeStamp.getTimeStampInfo().getHashAlgorithm(); final DigestAlgorithm digestAlgorithm = DigestAlgorithm.forOID(hashAlgorithm.getAlgorithm().getId()); signatureAlgorithm = SignatureAlgorithm.getAlgorithm(encryptionAlgorithm, digestAlgorithm); } return signatureValid; }
@Override public String toString(String indentStr) { try { final StringBuffer out = new StringBuffer(); out.append(indentStr) .append("TimestampToken[signedBy=") .append(issuerToken == null ? "?" : issuerToken.getDSSIdAsString()); out.append(", generated: ") .append(DSSUtils.formatInternal(timeStamp.getTimeStampInfo().getGenTime())); out.append(" / ").append(timeStampType).append('\n'); if (signatureValid) { indentStr += "\t"; out.append(indentStr).append("Timestamp's signature validity: VALID").append('\n'); indentStr = indentStr.substring(1); } else { if (!signatureInvalidityReason.isEmpty()) { indentStr += "\t"; out.append(indentStr) .append("Timestamp's signature validity: INVALID") .append(" - ") .append(signatureInvalidityReason) .append('\n'); indentStr = indentStr.substring(1); } } indentStr += "\t"; if (messageImprintIntact != null) { if (messageImprintIntact) { out.append(indentStr).append("Timestamp MATCHES the signed data.").append('\n'); } else { out.append(indentStr).append("Timestamp DOES NOT MATCH the signed data.").append('\n'); if (!signedDataMessage.isEmpty()) { out.append(indentStr).append("- ").append(signedDataMessage).append('\n'); } } } indentStr = indentStr.substring(1); if (issuerToken != null) { indentStr += "\t"; out.append(issuerToken.toString(indentStr)).append('\n'); indentStr = indentStr.substring(1); out.append(indentStr); } out.append("]"); return out.toString(); } catch (Exception e) { return getClass().getName(); } }
@Override public byte[] getEncoded() { try { return timeStamp.getEncoded(); } catch (IOException e) { throw new DSSException(e); } }
@Override public String getAbbreviation() { return timeStampType.name() + ": " + getDSSId() + ": " + DSSUtils.formatInternal(timeStamp.getTimeStampInfo().getGenTime()); }
/** * Returns an ASN.1 encoded bytes representing the {@code TimeStampToken} * * @param timeStampToken {@code TimeStampToken} * @return Returns an ASN.1 encoded bytes representing the {@code TimeStampToken} */ public static byte[] getEncoded(final TimeStampToken timeStampToken) throws DSSException { try { final byte[] encoded = timeStampToken.getEncoded(); return encoded; } catch (IOException e) { throw new DSSException(e); } }
private TimestampValidation validateTimestampToken( final TimeStampToken timeStampToken, final CertificateToken issuerToken) { TimestampValidity timestampValidity; try { final JcaSimpleSignerInfoVerifierBuilder verifierBuilder = new JcaSimpleSignerInfoVerifierBuilder(); final X509Certificate x509Certificate = issuerToken.getCertificate(); final SignerInformationVerifier verifier = verifierBuilder.build(x509Certificate); timeStampToken.validate(verifier); timestampValidity = TimestampValidity.VALID; } catch (IllegalArgumentException e) { if (logger.isDebugEnabled()) { logger.debug("No signing certificate for timestamp token: ", e); } else { logger.info("No signing certificate for timestamp token: ", e.getMessage()); } timestampValidity = TimestampValidity.NO_SIGNING_CERTIFICATE; } catch (TSPValidationException e) { if (logger.isDebugEnabled()) { logger.debug("No valid signature for timestamp token: ", e); } else { logger.info("No valid signature for timestamp token: " + e.getMessage()); } timestampValidity = TimestampValidity.NOT_VALID_SIGNATURE; } catch (TSPException e) { if (logger.isDebugEnabled()) { logger.debug("No valid structure for timestamp token: ", e); } else { logger.info("No valid structure for timestamp token: " + e.getMessage()); } timestampValidity = TimestampValidity.NOT_VALID_STRUCTURE; } catch (OperatorCreationException e) { if (logger.isDebugEnabled()) { logger.debug("No valid structure for timestamp token: ", e); } else { logger.info("No valid structure for timestamp token: " + e.getMessage()); } timestampValidity = TimestampValidity.NOT_VALID_STRUCTURE; } final TimestampValidation timestampValidation = new TimestampValidation(timestampValidity); return timestampValidation; }
/** * Constructor with an indication of the timestamp type. The default constructor for {@code * TimestampToken}. * * @param timeStamp {@code TimeStampToken} * @param type {@code TimestampType} * @param certPool {@code CertificatePool} which is used to identify the signing certificate of * the timestamp */ public TimestampToken( final TimeStampToken timeStamp, final TimestampType type, final CertificatePool certPool) { this.timeStamp = timeStamp; this.timeStampType = type; this.extraInfo = new TokenValidationExtraInfo(); wrappedSource = new CAdESCertificateSource(timeStamp, certPool); final Collection<CertificateToken> certs = wrappedSource.getCertificates(); for (final CertificateToken certificateToken : certs) { final byte[] encoded = certificateToken.getEncoded(); final Certificate certificate = Certificate.getInstance(encoded); final X509CertificateHolder x509CertificateHolder = new X509CertificateHolder(certificate); if (timeStamp.getSID().match(x509CertificateHolder)) { boolean valid = isSignedBy(certificateToken); if (valid) { break; } } } }
public byte[] getTimeStampToken(byte[] digest) throws NoSuchAlgorithmException, UnsupportedEncodingException, TSPException { TimeStampRequestGenerator tsqGenerator = new TimeStampRequestGenerator(); tsqGenerator.setCertReq(true); if (tsaOid != null) tsqGenerator.setReqPolicy(tsaOid); TimeStampRequest tsReq = tsqGenerator.generate(TSPAlgorithms.SHA1, digest, BigInteger.valueOf(100)); byte[] respBytes; try { byte[] requestBytes = tsReq.getEncoded(); URL url = new URL(tsaURL); HttpsURLConnection tsaConnection = (HttpsURLConnection) url.openConnection(); String user_pass = Base64.encodeToString((tsaUsername + ":" + tsaPassword).getBytes(), 0); tsaConnection.setRequestProperty("Authorization", "Basic " + user_pass); tsaConnection.setDoInput(true); tsaConnection.setDoOutput(true); tsaConnection.setUseCaches(false); tsaConnection.setRequestProperty("Content-Type", "application/timestamp-query"); tsaConnection.setRequestProperty("Content-Transfer-Encoding", "binary"); OutputStream out = tsaConnection.getOutputStream(); out.write(requestBytes); out.close(); InputStream inp = tsaConnection.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int bytesRead = 0; while ((bytesRead = inp.read(buffer, 0, buffer.length)) >= 0) { baos.write(buffer, 0, bytesRead); } respBytes = baos.toByteArray(); String encoding = tsaConnection.getContentEncoding(); if (encoding != null && encoding.equalsIgnoreCase("base64")) { respBytes = Base64.decode(new String(respBytes), 0); } if (respBytes == null) { String error = "Error: Impossible to get TSA response"; Log.e(TAG, error); } TimeStampResponse tsRes = new TimeStampResponse(respBytes); tsRes.validate(tsReq); PKIFailureInfo failure = tsRes.getFailInfo(); int value = (failure == null) ? 0 : failure.intValue(); if (value != 0) { String error = "Error: Invalid TSA response (" + tsRes.getStatusString() + ")"; Log.e(TAG, error); return null; } TimeStampToken myTSToken = tsRes.getTimeStampToken(); if (myTSToken == null) { String error = "Error: Invalid TSA response (NULL)"; Log.e(TAG, error); return null; } return myTSToken.getEncoded(); } catch (IOException | TSPException e) { Log.e(TAG, e.getMessage()); } return null; }
public byte[] timeStamp(byte[] data, RevocationData revocationData) throws Exception { // digest the message MessageDigest messageDigest = MessageDigest.getInstance(this.digestAlgo); byte[] digest = messageDigest.digest(data); // generate the TSP request BigInteger nonce = new BigInteger(128, new SecureRandom()); TimeStampRequestGenerator requestGenerator = new TimeStampRequestGenerator(); requestGenerator.setCertReq(true); if (null != this.requestPolicy) { requestGenerator.setReqPolicy(this.requestPolicy); } TimeStampRequest request = requestGenerator.generate(this.digestAlgoOid, digest, nonce); byte[] encodedRequest = request.getEncoded(); // create the HTTP client HttpClient httpClient = new HttpClient(); if (null != this.username) { Credentials credentials = new UsernamePasswordCredentials(this.username, this.password); httpClient.getState().setCredentials(AuthScope.ANY, credentials); } if (null != this.proxyHost) { httpClient.getHostConfiguration().setProxy(this.proxyHost, this.proxyPort); } // create the HTTP POST request PostMethod postMethod = new PostMethod(this.tspServiceUrl); RequestEntity requestEntity = new ByteArrayRequestEntity(encodedRequest, "application/timestamp-query"); postMethod.addRequestHeader("User-Agent", this.userAgent); postMethod.setRequestEntity(requestEntity); // invoke TSP service int statusCode = httpClient.executeMethod(postMethod); if (HttpStatus.SC_OK != statusCode) { LOG.error("Error contacting TSP server " + this.tspServiceUrl); throw new Exception("Error contacting TSP server " + this.tspServiceUrl); } // HTTP input validation Header responseContentTypeHeader = postMethod.getResponseHeader("Content-Type"); if (null == responseContentTypeHeader) { throw new RuntimeException("missing Content-Type header"); } String contentType = responseContentTypeHeader.getValue(); if (!contentType.startsWith("application/timestamp-reply")) { LOG.debug("response content: " + postMethod.getResponseBodyAsString()); throw new RuntimeException("invalid Content-Type: " + contentType); } if (0 == postMethod.getResponseContentLength()) { throw new RuntimeException("Content-Length is zero"); } // TSP response parsing and validation InputStream inputStream = postMethod.getResponseBodyAsStream(); TimeStampResponse timeStampResponse = new TimeStampResponse(inputStream); timeStampResponse.validate(request); if (0 != timeStampResponse.getStatus()) { LOG.debug("status: " + timeStampResponse.getStatus()); LOG.debug("status string: " + timeStampResponse.getStatusString()); PKIFailureInfo failInfo = timeStampResponse.getFailInfo(); if (null != failInfo) { LOG.debug("fail info int value: " + failInfo.intValue()); if (PKIFailureInfo.unacceptedPolicy == failInfo.intValue()) { LOG.debug("unaccepted policy"); } } throw new RuntimeException( "timestamp response status != 0: " + timeStampResponse.getStatus()); } TimeStampToken timeStampToken = timeStampResponse.getTimeStampToken(); SignerId signerId = timeStampToken.getSID(); BigInteger signerCertSerialNumber = signerId.getSerialNumber(); X500Principal signerCertIssuer = signerId.getIssuer(); LOG.debug("signer cert serial number: " + signerCertSerialNumber); LOG.debug("signer cert issuer: " + signerCertIssuer); // TSP signer certificates retrieval CertStore certStore = timeStampToken.getCertificatesAndCRLs("Collection", BouncyCastleProvider.PROVIDER_NAME); Collection<? extends Certificate> certificates = certStore.getCertificates(null); X509Certificate signerCert = null; Map<String, X509Certificate> certificateMap = new HashMap<String, X509Certificate>(); for (Certificate certificate : certificates) { X509Certificate x509Certificate = (X509Certificate) certificate; if (signerCertIssuer.equals(x509Certificate.getIssuerX500Principal()) && signerCertSerialNumber.equals(x509Certificate.getSerialNumber())) { signerCert = x509Certificate; } String ski = Hex.encodeHexString(getSubjectKeyId(x509Certificate)); certificateMap.put(ski, x509Certificate); LOG.debug( "embedded certificate: " + x509Certificate.getSubjectX500Principal() + "; SKI=" + ski); } // TSP signer cert path building if (null == signerCert) { throw new RuntimeException("TSP response token has no signer certificate"); } List<X509Certificate> tspCertificateChain = new LinkedList<X509Certificate>(); X509Certificate certificate = signerCert; do { LOG.debug("adding to certificate chain: " + certificate.getSubjectX500Principal()); tspCertificateChain.add(certificate); if (certificate.getSubjectX500Principal().equals(certificate.getIssuerX500Principal())) { break; } String aki = Hex.encodeHexString(getAuthorityKeyId(certificate)); certificate = certificateMap.get(aki); } while (null != certificate); // verify TSP signer signature timeStampToken.validate(tspCertificateChain.get(0), BouncyCastleProvider.PROVIDER_NAME); // verify TSP signer certificate this.validator.validate(tspCertificateChain, revocationData); LOG.debug("time-stamp token time: " + timeStampToken.getTimeStampInfo().getGenTime()); byte[] timestamp = timeStampToken.getEncoded(); return timestamp; }
public AttributeTable getUnsignedAttributes() { return timeStamp.getUnsignedAttributes(); }
/** * Retrieves the encoded signed data digest value. * * @return base 64 encoded {@code String} */ public String getEncodedSignedDataDigestValue() { final byte[] messageImprintDigest = timeStamp.getTimeStampInfo().getMessageImprintDigest(); return Base64.encodeBase64String(messageImprintDigest); }
/** * Retrieves the {@code DigestAlgorithm} used to generate the digest value to timestamp. * * @return {@code DigestAlgorithm} */ public DigestAlgorithm getSignedDataDigestAlgo() { final ASN1ObjectIdentifier oid = timeStamp.getTimeStampInfo().getHashAlgorithm().getAlgorithm(); return DigestAlgorithm.forOID(oid.getId()); }
/** * Retrieves the timestamp generation time. * * @return {@code Date} */ public Date getGenerationTime() { return timeStamp.getTimeStampInfo().getGenTime(); }