/** * sends TS Request and receives an answer in following definition * * <p>The TimeStampResp ASN.1 type definition: * * <pre> * * TimeStampResp ::= SEQUENCE { * status PKIStatusInfo, * timeStampToken TimeStampToken OPTIONAL ] * * PKIStatusInfo ::= SEQUENCE { * status PKIStatus, * statusString PKIFreeText OPTIONAL, * failInfo PKIFailureInfo OPTIONAL } * * PKIStatus ::= INTEGER { * granted (0), * -- when the PKIStatus contains the value zero a TimeStampToken, as * -- requested, is present. * grantedWithMods (1), * -- when the PKIStatus contains the value one a TimeStampToken, * -- with modifications, is present. * rejection (2), * waiting (3), * revocationWarning (4), * -- this message contains a warning that a revocation is * -- imminent * revocationNotification (5) * -- notification that a revocation has occurred } * * PKIFreeText ::= SEQUENCE SIZE (1..MAX) OF UTF8String * -- text encoded as UTF-8 String (note: each UTF8String SHOULD * -- include an RFC 1766 language tag to indicate the language * -- of the contained text) * * PKIFailureInfo ::= BIT STRING { * badAlg (0), * -- unrecognized or unsupported Algorithm Identifier * badRequest (2), * -- transaction not permitted or supported * badDataFormat (5), * -- the data submitted has the wrong format * timeNotAvailable (14), * -- the TSA's time source is not available * unacceptedPolicy (15), * -- the requested TSA policy is not supported by the TSA * unacceptedExtension (16), * -- the requested extension is not supported by the TSA * addInfoNotAvailable (17) * -- the additional information requested could not be understood * -- or is not available * systemFailure (25) * -- the request cannot be handled due to system failure } * * TimeStampToken ::= ContentInfo * -- contentType is id-signedData * -- content is SignedData * -- eContentType within SignedData is id-ct-TSTInfo * -- eContent within SignedData is TSTInfo * * </pre> * * @param tsq TimeStamp Request to be sent to TSA * @param server complete URL of the TSA server * @return TimeStamp Response created from TSA's response */ private TimeStampResponse getTSResponse(TimeStampRequest tsq, String server) throws IOException, TSPException { logger.trace("entering getTSResponse() method"); logger.entry(tsq, server); final byte[] request = tsq.getEncoded(); // open valid connection HttpURLConnection con; try { URL url = new URL(server); con = (HttpURLConnection) url.openConnection(); } catch (IOException e) { logger.error("TSA server couldn't be contacted"); throw e; } con.setDoOutput(true); con.setDoInput(true); con.setRequestProperty("Content-type", "application/timestamp-query"); // con.setRequestProperty("Content-length", String.valueOf(request.length)); logger.info("TSA server was successfully contacted"); // send request OutputStream out; try { out = con.getOutputStream(); out.write(request); out.flush(); } catch (IOException e) { logger.error("Failed to send the TS request."); throw e; } logger.debug("TS request sent"); // receive response InputStream in; TimeStampResp resp; TimeStampResponse response; try { // verify connection status if (con.getResponseCode() != HttpURLConnection.HTTP_OK) { throw new IOException( "Received HTTP error: " + con.getResponseCode() + " - " + con.getResponseMessage()); } else { logger.debug("Response Code: {}", con.getResponseCode()); } // accept the answer in = con.getInputStream(); resp = TimeStampResp.getInstance(new ASN1InputStream(in).readObject()); response = new TimeStampResponse(resp); // verify the answer response.validate(tsq); } catch (TSPException | IOException e) { logger.error("Cannot interpret incoming data."); throw e; } logger.debug("Status: {}", response.getStatusString()); // null means OK return logger.exit(response); }
/** * Main method for stamping given file. It serves as an example of whole process in one go. * * @param filename * @param is * @return * @throws java.io.IOException * @throws org.bouncycastle.tsp.TSPException */ public byte[] stamp(String filename, InputStream is) throws IOException, TSPException { logger.entry(); logger.info("File to be stamped: {}", filename); byte[] file = getBytesFromInputStream(is); ExtendedDigest digestAlgorithm = new SHA256Digest(); // select hash algorithm ASN1ObjectIdentifier requestAlgorithm; try { requestAlgorithm = getHashObjectIdentifier(digestAlgorithm.getAlgorithmName()); } catch (IllegalArgumentException e) { logger.catching(e); throw e; } logger.info("Selected algorithm: {}", digestAlgorithm.getAlgorithmName()); // create request byte[] digest = calculateMessageDigest(file, digestAlgorithm); TimeStampRequest tsq = getTSRequest(digest, requestAlgorithm); logger.debug("TS request generated"); // send request and receive response TimeStampResponse tsr; try { tsr = getTSResponse(tsq, server); } catch (IOException | TSPException e) { logger.catching(e); throw e; } logger.debug("TSA response received"); // log reason of failure if (tsr.getFailInfo() != null) { logFailReason(tsr.getFailInfo().intValue()); return null; } // log response logResponse(tsr); logger.exit(); return tsr.getEncoded(); }
/** * method for JSP to create valid TimeStampRequest * * @param file * @param tsr * @return */ public TimeStampRequest createCorrespondingTSQ(byte[] file, TimeStampResponse tsr) { // get hash algorithm ASN1ObjectIdentifier algID = tsr.getTimeStampToken().getTimeStampInfo().getMessageImprintAlgOID(); GeneralDigest digestAlgorithm = getDigestAlg(algID); logger.info( "The timestamp's algorithm was recognized as {}.", digestAlgorithm.getAlgorithmName()); // create new hashed request byte[] digest = calculateMessageDigest(file, digestAlgorithm); TimeStampRequest tsq = getTSRequest(digest, algID); return tsq; }
/** * prints details of received TimeStamp * * @param tsr {@link TimeStampResponse} */ private void logResponse(final TimeStampResponse tsr) { logger.info("Timestamp: {}", tsr.getTimeStampToken().getTimeStampInfo().getGenTime()); logger.info("TSA: {}", tsr.getTimeStampToken().getTimeStampInfo().getTsa()); logger.info("Serial number: {}", tsr.getTimeStampToken().getTimeStampInfo().getSerialNumber()); logger.info("Policy: {}", tsr.getTimeStampToken().getTimeStampInfo().getPolicy()); }
/** * Main method for validating files with stamps. It serves as an example of whole process in one * go. * * @param originalFile * @param timeStamp * @throws IOException * @throws TSPException * @throws CertificateException * @throws CertificateEncodingException * @throws OperatorCreationException */ public void verify(InputStream originalFile, InputStream timeStamp) throws IOException, TSPException, CertificateException, CertificateEncodingException, OperatorCreationException { logger.entry(); // open files byte[] file = getBytesFromInputStream(originalFile); TimeStampResponse tsr = parseTSR(timeStamp); logger.info("The timestamp was sucessfully opened."); logResponse(tsr); // get hash algorithm ASN1ObjectIdentifier algID = tsr.getTimeStampToken().getTimeStampInfo().getMessageImprintAlgOID(); GeneralDigest digestAlgorithm = getDigestAlg(algID); logger.info( "The timestamp's algorithm was recognized as {}.", digestAlgorithm.getAlgorithmName()); // create new hashed request byte[] digest = calculateMessageDigest(file, digestAlgorithm); TimeStampRequest tsq = getTSRequest(digest, algID); logger.info( "The timestamp fits the file (the file was not changed), now verifying certificates.."); InputStream is = null; if (is == null) { throw new UnsupportedOperationException( "Timestamp fits the file (the file was not changed), certificate not implemented yet."); } // <editor-fold defaultstate="collapsed" desc="varianta 1 - ze souboru"> CertificateFactory factory; X509Certificate cert; try { factory = CertificateFactory.getInstance("X.509"); cert = (X509Certificate) factory.generateCertificate(null); // TODO: get inputstream } catch (CertificateException e) { logger.catching(e); throw e; } // RSA Signature processing with BC X509CertificateHolder holder; SignerInformationVerifier siv; try { holder = new X509CertificateHolder(cert.getEncoded()); siv = new BcRSASignerInfoVerifierBuilder( new DefaultCMSSignatureAlgorithmNameGenerator(), new DefaultSignatureAlgorithmIdentifierFinder(), new DefaultDigestAlgorithmIdentifierFinder(), new BcDigestCalculatorProvider()) .build(holder); } catch (CertificateEncodingException | OperatorCreationException e) { logger.catching(e); throw e; } // Signature processing with JCA and other provider // X509CertificateHolder holderJca = new JcaX509CertificateHolder(cert); // SignerInformationVerifier sivJca = new // JcaSimpleSignerInfoVerifierBuilder().setProvider("anotherprovider").build(holderJca); try { tsr.getTimeStampToken().validate(siv); } catch (TSPException e) { logger.catching(e); throw e; } /* konec varianty 1 */ // </editor-fold> // <editor-fold defaultstate="collapsed" desc="varianta 2 - z razitka"> /*/ get certificates CollectionStore certs = (CollectionStore) tst.getCertificates(); SignerInformationStore signers = tst.toCMSSignedData().getSignerInfos(); int certsCount = 0; for (Iterator it = certs.iterator(); it.hasNext();) { certsCount++; // stupid hack to get actual number of certificates } if (certsCount < 1) { logger.error("No certificate found."); return; } for (SignerInformation signer : signers) { Collection<X509Certificate> col = certs.getMatches(signer.getSID()); X509Certificate[] signerCerts = new X509Certificate[0]; col.toArray(signerCerts); if (signerCerts.length != 1) { logger.error("Expected only one certificate per signer."); return; } try { signerCerts[0].checkValidity(); } catch (CertificateExpiredException | CertificateNotYetValidException ex) { java.util.logging.Logger.getLogger(TSAConnector.class.getName()).log(Level.SEVERE, null, ex); } // signer.verify(signerCerts[0]); // should verify that the timestamp is signed correctly } /* konec varianty 2 */ // </editor-fold> logger.info("All certificates successfully verified, the timestamp is trusted."); logger.exit(); }