Note
This is an update to my earlier post on the Nissan Leaf remote telematics. Nissan had taken their API offline in February 2016 to address the security vulnerabilities, and this post details how to connect to the new API that went into place in early April.
1 Nissan Connect EV
Most of this updated login information is courtesy of users on the MyNissanLeaf portal and specifically this post The new endpoints are now rooted at 'https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/' instead of 'https://gdcportalgw.its-mo.com/orchestration_1111/gdc/', and all accesses are now POST requests.
1.1 Initial login to Nissan servers
The initial login is now a two step process.
1.1.1 Request an encryption key
First, you need to call InitialApp.php with the parameter initial_app_string set to geORNtsZe5I4lRGjG9GZiA (taken from analyzing the iOS app traffic).
curl --data "RegionCode=NNA&lg=en-US&initial_app_strings=geORNtsZe5I4lRGjG9GZiA" -X POST "https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/InitialApp.php"
This returns an encryption key in the parameter baseprm
{"status":200,"message":"success","baseprm":"uyI5Dj9g8VCOFDnBRUbr3g"}
1.1.2 Initial logon request
Once you have the encryption key, you need to use it to encrypt the user's NissanConnect password using Blowfish ECB with PKCS5Padding. The encrypted key is then Base64 encoded, and then also urlencoded before being used in the login API request. Here's some sample Python code to show what needs to be done, assuming you have the pycrypto library installed.
from Crypto.Cipher import Blowfish
import base64
def pad_string(str):
new_str = str
pad_chars = 8-(len(str) % 8)
if pad_chars != 0:
for x in range(pad_chars):
new_str += chr(pad_chars)
return new_str
plaintext = "YOURPASSWORD"
crypt_obj = Blowfish.new('uyI5Dj9g8VCOFDnBRUbr3g', Blowfish.MODE_ECB)
ciphertext = crypt_obj.encrypt(pad_string(plaintext))
print "Plaintext: " + plaintext
print "Blowfish Cyphertext: " + ciphertext
print "Blowfish base64: " + base64.b64encode(ciphertext)
The base64 encoded string can then be used in the following API call:
curl --data "RegionCode=NNA&lg=en-US&initial_app_strings=geORNtsZe5I4lRGjG9GZiA&UserId=YOUREMAIL" --data-urlencode "Password=YOURBASE64PASSWORD" -X POST "https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/UserLoginRequest.php"
If successful, you will get back the following JSON, where custom_sessionid is what you'll be using for subsequent requests.
{
"status": 200,
"sessionId": "XXXXXXXX",
"VehicleInfoList": {
"VehicleInfo": [
{
"charger20066": "false",
"nickname": "VLeaf",
"telematicsEnabled": "true",
"vin": "XXXX"
}
],
"vehicleInfo": [
{
"charger20066": "false",
"nickname": "VLeaf",
"telematicsEnabled": "true",
"vin": "XXXX",
"custom_sessionid": "XXXX"
}
]
},
"vehicle": {
"profile": {
"vin": "XXXX",
"gdcUserId": "",
"gdcPassword": "",
"encAuthToken": "XXXX",
"dcmId": "XXXX",
"nickname": "XXXX",
"status": "ACCEPTED",
"statusDate": "Sep 24, 2015 12:00 AM"
}
},
"EncAuthToken": "XXXX",
"CustomerInfo": {
"UserId": "XXXX",
"Language": "en-US",
"Timezone": "America/New_York",
"RegionCode": "NNA",
"OwnerId": "XXXX",
"Nickname": "XXXX",
"Country": "US",
"VehicleImage": "/content/language/default/images/img/ph_car.jpg",
"UserVehicleBoundDurationSec": "946771200",
"VehicleInfo": {
"VIN": "XXXX",
"DCMID": "XXXX",
"SIMID": "XXXX",
"NAVIID": "XXXX",
"EncryptedNAVIID": "XXXX",
"MSN": "XXXX",
"LastVehicleLoginTime": "",
"UserVehicleBoundTime": "2015-09-24T18:10:06Z",
"LastDCMUseTime": "Apr 7, 2016 12:27 PM"
}
},
"UserInfoRevisionNo": "1"
}
1.2 Request current status
Request the current battery status (not necessarily the latest, but whatever is cached from the last update on Nissan's server), using the urlencoded custom_session_id
curl --data "RegionCode=NNA&lg=en-US&DCMID=XXXX&VIN=XXXX&tz=America/New_York" --data-urlencode "custom_sessionid=XXXXXXXXXX" -X POST "https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/BatteryStatusRecordsRequest.php"
Response:
"status": 200,
"BatteryStatusRecords": {
"OperationResult": "START",
"OperationDateAndTime": "Apr 6, 2016 05:57 PM",
"BatteryStatus": {
"BatteryChargingStatus": "NORMAL_CHARGING",
"BatteryCapacity": "12",
"BatteryRemainingAmount": "9",
"BatteryRemainingAmountWH": "",
"BatteryRemainingAmountkWH": ""
},
"PluginState": "CONNECTED",
"CruisingRangeAcOn": "110760.0",
"CruisingRangeAcOff": "112320.0",
"TimeRequiredToFull200_6kW": {
"HourRequiredToFull": "2",
"MinutesRequiredToFull": "30"
},
"NotificationDateAndTime": "2016/04/06 21:58",
"TargetDate": "2016/04/06 21:57"
}
1.3 Update status
Ask for an update from the car:
curl --data "RegionCode=NNA&lg=en-US&DCMID=XXXX&VIN=XXXX&tz=America/New_York" --data-urlencode "custom_sessionid=XXXXXXXXXX" -X POST "https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/BatteryStatusCheckRequest.php"
{ "status":200,"message":"success","userId":"XXXXXX","vin":"XXXXXXXXX","resultKey":"XXXXXXXXXX"}%
The resultKey can be used to poll for when the updated value is available:
curl --data "RegionCode=NNA&lg=en-US&DCMID=XXXX&VIN=XXXX&tz=America/New_York&resultKey=XXXXXX" --data-urlencode "custom_sessionid=XXXXXXXXXX" -X POST "https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/BatteryStatusCheckResultRequest.php"
If it's not ready yet:
{"status":200,"message":"success","responseFlag":"0"}
Once status is updated, you get:
{
"status":200,
"message":"success",
"responseFlag":"1",
"operationResult":"START",
"timeStamp":"2016-02-20 20:29:33",
"cruisingRangeAcOn":"129712.0",
"cruisingRangeAcOff":"133584.0",
"currentChargeLevel":"0",
"chargeMode":"NOT_CHARGING",
"pluginState":"NOT_CONNECTED",
"charging":"NO",
"chargeStatus":"CT",
"batteryDegradation":"11",
"batteryCapacity":"12",
"timeRequiredToFull":{"hours":"","minutes":""},
"timeRequiredToFull200":{"hours":"","minutes":""},
"timeRequiredToFull200_6kW":{"hours":"","minutes":""}
}
1.4 Remote Climate Control
1.4.1 Get current AC status
curl --data "RegionCode=NNA&lg=en-US&DCMID=XXXX&VIN=XXXX&tz=America/New_York" --data-urlencode "custom_sessionid=XXXXXXXXXX" -X POST "https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/remoteACRecordsRequest.php"
Response:
{
"status": 200,
"RemoteACRecords": {
"OperationResult": "FINISH",
"OperationDateAndTime": "Apr 6, 2016 07:19 PM",
"RemoteACOperation": "STOP",
"ACStartStopDateAndTime": "Apr 6, 2016 07:19 PM",
"CruisingRangeAcOn": "152880.0",
"CruisingRangeAcOff": "157248.0",
"ACStartStopURL": "",
"PluginState": "NOT_CONNECTED",
"ACDurationBatterySec": "900",
"ACDurationPluggedSec": "7200"
},
"OperationDateAndTime": ""
}
1.4.2 Turn on AC
curl --data "RegionCode=NNA&lg=en-US&DCMID=XXXX&VIN=XXXX&tz=America/New_York" --data-urlencode "custom_sessionid=XXXXXXXXXX" -X POST "https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/ACRemoteRequest.php"
Response:
{
"status": 200,
"userId": "virantha@gmail.com",
"vin": "XXXX",
"resultKey": "XXXXXXXXXXXXXX"
}
Use the resultKey to get the status of the AC request:
curl --data "RegionCode=NNA&lg=en-US&DCMID=XXXX&VIN=XXXX&tz=America/New_York&resultKey=XXXXXX" --data-urlencode "custom_sessionid=XXXXXXXXXX" -X POST "https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/ACRemoteRsult.php"
If the status is not ready yet:
{
"status": 200,
"responseFlag": "0"
}
Once the AC is turned on, the following response will be sent:
{
"status": 200,
"responseFlag": "1",
"operationResult": "START_BATTERY",
"acContinueTime": "15",
"cruisingRangeAcOn": "104720.0",
"cruisingRangeAcOff": "109208.0",
"timeStamp": "2016-04-07 16:47:41",
"hvacStatus": "ON"
}
1.4.3 Turn off AC
curl --data "RegionCode=NNA&lg=en-US&DCMID=XXXX&VIN=XXXX&tz=America/New_York" --data-urlencode "custom_sessionid=XXXXXXXXXX" -X POST "https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/ACRemoteOffRequest.php"
The response checking is identical to the AC on request.