Skip to content

Commit 00c29e6

Browse files
committed
Adding JSP no-SDK example
1 parent 5352e7f commit 00c29e6

10 files changed

Lines changed: 613 additions & 0 deletions

File tree

java8/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Java 8
2+
3+
## Contents
4+
5+
| Folder | Description |
6+
|------------------------|---------------------------------------------------|
7+
| `flex-sdk-spring-boot` | Spring Boot example using CyberSource SDKs. |
8+
| `jsp-microform` | JSP (Tomcat) example using hosted Flex microform. |
9+
| `spring-boot` | Custom Spring Boot integration with Flex API. |
10+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# JSP (Tomcat) example using hosted Flex microform
2+
3+
A minimalist Java JSP example integration using Flex-API tokenization and Flex microform embedded card capture.
4+
5+
## Prerequisites
6+
7+
- [Java 8](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html)
8+
- [JCE unlimited policy files](http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html)
9+
- [Maven](https://maven.apache.org/install.html)
10+
- [Tomcat 8 Web Server](http://tomcat.apache.org)
11+
12+
## Setup Instructions
13+
14+
1. Modify `./src/main/webapp/WEB-INF/credentials.properties` with the Cybersource Gate Keeper credentials created through [EBC Portal](https://ebc2.cybersource.com/).
15+
16+
```
17+
merchantId=YOUR MERCHANT ID
18+
keyId=YOUR KEY ID
19+
sharedSecret=YOUR SHARED SECRET
20+
```
21+
22+
2. Build and run the application using maven
23+
```bash
24+
mvn clean install
25+
```
26+
27+
This will produce a `.war` file that can be deployed to a Tomcat server instance.
28+
29+
## Tips
30+
31+
- If you are having issues, checkout the full [FLEX documentation](https://www.cybersource.com/developers/integration_methods/secure-acceptance-flexible-token/).
32+
33+
- If the application throws `java.security.InvalidKeyException: Illegal key size` you have probably not installed the [JCE unlimited policy files](http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html).

java8/jsp-microform-nosdk/pom.xml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>com.cybersource.examples.flex</groupId>
8+
<artifactId>jsp-microform-nosdk</artifactId>
9+
<version>1.0</version> <!-- This example relates to Flex-API BETA v1 -->
10+
<packaging>war</packaging>
11+
12+
<name>Flex microform no-SDK JSP Demo</name>
13+
<description>A mocked merchant checkout and payment pages using Flex-API and microform tokenization</description>
14+
<url>https://github.com/CyberSource/cybersource-flex-samples/java/jsp-microform</url>
15+
16+
<properties>
17+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
18+
<maven.compiler.source>1.6</maven.compiler.source>
19+
<maven.compiler.target>1.6</maven.compiler.target>
20+
</properties>
21+
22+
<build>
23+
<plugins>
24+
<plugin>
25+
<groupId>org.apache.maven.plugins</groupId>
26+
<artifactId>maven-war-plugin</artifactId>
27+
<version>3.2.0</version>
28+
</plugin>
29+
</plugins>
30+
</build>
31+
32+
<dependencies>
33+
<dependency>
34+
<groupId>javax.servlet</groupId>
35+
<artifactId>javax.servlet-api</artifactId>
36+
<version>3.1.0</version>
37+
<scope>provided</scope>
38+
</dependency>
39+
<dependency>
40+
<groupId>javax.servlet.jsp</groupId>
41+
<artifactId>javax.servlet.jsp-api</artifactId>
42+
<version>2.3.1</version>
43+
<scope>provided</scope>
44+
</dependency>
45+
46+
<dependency>
47+
<groupId>org.json</groupId>
48+
<artifactId>json</artifactId>
49+
<version>20171018</version>
50+
</dependency>
51+
<dependency>
52+
<groupId>commons-codec</groupId>
53+
<artifactId>commons-codec</artifactId>
54+
<version>1.11</version>
55+
</dependency>
56+
</dependencies>
57+
</project>
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* Copyright (c) 2017 by CyberSource
3+
* Governing licence: https://github.com/CyberSource/cybersource-flex-samples/blob/master/LICENSE.md
4+
*/
5+
package com.cybersource.example;
6+
7+
import java.io.IOException;
8+
import java.io.InputStream;
9+
import java.io.InputStreamReader;
10+
import java.io.Reader;
11+
import java.util.HashMap;
12+
13+
public class ByteArrayProperties extends HashMap<String, byte[]> {
14+
15+
public ByteArrayProperties(InputStream inputStream) throws IOException {
16+
Reader reader = null;
17+
try {
18+
reader = new InputStreamReader(inputStream);
19+
20+
StringBuilder key = new StringBuilder();
21+
StringBuilder value = new StringBuilder();
22+
boolean keyRead = false;
23+
24+
boolean eof;
25+
int r;
26+
while (true) {
27+
eof = (r = reader.read()) < 0;
28+
char ch = eof ? '\0' : (char) r;
29+
if (ch == '=') {
30+
if (!keyRead) {
31+
keyRead = true;
32+
} else {
33+
value.append(ch);
34+
}
35+
} else if (ch == '\r') {
36+
continue;
37+
} else if (ch == '\n' || eof) {
38+
final int valueLength = value.length();
39+
byte[] valueChars = new byte[valueLength];
40+
for (int i=0; i<valueLength; i++) {
41+
valueChars[i] = (byte) value.charAt(i);
42+
}
43+
put(key.toString(), valueChars);
44+
45+
key.setLength(0);
46+
value.setLength(0);
47+
keyRead = false;
48+
49+
if (eof) {
50+
break;
51+
}
52+
} else if (keyRead) {
53+
value.append(ch);
54+
} else {
55+
key.append(ch);
56+
}
57+
}
58+
} finally {
59+
if (reader != null) {
60+
try {
61+
reader.close();
62+
} catch (IOException e) {
63+
}
64+
}
65+
}
66+
}
67+
}
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/**
2+
* Copyright (c) 2017 by CyberSource
3+
* Governing licence: https://github.com/CyberSource/cybersource-flex-samples/blob/master/LICENSE.md
4+
*/
5+
package com.cybersource.example;
6+
7+
import java.io.BufferedReader;
8+
import javax.servlet.http.HttpSession;
9+
import java.io.IOException;
10+
import java.io.InputStream;
11+
import java.io.InputStreamReader;
12+
import java.io.OutputStreamWriter;
13+
import java.net.HttpURLConnection;
14+
import java.net.URL;
15+
import java.nio.charset.StandardCharsets;
16+
import java.security.KeyFactory;
17+
import java.security.MessageDigest;
18+
import java.security.NoSuchAlgorithmException;
19+
import java.security.PublicKey;
20+
import java.security.Signature;
21+
import java.security.spec.X509EncodedKeySpec;
22+
import java.text.SimpleDateFormat;
23+
import java.util.Calendar;
24+
import java.util.LinkedHashMap;
25+
import java.util.Locale;
26+
import java.util.Map;
27+
import java.util.TimeZone;
28+
import javax.crypto.Mac;
29+
import javax.crypto.spec.SecretKeySpec;
30+
import org.apache.commons.codec.binary.Base64;
31+
import org.json.JSONObject;
32+
33+
public class FlexKeyProvider {
34+
35+
private static final String HOST = "testflex.cybersource.com";
36+
private final MerchantCredentials merchantCredentials;
37+
38+
FlexKeyProvider(InputStream resource) {
39+
try {
40+
merchantCredentials = new MerchantCredentials(resource);
41+
} catch (IOException e) {
42+
throw new RuntimeException(e);
43+
}
44+
}
45+
46+
public String bindFlexKeyToSession(HttpSession session) {
47+
try {
48+
final JSONObject request = new JSONObject();
49+
request.put("encryptionType", "RsaOaep256");
50+
request.put("targetOrigin", "http://localhost:8080"); // the origin of web page that renders flex microform iframe.
51+
final String body = request.toString();
52+
final String date = getServerTime();
53+
54+
final Map<String, String> signedHeaders = new LinkedHashMap<String, String>();
55+
signedHeaders.put("host", HOST);
56+
signedHeaders.put("date", date);
57+
signedHeaders.put("(request-target)", "post /flex/v1/keys");
58+
signedHeaders.put("digest", getDigest(body));
59+
signedHeaders.put("v-c-merchant-id", merchantCredentials.getMerchantId());
60+
61+
final String signature = generateSignature(signedHeaders, merchantCredentials.getKeyId(), merchantCredentials.getSharedSecret());
62+
signedHeaders.put("signature", signature);
63+
signedHeaders.remove("(request-target)");
64+
65+
String response = post(signedHeaders, body);
66+
JSONObject flexPublicKey = new JSONObject(response);
67+
session.setAttribute("flexPublicKey", flexPublicKey);
68+
69+
return flexPublicKey.getJSONObject("jwk").toString();
70+
} catch (IOException ioe) {
71+
throw new RuntimeException("Error receiving Flex public key", ioe);
72+
}
73+
74+
}
75+
76+
public boolean verifyTokenResponse(HttpSession session, String flexResponse) {
77+
try {
78+
JSONObject flexPublicKey = (JSONObject) session.getAttribute("flexPublicKey");
79+
byte[] keyBytes = Base64.decodeBase64(flexPublicKey.getJSONObject("der").getString("publicKey"));
80+
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(keyBytes));
81+
82+
JSONObject token = new JSONObject(flexResponse);
83+
84+
String[] signedFieldKeys = token.getString("signedFields").split(",");
85+
StringBuilder signedValues = new StringBuilder();
86+
for (String key : signedFieldKeys) {
87+
signedValues.append(",").append(token.get(key).toString());
88+
}
89+
signedValues.deleteCharAt(0);
90+
91+
final Signature signInstance = Signature.getInstance("SHA512withRSA");
92+
signInstance.initVerify(publicKey);
93+
signInstance.update(signedValues.toString().getBytes());
94+
return signInstance.verify(Base64.decodeBase64(token.getString("signature")));
95+
} catch (Exception e) {
96+
return false;
97+
}
98+
}
99+
100+
private static String getServerTime() {
101+
Calendar calendar = Calendar.getInstance();
102+
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
103+
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
104+
return dateFormat.format(calendar.getTime());
105+
}
106+
107+
private static String getDigest(String body) {
108+
try {
109+
final MessageDigest digester = MessageDigest.getInstance("SHA-256");
110+
final byte[] digest = digester.digest(body.getBytes(StandardCharsets.UTF_8));
111+
return String.format("SHA-256=%s", Base64.encodeBase64String(digest));
112+
} catch (NoSuchAlgorithmException nsae) {
113+
throw new IllegalStateException(nsae); // never thrown unless SHA-256 is not provided.
114+
}
115+
}
116+
117+
public static String generateSignature(Map<String, String> headers, final String keyId, final byte[] sharedSecret) {
118+
try {
119+
final Mac sha256HMAC = Mac.getInstance("HmacSHA256");
120+
final SecretKeySpec secretKey = new SecretKeySpec(Base64.decodeBase64(sharedSecret), "HmacSHA256");
121+
sha256HMAC.init(secretKey);
122+
123+
final StringBuilder signatureString = new StringBuilder();
124+
final StringBuilder headersString = new StringBuilder();
125+
126+
for (Map.Entry<String, String> e : headers.entrySet()) {
127+
signatureString.append('\n').append(e.getKey()).append(": ").append(e.getValue());
128+
headersString.append(' ').append(e.getKey());
129+
}
130+
signatureString.delete(0, 1);
131+
headersString.delete(0, 1);
132+
133+
final StringBuilder signature = new StringBuilder();
134+
sha256HMAC.update(signatureString.toString().getBytes(StandardCharsets.UTF_8));
135+
final byte[] hashBytes = sha256HMAC.doFinal();
136+
137+
signature.append("keyid=\"").append(keyId).append("\", ")
138+
.append("algorithm=\"HmacSHA256\", ")
139+
.append("headers=\"").append(headersString).append("\", ")
140+
.append("signature=\"").append(Base64.encodeBase64String(hashBytes)).append('\"');
141+
142+
return signature.toString();
143+
} catch (Exception e) {
144+
throw new RuntimeException(e);
145+
}
146+
}
147+
148+
public static String post(Map<String, String> headers, String payload) throws IOException {
149+
HttpURLConnection connection = (HttpURLConnection) new URL("https://testflex.cybersource.com/flex/v1/keys").openConnection();
150+
connection.setRequestMethod("POST");
151+
connection.setDoOutput(true);
152+
connection.setConnectTimeout(5000);
153+
connection.setReadTimeout(5000);
154+
155+
// HEADERS
156+
connection.setRequestProperty("Accept", "application/json; charset=utf-8");
157+
connection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
158+
connection.setRequestProperty("User-Agent", "URLConnection Java JSP GitHub Example");
159+
for (Map.Entry<String, String> header : headers.entrySet()) {
160+
connection.setRequestProperty(header.getKey(), header.getValue());
161+
}
162+
163+
OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), StandardCharsets.UTF_8);
164+
try {
165+
out.write(payload);
166+
} finally {
167+
out.close();
168+
}
169+
170+
StringBuilder result = new StringBuilder();
171+
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));
172+
try {
173+
final char[] buffer = new char[4096];
174+
int bytesRead;
175+
176+
while ((bytesRead = reader.read(buffer)) != -1) {
177+
result.append(buffer, 0, bytesRead);
178+
}
179+
} finally {
180+
reader.close();
181+
}
182+
return result.toString();
183+
}
184+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Copyright (c) 2017 by CyberSource
3+
* Governing licence: https://github.com/CyberSource/cybersource-flex-samples/blob/master/LICENSE.md
4+
*/
5+
package com.cybersource.example;
6+
7+
import java.io.InputStream;
8+
import javax.servlet.ServletContextEvent;
9+
import javax.servlet.ServletContextListener;
10+
import javax.servlet.annotation.WebListener;
11+
12+
@WebListener
13+
public class FlexServletContextListener implements ServletContextListener {
14+
15+
public void contextInitialized(ServletContextEvent sce) {
16+
InputStream merchantConf = sce.getServletContext().getResourceAsStream("/WEB-INF/credentials.properties");
17+
FlexKeyProvider fkp = new FlexKeyProvider(merchantConf);
18+
sce.getServletContext().setAttribute(FlexKeyProvider.class.getName(), fkp);
19+
}
20+
21+
public void contextDestroyed(ServletContextEvent sce) {
22+
}
23+
24+
}

0 commit comments

Comments
 (0)