├── .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 | } --------------------------------------------------------------------------------