Skip to content

Commit

Permalink
Add custom hostname verifier for CRTM certificate
Browse files Browse the repository at this point in the history
  • Loading branch information
jamezrin committed Dec 2, 2018
1 parent f56387d commit c2614c6
Show file tree
Hide file tree
Showing 10 changed files with 379 additions and 17 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.github.jamezrin</groupId>
<artifactId>crtmcards</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.1-SNAPSHOT</version>
<name>crtm-cards-java</name>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.github.jamezrin.crtmcards;

import com.github.jamezrin.crtmcards.types.Card;
import com.github.jamezrin.crtmcards.types.CardRenewal;
import com.github.jamezrin.crtmcards.types.CardType;
import com.github.jamezrin.crtmcards.types.CrtmCard;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
Expand All @@ -21,7 +21,7 @@ public class DocumentExtractor {
public static final Pattern PROFILE_DATE_PATTERN = Pattern.compile("^Perfil (?<type>[\\s\\S]+) caduca: (?<date>[0-9]{2}-[0-9]{2}-[0-9]{4})$");
public static final Pattern PROFILE_LINE_PATTERN = Pattern.compile("<br>(\\s+)?");

public static Card processCard(Document document) {
public static CrtmCard processCard(Document document) {
// Full card number
Element fullNumEl = document.getElementById("ctl00_cntPh_lblNumeroTarjeta");

Expand All @@ -30,13 +30,13 @@ public static Card processCard(Document document) {
Element resultRowEl = resultsTableEl.getElementsByTag("td").get(1);
Elements resultsEls = resultRowEl.getElementsByTag("span");

// Card expiration date
// CrtmCard expiration date
Element expirationEl = document.getElementById("ctl00_cntPh_lblFechaCaducidadTarjeta");

// Card relevant profiles w/ exp. dates
// CrtmCard relevant profiles w/ exp. dates
Element profilesEl = document.getElementById("ctl00_cntPh_lblInfoDatosDeLaTarjeta");

return new Card(
return new CrtmCard(
fullNumEl.text(),
resultsEls.get(0).text(),
CardType.fromId(resultsEls.get(1).text()),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
package com.github.jamezrin.crtmcards;

import com.github.jamezrin.crtmcards.exceptions.UnsuccessfulRequestException;
import com.github.jamezrin.crtmcards.security.AdditionalSubjectHostnameVerifier;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;

import javax.net.ssl.HostnameVerifier;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

Expand Down Expand Up @@ -103,9 +104,11 @@ public static CloseableHttpClient makeHttpClient(int timeout) {
.setSocketTimeout(timeout)
.setConnectionRequestTimeout(timeout)
.build();
HostnameVerifier hostnameVerifier = new AdditionalSubjectHostnameVerifier(
CRTM_BASE_DOMAIN);
return HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.setSSLHostnameVerifier(hostnameVerifier)
.setUserAgent(CRTM_USER_AGENT)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.github.jamezrin.crtmcards;

public class EndpointConstants {
public static final String CRTM_BASE_URI = "https://www.tarjetatransportepublico.es";
public static final String CRTM_BASE_DOMAIN = "www.tarjetatransportepublico.es";
public static final String CRTM_BASE_URI = "https://" + CRTM_BASE_DOMAIN;
public static final String CRTM_QUERY_URI = CRTM_BASE_URI + "/CRTM-ABONOS/consultaSaldo.aspx";
public static final String CRTM_USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36";
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.github.jamezrin.crtmcards;

import com.github.jamezrin.crtmcards.exceptions.ScraperException;
import com.github.jamezrin.crtmcards.types.Card;
import com.github.jamezrin.crtmcards.types.CrtmCard;
import org.apache.http.HttpResponse;
import org.apache.http.entity.ContentType;
import org.jsoup.Jsoup;
Expand Down Expand Up @@ -40,11 +40,11 @@ public ResponseParser(HttpResponse response) throws IOException, ScraperExceptio
);
}

public Card parse() throws ScraperException {
public CrtmCard parse() throws ScraperException {
// Runs all checks and throws exceptions accordingly
ErrorChecker.checkForErrors(document);

// Extracts properties and creates Card
// Extracts properties and creates CrtmCard
return DocumentExtractor.processCard(document);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.github.jamezrin.crtmcards.security;

import org.apache.http.conn.ssl.DefaultHostnameVerifier;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;

public class AdditionalSubjectHostnameVerifier implements HostnameVerifier {
private final HostnameVerifier hostnameVerifier = new DefaultHostnameVerifier();
private final String[] additionalSubjectAlts;

public AdditionalSubjectHostnameVerifier(String... additionalSubjectAlts) {
this.additionalSubjectAlts = additionalSubjectAlts;
}

@Override
public boolean verify(String hostname, SSLSession session) {
SSLSession sessionWrapper = new AdditionalSubjectSessionWrapper(
session, additionalSubjectAlts);
return hostnameVerifier.verify(hostname, sessionWrapper);
}

public String[] getAdditionalSubjectAlts() {
return additionalSubjectAlts;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package com.github.jamezrin.crtmcards.security;

import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.security.cert.X509Certificate;
import java.security.Principal;
import java.security.cert.Certificate;

public class AdditionalSubjectSessionWrapper implements SSLSession {
private final SSLSession wrappedSession;
private final String[] additionalSubjectAlts;

public AdditionalSubjectSessionWrapper(SSLSession wrappedSession, String[] additionalSubjectAlts) {
this.wrappedSession = wrappedSession;
this.additionalSubjectAlts = additionalSubjectAlts;
}

@Override
public byte[] getId() {
return wrappedSession.getId();
}

@Override
public SSLSessionContext getSessionContext() {
return wrappedSession.getSessionContext();
}

@Override
public long getCreationTime() {
return wrappedSession.getCreationTime();
}

@Override
public long getLastAccessedTime() {
return wrappedSession.getLastAccessedTime();
}

@Override
public void invalidate() {
wrappedSession.invalidate();
}

@Override
public boolean isValid() {
return wrappedSession.isValid();
}

@Override
public void putValue(String s, Object o) {
wrappedSession.putValue(s, o);
}

@Override
public Object getValue(String s) {
return wrappedSession.getValue(s);
}

@Override
public void removeValue(String s) {
wrappedSession.removeValue(s);
}

@Override
public String[] getValueNames() {
return wrappedSession.getValueNames();
}

@Override
public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
Certificate[] certificates = wrappedSession.getPeerCertificates();
Certificate[] results = new Certificate[certificates.length];

for (int i = 0; i < certificates.length; i++) {
Certificate certificate = certificates[i];
if (certificate instanceof java.security.cert.X509Certificate) {
java.security.cert.X509Certificate x509cert = (java.security.cert.X509Certificate) certificate;
results[i] = new AdditionalSubjectX509CertificateWrapper(x509cert, additionalSubjectAlts);
} else {
results[i] = certificate;
}
}

return results;
}

@Override
public Certificate[] getLocalCertificates() {
return wrappedSession.getLocalCertificates();
}

@Override
public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
return wrappedSession.getPeerCertificateChain();
}

@Override
public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
return wrappedSession.getPeerPrincipal();
}

@Override
public Principal getLocalPrincipal() {
return wrappedSession.getLocalPrincipal();
}

@Override
public String getCipherSuite() {
return wrappedSession.getCipherSuite();
}

@Override
public String getProtocol() {
return wrappedSession.getProtocol();
}

@Override
public String getPeerHost() {
return wrappedSession.getPeerHost();
}

@Override
public int getPeerPort() {
return wrappedSession.getPeerPort();
}

@Override
public int getPacketBufferSize() {
return wrappedSession.getPacketBufferSize();
}

@Override
public int getApplicationBufferSize() {
return wrappedSession.getApplicationBufferSize();
}

public String[] getAdditionalSubjectAlts() {
return additionalSubjectAlts;
}

public SSLSession getWrappedSession() {
return wrappedSession;
}
}
Loading

0 comments on commit c2614c6

Please sign in to comment.