An open letter to Nissan; Resistance is Futile ✊

November 2021 · 3 minute read

Time for an update on My Leaf! As apparent from the title of this blog post this post is a sort of open letter about Nissan’s determined effort to block/break/disable third party clients from using their API and services. This is mostly the case for the North American NissanConnect API. Again and again have Nissan tried to break third party clients these last months. They have used time and effort on breaking stuff on purpose. No new features are the cause of this. The only ones who suffer from these changes are purely Nissan’s own customers. Simply put.

Here is a brief overview of the “breaking stuff on purpose” Nissan has been up to;

  • Use of hardcoded API key replaced by generating the key at runtime in the app using some static values
  • Using the User-Agent HTTP header to block third party applications
  • Introducing a User-Agent-Key HTTP header that is generated at runtime in the app using its own packaged package signatures

Bottom line; Nissan is using time, resources and money blocking third party applications on purpose. All of these “roadblocks” have been circumvented with moderate reverse engineering effort. In the end the only ones getting affected by this is Nissan’s own customers. Why should they not be allowed to use a third party application if they choose so? Why are Nissan so appalled/afraid of this?

If Nissan will continue with this behavior I will be inclined to terminate support for the North American NissanConnect API. On the other hand I enjoy reverse engineering stuff 🤗

Here are some examples of Nissan’s “innovative roadblocks” (someone seriously used their precious time doing this);

The infamous getKeyHash generator for the User-Agent-Key HTTP header

public String getKeyHash(Context context, String str) {
        String str2;
        String str3;
        String str4;
        try {
            Signature[] signatureArr = context.getPackageManager().getPackageInfo("com.aqsmartphone.android.nissan", 64).signatures;
            int length = signatureArr.length;
            str2 = "";
            int i = 0;
            while (i < length) {
                try {
                    Signature signature = signatureArr[i];
                    MessageDigest instance = MessageDigest.getInstance(str);
                    instance.update(signature.toByteArray());
                    i++;
                    str2 = new String(Base64.encode(instance.digest(), 0));
                } catch (PackageManager.NameNotFoundException e) {
                    e = e;
                    str4 = e.toString();
                    str3 = "name not found";
                    Log.e(str3, str4);
                    return str2;
                } catch (NoSuchAlgorithmException e2) {
                    e = e2;
                    str4 = e.toString();
                    str3 = "no such an algorithm";
                    Log.e(str3, str4);
                    return str2;
                } catch (Exception e3) {
                    e = e3;
                    str4 = e.toString();
                    str3 = "exception";
                    Log.e(str3, str4);
                    return str2;
                }
            }
        } catch (PackageManager.NameNotFoundException e4) {
            e = e4;
            str2 = "";
            str4 = e.toString();
            str3 = "name not found";
            Log.e(str3, str4);
            return str2;
        } catch (NoSuchAlgorithmException e5) {
            e = e5;
            str2 = "";
            str4 = e.toString();
            str3 = "no such an algorithm";
            Log.e(str3, str4);
            return str2;
        } catch (Exception e6) {
            e = e6;
            str2 = "";
            str4 = e.toString();
            str3 = "exception";
            Log.e(str3, str4);
            return str2;
        }
        return str2;
    }

The infamous encryptnew generator for the API-Key HTTP header

public String encryptnew() {
        byte[] parseHexBinary = DatatypeConverter.parseHexBinary("3349396A52547068694A76646E3771584B39336135666E704576764453597933");
        byte[] parseHexBinary2 = DatatypeConverter.parseHexBinary("39323930663066612D646435612D346132662D396163622D656264343931643261313538");
        byte[] parseHexBinary3 = DatatypeConverter.parseHexBinary("4C39655169454D513952796B554C3364");
        SecretKeySpec secretKeySpec = new SecretKeySpec(parseHexBinary, Constants.CRYPTOGRAPHIC_ALGORITHM);
        try {
            Cipher instance = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            instance.init(1, secretKeySpec, new IvParameterSpec(parseHexBinary3));
            return Base64.encodeToString(instance.doFinal(parseHexBinary2), 3);
        } catch (Exception unused) {
            return null;
        }
    }

That’s all for now; if you have any questions or request please comment or send me an email!

Links and references;