At first you have to create an Private Application in XERO. If you didn't yet you can follow the below link: Connect to Xero Private Application Using Php Application |
You already notified that there are two combinations of public/private key file. Public key need to create application and Private key need to sign data before send to XERO. Below is a sample Java script which will connect to XERO private application. |
package com.pkm; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.io.BufferedReader; import java.io.FileReader; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.KeyFactory; import java.security.Signature; import java.security.interfaces.RSAPrivateKey; import java.security.spec.PKCS8EncodedKeySpec; import java.util.*; /** * Created by pritom on 2/01/2018. */ public class XeroPrivateApiUtils { private static final String NONCE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; private static final String ORGANIZATION_URL = "https://api.xero.com/api.xro/2.0/Organisation"; private String sbs; private String path; private String method; private Map<Object, Object> configs = new HashMap<>(); private Map<Object, Object> params = new HashMap<>(); private Map<Object, Object> sign = new HashMap<>(); private String KEY_FILE = null; private String CONSUMER_KEY = null; private String CONSUMER_SECRET = null; public static void main(String[] args) throws Exception { XeroPrivateApiUtils xero = new XeroPrivateApiUtils(); HttpJavaClient.Response response = xero.execute("GET", XeroPrivateApiUtils.ORGANIZATION_URL, new HashMap(), new HashMap()); System.out.println(response.toString()); } private XeroPrivateApiUtils() { this.KEY_FILE = "private.key"; this.CONSUMER_KEY = "IKAEAI8BA..........1MAOR9XBEHU"; this.CONSUMER_SECRET = "STXPEDM..........0FWMPLIDGB6DS"; putToMap(this.configs, "consumer_key", CONSUMER_KEY); putToMap(this.configs, "consumer_secret", CONSUMER_SECRET); putToMap(this.configs, "core_version", "2.0"); putToMap(this.configs, "payroll_version", "1.0"); putToMap(this.configs, "file_version", "1.0"); putToMap(this.configs, "application_type", "Private"); } HttpJavaClient.Response execute( String method, String path, Map<Object, Object> configs, Map<Object, Object> params ) throws Exception { this.method = method; this.path = path; this.params = params; putToMap(this.configs, configs); this.buildParameters(); this.sign(); Map<Object, Object> headers = new HashMap<>(); putToMap(headers, "Accept", "application/json"); if (this.method.equalsIgnoreCase("get")) { return HttpJavaClient.doGet((String) this.sign.get("signed_url"), headers); } return null; } private void sign() throws Exception { putToMap(this.params, "oauth_signature", this.generateSignature()); putToMap(this.sign, "parameters", this.params); putToMap(this.sign, "signature", escape((String) this.params.get("oauth_signature"))); putToMap(this.sign, "signed_url", this.path + "?" + normalizeParameters(true)); putToMap(this.sign, "sbs", this.sbs); } private String generateSignature() throws Exception { switch ((String) this.params.get("oauth_signature_method")) { case "RSA-SHA1": this.sbs = escape(this.method.toUpperCase()); this.sbs += "&" + escape(this.path); this.sbs += "&" + escape(this.normalizeParameters(false)); RSAPrivateKey privateKey = getPrivateKey(KEY_FILE); Signature sign = Signature.getInstance("SHA1withRSA"); sign.initSign(privateKey); sign.update(this.sbs.getBytes("UTF-8")); return encodeBase64(sign.sign()); case "HMAC-SHA1": String secret = this.configs.get("consumer_secret") + "&"; if (this.configs.get("access_token_secret") != null) { secret = secret + this.configs.get("access_token_secret"); } this.sbs = escape(this.method.toUpperCase()); this.sbs += "&" + escape(this.path); this.sbs += "&" + escape(this.normalizeParameters(false)); return hmacSha(secret, this.sbs); default: throw new Exception("Undefined signature method"); } } private static String hmacSha(String key, String value) throws Exception { SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "HmacSHA1"); Mac mac = Mac.getInstance("HmacSHA1"); mac.init(keySpec); byte[] result = mac.doFinal(value.getBytes()); return encodeBase64(result); } public static String encodeBase64(String o) { BASE64Encoder base64Encoder = new BASE64Encoder(); return base64Encoder.encodeBuffer(o.getBytes()).replace("\r\n", "").replace("\n", ""); } private static String encodeBase64(byte[] o) { BASE64Encoder base64Encoder = new BASE64Encoder(); return base64Encoder.encodeBuffer(o).replace("\r\n", "").replace("\n", ""); } private static RSAPrivateKey getPrivateKey(String fileName) throws Exception { byte[] encoded = decodeBase64(getKey(fileName)); KeyFactory kf = KeyFactory.getInstance("RSA"); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded); return (RSAPrivateKey) kf.generatePrivate(keySpec); } private static byte[] decodeBase64(String o) throws Exception { BASE64Decoder decoder = new BASE64Decoder(); return decoder.decodeBuffer(o); } private String normalizeParameters(Boolean filter) throws Exception { String normalized = ""; for (Object o : this.params.entrySet()) { Map.Entry me2 = (Map.Entry) o; String key = me2.getKey().toString(); Object value = me2.getValue(); Boolean allow = !(key.equalsIgnoreCase("xml") && filter) && !key.endsWith("_secret"); if (allow) { normalized += escape(key) + "=" + escape(value.toString()) + "&"; } } return normalized.length() > 0 ? normalized.substring(0, normalized.length() - 1) : normalized; } private static String escape(String context) throws Exception { context = rawUrlEncode(context); context = stringReplace("+", "%20", context); context = stringReplace("!", "%21", context); context = stringReplace("*", "%2A", context); context = stringReplace("\'", "%27", context); context = stringReplace("(", "%28", context); context = stringReplace(")", "%29", context); return context; } private static String rawUrlEncode(String context) throws Exception { return URLEncoder.encode(context, "UTF-8"); } private static String rawUrlDecode(String context) throws Exception { return URLDecoder.decode(context, "UTF-8"); } private static String stringReplace(String search, String fill, String context) { return context.replace(search, fill); } private void buildParameters() { putToMap(this.params, "oauth_nonce", shuffle(5)); putToMap(this.params, "oauth_token", CONSUMER_KEY); putToMap(this.params, "oauth_version", "1.0"); putToMap(this.params, "oauth_timestamp", "" + ((System.currentTimeMillis()) / 1000)); putToMap(this.params, "oauth_consumer_key", CONSUMER_KEY); putToMap(this.params, "oauth_signature_method", "RSA-SHA1"); this.params = new TreeMap(this.params); //Sorted map by key } private static String shuffle(Integer length) { List<Character> characters = new ArrayList<Character>(); for(char c : NONCE.toCharArray()){ characters.add(c); } StringBuilder output = new StringBuilder(length); while(length != 0) { int randPicker = (int) (Math.random() * characters.size()); output.append(characters.get(randPicker)); length --; } return output.toString(); } private static String getKey(String filename) throws Exception { String key = ""; BufferedReader br = new BufferedReader(new FileReader(filename)); String line; while ((line = br.readLine()) != null) { if (!line.startsWith("-")) { key += line + "\n"; } } br.close(); return key; } private static void putToMap(Map<Object, Object> map, Map fromMap) { for (Object key : fromMap.keySet().toArray()) { putToMap(map, key.toString(), fromMap.get(key)); } } private static void putToMap(Map<Object, Object> map, Object key, Object value) { map.put(key, value); } private static void println(Object o) { System.out.println("" + o); } private static void exception() throws Exception { throw new Exception(); } } |
You can download a sample Private Key File from this link |
And output? Obviously as below: |
{ "Id": "966aed9c-....-4d86-....-c59a6b34eb2c", "Status": "OK", "ProviderName": "Dev Private", "DateTimeUTC": "\/Date(1515683933534)\/", "Organisations": [ { "APIKey": "WXP9HDM..........ILMSZHZIUSIA5", "Name": "Dev Organisation", "LegalName": "Dev Organisation", "PaysTax": true, "Version": "AU", "OrganisationType": "COMPANY", "BaseCurrency": "AUD", "CountryCode": "AU", "IsDemoCompany": false, "OrganisationStatus": "ACTIVE", "FinancialYearEndDay": 30, "FinancialYearEndMonth": 6, "SalesTaxBasis": "ACCRUALS", "SalesTaxPeriod": "QUARTERLY1", "DefaultSalesTax": "Tax Exclusive", "DefaultPurchasesTax": "Tax Inclusive", "CreatedDateUTC": "\/Date(1514794572000)\/", "OrganisationEntityType": "COMPANY", "Timezone": "AUSEASTERNSTANDARDTIME", "ShortCode": "!wnfkR", "OrganisationID": "91de7ce4-....-48e8-....-74573ab8b4e0", "LineOfBusiness": "tests xero integration", "Addresses": [], "Phones": [], "ExternalLinks": [], "PaymentTerms": {} } ] } |
Thursday, January 11, 2018
XERO Private Application | XERO Connect Using Private Application Java | Java to Connect to XERO Private Application
Subscribe to:
Posts (Atom)