├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src
├── main
└── java
│ └── com
│ └── imohsenb
│ └── ISO8583
│ ├── builders
│ ├── BaseMessageClassBuilder.java
│ ├── GeneralMessageClassBuilder.java
│ ├── ISOClientBuilder.java
│ └── ISOMessageBuilder.java
│ ├── entities
│ └── ISOMessage.java
│ ├── enums
│ ├── FIELDS.java
│ ├── MESSAGE_FUNCTION.java
│ ├── MESSAGE_ORIGIN.java
│ ├── PC_ATC.java
│ ├── PC_TTC_100.java
│ ├── PC_TTC_200.java
│ └── VERSION.java
│ ├── exceptions
│ ├── ISOClientException.java
│ └── ISOException.java
│ ├── handlers
│ ├── IOSocketHandler.java
│ ├── NIOSocketHandler.java
│ └── SSLHandler.java
│ ├── interfaces
│ ├── DataElement.java
│ ├── Financial.java
│ ├── ISOClient.java
│ ├── ISOClientEventListener.java
│ ├── MessageBuilder.java
│ ├── MessageClass.java
│ ├── MessagePacker.java
│ ├── NetworkManagement.java
│ ├── ProcessCode.java
│ ├── SSLKeyManagers.java
│ ├── SSLProtocol.java
│ ├── SSLTrustManagers.java
│ ├── SocketHandler.java
│ ├── UnpackMessage.java
│ └── UnpackMethods.java
│ ├── security
│ └── ISOMacGenerator.java
│ └── utils
│ ├── ByteArray.java
│ ├── FixedBitSet.java
│ ├── StringUtil.java
│ └── TLVParser.java
└── test
└── java
└── com
└── imohsenb
└── ISO8583
└── builders
└── GeneralMessageClassBuilderTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | .*
2 | !.gitignore
3 | /target
4 | *.iml
5 | /doc
6 | settings.xml
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Mohsen Beiranvand
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ISO-8583 Java Lib
2 |
3 | #### ISO8583 Message Packer and Unpakcer with ISOClient for communication with iso server
4 | ###### (Supporting both Blocking IO and NIO)
5 | ###### (Supporting SSL/TLS)
6 |
7 |
8 | A lightweight ISO8583 (is an international standard for financial transaction card originated interchange messaging - [wikipedia][iso8583-Wiki] ) library for Java and Android base on builder pattern and provide very simple use as you will see later.
9 |
10 | - Supporting Blocking IO and Non-blocking IO (NIO)
11 | - Supporting SSL/TLS
12 | - Base on Builder pattern
13 | - String not used for security reasons
14 | - Lightweight ISO-8583 lib for java and android
15 | - working with some enums, it's more readable
16 | - no dependency
17 |
18 |
19 | ### Usage
20 |
21 | Library will be available at [Maven Centeral][mvn]
22 | so you can add this dependency to your `pom.xml` like below:
23 |
24 | ```xml
25 |
26 | com.imohsenb
27 | ISO8583
28 | 1.0.5
29 |
30 | ```
31 | or direct download from [HERE][ddwn]
32 |
33 | ## ISOMessage
34 | #### Create and pack an ISO message
35 | To create an ISO message you must use ISOMessageBuilder which produce iso message for you:
36 |
37 | ```java
38 |
39 | ISOMessage isoMessage = ISOMessageBuilder.Packer(VERSION.V1987)
40 | .networkManagement()
41 | .mti(MESSAGE_FUNCTION.Request, MESSAGE_ORIGIN.Acquirer)
42 | .processCode("920000")
43 | .setField(FIELDS.F11_STAN, "1")
44 | .setField(FIELDS.F12_LocalTime, "023120")
45 | .setField(FIELDS.F13_LocalDate, "0332")
46 | .setField(FIELDS.F24_NII_FunctionCode, "333")
47 | .setHeader("1002230000")
48 | .build();
49 |
50 | ```
51 | with `ISOMessageBuilder.Packer(VERSION.V1987)` you can build iso message as you can see above. the 'Packer' method return 8 method for 8 iso message class (authorization, financial, networkManagment, ...) based on ISO8583 after that you can set message function and message origin by `mti` method.
52 | `mti` method accept string and enums as parameter, and I think enums are much clear and readable.
53 | As you know an iso message need a 'Processing Code' and you can set it's value by `processCode' method, and then we can start setting required fields by 'setField' method and accept String and enums as field number parameter.
54 | After all, you must call build method to generate iso message object.
55 | #### Unpack a buffer and parse it to ISO message
56 | For unpacking a buffer received from server you need to use `ISOMessageBuilder.Unpacker()`:
57 |
58 | ```java
59 | ISOMessage isoMessage = ISOMessageBuilder.Unpacker()
60 | .setMessage(SAMPLE_HEADER + SAMPLE_MSG)
61 | .build();
62 | ```
63 | #### Working with ISOMessage object
64 | ISOMessage object has multiple method provide fields, message body, header and ...
65 | for security reason they will return byte array exept `.toString` and `.getStringField` method, because Strings stay alive in memory until a garbage collector will come to collect that. but you can clear byte or char arrays after use and calling garbage collector is not important anymore.
66 | If you use Strings, taking memory dumps will be dangerous.
67 | ```java
68 | byte[] body = isoMessage.getBody();
69 | ```
70 | ```java
71 | byte[] trace = isoMessage.getField(11);
72 | ```
73 | ```java
74 | String trace = isoMessage.getStringField(FIELDS.F11_STAN);
75 | ```
76 | ## ISOClient
77 | #### Sending message to iso server
78 | Sending message to iso server and received response from server can be done with ISOClient in many ways:
79 | ```java
80 | ISOClient client = ISOClientBuilder.createSocket(HOST, PORT)
81 | .build();
82 |
83 | client.connect();
84 | String response = Arrays.toString(client.sendMessageSync(new SampleIsoMessage()));
85 | System.out.println("response = " + response);
86 | client.disconnect();
87 | ```
88 | #### Sending over SSL/TLS
89 | Sending a message to ISO server over SSL/TLS is more complicated, especially with NIO methods. but I try to make it more simple and usefull:
90 | ```java
91 | ISOClient client = ISOClientBuilder.createSocket(HOST, PORT)
92 | .enableSSL()
93 | .setSSLProtocol("TLSv1.2")
94 | .setKeyManagers(km.getKeyManagers())
95 | .setTrustManagers(null)
96 | .build();
97 | ```
98 |
99 | it's enough to adding `.enableSSL()` and another requirement parameters before `.build()` and may you need to prepare KeyStore and another things before. TrustManagers can be null.
100 |
101 | #### Send a message with NIO benefits!
102 | NIO (stands for non-blocking I/O) is a collection of Java programming language APIs that offer features for intensive I/O operations - [wikipedia][nio].
103 | It's so simple to use in this library just see below : 😎
104 | ```java
105 | ISOClient client = ISOClientBuilder.createSocket(HOST, PORT)
106 | .configureBlocking(false)
107 | .build();
108 | ```
109 |
110 | 😁😁‚ it's ready for use, with just set `.configureBlocking` to false.
111 | it is posibble to use with `.enableSSL()` too.
112 |
113 |
114 | License
115 | -------
116 | Copyright 2018 Mohsen Beiranvand
117 |
118 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
119 |
120 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
121 |
122 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
123 |
124 | [iso8583-Wiki]:
125 | [mvn]:
126 | [mit]:
127 | [nio]:
128 | [ddwn]:
129 |
130 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | com.imohsenb
6 | ISO8583
7 | 1.0.6-SNAPSHOT
8 |
9 | ISO-8583 Message Packer and Unpacker with ISOClient in Java
10 | ISO8583 Message Packer and Unpakcer with ISOClient for communication with iso server
11 | https://github.com/imohsenb/ISO8583-Message-Client-java
12 |
13 |
14 |
15 | MIT License
16 | https://github.com/imohsenb/ISO8583-Message-Client-java/blob/master/LICENSE
17 |
18 |
19 |
20 |
21 |
22 | Mohsen Beiranvand
23 | imohsen.b@gmail.com
24 |
25 |
26 |
27 |
28 | 3.6.0
29 | 3.0.1
30 | 2.10.4
31 | 1.6.7
32 | 2.5.3
33 | 2.8.2
34 | 1.6
35 |
36 |
37 |
38 | scm:git:git://github.com/imohsenb/ISO8583-Message-Client-java.git
39 | scm:git:git@github.com:imohsenb/ISO8583-Message-Client-java.git
40 | https://github.com/imohsenb/ISO8583-Message-Client-java
41 | HEAD
42 |
43 |
44 |
45 |
46 | ossrh
47 | https://oss.sonatype.org/content/repositories/snapshots
48 |
49 |
50 | ossrh
51 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
52 |
53 |
54 |
55 |
56 | Github
57 | https://github.com/imohsenb/ISO8583-Message-Client-java/issues
58 |
59 |
60 |
61 |
62 |
63 |
64 | org.apache.maven.plugins
65 | maven-compiler-plugin
66 | ${version.maven-compiler-plugin}
67 |
68 | UTF-8
69 | 1.8
70 | 1.8
71 | true
72 |
73 |
74 |
75 |
76 | org.apache.maven.plugins
77 | maven-source-plugin
78 | ${version.maven-source-plugin}
79 |
80 |
81 | attach-sources
82 |
83 | jar
84 |
85 |
86 |
87 |
88 |
89 |
90 | maven-deploy-plugin
91 | ${version.maven-deploy-plugin}
92 |
93 |
94 | default-deploy
95 | deploy
96 |
97 | deploy
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | org.sonatype.plugins
106 | nexus-staging-maven-plugin
107 | 1.6.7
108 | true
109 |
110 | ossrh
111 | https://oss.sonatype.org/
112 | true
113 |
114 |
115 |
116 |
117 | org.apache.maven.plugins
118 | maven-javadoc-plugin
119 | ${version.maven-javadoc-plugin}
120 |
121 | UTF-8
122 |
123 |
124 |
125 | attach-javadoc
126 |
127 | jar
128 |
129 |
130 |
131 |
132 |
133 |
134 | org.apache.maven.plugins
135 | maven-release-plugin
136 | ${version.maven-release-plugin}
137 |
138 | true
139 | false
140 | forked-path
141 | -Dgpg.passphrase=${gpg.passphrase}
142 |
143 |
144 |
145 | org.apache.maven.scm
146 | maven-scm-provider-gitexe
147 | 1.9.5
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 | release-sign-artifacts
159 |
160 |
161 | performRelease
162 | true
163 |
164 |
165 |
166 |
167 |
168 | org.apache.maven.plugins
169 | maven-gpg-plugin
170 | ${version.maven-gpg-plugin}
171 |
172 |
173 | sign-artifacts
174 | verify
175 |
176 | sign
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 | junit
189 | junit
190 | 4.12
191 |
192 |
193 | junit
194 | junit
195 | RELEASE
196 | test
197 |
198 |
199 | org.assertj
200 | assertj-core
201 | 3.13.2
202 | test
203 |
204 |
205 |
206 |
207 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/builders/BaseMessageClassBuilder.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.builders;
2 |
3 | import com.imohsenb.ISO8583.entities.ISOMessage;
4 | import com.imohsenb.ISO8583.enums.*;
5 | import com.imohsenb.ISO8583.exceptions.ISOException;
6 | import com.imohsenb.ISO8583.interfaces.DataElement;
7 | import com.imohsenb.ISO8583.interfaces.MessagePacker;
8 | import com.imohsenb.ISO8583.interfaces.ProcessCode;
9 | import com.imohsenb.ISO8583.security.ISOMacGenerator;
10 | import com.imohsenb.ISO8583.utils.ByteArray;
11 | import com.imohsenb.ISO8583.utils.FixedBitSet;
12 | import com.imohsenb.ISO8583.utils.StringUtil;
13 |
14 | import java.util.Arrays;
15 | import java.util.Map;
16 | import java.util.TreeMap;
17 |
18 | /**
19 | * @author Mohsen Beiranvand
20 | */
21 | public abstract class BaseMessageClassBuilder implements
22 | DataElement, ProcessCode, MessagePacker
23 | {
24 |
25 | private String version;
26 | private String messageClass = "0";
27 | private String messageFunction = "0";
28 | private String messageOrigin = "0";
29 | private String processCode;
30 | private TreeMap dataElements = new TreeMap<>();
31 | private String header;
32 | private byte paddingByte = 0xF;
33 | private boolean leftPadding = false;
34 |
35 | public BaseMessageClassBuilder(String version, String messageClass)
36 | {
37 | this.version = version;
38 | this.messageClass = messageClass;
39 | }
40 |
41 | public ISOMessage build() throws ISOException {
42 |
43 | ISOMessage finalMessage = new ISOMessage();
44 | finalMessage.setMessage(buildBuffer(true),this.header != null);
45 |
46 | //clear();
47 |
48 | return finalMessage;
49 | }
50 |
51 | private void clear()
52 | {
53 | for(Map.Entry elem : dataElements.entrySet()) {
54 | Arrays.fill(elem.getValue(), (byte) 0);
55 | }
56 | dataElements = new TreeMap<>();
57 | }
58 |
59 | private byte[] buildBuffer(boolean generateBitmap)
60 | {
61 | FixedBitSet primaryBitmap = new FixedBitSet(64);
62 | ByteArray dataBuffer = new ByteArray();
63 |
64 | for(Map.Entry elem : dataElements.entrySet()) {
65 | if(generateBitmap)
66 | primaryBitmap.flip(elem.getKey() - 1);
67 | dataBuffer.append(elem.getValue());
68 | }
69 |
70 | if(generateBitmap)
71 | dataBuffer.prepend(StringUtil.hexStringToByteArray(primaryBitmap.toHexString()));
72 |
73 | dataBuffer.prepend(StringUtil.hexStringToByteArray((version + messageClass + messageFunction + messageOrigin)));
74 |
75 | if(header!=null && generateBitmap)
76 | dataBuffer.prepend(StringUtil.hexStringToByteArray(header));
77 |
78 | return dataBuffer.array();
79 | }
80 |
81 | public DataElement setHeader(String header)
82 | {
83 | this.header = header;
84 | return this;
85 | }
86 |
87 | @Override
88 | public DataElement setField(int no, byte[] value) throws ISOException {
89 | setField(FIELDS.valueOf(no),value);
90 | return this;
91 | }
92 |
93 | @Override
94 | public DataElement setField(FIELDS field, byte[] value) throws ISOException {
95 | return setField(field,value,value.length);
96 | }
97 |
98 | public DataElement setField(FIELDS field, byte[] value, int valueLength) throws ISOException {
99 |
100 | byte[] fValue = value;
101 |
102 | if(value == null)
103 | throw new ISOException(field.name()+" is Null");
104 | //length check and padding
105 | if(field.isFixed())
106 | {
107 | if(field.getLength()%2 !=0)
108 | {
109 | if(field.getType().equals("n")) {
110 | fValue = padding(field, value, fValue);
111 | }
112 | }else if(field.getLength()-(fValue.length*2) > 0 && field.getType().equals("n")){
113 |
114 | ByteArray valueBuffer = new ByteArray();
115 | valueBuffer.append(fValue);
116 | valueBuffer.prepend(new String(new char[(field.getLength()-(fValue.length*2))/2]).getBytes());
117 | fValue = valueBuffer.array();
118 | valueBuffer.clear();
119 | valueBuffer = null;
120 | }
121 |
122 | if(fValue.length > field.getLength())
123 | {
124 | fValue = Arrays.copyOfRange(fValue,fValue.length-field.getLength(),fValue.length);
125 | }
126 |
127 | }else{
128 |
129 | int dLen = fValue.length;
130 | switch (field.getType())
131 | {
132 | case "z":
133 | if(dLen > field.getLength())
134 | fValue = Arrays.copyOfRange(fValue,fValue.length - field.getLength(),fValue.length);
135 |
136 |
137 |
138 | dLen = fValue.length * 2;
139 |
140 | break;
141 | }
142 |
143 | ByteArray valueBuffer = new ByteArray();
144 | valueBuffer.append(fValue);
145 |
146 | switch (field.getFormat())
147 | {
148 | case "LL":
149 | if(2 - String.valueOf(valueLength).length() <= 0 )
150 | valueBuffer.prepend(StringUtil.hexStringToByteArray(valueLength + ""));
151 | else
152 | valueBuffer.prepend(StringUtil.hexStringToByteArray(String.format("%" + (2 - String.valueOf(valueLength).length()) + "d%s", 0, valueLength)));
153 | break;
154 | case "LLL":
155 | valueBuffer.prepend(StringUtil.hexStringToByteArray(String.format("%0" + (4 - String.valueOf(dLen).length()) + "d%s", 0, dLen)));
156 | break;
157 | }
158 |
159 | fValue = valueBuffer.array();
160 | valueBuffer.clear();
161 | valueBuffer = null;
162 | }
163 |
164 | dataElements.put(field.getNo(),fValue);
165 |
166 | return this;
167 | }
168 |
169 | private byte[] padding(FIELDS field, byte[] value, byte[] fValue) {
170 | byte[] fixed = new byte[(int) Math.ceil(field.getLength() / 2) * 2];
171 |
172 | if (leftPadding) {
173 | leftPad(value, fValue, fixed);
174 | } else {
175 | rightPad(value, fValue, fixed);
176 | }
177 | fValue = fixed;
178 | return fValue;
179 | }
180 |
181 | private void leftPad(byte[] value, byte[] fValue, byte[] fixed) {
182 | for (int i = 0; i < fValue.length; i++) {
183 | fixed[i] = fValue[i];
184 | }
185 | fixed[0] = (byte) (fixed[0] + (paddingByte << 4));
186 | }
187 |
188 | private void rightPad(byte[] value, byte[] fValue, byte[] fixed) {
189 | for (int i = 0; i < fValue.length; i++) {
190 | fixed[i] = (byte) ((fValue[i] & 0x0F) << 4);
191 | if (i + 1 < value.length)
192 | fixed[i] += (fValue[i + 1] & 0xF0) >> 4;
193 | }
194 | fixed[fValue.length - 1] = (byte) (fixed[fValue.length - 1] + paddingByte);
195 | }
196 |
197 | public DataElement setField(int no, String value) throws ISOException {
198 | setField(FIELDS.valueOf(no), value);
199 | return this;
200 | }
201 |
202 | public DataElement setField(FIELDS field, String value) throws ISOException {
203 | switch (field.getType())
204 | {
205 | case "n":
206 | setField(field,StringUtil.hexStringToByteArray(value),value.length());
207 | break;
208 | default:
209 | byte[] bytes = value.getBytes();
210 | setField(field,bytes,bytes.length);
211 | }
212 |
213 | return this;
214 | }
215 |
216 | @Override
217 | public DataElement generateMac(ISOMacGenerator generator) throws ISOException {
218 |
219 | if(generator != null)
220 | {
221 | byte[] mac = generator.generate(buildBuffer(true));
222 | if(mac != null)
223 | setField(FIELDS.F64_MAC,mac);
224 | else
225 | throw new ISOException("MAC is null");
226 | }
227 |
228 | return this;
229 | }
230 |
231 | public ProcessCode mti(MESSAGE_FUNCTION mFunction, MESSAGE_ORIGIN mOrigin) {
232 | this.messageFunction = mFunction.getCode();
233 | this.messageOrigin = mOrigin.getCode();
234 | return this;
235 | }
236 |
237 | @Override
238 | public MessagePacker setLeftPadding(byte character) {
239 | this.leftPadding = true;
240 | this.paddingByte = character;
241 | return this;
242 | }
243 |
244 | @Override
245 | public MessagePacker setRightPadding(byte character) {
246 | this.leftPadding = false;
247 | this.paddingByte = character;
248 | return this;
249 | }
250 |
251 | //
252 | public DataElement processCode(String code) throws ISOException {
253 | this.processCode = code;
254 | this.setField(FIELDS.F3_ProcessCode,this.processCode);
255 | return this;
256 | }
257 |
258 | public DataElement processCode(PC_TTC_100 ttc) throws ISOException {
259 | this.processCode = ttc.getCode() + PC_ATC.Default.getCode() + PC_ATC.Default.getCode();
260 | this.setField(FIELDS.F3_ProcessCode,this.processCode);
261 | return this;
262 | }
263 |
264 | public DataElement processCode(PC_TTC_100 ttc, PC_ATC atcFrom, PC_ATC atcTo) throws ISOException {
265 | this.processCode = ttc.getCode() + atcFrom.getCode() + atcTo.getCode();
266 | this.setField(FIELDS.F3_ProcessCode,this.processCode);
267 | return this;
268 | }
269 |
270 | public DataElement processCode(PC_TTC_200 ttc) throws ISOException {
271 | this.processCode = ttc.getCode() + PC_ATC.Default.getCode() + PC_ATC.Default.getCode();
272 | this.setField(FIELDS.F3_ProcessCode,this.processCode);
273 | return this;
274 | }
275 |
276 | public DataElement processCode(PC_TTC_200 ttc, PC_ATC atcFrom, PC_ATC atcTo) throws ISOException {
277 | this.processCode = ttc.getCode() + atcFrom.getCode() + atcTo.getCode();
278 | this.setField(FIELDS.F3_ProcessCode,this.processCode);
279 | return this;
280 | }
281 |
282 |
283 |
284 | }
285 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/builders/GeneralMessageClassBuilder.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.builders;
2 |
3 | /**
4 | * Created by Mohsen Beiranvand on 18/04/01.
5 | */
6 | public class GeneralMessageClassBuilder extends BaseMessageClassBuilder {
7 |
8 | public GeneralMessageClassBuilder(String version, String messageClass) {
9 | super(version, messageClass);
10 | }
11 | }
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/builders/ISOClientBuilder.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.builders;
2 |
3 | import com.imohsenb.ISO8583.entities.ISOMessage;
4 | import com.imohsenb.ISO8583.exceptions.ISOClientException;
5 | import com.imohsenb.ISO8583.handlers.IOSocketHandler;
6 | import com.imohsenb.ISO8583.handlers.NIOSocketHandler;
7 | import com.imohsenb.ISO8583.handlers.SSLHandler;
8 | import com.imohsenb.ISO8583.interfaces.ISOClient;
9 | import com.imohsenb.ISO8583.interfaces.ISOClientEventListener;
10 | import com.imohsenb.ISO8583.interfaces.SSLProtocol;
11 | import com.imohsenb.ISO8583.interfaces.SocketHandler;
12 |
13 | import java.io.IOException;
14 | import java.nio.ByteBuffer;
15 | import java.util.Arrays;
16 |
17 | /**
18 | * @author Mohsen Beiranvand
19 | */
20 | public class ISOClientBuilder {
21 |
22 |
23 | private static ClientBuilder clientBuilder;
24 |
25 | public static ClientBuilder createSocket(String host, int port) {
26 | clientBuilder = new ClientBuilder(host, port);
27 | return clientBuilder;
28 | }
29 |
30 | /**
31 | * ClientBuilder
32 | */
33 | public static class ClientBuilder {
34 |
35 | private DefaultISOClient client;
36 | /**
37 | * Create ISO Client after initializing
38 | * @param host socket Host
39 | * @param port socket ip
40 | */
41 | public ClientBuilder(String host, int port) {
42 | client = new DefaultISOClient();
43 | client.setSocketAddress(host,port);
44 | }
45 |
46 | /**
47 | * Sending with NIO (false) or Blocking IO (true)
48 | * @param blocking:true
49 | * @return {@link ClientBuilder}
50 | */
51 | public ClientBuilder configureBlocking(boolean blocking) {
52 | client.setBlocking(blocking);
53 | return this;
54 | }
55 |
56 | /**
57 | * Enable sending over SSL/TLS
58 | * @return {@link ClientBuilder}
59 | */
60 | public SSLProtocol enableSSL() {
61 | return client.enableSSL(new SSLHandler(this));
62 | }
63 |
64 | /**
65 | * Build ISOClient for sending label
66 | * @return {@link ClientBuilder}
67 | */
68 | public ISOClient build() {
69 | return client;
70 | }
71 |
72 | /**
73 | * set Timeout for read from socket
74 | * @param millisecond timeout in millisecond
75 | * @return {@link ClientBuilder}
76 | */
77 | public ClientBuilder setReadTimeout(int millisecond) {
78 | client.setReadTimeout(millisecond);
79 | return this;
80 | }
81 |
82 | /**
83 | * Set Message length in Byte
84 | * @param bytes default: 2 byte
85 | * @return {@link ClientBuilder}
86 | */
87 | public ClientBuilder length(int bytes) {
88 | client.setLength(bytes);
89 | return this;
90 | }
91 |
92 | /**
93 | * Set event listener for dispatch events
94 | * @param eventListener Implementation of {@link ISOClientEventListener}
95 | * @return {@link ClientBuilder}
96 | */
97 | public ClientBuilder setEventListener(ISOClientEventListener eventListener) {
98 | if(eventListener != null)
99 | client.setEventListener(eventListener);
100 | return this;
101 | }
102 | }
103 |
104 | private static class DefaultISOClient implements ISOClient {
105 |
106 | private SSLHandler sslHandler = null;
107 | private SocketHandler socketHandler;
108 | private ByteBuffer buffer;
109 | private boolean blocking = true;
110 | private volatile boolean connected = false;
111 | private String host;
112 | private int port;
113 | private int readTimeout = 10000;
114 | private int length = 2;
115 |
116 | private final Object lock = new Object();
117 | private ISOClientEventListener isoClientEventListener;
118 |
119 | DefaultISOClient()
120 | {
121 | if(this.blocking) {
122 | socketHandler = new IOSocketHandler();
123 | }else{
124 | socketHandler = new NIOSocketHandler();
125 | }
126 |
127 | isoClientEventListener = new EmptyISOClientEventListener();
128 | }
129 |
130 | public void connect() throws ISOClientException, IOException {
131 | isoClientEventListener.connecting();
132 |
133 | if(sslHandler != null)
134 | socketHandler.init(host, port, isoClientEventListener, sslHandler);
135 | else socketHandler.init(host,port, isoClientEventListener);
136 |
137 | socketHandler.setReadTimeout(this.readTimeout);
138 | this.connected = true;
139 |
140 | isoClientEventListener.connected();
141 | }
142 |
143 | public void disconnect() {
144 | if(socketHandler!=null)
145 | socketHandler.close();
146 | if(buffer!=null) {
147 | buffer.flip();
148 | buffer.put(ByteBuffer.allocate(buffer.limit()));
149 | buffer = null;
150 | }
151 | connected = false;
152 |
153 | isoClientEventListener.disconnected();
154 |
155 | }
156 |
157 | private ByteBuffer initBuffer(ISOMessage isoMessage) {
158 | int len = isoMessage.getBody().length + isoMessage.getHeader().length;
159 |
160 | buffer = ByteBuffer.allocate(len + length);
161 |
162 | if(length > 0)
163 | {
164 | byte[] mlen = ByteBuffer.allocate(4).putInt(len).array();
165 | buffer.put(Arrays.copyOfRange(mlen, 2,4));
166 | }
167 |
168 | buffer.put(isoMessage.getHeader())
169 | .put(isoMessage.getBody());
170 |
171 | return buffer;
172 | }
173 |
174 | public byte[] sendMessageSync(ISOMessage isoMessage) throws ISOClientException, IOException {
175 |
176 | byte[] result = new byte[0];
177 |
178 | synchronized (lock) {
179 | if (!isConnected())
180 | throw new ISOClientException("Client does not connected to a server!");
181 |
182 | ByteBuffer buffer = initBuffer(isoMessage);
183 |
184 |
185 | result = socketHandler.sendMessageSync(buffer, length);
186 | }
187 |
188 | return result;
189 | }
190 |
191 | @Override
192 | public boolean isConnected() {
193 | return socketHandler != null && socketHandler.isConnected();
194 | }
195 |
196 | @Override
197 | public boolean isClosed() {
198 | return socketHandler != null && socketHandler.isClosed();
199 | }
200 |
201 | @Override
202 | public void setEventListener(ISOClientEventListener isoClientEventListener) {
203 | this.isoClientEventListener = isoClientEventListener;
204 | }
205 |
206 | private void setSocketAddress(String host, int port) {
207 | this.host = host;
208 | this.port = port;
209 | }
210 |
211 | private SSLHandler enableSSL(SSLHandler sslHandler) {
212 | this.sslHandler = sslHandler;
213 | return sslHandler;
214 | }
215 |
216 | private void setBlocking(boolean blocking) {
217 | this.blocking = blocking;
218 | }
219 |
220 |
221 | private void setReadTimeout(int readTimeout) {
222 | this.readTimeout = readTimeout;
223 | }
224 |
225 | private void setLength(int length) {
226 | this.length = length;
227 | }
228 |
229 | private int getLength() {
230 | return length;
231 | }
232 |
233 | }
234 |
235 | private static class EmptyISOClientEventListener implements ISOClientEventListener {
236 | @Override
237 | public void connecting() {
238 |
239 | }
240 |
241 | @Override
242 | public void connected() {
243 |
244 | }
245 |
246 | @Override
247 | public void connectionFailed() {
248 |
249 | }
250 |
251 | @Override
252 | public void connectionClosed() {
253 |
254 | }
255 |
256 | @Override
257 | public void disconnected() {
258 |
259 | }
260 |
261 | @Override
262 | public void beforeSendingMessage() {
263 |
264 | }
265 |
266 | @Override
267 | public void afterSendingMessage() {
268 |
269 | }
270 |
271 | @Override
272 | public void onReceiveData() {
273 |
274 | }
275 |
276 | @Override
277 | public void beforeReceiveResponse() {
278 |
279 | }
280 |
281 | @Override
282 | public void afterReceiveResponse() {
283 |
284 | }
285 | }
286 |
287 |
288 | }
289 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/builders/ISOMessageBuilder.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.builders;
2 |
3 | import com.imohsenb.ISO8583.entities.ISOMessage;
4 | import com.imohsenb.ISO8583.enums.VERSION;
5 | import com.imohsenb.ISO8583.exceptions.ISOException;
6 | import com.imohsenb.ISO8583.interfaces.MessageClass;
7 | import com.imohsenb.ISO8583.interfaces.MessagePacker;
8 | import com.imohsenb.ISO8583.interfaces.UnpackMessage;
9 | import com.imohsenb.ISO8583.interfaces.UnpackMethods;
10 | import com.imohsenb.ISO8583.utils.StringUtil;
11 |
12 | /**
13 | * @author Mohsen Beiranvand
14 | */
15 | public class ISOMessageBuilder {
16 |
17 | public static MessageClass Packer(VERSION version)
18 | {
19 | return new Builder(version.getCode());
20 | }
21 |
22 | private static class Builder implements MessageClass {
23 |
24 | private final String version;
25 |
26 | public Builder(String version) {
27 | this.version = version;
28 | }
29 |
30 |
31 | @Override
32 | public MessagePacker authorization() {
33 | return new GeneralMessageClassBuilder(version,"1");
34 | }
35 |
36 | @Override
37 | public MessagePacker financial() {
38 | return new GeneralMessageClassBuilder(version,"2");
39 | }
40 |
41 | @Override
42 | public MessagePacker fileAction() {
43 | return new GeneralMessageClassBuilder(version,"3");
44 | }
45 |
46 | @Override
47 | public MessagePacker reversal() {
48 | return new GeneralMessageClassBuilder(version,"4");
49 | }
50 |
51 | @Override
52 | public MessagePacker reconciliation() {
53 | return new GeneralMessageClassBuilder(version,"5");
54 | }
55 |
56 | @Override
57 | public MessagePacker administrative() {
58 | return new GeneralMessageClassBuilder(version,"6");
59 | }
60 |
61 | @Override
62 | public MessagePacker feeCollection() {
63 | return new GeneralMessageClassBuilder(version,"7");
64 | }
65 |
66 | @Override
67 | public MessagePacker networkManagement() {
68 | return new GeneralMessageClassBuilder(version,"8");
69 | }
70 |
71 | }
72 |
73 |
74 | public static UnpackMessage Unpacker()
75 | {
76 | return new UnpackBuilder();
77 | }
78 |
79 | public static class UnpackBuilder implements UnpackMessage,UnpackMethods {
80 |
81 | private byte[] message;
82 |
83 | @Override
84 | public UnpackMethods setMessage(byte[] message) {
85 | this.message = message;
86 | return this;
87 | }
88 |
89 | @Override
90 | public UnpackMethods setMessage(String message) {
91 | setMessage(StringUtil.hexStringToByteArray(message));
92 | return this;
93 | }
94 |
95 | @Override
96 | public ISOMessage build() throws ISOException {
97 |
98 | ISOMessage finalMessage = new ISOMessage();
99 | finalMessage.setMessage(message);
100 | return finalMessage;
101 | }
102 |
103 |
104 |
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/entities/ISOMessage.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.entities;
2 |
3 | import com.imohsenb.ISO8583.enums.FIELDS;
4 | import com.imohsenb.ISO8583.exceptions.ISOException;
5 | import com.imohsenb.ISO8583.security.ISOMacGenerator;
6 | import com.imohsenb.ISO8583.utils.FixedBitSet;
7 | import com.imohsenb.ISO8583.utils.StringUtil;
8 |
9 | import java.util.Arrays;
10 | import java.util.Map;
11 | import java.util.Set;
12 | import java.util.TreeMap;
13 |
14 | /**
15 | * ISO Message Entity
16 | *
17 | * @author Mohsen Beiranvand
18 | */
19 | public class ISOMessage {
20 |
21 | private TreeMap dataElements = new TreeMap<>();
22 |
23 | private boolean isNil = true;
24 | private String message;
25 | private String mti;
26 | private byte[] msg;
27 | private byte[] header;
28 | private byte[] body;
29 | private byte[] primaryBitmap;
30 | private int msgClass;
31 | private int msgFunction;
32 | private int msgOrigin;
33 | private int len = 0;
34 |
35 | public static ISOMessage NullObject() {
36 | return new ISOMessage();
37 | }
38 |
39 | public boolean isNil() {
40 | return isNil;
41 | }
42 |
43 | public byte[] getHeader() {
44 | return header;
45 | }
46 |
47 | public byte[] getBody() {
48 | return body;
49 | }
50 |
51 | /**
52 | * Get primary bitmap
53 | *
54 | * @return returns primary byte array
55 | * @since 1.0.4-SNAPSHOT
56 | */
57 | public byte[] getPrimaryBitmap() {
58 | return primaryBitmap;
59 | }
60 |
61 | /**
62 | * Message length
63 | *
64 | * @return returns message length
65 | */
66 | public int length() {
67 | return len;
68 | }
69 |
70 | /**
71 | * Get field value in byte array format
72 | *
73 | * @param fieldNo field number
74 | * @return returns field value in byte array format
75 | * @throws ISOException throws exception
76 | */
77 | public byte[] getField(int fieldNo) throws ISOException {
78 | if (!dataElements.containsKey(fieldNo))
79 | throw new ISOException("Field No " + fieldNo + " does not exists");
80 | return dataElements.get(fieldNo);
81 | }
82 |
83 | /**
84 | * Get field value in byte array format
85 | *
86 | * @param field field in {@link FIELDS} format
87 | * @return returns field value in byte array format
88 | */
89 | public byte[] getField(FIELDS field) {
90 | return dataElements.get(field.getNo());
91 | }
92 |
93 | /**
94 | * Get field value in string format
95 | *
96 | * @param fieldNo field number
97 | * @return returns field value in String format
98 | * @throws ISOException throws exception
99 | */
100 | public String getStringField(int fieldNo) throws ISOException {
101 | return getStringField(FIELDS.valueOf(fieldNo));
102 |
103 | }
104 |
105 | /**
106 | * Get field value in string format
107 | *
108 | * @param field field in {@link FIELDS} format
109 | * @return returns field value in String format
110 | * @throws ISOException throws exception
111 | */
112 | public String getStringField(FIELDS field) throws ISOException {
113 |
114 | return getStringField(field, false);
115 | }
116 |
117 | /**
118 | * Get field value in string format
119 | *
120 | * @param fieldNo field number
121 | * @param asciiFix set true if you want result in ASCII format
122 | * @return returns field value in String format
123 | * @throws ISOException throws exception
124 | */
125 | public String getStringField(int fieldNo, boolean asciiFix) throws ISOException {
126 | return getStringField(FIELDS.valueOf(fieldNo), asciiFix);
127 |
128 | }
129 |
130 | /**
131 | * Get field value in string format
132 | *
133 | * @param field field in {@link FIELDS} format
134 | * @param asciiFix set true if you want result in ASCII format
135 | * @return returns field value in String format
136 | * @throws ISOException throws exception
137 | */
138 | public String getStringField(FIELDS field, boolean asciiFix) throws ISOException {
139 |
140 | String temp = StringUtil.fromByteArray(getField(field.getNo()));
141 | if (asciiFix && !field.getType().equals("n"))
142 | return StringUtil.hexToAscii(temp);
143 | return temp;
144 | }
145 |
146 | /**
147 | * Set and parse ISO8583 message from buffer
148 | *
149 | * @param message ISO8583 in byte array format
150 | * @param headerAvailable set true if header is available in buffer
151 | * @return returns ISO8583 message in ISOMessage type
152 | * @throws ISOException throws exception
153 | */
154 | public ISOMessage setMessage(byte[] message, boolean headerAvailable) throws ISOException {
155 |
156 | isNil = false;
157 |
158 | msg = message;
159 | len = msg.length / 2;
160 |
161 | int headerOffset = 0;
162 |
163 | if (headerAvailable) {
164 | headerOffset = 5;
165 | }
166 |
167 | try {
168 |
169 | this.header = Arrays.copyOfRange(msg, 0, headerOffset);
170 | this.body = Arrays.copyOfRange(msg, headerOffset, msg.length);
171 | this.primaryBitmap = Arrays.copyOfRange(body, 2, 10);
172 |
173 | parseHeader();
174 | parseBody();
175 |
176 | } catch (Exception e) {
177 | throw new ISOException(e.getMessage(), e.getCause());
178 | }
179 |
180 | return this;
181 | }
182 |
183 | /**
184 | * Set and parse ISO8583 message from buffer
185 | *
186 | * @param message ISO8583 in byte array format
187 | * @return returns ISO8583 message in ISOMessage type
188 | * @throws ISOException throws exception
189 | */
190 | public ISOMessage setMessage(byte[] message) throws ISOException {
191 | return this.setMessage(message, true);
192 | }
193 |
194 | private void parseHeader() {
195 | if (body.length > 2) {
196 | mti = StringUtil.fromByteArray(Arrays.copyOfRange(body, 0, 2));
197 | msgClass = Integer.parseInt(mti.substring(1, 2));
198 | msgFunction = Integer.parseInt(mti.substring(2, 3));
199 | msgOrigin = Integer.parseInt(mti.substring(3, 4));
200 | }
201 | }
202 |
203 | private void parseBody() {
204 | FixedBitSet pb = new FixedBitSet(64);
205 | pb.fromHexString(StringUtil.fromByteArray(primaryBitmap));
206 | int offset = 10;
207 |
208 | for (int o : pb.getIndexes()) {
209 |
210 | FIELDS field = FIELDS.valueOf(o);
211 |
212 | if (field.isFixed()) {
213 | int len = field.getLength();
214 | switch (field.getType()) {
215 | case "n":
216 | if (len % 2 != 0)
217 | len++;
218 | len = len / 2;
219 | addElement(field, Arrays.copyOfRange(body, offset, offset + len));
220 | break;
221 | default:
222 | addElement(field, Arrays.copyOfRange(body, offset, offset + len));
223 | break;
224 | }
225 | offset += len;
226 | } else {
227 |
228 | int formatLength = 1;
229 | switch (field.getFormat()) {
230 | case "LL":
231 | formatLength = 1;
232 | break;
233 | case "LLL":
234 | formatLength = 2;
235 | break;
236 | }
237 |
238 | int flen = Integer.valueOf(
239 | StringUtil.fromByteArray(Arrays.copyOfRange(body, offset, offset + formatLength)));
240 |
241 | switch (field.getType()) {
242 | case "z":
243 | case "n":
244 | flen /= 2;
245 | }
246 |
247 | offset = offset + formatLength;
248 |
249 | addElement(field, Arrays.copyOfRange(body, offset, offset + flen));
250 |
251 | offset += flen;
252 | }
253 |
254 | }
255 | }
256 |
257 | private void addElement(FIELDS field, byte[] data) {
258 | dataElements.put(field.getNo(), data);
259 | }
260 |
261 |
262 | /**
263 | * Get EntrySet
264 | *
265 | * @return returns data elements entry set
266 | */
267 | public Set> getEntrySet() {
268 | return dataElements.entrySet();
269 | }
270 |
271 | /**
272 | * Check Field exists by {@link FIELDS} enum
273 | *
274 | * @param field field enum
275 | * @return Returns true if field has value in message
276 | */
277 | public boolean fieldExits(FIELDS field) {
278 | return fieldExits(field.getNo());
279 | }
280 |
281 | /**
282 | * Check Field exists field number
283 | *
284 | * @param no field number
285 | * @return Returns true if field has value in message
286 | */
287 | public boolean fieldExits(int no) {
288 | return dataElements.containsKey(no);
289 | }
290 |
291 | /**
292 | * Get Message MTI
293 | * @return returns MTI in String format
294 | */
295 | public String getMti() {
296 | return mti;
297 | }
298 |
299 | /**
300 | * Get message class
301 | * @return returns message class
302 | */
303 | public int getMsgClass() {
304 | return msgClass;
305 | }
306 |
307 | /**
308 | * Get message function
309 | * @return returns message function
310 | */
311 | public int getMsgFunction() {
312 | return msgFunction;
313 | }
314 |
315 | /**
316 | * Get message origin
317 | * @return returns message origin
318 | */
319 | public int getMsgOrigin() {
320 | return msgOrigin;
321 | }
322 |
323 | /**
324 | * Validate mac
325 | * it's useful method to validate response MAC
326 | *
327 | * @param isoMacGenerator implementation of {@link ISOMacGenerator}
328 | * @return returns true if response message MAC is valid
329 | * @throws ISOException throws exception
330 | */
331 | public boolean validateMac(ISOMacGenerator isoMacGenerator) throws ISOException {
332 |
333 | if (!fieldExits(FIELDS.F64_MAC) || getField(FIELDS.F64_MAC).length == 0) {
334 | System.out.println("validate mac : not exists");
335 | return false;
336 | }
337 | byte[] mBody = new byte[getBody().length - 8];
338 | System.arraycopy(getBody(), 0, mBody, 0, getBody().length - 8);
339 | byte[] oMac = Arrays.copyOf(getField(FIELDS.F64_MAC), 8);
340 | byte[] vMac = isoMacGenerator.generate(mBody);
341 |
342 | return Arrays.equals(oMac, vMac);
343 | }
344 |
345 | /**
346 | * Convert ISOMessage to String
347 | * @return ISOMessage in String format
348 | */
349 | public String toString() {
350 | if (message == null)
351 | message = StringUtil.fromByteArray(msg);
352 | return message;
353 | }
354 |
355 | /**
356 | * Convert all fields in String format
357 | * @return returns strings of fields
358 | */
359 | public String fieldsToString() {
360 | StringBuilder stringBuilder = new StringBuilder();
361 |
362 | stringBuilder.append("\r\n");
363 | for (Map.Entry item :
364 | dataElements.entrySet()) {
365 | stringBuilder
366 | .append(FIELDS.valueOf(item.getKey()).name())
367 | .append(" : ")
368 | .append(StringUtil.fromByteArray(item.getValue()))
369 | .append("\r\n");
370 | }
371 | stringBuilder.append("\r\n");
372 | return stringBuilder.toString();
373 | }
374 |
375 | /**
376 | * Clean up message
377 | */
378 | public void clear() {
379 |
380 | Arrays.fill(header, (byte) 0);
381 | Arrays.fill(body, (byte) 0);
382 | Arrays.fill(primaryBitmap, (byte) 0);
383 |
384 | message = null;
385 | header = null;
386 | body = null;
387 | primaryBitmap = null;
388 |
389 | }
390 |
391 | }
392 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/enums/FIELDS.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.enums;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | /**
7 | * @author Mohsen Beiranvand
8 | */
9 | public enum FIELDS {
10 | // |Field title |no |type |len |fixed |format|
11 | F1_Bitmap (1, "b", 64, true, null),
12 | F2_PAN (2, "n", 19, false, "LL"),
13 | F3_ProcessCode (3, "n", 6, true, null),
14 | F4_AmountTransaction (4, "n", 12, true, null),
15 | F5_AmountSettlement (5, "n", 12, true, null),
16 | F6_AmountCardholder (6, "n", 12, true, null),
17 | F7_TransmissionDateTime (7, "n", 10, true, null),
18 | F8_AmountCardholder_BillingFee (8, "n", 8, true, null),
19 | F9_ConversionRate_Settlement (9, "n", 8, true, null),
20 | F10_ConversionRate_Cardholder (10, "n", 8, true, null),
21 | F11_STAN (11, "n", 6, true, null),
22 | F12_LocalTime (12, "n", 6, true, null),
23 | F13_LocalDate (13, "n", 4, true, null),
24 | F14_ExpirationDate (14, "n", 4, true, null),
25 | F15_SettlementDate (15, "n", 4, true, null),
26 | F16_CurrencyConversionDate (16, "n", 4, true, null),
27 | F17_CaptureDate (17, "n", 4, true, null),
28 | F18_MerchantType (18, "n", 4, true, null),
29 | F19_AcquiringInstitution (19, "n", 3, true, null),
30 | F20_PANExtended (20, "n", 3, true, null),
31 | F21_ForwardingInstitution (21, "n", 3, true, null),
32 | F22_EntryMode (22, "n", 3, true, null),
33 | F23_PANSequence (23, "n", 3, true, null),
34 | F24_NII_FunctionCode (24, "n", 3, true, null),
35 | F25_POS_ConditionCode (25, "n", 2, true, null),
36 | F26_POS_CaptureCode (26, "n", 2, true, null),
37 | F27_AuthIdResponseLength (27, "n", 1, true, null),
38 | F28_Amount_TransactionFee (28, "x+n", 8, true, null),
39 | F29_Amount_SettlementFee (29, "x+n", 8, true, null),
40 | F30_Amount_TransactionProcessingFee (30, "x+n", 8, true, null),
41 | F31_Amount_SettlementProcessingFee (31, "x+n", 8, true, null),
42 | F32_AcquiringInstitutionIdCode (32, "n", 11, false, "LL"),
43 | F33_ForwardingInstitutionIdCode (33, "n", 11, false, "LL"),
44 | F34_PAN_Extended (34, "ns", 28, false, "LL"),
45 | F35_Track2 (35, "z", 37, false, "LL"),
46 | F36_Track3 (36, "z", 104, false, "LLL"),
47 | F37_RRN (37, "an", 12, true, null),
48 | F38_AuthIdResponse (38, "an", 6, true, null),
49 | F39_ResponseCode (39, "an", 2, true, null),
50 | F40_ServiceRestrictionCode (40, "an", 3, true, null),
51 | F41_CA_TerminalID (41, "ans", 8, true, null),
52 | F42_CA_ID (42, "ans", 15, true, null),
53 | F43_CardAcceptorInfo (43, "ans", 40, true, null),
54 | F44_AddResponseData (44, "an", 25, false, "LL"),
55 | F45_Track1 (45, "an", 76, false, "LL"),
56 | F46_AddData_ISO (46, "an", 999, false, "LLL"),
57 | F47_AddData_National (47, "an", 999, false, "LLL"),
58 | F48_AddData_Private (48, "an", 999, false, "LLL"),
59 | F49_CurrencyCode_Transaction (49, "a|n", 3, true, null),
60 | F50_CurrencyCode_Settlement (50, "a|n", 3, true, null),
61 | F51_CurrencyCode_Cardholder (51, "a|n", 3, true, null),
62 | F52_PIN (52, "b", 8, true, null),
63 | F53_SecurityControlInfo (53, "n", 16, true, null),
64 | F54_AddAmount (54, "an", 120, false, "LLL"),
65 | F55_ICC (55, "ans", 999, false, "LLL"),
66 | F56_Reserved_ISO (56, "ans", 999, false, "LLL"),
67 | F57_Reserved_National (57, "ans", 999, false, "LLL"),
68 | F58_Reserved_National (58, "ans", 999, false, "LLL"),
69 | F59_Reserved_National (59, "ans", 999, false, "LLL"),
70 | F60_Reserved_National (60, "ans", 999, false, "LLL"),
71 | F61_Reserved_Private (61, "ans", 999, false, "LLL"),
72 | F62_Reserved_Private (62, "ans", 999, false, "LLL"),
73 | F63_Reserved_Private (63, "ans", 999, false, "LLL"),
74 | F64_MAC (64, "b", 16, true, null);
75 |
76 |
77 |
78 |
79 | private final int no;
80 | private final String type;
81 | private final int length;
82 | private final boolean fixed;
83 | private final String format;
84 |
85 | FIELDS(int no, String type, int length, boolean fixed, String format) {
86 | this.no = no;
87 | this.type = type;
88 | this.length = length;
89 | this.fixed = fixed;
90 | this.format = format;
91 | }
92 |
93 | private static Map map = new HashMap();
94 |
95 | static {
96 | for (FIELDS field : FIELDS.values()) {
97 | map.put(field.getNo(), field);
98 | }
99 | }
100 |
101 | public int getNo() {
102 | return no;
103 | }
104 |
105 | public String getType() {
106 | return type;
107 | }
108 |
109 | public int getLength() {
110 | return length;
111 | }
112 |
113 | public boolean isFixed() {
114 | return fixed;
115 | }
116 |
117 | public String getFormat() {
118 | return format;
119 | }
120 |
121 | public static FIELDS valueOf(int no) {
122 | return map.get(no);
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/enums/MESSAGE_FUNCTION.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.enums;
2 |
3 | /**
4 | * @author Mohsen Beiranvand
5 | */
6 | public enum MESSAGE_FUNCTION {
7 |
8 | Request("0"),
9 | Advice("2");
10 |
11 | private final String code;
12 |
13 | MESSAGE_FUNCTION(String code) {
14 | this.code = code;
15 | }
16 |
17 | public String getCode() {
18 | return code;
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/enums/MESSAGE_ORIGIN.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.enums;
2 |
3 | /**
4 | * @author Mohsen Beiranvand
5 | */
6 | public enum MESSAGE_ORIGIN {
7 |
8 | Acquirer("0");
9 |
10 | private final String code;
11 |
12 | MESSAGE_ORIGIN(String code) {
13 | this.code = code;
14 | }
15 |
16 | public String getCode() {
17 | return code;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/enums/PC_ATC.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.enums;
2 |
3 | /**
4 | * @author Mohsen Beiranvand
5 | */
6 | public enum PC_ATC {
7 |
8 | Default("00"),
9 | SavingAccount("10"),
10 | CheckingAccount("20"),
11 | CreditCardAccount("30");
12 |
13 | private final String code;
14 |
15 | PC_ATC(String code) {
16 | this.code = code;
17 | }
18 |
19 | public String getCode() {
20 | return code;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/enums/PC_TTC_100.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.enums;
2 |
3 | /**
4 | * @author Mohsen Beiranvand
5 | */
6 | public enum PC_TTC_100 {
7 |
8 | Authorization("00"),
9 | AuthorizationVoid("02"),
10 | Refund_Return("20"),
11 | Refund_Return_void("22"),
12 | BalanceInquiry("30");
13 |
14 | private final String code;
15 |
16 | PC_TTC_100(String code) {
17 | this.code = code;
18 | }
19 |
20 | public String getCode() {
21 | return code;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/enums/PC_TTC_200.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.enums;
2 |
3 | /**
4 | * @author Mohsen Beiranvand
5 | */
6 | public enum PC_TTC_200 {
7 |
8 | Purchase("00"),
9 | Withdrawal("01"),
10 | Void("02"),
11 | Refund_Return("20"),
12 | Payment_Deposit_Refresh("21"),
13 | AccountTransfer("40"),
14 | PurchaseAdvise("00"),
15 | Refund_Return_advise("20");
16 |
17 | private final String code;
18 |
19 | PC_TTC_200(String code) {
20 | this.code = code;
21 | }
22 |
23 | public String getCode() {
24 | return code;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/enums/VERSION.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.enums;
2 |
3 | /**
4 | * @author Mohsen Beiranvand
5 | */
6 | public enum VERSION {
7 |
8 | V1987("0"),
9 | V1993("1"),
10 | V2003("2");
11 |
12 | private final String code;
13 |
14 | VERSION(String versionCode) {
15 | this.code = versionCode;
16 | }
17 |
18 | public String getCode() {
19 | return code;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/exceptions/ISOClientException.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.exceptions;
2 |
3 | /**
4 | * @author Mohsen Beiranvand
5 | */
6 | public class ISOClientException extends Exception {
7 |
8 | public ISOClientException(String message) {
9 | super(message);
10 | }
11 |
12 | public ISOClientException(Exception e) {
13 | super(e);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/exceptions/ISOException.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.exceptions;
2 |
3 | /**
4 | * @author Mohsen Beiranvand
5 | */
6 | public class ISOException extends Exception {
7 |
8 | public ISOException(String message) {
9 | super(message);
10 | }
11 |
12 | public ISOException(String message, Throwable cause) {
13 | super(message, cause);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/handlers/IOSocketHandler.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.handlers;
2 |
3 | import com.imohsenb.ISO8583.exceptions.ISOClientException;
4 | import com.imohsenb.ISO8583.interfaces.ISOClientEventListener;
5 | import com.imohsenb.ISO8583.interfaces.SocketHandler;
6 |
7 | import javax.net.ssl.SSLContext;
8 | import javax.net.ssl.SSLSocket;
9 | import javax.net.ssl.SSLSocketFactory;
10 | import java.io.BufferedInputStream;
11 | import java.io.BufferedOutputStream;
12 | import java.io.IOException;
13 | import java.net.Socket;
14 | import java.net.SocketException;
15 | import java.net.SocketTimeoutException;
16 | import java.nio.ByteBuffer;
17 | import java.util.Arrays;
18 |
19 | /**
20 | * @author Mohsen Beiranvand
21 | */
22 | public class IOSocketHandler implements SocketHandler {
23 | private Socket socket;
24 | private BufferedOutputStream socketWriter;
25 | private BufferedInputStream socketReader;
26 | private ISOClientEventListener isoClientEventListener;
27 |
28 | public void init(String host, int port, ISOClientEventListener isoClientEventListener, SSLHandler sslHandler) throws ISOClientException {
29 |
30 | this.isoClientEventListener = isoClientEventListener;
31 |
32 | SSLContext context = null;
33 | try {
34 |
35 | context = sslHandler.getContext();
36 | SSLSocketFactory sslsocketfactory = context.getSocketFactory();
37 | SSLSocket socket = (SSLSocket) sslsocketfactory.createSocket(
38 | host, port);
39 | socket.setNeedClientAuth(false);
40 |
41 | socket.startHandshake();
42 |
43 | this.socket = socket;
44 |
45 | postInit();
46 |
47 | } catch (Exception e) {
48 | throw new ISOClientException(e);
49 | }
50 |
51 | }
52 |
53 | public void init(String host, int port, ISOClientEventListener isoClientEventListener) throws IOException {
54 |
55 | this.isoClientEventListener = isoClientEventListener;
56 | this.socket = new Socket(host, port);
57 | postInit();
58 |
59 | }
60 |
61 |
62 | private void postInit() throws IOException {
63 | socketWriter = new BufferedOutputStream(socket.getOutputStream());
64 | }
65 |
66 | public byte[] sendMessageSync(ByteBuffer buffer, int length) throws IOException, ISOClientException {
67 |
68 | isoClientEventListener.beforeSendingMessage();
69 |
70 | for (byte v :
71 | buffer.array()) {
72 | socketWriter.write(v);
73 | }
74 |
75 | socketWriter.flush();
76 |
77 | ByteBuffer readBuffer = ByteBuffer.allocate(1024);
78 | socketReader = new BufferedInputStream(socket.getInputStream());
79 |
80 | isoClientEventListener.afterSendingMessage();
81 |
82 |
83 | isoClientEventListener.beforeReceiveResponse();
84 |
85 | try {
86 |
87 | if(length > 0)
88 | {
89 | byte[] bLen = new byte[length];
90 | socketReader.read(bLen,0,length);
91 | int mLen = (bLen[0] & 0xff) + (bLen[1] & 0xff);
92 | }
93 |
94 | int r;
95 | int fo = 512;
96 | do{
97 | r = socketReader.read();
98 | if (!(r == -1 && socketReader.available() == 0)) {
99 | readBuffer.put((byte) r);
100 | } else {
101 | fo--;
102 | }
103 | }while (
104 | ((r > -1 && socketReader.available() > 0) ||
105 | (r == -1 && readBuffer.position() <= 1)) &&
106 | fo > 0
107 |
108 | );
109 |
110 |
111 | byte[] resp = Arrays.copyOfRange(readBuffer.array(),0,readBuffer.position());
112 |
113 | isoClientEventListener.afterReceiveResponse();
114 |
115 | return resp;
116 |
117 | } catch (SocketTimeoutException e) {
118 | throw new ISOClientException("Read Timeout");
119 | } finally {
120 | readBuffer.clear();
121 | readBuffer.compact();
122 | readBuffer = null;
123 | }
124 | }
125 |
126 | public synchronized void close() {
127 | try {
128 | if(socketWriter!=null)
129 | socketWriter.close();
130 | if(socketReader!=null)
131 | socketReader.close();
132 | if(socket!=null)
133 | socket.close();
134 | } catch (IOException e) {
135 | e.printStackTrace();
136 | }
137 | }
138 |
139 | @Override
140 | public void setReadTimeout(int readTimeout) throws SocketException {
141 | socket.setSoTimeout(readTimeout);
142 | }
143 |
144 | @Override
145 | public boolean isConnected() {
146 | if(socket != null)
147 | return socket.isConnected();
148 | return false;
149 | }
150 |
151 | @Override
152 | public boolean isClosed() {
153 | return socket == null || socket.isClosed();
154 | }
155 | }
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/handlers/NIOSocketHandler.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.handlers;
2 |
3 | import com.imohsenb.ISO8583.exceptions.ISOClientException;
4 | import com.imohsenb.ISO8583.interfaces.ISOClientEventListener;
5 | import com.imohsenb.ISO8583.interfaces.SocketHandler;
6 | import com.imohsenb.ISO8583.utils.StringUtil;
7 |
8 | import javax.net.ssl.SSLEngine;
9 | import javax.net.ssl.SSLEngineResult;
10 | import javax.net.ssl.SSLException;
11 | import javax.net.ssl.SSLSession;
12 | import java.io.IOException;
13 | import java.net.InetSocketAddress;
14 | import java.net.SocketException;
15 | import java.nio.ByteBuffer;
16 | import java.nio.channels.SocketChannel;
17 | import java.util.Arrays;
18 |
19 | /**
20 | * @author Mohsen Beiranvand
21 | */
22 | public class NIOSocketHandler implements SocketHandler {
23 |
24 | private SocketChannel socketChannel;
25 | private ByteBuffer myAppData;
26 | private ByteBuffer myNetData;
27 | private ByteBuffer peerAppData;
28 | private ByteBuffer peerNetData;
29 | private SSLEngine engine;
30 | private SSLHandler sslHandler;
31 |
32 | public void init(String host, int port, ISOClientEventListener isoClientEventListener, SSLHandler sslHandler) throws ISOClientException {
33 |
34 | try {
35 | this.sslHandler = sslHandler;
36 | engine = sslHandler.getContext().createSSLEngine(host, port);
37 | engine.setUseClientMode(true);
38 | engine.setNeedClientAuth(false);
39 |
40 |
41 | socketChannel = SocketChannel.open();
42 | socketChannel.configureBlocking(false);
43 | socketChannel.connect(new InetSocketAddress(host, port));
44 |
45 |
46 | while (!socketChannel.finishConnect()) {
47 | }
48 |
49 | // Create byte buffers to use for holding application and encoded data
50 | SSLSession session = engine.getSession();
51 | myAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
52 | myNetData = ByteBuffer.allocate(session.getPacketBufferSize());
53 | peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
54 | peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
55 |
56 | boolean connected = sslHandler.doHandshake(socketChannel, engine, myNetData, peerNetData,peerAppData,myAppData);
57 |
58 | if(!connected)
59 | throw new ISOClientException("Handshake not performed well");
60 |
61 | postInit();
62 |
63 | } catch (Exception e) {
64 | throw new ISOClientException(e);
65 | }
66 |
67 | }
68 |
69 | public void init(String host, int port, ISOClientEventListener isoClientEventListener) throws IOException {
70 |
71 | socketChannel = SocketChannel.open();
72 | socketChannel.configureBlocking(false);
73 | socketChannel.connect(new InetSocketAddress(host, port));
74 |
75 |
76 | while (!socketChannel.finishConnect()) {
77 | }
78 |
79 | myAppData = ByteBuffer.allocate(1024);
80 |
81 | postInit();
82 | }
83 |
84 | private void postInit() throws SocketException {
85 | socketChannel.socket().setSoTimeout(10000);
86 | }
87 |
88 | public byte[] sendMessageSync(ByteBuffer buffer, int length) throws IOException {
89 |
90 | if(sslHandler != null)
91 | {
92 | byte[] data = sendMessageSyncOverSsl(buffer);
93 | return Arrays.copyOfRange(data,(length>0)?(length):(0),data.length);
94 | }
95 | else{
96 |
97 | myAppData.clear();
98 | myAppData.put(buffer.array());
99 | myAppData.flip();
100 |
101 | while(myAppData.hasRemaining())
102 | {
103 | socketChannel.write(myAppData);
104 | }
105 |
106 | myAppData.clear();
107 | myAppData.compact();
108 | myAppData.flip();
109 |
110 |
111 |
112 | int r;
113 | do{
114 | r = socketChannel.read(myAppData);
115 | }
116 | while (myAppData.remaining() >=0 && r == 0);
117 |
118 |
119 | if(myAppData.position() > length)
120 | return Arrays.copyOfRange(myAppData.array(),(length>0)?(length):(0),myAppData.position());
121 |
122 | return new byte[0];
123 | }
124 | }
125 |
126 | private byte[] sendMessageSyncOverSsl(ByteBuffer buffer) throws IOException {
127 |
128 | write(buffer);
129 | return read();
130 |
131 | }
132 |
133 | private void write(ByteBuffer buffer) throws IOException {
134 |
135 | myAppData.clear();
136 | myAppData.put(buffer.array());
137 | myAppData.flip();
138 |
139 | while (myAppData.hasRemaining()) {
140 | // The loop has a meaning for (outgoing) messages larger than 16KB.
141 | // Every wrap call will remove 16KB from the original label and send it to the remote peer.
142 | myNetData.clear();
143 | SSLEngineResult result = engine.wrap(myAppData, myNetData);
144 | switch (result.getStatus()) {
145 | case OK:
146 | myNetData.flip();
147 | while (myNetData.hasRemaining()) {
148 | socketChannel.write(myNetData);
149 | }
150 | System.out.println("Message sent to the server: " + StringUtil.fromByteArray(myAppData.array()));
151 | break;
152 | case BUFFER_OVERFLOW:
153 | myNetData = sslHandler.enlargePacketBuffer(engine, myNetData);
154 | break;
155 | case BUFFER_UNDERFLOW:
156 | throw new SSLException("Buffer underflow occured after a wrap. I don't think we should ever get here.");
157 | case CLOSED:
158 | close();
159 | return;
160 | default:
161 | throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
162 | }
163 | }
164 | }
165 |
166 | private byte[] read() throws IOException {
167 |
168 | byte[] response = new byte[0];
169 |
170 |
171 | peerNetData.clear();
172 | int waitToReadMillis = 50;
173 | boolean exitReadLoop = false;
174 | while (!exitReadLoop) {
175 | int bytesRead = socketChannel.read(peerNetData);
176 | if (bytesRead > 0) {
177 | peerNetData.flip();
178 | while (peerNetData.hasRemaining()) {
179 | peerAppData.clear();
180 | SSLEngineResult result = engine.unwrap(peerNetData, peerAppData);
181 | switch (result.getStatus()) {
182 | case OK:
183 | peerAppData.flip();
184 | byte[] resp = new byte[peerAppData.limit()];
185 | for (int i = 0; i < resp.length; i++) {
186 | resp[i] = peerAppData.get(i);
187 | }
188 | response = resp;
189 | exitReadLoop = true;
190 | break;
191 | case BUFFER_OVERFLOW:
192 | peerAppData = sslHandler.enlargeApplicationBuffer(engine, peerAppData);
193 | break;
194 | case BUFFER_UNDERFLOW:
195 | peerNetData = sslHandler.handleBufferUnderflow(engine, peerNetData);
196 | break;
197 | case CLOSED:
198 | close();
199 | return response;
200 | default:
201 | throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
202 | }
203 | }
204 | } else if (bytesRead < 0) {
205 | engine.closeInbound();
206 | return response;
207 | }
208 | try {
209 | Thread.sleep(waitToReadMillis);
210 | } catch (InterruptedException e) {
211 | e.printStackTrace();
212 | }
213 | }
214 |
215 | return response;
216 | }
217 |
218 | public void close() {
219 | try {
220 | if(socketChannel!=null)
221 | socketChannel.close();
222 | if(myAppData!=null) {
223 | myAppData.compact();
224 | myAppData.clear();
225 | myAppData = null;
226 | }
227 | if(myNetData!=null) {
228 | myNetData.compact();
229 | myNetData.clear();
230 | myNetData = null;
231 | }
232 | } catch (IOException e) {
233 | e.printStackTrace();
234 | }
235 | }
236 |
237 | @Override
238 | public void setReadTimeout(int readTimeout) throws SocketException {
239 | socketChannel.socket().setSoTimeout(10000);
240 | }
241 |
242 | @Override
243 | public boolean isConnected() {
244 | return socketChannel != null && socketChannel.isConnected();
245 | }
246 |
247 | @Override
248 | public boolean isClosed() {
249 | return socketChannel == null || socketChannel.socket().isClosed();
250 | }
251 |
252 | }
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/handlers/SSLHandler.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.handlers;
2 |
3 | import com.imohsenb.ISO8583.builders.ISOClientBuilder;
4 | import com.imohsenb.ISO8583.interfaces.SSLKeyManagers;
5 | import com.imohsenb.ISO8583.interfaces.SSLProtocol;
6 | import com.imohsenb.ISO8583.interfaces.SSLTrustManagers;
7 |
8 | import javax.net.ssl.*;
9 | import java.nio.ByteBuffer;
10 | import java.nio.channels.SocketChannel;
11 | import java.security.KeyManagementException;
12 | import java.security.NoSuchAlgorithmException;
13 | import java.security.SecureRandom;
14 |
15 | /**
16 | * @author Mohsen Beiranvand
17 | */
18 | public class SSLHandler implements SSLProtocol,SSLKeyManagers,SSLTrustManagers
19 | {
20 |
21 | private final ISOClientBuilder.ClientBuilder clientBuilder;
22 | private String protocol;
23 | private KeyManager[] keyManagers;
24 | private TrustManager[] trustManagers;
25 |
26 | public SSLHandler(ISOClientBuilder.ClientBuilder clientBuilder) {
27 | this.clientBuilder = clientBuilder;
28 | }
29 |
30 |
31 | public SSLKeyManagers setSSLProtocol(String protocol) {
32 | this.protocol = protocol;
33 | return this;
34 | }
35 |
36 | public SSLTrustManagers setKeyManagers(KeyManager[] keyManagers){
37 | this.keyManagers = keyManagers;
38 | return this;
39 | }
40 |
41 | public ISOClientBuilder.ClientBuilder setTrustManagers(TrustManager[] trustManagers) {
42 | this.trustManagers = trustManagers;
43 | return clientBuilder;
44 | }
45 |
46 |
47 | public SSLContext getContext() throws NoSuchAlgorithmException, KeyManagementException {
48 |
49 | SSLContext context = SSLContext.getInstance(protocol);
50 |
51 | //init trust manager
52 | if(trustManagers == null)
53 | trustManagers = new TrustManager[]{
54 | new X509TrustManager() {
55 | public java.security.cert.X509Certificate[] getAcceptedIssuers() {
56 | return new java.security.cert.X509Certificate[] {};
57 | }
58 | public void checkClientTrusted(
59 | java.security.cert.X509Certificate[] certs, String authType) {
60 | }
61 | public void checkServerTrusted(
62 | java.security.cert.X509Certificate[] certs, String authType) {
63 | }
64 | }
65 | };
66 |
67 | context.init(keyManagers,trustManagers, SecureRandom.getInstance("SHA1PRNG"));
68 |
69 | return context;
70 | }
71 |
72 | public boolean doHandshake(SocketChannel socketChannel, SSLEngine engine,
73 | ByteBuffer myNetData, ByteBuffer peerNetData, ByteBuffer peerAppData, ByteBuffer myAppData) throws Exception {
74 |
75 | System.out.println("About to do handshake...");
76 |
77 | SSLEngineResult result;
78 | SSLEngineResult.HandshakeStatus handshakeStatus;
79 |
80 | myNetData.clear();
81 | peerNetData.clear();
82 |
83 | // Begin handshake
84 | engine.beginHandshake();
85 |
86 | handshakeStatus = engine.getHandshakeStatus();
87 |
88 | while (handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED && handshakeStatus != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
89 |
90 | switch (handshakeStatus) {
91 | case NEED_UNWRAP:
92 | if (socketChannel.read(peerNetData) < 0) {
93 | if (engine.isInboundDone() && engine.isOutboundDone()) {
94 | return false;
95 | }
96 | try {
97 | engine.closeInbound();
98 | } catch (SSLException e) {
99 | System.out.println("This engine was forced to close inbound, without having received the proper SSL/TLS close notification label from the peer, due to end of stream.");
100 | }
101 | engine.closeOutbound();
102 | // After closeOutbound the engine will be set to WRAP state, in order to try to send a close label to the client.
103 | handshakeStatus = engine.getHandshakeStatus();
104 | break;
105 | }
106 | peerNetData.flip();
107 | try {
108 | result = engine.unwrap(peerNetData, peerAppData);
109 | peerNetData.compact();
110 | handshakeStatus = result.getHandshakeStatus();
111 | } catch (SSLException sslException) {
112 | System.out.println("A problem was encountered while processing the data that caused the SSLEngine to abort. Will try to properly close connection...");
113 | engine.closeOutbound();
114 | handshakeStatus = engine.getHandshakeStatus();
115 | break;
116 | }
117 | switch (result.getStatus()) {
118 | case OK:
119 | break;
120 | case BUFFER_OVERFLOW:
121 | // Will occur when peerAppData's capacity is smaller than the data derived from peerNetData's unwrap.
122 | peerAppData = enlargeApplicationBuffer(engine, peerAppData);
123 | break;
124 | case BUFFER_UNDERFLOW:
125 | // Will occur either when no data was read from the peer or when the peerNetData buffer was too small to hold all peer's data.
126 | peerNetData = handleBufferUnderflow(engine, peerNetData);
127 | break;
128 | case CLOSED:
129 | if (engine.isOutboundDone()) {
130 | return false;
131 | } else {
132 | engine.closeOutbound();
133 | handshakeStatus = engine.getHandshakeStatus();
134 | break;
135 | }
136 | default:
137 | throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
138 | }
139 | break;
140 | case NEED_WRAP:
141 | myNetData.clear();
142 | try {
143 | result = engine.wrap(myAppData, myNetData);
144 | handshakeStatus = result.getHandshakeStatus();
145 | } catch (SSLException sslException) {
146 | System.out.println("A problem was encountered while processing the data that caused the SSLEngine to abort. Will try to properly close connection...");
147 | engine.closeOutbound();
148 | handshakeStatus = engine.getHandshakeStatus();
149 | break;
150 | }
151 | switch (result.getStatus()) {
152 | case OK :
153 | myNetData.flip();
154 | while (myNetData.hasRemaining()) {
155 | socketChannel.write(myNetData);
156 | }
157 | break;
158 | case BUFFER_OVERFLOW:
159 | // Will occur if there is not enough space in myNetData buffer to write all the data that would be generated by the method wrap.
160 | // Since myNetData is set to session's packet size we should not get to this point because SSLEngine is supposed
161 | // to produce messages smaller or equal to that, but a general handling would be the following:
162 | myNetData = enlargePacketBuffer(engine, myNetData);
163 | break;
164 | case BUFFER_UNDERFLOW:
165 | throw new SSLException("Buffer underflow occured after a wrap. I don't think we should ever get here.");
166 | case CLOSED:
167 | try {
168 | myNetData.flip();
169 | while (myNetData.hasRemaining()) {
170 | socketChannel.write(myNetData);
171 | }
172 | // At this point the handshake status will probably be NEED_UNWRAP so we make sure that peerNetData is clear to read.
173 | peerNetData.clear();
174 | } catch (Exception e) {
175 | System.out.println("Failed to send server's CLOSE label due to socket channel's failure.");
176 | handshakeStatus = engine.getHandshakeStatus();
177 | }
178 | break;
179 | default:
180 | throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
181 | }
182 | break;
183 | case NEED_TASK:
184 | Runnable task;
185 | while ((task = engine.getDelegatedTask()) != null) {
186 | new Thread(task).start();
187 | }
188 | handshakeStatus = engine.getHandshakeStatus();
189 | break;
190 | case FINISHED:
191 | break;
192 | case NOT_HANDSHAKING:
193 | break;
194 | default:
195 | throw new IllegalStateException("Invalid SSL status: " + handshakeStatus);
196 | }
197 | }
198 |
199 | return true;
200 |
201 | }
202 |
203 | public ByteBuffer handleBufferUnderflow(SSLEngine engine, ByteBuffer buffer) {
204 | if (engine.getSession().getPacketBufferSize() < buffer.limit()) {
205 | return buffer;
206 | } else {
207 | ByteBuffer replaceBuffer = enlargePacketBuffer(engine, buffer);
208 | buffer.flip();
209 | replaceBuffer.put(buffer);
210 | return replaceBuffer;
211 | }
212 | }
213 |
214 | public ByteBuffer enlargePacketBuffer(SSLEngine engine, ByteBuffer buffer) {
215 | return enlargeBuffer(buffer, engine.getSession().getPacketBufferSize());
216 | }
217 |
218 | public ByteBuffer enlargeApplicationBuffer(SSLEngine engine, ByteBuffer buffer) {
219 | return enlargeBuffer(buffer, engine.getSession().getApplicationBufferSize());
220 | }
221 |
222 |
223 | public ByteBuffer enlargeBuffer(ByteBuffer buffer, int sessionProposedCapacity) {
224 | if (sessionProposedCapacity > buffer.capacity()) {
225 | buffer = ByteBuffer.allocate(sessionProposedCapacity);
226 | } else {
227 | buffer = ByteBuffer.allocate(buffer.capacity() * 2);
228 | }
229 | return buffer;
230 | }
231 |
232 | }
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/interfaces/DataElement.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.interfaces;
2 |
3 | import com.imohsenb.ISO8583.entities.ISOMessage;
4 | import com.imohsenb.ISO8583.enums.FIELDS;
5 | import com.imohsenb.ISO8583.exceptions.ISOException;
6 | import com.imohsenb.ISO8583.security.ISOMacGenerator;
7 |
8 | /**
9 | * @author Mohsen Beiranvand
10 | */
11 | public interface DataElement {
12 |
13 | ISOMessage build() throws ISOException;
14 |
15 | DataElement generateMac(ISOMacGenerator generator) throws ISOException;
16 |
17 | DataElement setField(int no, String value) throws ISOException;
18 | DataElement setField(FIELDS field, String value) throws ISOException;
19 | DataElement setField(int no, byte[] value) throws ISOException;
20 | DataElement setField(FIELDS field, byte[] value) throws ISOException;
21 |
22 |
23 | DataElement setHeader(String header);
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/interfaces/Financial.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.interfaces;
2 |
3 | import java.math.BigDecimal;
4 |
5 | /**
6 | * @author Mohsen Beiranvand
7 | */
8 | public interface Financial {
9 |
10 |
11 | DataElement setAmount(BigDecimal amount);
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/interfaces/ISOClient.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.interfaces;
2 |
3 | import com.imohsenb.ISO8583.entities.ISOMessage;
4 | import com.imohsenb.ISO8583.exceptions.ISOClientException;
5 |
6 | import java.io.IOException;
7 |
8 | /**
9 | * @author Mohsen Beiranvand
10 | */
11 | public interface ISOClient {
12 |
13 | /**
14 | *
15 | * @throws ISOClientException
16 | * @throws IOException
17 | */
18 | void connect() throws ISOClientException, IOException;
19 |
20 | /**
21 | *
22 | */
23 | void disconnect();
24 |
25 | /**
26 | *
27 | * @param isoMessage
28 | * @return
29 | * @throws ISOClientException
30 | * @throws IOException
31 | */
32 | byte[] sendMessageSync(ISOMessage isoMessage) throws ISOClientException, IOException;
33 |
34 | /**
35 | *
36 | *
37 | * @return
38 | */
39 | boolean isConnected();
40 |
41 | /**
42 | *
43 | * @return
44 | */
45 | boolean isClosed();
46 |
47 | /**
48 | *
49 | * @param isoClientEventListener
50 | */
51 | void setEventListener(ISOClientEventListener isoClientEventListener);
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/interfaces/ISOClientEventListener.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.interfaces;
2 |
3 | /**
4 | * @author Mohsen Beiranvand
5 | */
6 | public interface ISOClientEventListener {
7 |
8 | void connecting();
9 | void connected();
10 | void connectionFailed();
11 | void connectionClosed();
12 | void disconnected();
13 | void beforeSendingMessage();
14 | void afterSendingMessage();
15 | void onReceiveData();
16 | void beforeReceiveResponse();
17 | void afterReceiveResponse();
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/interfaces/MessageBuilder.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.interfaces;
2 |
3 | /**
4 | * @author Mohsen Beiranvand
5 | */
6 | public interface MessageBuilder {
7 |
8 | void build();
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/interfaces/MessageClass.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.interfaces;
2 |
3 | import com.imohsenb.ISO8583.builders.GeneralMessageClassBuilder;
4 |
5 | /**
6 | * @author Mohsen Beiranvand
7 | */
8 | public interface MessageClass {
9 | /**
10 | * Determine if funds are available, get an approval but do not post to account for reconciliation.
11 | * @return
12 | */
13 | MessagePacker authorization();
14 |
15 | /**
16 | * Determine if funds are available, get an approval and post directly to the account.
17 | * @return
18 | */
19 | MessagePacker financial();
20 |
21 | /**
22 | * Used for hot-card, TMS and other exchanges
23 | * @return
24 | */
25 | MessagePacker fileAction();
26 |
27 | /**
28 | * Reverses the action of a previous authorization.
29 | * @return
30 | */
31 | MessagePacker reversal();
32 |
33 | /**
34 | * Transmits settlement information label.
35 | * @return
36 | */
37 | MessagePacker reconciliation();
38 |
39 | /**
40 | * Transmits administrative advice.
41 | * @return
42 | */
43 | MessagePacker administrative();
44 | MessagePacker feeCollection();
45 |
46 | /**
47 | * Used for secure key exchange, logon, echo test and other network functions
48 | * @return
49 | */
50 | MessagePacker networkManagement();
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/interfaces/MessagePacker.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.interfaces;
2 |
3 | import com.imohsenb.ISO8583.enums.MESSAGE_FUNCTION;
4 | import com.imohsenb.ISO8583.enums.MESSAGE_ORIGIN;
5 |
6 | /**
7 | * @author Mohsen Beiranvand
8 | */
9 | public interface MessagePacker {
10 |
11 | ProcessCode mti(MESSAGE_FUNCTION mFunction, MESSAGE_ORIGIN mOrigin);
12 |
13 | MessagePacker setLeftPadding(byte character);
14 |
15 | MessagePacker setRightPadding(byte character);
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/interfaces/NetworkManagement.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.interfaces;
2 |
3 |
4 | /**
5 | * @author Mohsen Beiranvand
6 | */
7 | public interface NetworkManagement {
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/interfaces/ProcessCode.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.interfaces;
2 |
3 | import com.imohsenb.ISO8583.enums.PC_ATC;
4 | import com.imohsenb.ISO8583.enums.PC_TTC_100;
5 | import com.imohsenb.ISO8583.enums.PC_TTC_200;
6 | import com.imohsenb.ISO8583.exceptions.ISOException;
7 |
8 | /**
9 | * @author Mohsen Beiranvand
10 | */
11 | public interface ProcessCode {
12 | DataElement processCode(String code) throws ISOException;
13 | DataElement processCode(PC_TTC_100 ttc) throws ISOException;
14 | DataElement processCode(PC_TTC_100 ttc, PC_ATC atcFrom, PC_ATC atcTo) throws ISOException;
15 | DataElement processCode(PC_TTC_200 ttc) throws ISOException;
16 | DataElement processCode(PC_TTC_200 ttc, PC_ATC atcFrom, PC_ATC atcTo) throws ISOException;
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/interfaces/SSLKeyManagers.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.interfaces;
2 |
3 | import javax.net.ssl.KeyManager;
4 |
5 | /**
6 | * @author Mohsen Beiranvand
7 | */
8 | public interface SSLKeyManagers
9 | {
10 | SSLTrustManagers setKeyManagers(KeyManager[] keyManagers);
11 | }
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/interfaces/SSLProtocol.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.interfaces;
2 |
3 | /**
4 | * @author Mohsen Beiranvand
5 | */
6 | public interface SSLProtocol {
7 | SSLKeyManagers setSSLProtocol(String protocol);
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/interfaces/SSLTrustManagers.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.interfaces;
2 |
3 | import com.imohsenb.ISO8583.builders.ISOClientBuilder;
4 |
5 | import javax.net.ssl.TrustManager;
6 |
7 | /**
8 | * @author Mohsen Beiranvand
9 | */
10 | public interface SSLTrustManagers
11 | {
12 | ISOClientBuilder.ClientBuilder setTrustManagers(TrustManager[] trustManagers);
13 | }
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/interfaces/SocketHandler.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.interfaces;
2 |
3 | import com.imohsenb.ISO8583.exceptions.ISOClientException;
4 | import com.imohsenb.ISO8583.handlers.SSLHandler;
5 |
6 | import java.io.IOException;
7 | import java.net.SocketException;
8 | import java.nio.ByteBuffer;
9 |
10 | /**
11 | * Socket Handler Interface
12 | * Its responsible about initializing socket and connection to ISO switch
13 | * @author Mohsen Beiranvand
14 | */
15 | public interface SocketHandler {
16 |
17 |
18 | /**
19 | * Initialize SSL connection to switch
20 | * @param host IP address of switch
21 | * @param port Switch port number
22 | * @param isoClientEventListener Event listener for dispatch state of operation
23 | * @param sslHandler Implementation of {@link SSLHandler} for handling ssl handshakes
24 | * @throws ISOClientException
25 | */
26 | void init(String host, int port, ISOClientEventListener isoClientEventListener, SSLHandler sslHandler) throws ISOClientException;
27 |
28 | /**
29 | * Initialize NONE SSL connection to switch
30 | * @param host IP address of switch
31 | * @param port Switch port number
32 | * @param isoClientEventListener Event listener for dispatch state of operation
33 | * @throws IOException
34 | */
35 | void init(String host, int port, ISOClientEventListener isoClientEventListener) throws IOException;
36 |
37 | /**
38 | * Send message in sync way and return result
39 | * @param buffer buffer for sending
40 | * @param length length of message length
41 | * @return response buffer from message
42 | * @throws IOException
43 | * @throws ISOClientException
44 | */
45 | byte[] sendMessageSync(ByteBuffer buffer, int length) throws IOException, ISOClientException ;
46 |
47 | /**
48 | * Close current socket
49 | */
50 | void close();
51 |
52 | /**
53 | * Set waiting time for take a response from switch
54 | * @param readTimeout time out in milliseconds
55 | * @throws SocketException
56 | */
57 | void setReadTimeout(int readTimeout) throws SocketException;
58 |
59 | /**
60 | * Check socket already connected to the host.
61 | * @return true if is connected
62 | */
63 | boolean isConnected();
64 |
65 | /**
66 | * Check if socket is closed.
67 | * @return true if socket already closed
68 | */
69 | boolean isClosed();
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/interfaces/UnpackMessage.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.interfaces;
2 |
3 | /**
4 | * @author Mohsen Beiranvand
5 | */
6 | public interface UnpackMessage {
7 |
8 | UnpackMethods setMessage(byte[] message);
9 | UnpackMethods setMessage(String message);
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/interfaces/UnpackMethods.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.interfaces;
2 |
3 | import com.imohsenb.ISO8583.entities.ISOMessage;
4 | import com.imohsenb.ISO8583.exceptions.ISOException;
5 |
6 | /**
7 | * @author Mohsen Beiranvand
8 | */
9 | public interface UnpackMethods {
10 |
11 | ISOMessage build() throws ISOException;
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/security/ISOMacGenerator.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.security;
2 |
3 | /**
4 | * ISOMacGenerator
5 | * @author Mohsen Beiranvand
6 | */
7 | public abstract class ISOMacGenerator {
8 |
9 | public abstract byte[] generate(byte[] data);
10 |
11 | }
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/utils/ByteArray.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.utils;
2 |
3 | import java.util.Arrays;
4 |
5 | /**
6 | * @author Mohsen Beiranvand
7 | */
8 | public final class ByteArray {
9 |
10 | private static int frameSize = 512;
11 | private int size;
12 | private byte[] data;
13 | private int position = 0;
14 |
15 | public ByteArray()
16 | {
17 | init();
18 | }
19 |
20 | private void init() {
21 | this.size = frameSize;
22 | this.data = new byte[size];
23 | this.position = 0;
24 | }
25 |
26 | public ByteArray append(byte[] value)
27 | {
28 | if(value.length + position > size)
29 | expandBuffer();
30 |
31 | System.arraycopy(
32 | value,
33 | 0,
34 | data,
35 | position,
36 | value.length
37 | );
38 |
39 | position += value.length;
40 | return this;
41 | }
42 |
43 | public ByteArray append(byte value) {
44 | append(new byte[]{value});
45 | return this;
46 | }
47 |
48 | public ByteArray prepend(byte[] value)
49 | {
50 | int vlen = value.length;
51 | int newSize = (int) (Math.ceil((size + vlen) / frameSize) * frameSize);
52 | byte[] dest = new byte[newSize];
53 |
54 | System.arraycopy(value,0,dest,0,vlen);
55 | System.arraycopy(data,0,dest,vlen,position);
56 |
57 | data = dest;
58 | position = vlen + position;
59 | size = newSize;
60 | dest = null;
61 |
62 | return this;
63 | }
64 |
65 | public ByteArray prepend(byte value) {
66 | prepend(new byte[]{value});
67 | return this;
68 | }
69 |
70 | private void expandBuffer() {
71 |
72 | int newSize = size + frameSize;
73 | byte[] dest = new byte[size];
74 | System.arraycopy(data,0,dest,0,size);
75 | data = dest;
76 | size = newSize;
77 | dest = null;
78 |
79 | }
80 |
81 | public int position()
82 | {
83 | return position;
84 | }
85 |
86 | public int limit()
87 | {
88 | return size;
89 | }
90 |
91 | public byte[] array()
92 | {
93 | return Arrays.copyOfRange(data,0,position);
94 | }
95 |
96 |
97 | public ByteArray compact()
98 | {
99 | data = Arrays.copyOfRange(data,0,position);
100 | size = position;
101 | return this;
102 | }
103 |
104 | public String toString()
105 | {
106 | return new String(array());
107 | }
108 |
109 | public ByteArray clear()
110 | {
111 | Arrays.fill(data, (byte) 0);
112 | init();
113 | return this;
114 | }
115 |
116 | public ByteArray replace(byte[] value) {
117 |
118 | init();
119 | append(value);
120 | return this;
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/utils/FixedBitSet.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.utils;
2 |
3 | import java.util.ArrayList;
4 | import java.util.BitSet;
5 |
6 | /**
7 | * @author Mohsen Beiranvand
8 | */
9 | public class FixedBitSet extends BitSet {
10 |
11 | private final int nbits;
12 |
13 | public FixedBitSet(final int nbits) {
14 | super(nbits);
15 | this.nbits = nbits;
16 | }
17 |
18 | @Override
19 | public String toString() {
20 | final StringBuilder buffer = new StringBuilder(nbits);
21 |
22 | for (int i = 0; i < nbits; i++) {
23 | buffer.append(get(i) ? '1' : '0');
24 | }
25 |
26 | return buffer.toString();
27 | }
28 |
29 | public FixedBitSet fromHexString(String value)
30 | {
31 | int offset = 0;
32 | for (int i = 0; i < value.length(); i=i+1) {
33 | String item = value.substring(i,i+1);
34 | byte bitem = (byte) Integer.parseInt(item, 16);
35 | if((bitem & 0b1000) > 0)
36 | set(offset);
37 | if((bitem & 0b0100) > 0)
38 | set(offset + 1);
39 | if((bitem & 0b0010) > 0)
40 | set(offset + 2);
41 | if((bitem & 0b0001) > 0)
42 | set(offset + 3);
43 | offset+=4;
44 | }
45 | return this;
46 | }
47 |
48 | public String toHexString()
49 | {
50 | final StringBuilder buffer = new StringBuilder(nbits);
51 | String bStr = toString();
52 |
53 | for(int c=0;c< nbits; c=c+4)
54 | {
55 | int decimal = Integer.parseInt(bStr.substring(c,c+4),2);
56 | String hexStr = Integer.toString(decimal,16);
57 | buffer.append(hexStr);
58 | }
59 | return buffer.toString();
60 | }
61 |
62 | public ArrayList getIndexes()
63 | {
64 | ArrayList list = new ArrayList<>();
65 | int indx = -1;
66 | int size = size();
67 | while (indx < size)
68 | {
69 | indx = nextSetBit(indx+1);
70 | if(indx == -1)
71 | break;
72 | list.add(indx + 1);
73 | }
74 | return list;
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/utils/StringUtil.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.utils;
2 |
3 | import java.nio.ByteBuffer;
4 | import java.util.Arrays;
5 |
6 | /**
7 | * @author Mohsen Beiranvand
8 | */
9 | public final class StringUtil {
10 |
11 | private final static char[] hexArray = "0123456789ABCDEF".toCharArray();
12 |
13 | public static String fromByteArray(byte[] data)
14 | {
15 | char[] hexChars = new char[data.length * 2];
16 | for ( int j = 0; j < data.length; j++ ) {
17 | int v = data[j] & 0xFF;
18 | hexChars[j * 2] = hexArray[v >>> 4];
19 | hexChars[j * 2 + 1] = hexArray[v & 0x0F];
20 | }
21 | return new String(hexChars);
22 | }
23 |
24 | public static String asciiFromByteArray(byte[] data)
25 | {
26 | return hexToAscii(fromByteArray(data));
27 | }
28 |
29 | //it's come from http://www.baeldung.com/java-convert-hex-to-ascii
30 | public static String asciiToHex(String asciiStr) {
31 | char[] chars = asciiStr.toCharArray();
32 | StringBuilder hex = new StringBuilder();
33 | for (char ch : chars) {
34 | hex.append(Integer.toHexString((int) ch));
35 | }
36 |
37 | return hex.toString();
38 | }
39 |
40 | //it's come from http://www.baeldung.com/java-convert-hex-to-ascii
41 | public static String hexToAscii(String hexStr) {
42 | StringBuilder output = new StringBuilder("");
43 |
44 | for (int i = 0; i < hexStr.length(); i += 2) {
45 | String str = hexStr.substring(i, i + 2);
46 | output.append((char) Integer.parseInt(str, 16));
47 | }
48 |
49 | return output.toString();
50 | }
51 |
52 | public static byte[] asciiToHex(byte[] data) {
53 |
54 | char[] hexChars = new char[data.length * 2];
55 | for ( int j = 0; j < data.length; j++ ) {
56 | int v = data[j] & 0xFF;
57 | hexChars[j * 2] = hexArray[v >>> 4];
58 | hexChars[j * 2 + 1] = hexArray[v & 0x0F];
59 | }
60 |
61 | byte[] res = new byte[hexChars.length];
62 | for (int i = 0; i
120 | * example)a?\u0061
121 | * @param ch
122 | * @return
123 | */
124 | public static String toHexString(char ch) {
125 | String hex = Integer.toHexString((int) ch);
126 | while (hex.length() < 4) {
127 | hex = "0" + hex;
128 | }
129 | hex = "\\u" + hex;
130 | return hex;
131 | }
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/src/main/java/com/imohsenb/ISO8583/utils/TLVParser.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.utils;
2 |
3 |
4 | import java.util.Arrays;
5 | import java.util.HashMap;
6 |
7 | /**
8 | * @author Mohsen Beiranvand
9 | */
10 | public final class TLVParser {
11 |
12 | public static HashMap parse(byte[] message, int tagLength, int lengthOfDataLen)
13 | {
14 | HashMap parts = new HashMap<>();
15 |
16 | int offset = 0;
17 |
18 | while (offset < message.length) {
19 | String tag = new String(Arrays.copyOfRange(message, offset, offset+tagLength));
20 | int len = Integer.parseInt(new String(Arrays.copyOfRange(message, offset + tagLength, offset + tagLength + lengthOfDataLen)));
21 |
22 | if(message.length >= offset + tagLength + lengthOfDataLen + len)
23 | parts.put(tag, Arrays.copyOfRange(message, offset + tagLength + lengthOfDataLen , offset + tagLength + lengthOfDataLen + len));
24 | offset += len + tagLength + lengthOfDataLen;
25 | }
26 |
27 | return parts;
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/test/java/com/imohsenb/ISO8583/builders/GeneralMessageClassBuilderTest.java:
--------------------------------------------------------------------------------
1 | package com.imohsenb.ISO8583.builders;
2 |
3 | import com.imohsenb.ISO8583.entities.ISOMessage;
4 | import com.imohsenb.ISO8583.enums.FIELDS;
5 | import com.imohsenb.ISO8583.enums.MESSAGE_FUNCTION;
6 | import com.imohsenb.ISO8583.enums.MESSAGE_ORIGIN;
7 | import com.imohsenb.ISO8583.enums.VERSION;
8 | import com.imohsenb.ISO8583.exceptions.ISOException;
9 | import org.junit.Test;
10 |
11 | import static org.assertj.core.api.Assertions.assertThat;
12 |
13 |
14 | public class GeneralMessageClassBuilderTest {
15 |
16 | @Test
17 | public void checkLeftPadding() throws Exception {
18 | ISOMessage isoMessage = ISOMessageBuilder.Packer(VERSION.V1987)
19 | .networkManagement()
20 | .setLeftPadding((byte) 0xF)
21 | .mti(MESSAGE_FUNCTION.Request, MESSAGE_ORIGIN.Acquirer)
22 | .processCode("920000")
23 | .setField(FIELDS.F11_STAN, "1")
24 | .setField(FIELDS.F24_NII_FunctionCode, "333")
25 | .build();
26 | System.out.println(isoMessage.toString());
27 | assertThat(isoMessage.toString()).isEqualTo("08002020010000000000920000000001F333");
28 |
29 | }
30 |
31 | @Test
32 | public void checkRightPadding() throws Exception {
33 | ISOMessage isoMessage = ISOMessageBuilder.Packer(VERSION.V1987)
34 | .networkManagement()
35 | .setRightPadding((byte) 0xF)
36 | .mti(MESSAGE_FUNCTION.Request, MESSAGE_ORIGIN.Acquirer)
37 | .processCode("920000")
38 | .setField(FIELDS.F11_STAN, "1")
39 | .setField(FIELDS.F24_NII_FunctionCode, "333")
40 | .build();
41 | System.out.println(isoMessage.toString());
42 | assertThat(isoMessage.toString()).isEqualTo("08002020010000000000920000000001333F");
43 | }
44 |
45 | @Test
46 | public void checkSetHeader() throws ISOException {
47 | ISOMessage isoMessage = ISOMessageBuilder.Packer(VERSION.V1987)
48 | .networkManagement()
49 | .setRightPadding((byte) 0xF)
50 | .mti(MESSAGE_FUNCTION.Request, MESSAGE_ORIGIN.Acquirer)
51 | .processCode("920000")
52 | .setHeader("1002230000")
53 | .build();
54 | //Then
55 | assertThat(isoMessage.toString()).isEqualTo("100223000008002000000000000000920000");
56 | }
57 |
58 | @Test
59 | public void checkWithoutSetHeader() throws ISOException {
60 | ISOMessage isoMessage = ISOMessageBuilder.Packer(VERSION.V1987)
61 | .networkManagement()
62 | .setRightPadding((byte) 0xF)
63 | .mti(MESSAGE_FUNCTION.Request, MESSAGE_ORIGIN.Acquirer)
64 | .processCode("920000")
65 | .build();
66 | //Then
67 | assertThat(isoMessage.toString()).isEqualTo("08002000000000000000920000");
68 | }
69 |
70 |
71 | @Test
72 | public void evenPanShouldHaveCorrectLengthPrefix() throws Exception {
73 | ISOMessage isoMessage = ISOMessageBuilder.Packer(VERSION.V1987)
74 | .networkManagement()
75 | .setLeftPadding((byte) 0xF)
76 | .mti(MESSAGE_FUNCTION.Request, MESSAGE_ORIGIN.Acquirer)
77 | .processCode("920000")
78 | .setField(FIELDS.F2_PAN, "1234567890123456")
79 | .build();
80 | System.out.println(isoMessage.toString());
81 | assertThat(isoMessage.toString()).isEqualTo("08006000000000000000161234567890123456920000");
82 | }
83 |
84 | @Test
85 | public void OddPanShouldHaveCorrectLengthPrefixAndPaddingChar() throws Exception {
86 | ISOMessage isoMessage = ISOMessageBuilder.Packer(VERSION.V1987)
87 | .networkManagement()
88 | .setLeftPadding((byte) 0xF)
89 | .mti(MESSAGE_FUNCTION.Request, MESSAGE_ORIGIN.Acquirer)
90 | .processCode("920000")
91 | .setField(FIELDS.F2_PAN, "1234567890123456789")
92 | .build();
93 | System.out.println(isoMessage.toString());
94 | assertThat(isoMessage.toString()).isEqualTo("080060000000000000001901234567890123456789920000");
95 | }
96 | }
--------------------------------------------------------------------------------