/** * 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(); }
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 static Signature finalizeXadesT(SignedDoc sdoc, Signature sig) throws DigiDocException { if (m_logger.isDebugEnabled()) m_logger.debug("Finalize XAdES-T: " + sig.getId() + " profile: " + sig.getProfile()); UnsignedProperties usp = new UnsignedProperties(sig); sig.setUnsignedProperties(usp); if (sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC)) { DigiDocXmlGenFactory genFac = new DigiDocXmlGenFactory(sdoc); TimestampFactory tsFac = ConfigManager.instance().getTimestampFactory(); // get <SignatureValueTimeStamp> StringBuffer sb = new StringBuffer(); String tsaUrl = ConfigManager.instance().getProperty("DIGIDOC_TSA_URL"); genFac.signatureValue2xml(sb, sig.getSignatureValue(), true); String sSigValXml = sb.toString().trim(); byte[] hash = SignedDoc.digestOfType( sSigValXml.getBytes(), (sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC) ? SignedDoc.SHA256_DIGEST_TYPE : SignedDoc.SHA1_DIGEST_TYPE)); if (m_logger.isDebugEnabled()) m_logger.debug( "Get sig-val-ts for: " + Base64Util.encode(hash) + " uri: " + tsaUrl + " DATA:\n---\n" + sSigValXml + "\n---\n"); TimeStampResponse tresp = tsFac.requestTimestamp(TSPAlgorithms.SHA1.getId(), hash, tsaUrl); if (tresp != null) { TimestampInfo ti = new TimestampInfo( sig.getId() + "-T0", sig, TimestampInfo.TIMESTAMP_TYPE_SIGNATURE, hash, tresp); ti.addIncludeInfo(new IncludeInfo("#" + sig.getId() + "-SIG")); sig.addTimestampInfo(ti); try { if (m_logger.isDebugEnabled()) m_logger.debug("Timestamp: " + Base64Util.encode(tresp.getEncoded())); } catch (Exception ex) { } // sb = new StringBuffer(); // genFac.timestampInfo2xml(sb, ti, true); // String sToXml = sb.toString(); // TODO: add TSA refs and certs ? Not in TSL yet! sig.setProfile(SignedDoc.BDOC_PROFILE_T); try { X509Certificate cert = SignedDoc.readCertificate( new java.io.File("/Users/veiko/workspace/jdigidoc/trunk/iaik-tsa.crt")); /*Store st = tresp.getTimeStampToken().getCertificates(); if(st != null) { SignerInformationStore signers = st.getSignerInfos(); Collection c = signers.getSigners(); Iterator it = c.iterator(); while (it.hasNext()) { SignerInformation signer = (SignerInformation)it.next(); Collection certCollection = certStore.getMatches(signer.getSID()); Iterator certIt = certCollection.iterator(); X509CertificateHolder cert = (X509CertificateHolder)certIt.next(); } }*/ } catch (Exception ex) { m_logger.error("Error ts: " + ex); } } } return sig; }