Search code examples
codenameone

Generating a Signature for Apple Promotional Offers


This is a follow up on a question I had asked and thank you to Codename One for implementing promotional offers!

I am having trouble generating the signature. The following snippet of code is what I have to generate the signature, but it is giving a java.lang.IllegalArgumentException: initialisation vector must be the same length as block size error

byte[] encoded = Base64.decode(key.getBytes("UTF-8"));
KeyFactory keyFactory = KeyFactory.getInstance("EC");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);


Signature ecdsaSign = Signature.getInstance("SHA256withECDSA");
ecdsaSign.initSign(keyFactory.generatePrivate(keySpec));
ecdsaSign.update(stringToSign.getBytes("UTF-8"));

The key variable is a string that contains the downloaded PKCS #8 private key from App Store Connect without the "-----BEGIN PRIVATE KEY-----" and "-----END PRIVATE KEY-----" parts/

And the stringToSign contains a combined string of what is listed on here for promotional offers.

String stringToEncrypt = "appBundleId" + "\u2063"
                                + applePromotionalOffer.getKeyIdentifier() + "\u2063"
                                + "productIdentifier" + "\u2063"
                                + applePromotionalOffer.getOfferIdentifier() + "\u2063"
                                + "" + "\u2063"
                                + applePromotionalOffer.getNonce() + "\u2063" + applePromotionalOffer.getTimestamp();

There may be problems with my combined string as well

ApplePromotionalOffer applePromotionalOffer = new ApplePromotionalOffer();
applePromotionalOffer.setOfferIdentifier("offerIdentifier");
applePromotionalOffer.setNonce(getGUID().toLowerCase()); // getGUID is a method we have made
applePromotionalOffer.setKeyIdentifier("keyIdentifier");
applePromotionalOffer.setTimestamp(getCurrentEpochInGMTTime()); // getCurrentEpochInGMTTime is a method we have made

Solution

  • Got the promotional offer to show and writing an answer for future developers trying to use the implemented support by Codename One for promotional offers on iOS:

    When creating the combined string of appBundleId + '\u2063' + keyIdentifier + '\u2063' + productIdentifier + '\u2063' + offerIdentifier + '\u2063' + appAccountToken + '\u2063' + nonce + '\u2063' + timestamp, it's best to avoid spacing and special characters for both the productIdentifier and offerIdentifier.

    And the offerIdentifier in this combined string should be what is associated to the promotional offer itself, but the productIdentifier(SKU) that you pass into the subscribe(String sku, PromotionalOffer promotionalOffer) should be the "parent" or "base" productIdentifier that the promotional offer is attached to. The same goes with the productIdentifier that goes in the combined string

    Thank you Shai Almog, Steve Hannah, and the rest of the Codename One team for the hard work!