WWDC2014 Session 305

Transcript

X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Silence ]
>> Hi. My name is James Wilson
and I'm an engineering manager.
And one of the things that my
team works is on the frameworks
that power the App Store
and the iBookstore in OS X,
which in course -- of course
includes the unified receipt
format that we introduced
last year.
So in this session, Preventing
Unauthorized Purchases
with Receipts, what we're
specifically going to talk
about is a way in which you can
use this concept of a receipt
to protect your revenue
and enforce protection
of your business model
directly into your app,
as well as into your servers
that are issuing content based
on an in-app purchase
having been made.
Ultimately, we use receipts
to know exactly what the
customer has paid for.
And you can do this both within
your app, so you can build logic
into your app binary itself
that verifies the receipt,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
into your app binary itself
that verifies the receipt,
knows that it came from Apple,
and knows exactly what
the user has paid for,
as well as you can engineer
logic into your servers to do
that same verification to make
sure a real monetary transaction
has occurred with the
App Store to make sure
that you're going
to be paid for it.
All this comes down
to one simple thing,
protecting your revenue.
Now, the receipt is a lot like
the physical proof of purchase,
docket, or receipt that you get
when you're shopping in a store.
It's the same sort of concept.
It's a trusted record of the app
and in-app purchases
that have been made.
And just like that physical
receipt that you get
when you're shopping in
a store, it's the way
that you can build security
into your app to make sure
that you are going to be paid
for those features and content
that you're unlocking and
providing to your users.
You know, just like a department
store can look at your receipt
and know exactly what you
paid for before you walk
out with goods, you can do the
exact same thing in your app,
and that's what we're going
to show you in this session.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and that's what we're going
to show you in this session.
The receipt itself is stored
on the device and there's APIs
that you can use to access it.
And the receipt is
issued by the App Store.
We issue a receipt every time
a transaction takes place.
So when an app is
purchased and installed,
there's a receipt in there.
When an in-app purchase
occurs, a receipt is issued
and it's available to you
to verify that purchase.
Likewise, when previous
transactions are restored,
another receipt is
issued that will allow you
to verify the authenticity
of those purchases.
And we achieve that by having
the receipts signed in a way
that is verifiable by
you at the code level.
So once you get this receipt,
you can confirm it really came
from Apple and hasn't
been tampered with at all.
But most importantly, the
receipt file is unique
to your app on a single device.
It makes sure that when
you verify that receipt,
you can be absolutely certain
that this purchase happened
for your app, from this user,
on this single device alone.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
for your app, from this user,
on this single device alone.
Now, when we're working
with receipts, as you'll see
in this session, it's
a lot like a recipe.
There's a recipe that Apple
gives you, but there are things
that you are going to need to
bring to the table as well.
There's decisions that you
will need to make along the way
to make sure that your receipt
implementation, your ability
to protect your revenue in your
app, suits your unique needs.
There's a lot of flexibility
built into this system.
Now, in particular,
what Apple provides you
with is the receipt
format specification,
and we've built this receipt
on a bunch of open standards.
There's nothing proprietary
about these receipts
and the great thing
about that is,
because they're all open
standards that are very,
very widely used in many
places, you have lots
of options available for you
for how to work with receipts.
We give you the receipt itself
and there's APIs to find
where the receipt is located.
And we're giving you
instructions on how
to do on-device receipt
validation,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to do on-device receipt
validation,
and that'll be the main
focus of this session.
And we also provide an online
service that allows your servers
to verify the authenticity
of a receipt.
But what you, as the developer,
need to choose, is the level
of security that's
appropriate for your product.
And likewise, that
will translate
into determining the
level of complexity
in your implementation
that suits your needs.
There's three key decisions that
you're going to need to make,
and we'll call these out as
we go through this session
to help you with that.
The first decision
you're going to need
to make is how will you verify
the signature in the receipt?
What will you use to determine
that the receipt definitely came
from Apple and hasn't
been tampered
with since we minted it?
Second is how will you verify
that the receipt is intended
for your app on this device?
And lastly, you will need to
decide how you wish to interpret
that purchase data
that's contained
within the receipt itself
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
within the receipt itself
so you know exactly
what the user paid for.
When you're making these
decisions, the primary thing
that factors into this is
the value of your products
and therefore how much
importance you place
on protecting that revenue.
Here's one way to look at it.
If you are a very
high value product,
you're typically selling
in perhaps an in-app
purchase that's designed
to be a one-off purchase,
large value,
and that one single in-app
purchase is the primary revenue
stream for your app
and the primary enabler
for all the features and content
that you're selling to the user.
Now, in that sense, you're a lot
like a high-end jewelry store.
And when you think about
your shopping experience
in a jewelry store, you notice
that they implement very complex
levels of security to make sure
that not one single pearl
can walk out that door
without having been paid for.
You know, when you go
to a jewelry store,
you see jewelry hidden
behind glass cases.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you see jewelry hidden
behind glass cases.
You see security staff
positioned at the door.
You might see security
wandering through,
making sure that
everything's okay.
There might be metal
detectors, you know,
proof of purchase
checking, et cetera.
It's a very high level of
security and quite complex.
But the reason they
go to these lengths is
because their business
model is centered
around that one-off large
purchase that you're not going
to make very frequently.
But at the complete other end
of the spectrum is
lower value product
and these are typically the
lower value in-app purchases,
dollar, $2, maybe up to $5,
and they're typically things
that are consumables and your
business model probably relies
on customers coming
back again and again
to enjoy the same product,
buying more currency,
buying more gas for their car,
more blocks so they can
build amazing things.
In this sense, you're a lot
more like a grocery store.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Think about your shopping
experience in a grocery store.
The grocery store wants you
to feel incredibly comfortable
and happy shopping there so that
you'll come back again and again
and again and keep
spending with them.
Grocery stores filled
with hundreds and hundreds
of products that are
probably selling for $1
or $2 and not much more.
They rely on the same sort of
business model that you would
if you were selling those
low-value consumables.
They want you to have a
great shopping experience
to feel comfortable and welcome
and to enjoy it every
single time.
And they're not going to
care too much if a couple
of grapes walk out of the
store without being paid for,
unless you keep coming
back again and again
and stealing the grapes.
So let's take a look
at how we work
with receipts in the workflow.
Starting off with the basics,
the receipt itself is stored
in the App Bundle or in
your Sandbox container,
and there's an API and NS bundle
that gets you the URL to locate
where that receipt is.
It's a single flat file.
So it's really easy
to work with.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So it's really easy
to work with.
It's not too complicated.
Contained within that single
flat file is purchased data
about the app purchase itself,
as well as in-app purchases
that have been made.
And there's also a
signature that allows us
to check the authenticity.
You see, we take that
purchase information,
what you're particularly
interested in as a developer,
and wrap it around certificates
and signatures that make sure
that when we write that purchase
information into the receipt,
we sign it and add a signature
that means you can verify
that it's exactly as we intended
when we minted the receipt
and no one's tried to fake
it by adding in any products,
changing purchase
information, et cetera.
And we built this
on open standards.
In particular, the signing that
we use to wrap that payload
of data in a signature so it
can't be tampered with is known
as a PKCS#7 Cryptographic
Container.
Don't let the acronyms
fool you here.
There's a lot of
information available online
and these are very widely
used in many, many places.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and these are very widely
used in many, many places.
The data itself in that payload,
the purchase information,
we encode that.
That is, we structure
it at the byte level,
using a standard called ASN.1.
Now, you would be
amazed at the huge number
of places that ASN.1 is used.
It's a standard that has
been around for a very,
very long time, which is great
because when you combine
the fact that both
of these are very widely used,
it means that you're going
to find that you're not
the first person to try
and implement support for this.
In fact, as we progress
through this session,
you'll see that all those
decisions you need to make,
they generally span this gamut.
From at one end of the extreme,
there will be off
the shelf offerings
that you can simply pick up
and run with straight away.
In the middle ground, there's
probably a lot of sample code
out there that you can find
and maybe copy and adjust it
to suit your needs somewhat.
But then, because these
are open standards
and the specifications
are freely available,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and the specifications
are freely available,
you're completely empowered to
write your own implementation
if that was important to you.
That is, if you want
complete control
over how you protect your
revenue, you can do that.
So some of the options.
OpenSSL, which I'm sure
everyone has heard of,
is a cryptographic library
that serves many
functions beyond just HTDPS
and secure sockets that
it's more widely known for.
OpenSSL includes support for not
only doing the PKCS#7 signature
verification, but also
for reading asn.1.
So it's kind of a one-stop shop.
If you wanted a library
that was available
that could do this for you.
There's also some other command
line tools that can help you
out with this, and I'm
sure there is a lot
of other offerings out there.
But you can still
create your own
if that's what you want to do.
So what I'm going to take you
through in this session
is a three-step process
that you can use to
verify these receipts.
The first thing we have
to do is make sure we know
that the receipt is
authentic and trusted.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that the receipt is
authentic and trusted.
That is, it came from Apple
and no one's tampered with it.
Once we know we have a receipt
that is authentic and trusted,
we can move on to confirming
that the receipt was
in fact intended for
our app on this device.
Because just as it's
important to make sure
that no one's tampered with
the receipt, we also want
to make sure no one's just
copied it from another device.
Now, once we have a receipt
that we know we trust and it's
for our app on this single
device, we can go ahead
and start interpreting
that purchase data
to know exactly what
the user has paid for.
I'm going to start
this off with a demo.
I have here a very simple
project that's just going
to get us started in terms of
getting a receipt to work with.
I mean, all of this is great,
but if you haven't been able
to get a receipt to actually
start verifying the signature
with, this process
can be difficult.
So let's see how we
go about doing that.
Really simple app.
Application did finish launching
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Application did finish launching
and the first thing I do
is I call this NSBundle API
to get the URL to the
App Store receipt.
I'm simply going to use
NSFileManager here to determine
if the file exists or not.
That is, do we have a
receipt to work with?
If we do, I've set myself up a
method that can go ahead and do
that validation for me.
But if not, if I don't have a
receipt, what I do is on OS X,
I exit my app with this
special exit code of 173.
That tells the OS
and the App Store
that you believe your
receipt is bad or missing
and it lets the App Store go
and get a receipt for you.
So this is all in
the documentation
and it seems pretty straight
forward at this point.
So I'm going to clean
this project
to make sure we have no receipt.
I'm going to build it.
I'm going to run it.
Now, notice I've got two
break points set up here.
The first is I wanted to check
that I got my App Store receipt.
And if we see down here in the
debug console, I definitely did.
And I'm going to ask FileManager
if that receipt exists.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And I'm going to ask FileManager
if that receipt exists.
It says no.
Of course, because I'm running
from Xcode and I have not
yet put my app on the store.
So of course we don't
have a receipt.
But we want one so we can
start testing out validation.
So the documentation
says to exit 173.
Let's do that.
And nothing happens.
This is the first point
the developers kind
of get stuck with.
They get started with receipt
validation, put their code
in place, run it in Xcode, and
go why doesn't this do anything?
The reason for that is when you
run your app in Xcode like that
and it hits that exit 173 code,
only Xcode sees that exit code.
So the OS and the App
Store is not aware
that you're actually
running your app
and wanting to get a receipt.
So here's a trick
to help you do that.
This time when I run the app
and I hit the same break point,
I'm going to go down here
to the Dock, control click
on the app icon down here, and
I use options, show in Finder.
That brings up Finder to
my debug-built version
of the app that we're running.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Because remember, I want this
to exit with that special code
and for the App Store
to see that exit code
so it gets me a new receipt.
So watch what I do here.
I go back to Xcode and I stop
that instance of the app.
Back to Finder and now I
run the app from Finder.
And look what happened.
We did the same logic, receipt
wasn't there, and we exited 173.
Now, the App Store has seen
that and is prompting me to sign
in so that we can go and get a
receipt to start working with.
And because my app was signed
with my development certificate,
I'm connected to the App Store's
test environment using my
iTunes-connected test accounts
to get a test receipt for us
to start working with.
So I sign in, we
get our receipt,
and my really simple app
has finished launching
with the receipt.
Now, just to double check,
if I go back here into Xcode,
we go to our URL to the receipt,
and this time we have the
receipt and we're ready
to start working with the
validation of that receipt.
Now, if you are working with
iOS receipts, one other tip
that you might find handy is
if you're having
trouble getting a receipt
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
if you're having
trouble getting a receipt
in the test environment,
make an in-app purchase.
When you make an
in-app purchase,
you will get a new
receipt and you can use
that to test your
validation code.
So let's go to first step,
verifying the signature
in that receipt.
So we know that this
receipt is authentic,
untrusted, and unaltered.
So this verification
of the signature,
make sure the receipt
hasn't been altered
since it left Apple, because
we don't want anyone tampering
with it, and that
it came from Apple
so that no one else can
just mint their own receipts
that look otherwise valid,
but are in fact frauds.
This is where the PKCS#7
cryptographic container
comes in.
It's how we wrap that protected
body of purchase information
around those signatures
and certificates
so that we can confirm
authenticity.
Now, your options
available for you
for doing this authenticity
check,
we mentioned before there's --
OpenSSL can do this for you,
there's no doubt many
other frameworks out there
that can do this for you.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that can do this for you.
But of course, you can roll
your own if you choose.
To get you started, though,
the first thing we need
to do is find that receipt.
We call NSBundle mainBundle
appStoreReceiptURL.
That gives a URL to the receipt.
Once we've got that URL, we
need to get it read into memory
so we can start working on
it, and that's as simple
as using something like NSData
initWith ContentsOfURL..
I'm going to walk you through
here an OpenSSL example of how
to do this signature
verification.
This is straight out of our
documentation we have online.
But one thing to note is, this
example makes two assumptions.
We assume that you've already
read the receipt into memory
and have stored it in this
variable called asterick b
underscore receipt;.
And we also assume that you've
got a copy of Apple's root CA,
our Route Certificate Authority,
which you can find online,
and you have that in your
app bundle and you've read it
into memory and stored it
in this variable called
asterick b underscore x509;.
Why 509? Because
x509 is the standard
by which that CA is encoded.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So once we've got those
two key ingredients,
the first thing we want to do
is convert that blob of binary
that represents the
receipt in memory
into a useful data structure
that we can start working with.
And we do that by
calling the really simply
and aptly named d2i
underscore PKCS7 underscore bio.
Don't let these names fool you.
What you'll see here is despite
the fact that these were named
by someone very arcane,
these are simple concepts.
We're reading a file into
memory, we're setting it
up in a structure so we can work
with it, and we're checking --
we're calling some functions
on it and checking the result.
Anyone can do that.
So once we've got
our data structure,
we've got our receipt
in one hand.
But the other thing we
need is our certificate
that we expect this
receipt to match up against.
Right? We want to
compare these two things.
We have a receipt, but we
want to make sure that it came
from Apple and hasn't been
altered since Apple created it.
So we set up this
certificate store and we add
into that store Apple's Root CA.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
into that store Apple's Root CA.
Because if the certificate --
if the signature in the receipt
is valid, it has to appear
that it's come from
Apple's Root CA.
Now, we get into
the meat of this.
We call the much better
named PKCS7 underscore verify
function, pass into
it our receipt
and our certificate store,
and we just check the result.
If the result is equal to one,
that means our receipt
signature is valid.
It came from Apple, is
trusted, hasn't be altered
since Apple minted the receipt.
Now, you also get another bonus
from calling this function
and that is that it will return
back to you the actual payload
of the receipt itself.
So that body of purchase
information
that you can start inspecting
to know what the user paid for.
And you get that
back in this example
in the b underscore
receiptPayload variable.
So I have another project
here that builds upon
that simple example
that we saw before.
You see here that I have my
same did finish launching method
where I check the receipt.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
where I check the receipt.
We know we've got a
receipt from the last demo.
And so, I've fleshed out my
validate receipted URL method
with these exact same calls
that we just saw in
that slide before.
So I'm going to build
this -- huh.
Twelve warnings, 12 errors.
Not a great start.
Again, this is another point
that the developers can
find really frustrating
with this process.
When you take this example code
and try and use it, you're going
to run into a few roadblocks.
But we can get through this.
The first thing is, the reason
you're getting all these
compiler warnings
here, telling you
that all these methods
were deprecated,
is because, in fact, they are.
Why am I up here on
stage telling you
to use deprecated methods?
We deprecated these in OS X
10.7 because we want developers
that especially if you're
concerned about, you know,
strong cryptography and security
like this, we want developers
to create and roll their
own build of OpenSSL
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to create and roll their
own build of OpenSSL
as a static library
and integrate
that OpenSSL build straight
into your application's binary.
Think of it this way.
When you build your app and
link against things like UIKit
and AppKit, that's an
external dependency.
Your app can run on one machine,
on one version of the OS,
and run just fine on
another version of the OS,
even thought AppKit and UIKit
might be very different.
They're external dependencies
that can be swapped out from
under you and as long
as those APIs match
up to what your app is
expecting, it'll run.
But imagine how easy it would
be for someone who wanted to try
and attack your app, to try
and rip you off, to simply swap
out an external dependency,
like OpenSSL, for a version
of OpenSSL that they
brought themselves
that simply said, yeah.
Everything's fine.
This receipt's valid.
Trust me. You see?
When you use that
external dependency
for strong cryptography and
privacy sort of stuff like this,
you can run into problems where
you create a big attack vector
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you can run into problems where
you create a big attack vector
that makes it really
easy for someone
to circumvent the logic
you're putting in your app
to protect your revenue and
enforce your business model.
So what we need to do here
is build our own OpenSSL.
Now, that's not as nearly
as scary as it sounds
and I've got some tips
for you in the next slide.
In fact, I've already built my
own OpenSSL and I have it here
on the desktop in a
folder called OpenSSL.
Now, when you build OpenSSL,
it produces two things for you,
a folder called include,
full of the header files
for what we want to work
with, and also a lib folder
that contains that .a static
library that we've built.
Now, using this is
actually pretty simple.
I like to keep my projects
pretty clean, so I'm going
to go ahead and create myself
a group here called OpenSSL,
if I can spell.
There we go.
And all I have to do is simply
drag and drop this built product
from OpenSSL straight
into my project,
header files and
static libraries.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
header files and
static libraries.
Make sure you click
this copy items
into destination group folder.
That makes sure that
we copy those headers
and the static library
you've built directly
into your project itself.
Otherwise, you'll have a
dangling external dependency
that might go away.
So finish.
I add in my freshly built
OpenSSL to my product.
Cool. Build it.
Build succeeds, but I still
have 11 compiler warnings
and if you're like
me, you care a lot
about your project
building with 0 warnings.
So why are we still getting
these deprecated warnings?
Because Xcode is trying to
build against the SDK that ships
with the OS that has
that deprecated OpenSSL.
The way we get around that
is we have to tell Xcode
that for this app that we're
building, we actually want Xcode
to use our OpenSSL that we
just added to the project.
So I went to my project
settings here.
I have clicked on this
target and I'm going
to go to build settings.
And as an option, you'll find
called Header Search Paths.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And as an option, you'll find
called Header Search Paths.
I'm going to double
click on that and I need
to tell Xcode here where
to find those OpenSSL files
that we just added.
An easy trick for doing
that is pop this open here
on the side panel, find
one of the headers,
open up this side inspector, and
there you'll see the full path
to where that header file
is located in the project.
Now, notice in this case, I've
got it within my project folder
and it's in a folder hierarchy
called Include/include.
Okay. So we just need to tell
Xcode to go and allow files
to be imported from that
location for headers.
So I double click here
and rather than having
to enter the full directory
path, I can use a shortcut
of saying it's in SourceRoot
/Include/include, because that's
where we saw the
file just before.
I add that in, put it to
the top, because I want
that before anything
else in the system.
I want to make sure we look
at my OpenSSL files first.
Done. We see that was updated
there and close this file
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Done. We see that was updated
there and close this file
and go back to our AppDelegate,
where we had all these warnings.
Done. All those compiler
warnings went away.
Because now, we're building
our project against our OpenSSL
that we built, that we know the
integrity of, that we're going
to ship built into our app
binary to protect our revenue
and check our receipts.
So let's run this
and see how it goes.
So we've already got a
receipt from the earlier demo
that we did and now we can
enter our validateReceipt AtURL
method here.
These are the same calls we
saw just on the slide before.
I load the receipt into memory.
I load the Root CA
in the memory.
d2i underscore PKCS7 underscore
bio to load that ball of receipt
into a usable data
structure called P7.
Create my certificate store and
now I'm ready to do the magic.
Does this receipt
stack up against
that Root CA Certificate?
I step over that and
if I look down here
in the console, sure enough.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in the console, sure enough.
Result is 1.
That means we can now
move forward knowing
that we have a real
valid and trusted receipt
that we can start getting
purchase information out of.
And well, before we do that, we
need to confirm that it's for --
intended for our
app on this device.
But now, we're over the
hurdle of not only working
with the receipt that we got
from the test environment,
but we're now got OpenSSL
integrated into our project
to be ready to be built
for both iOS and OS X.
Although I'm demoing
everything here using OS X,
everything I cover here applies
to iOS, just as it does on OS X,
and if there's any
platform differences,
I'll call them out for you.
A couple of tips on
building OpenSSL for you.
First is, you need to build
it as a static library,
not a dylib, not a
framework, not something
that can be external
to your binary.
You need it to be a
static library so that
when you build your app and
link it, it takes that .a
and puts it right
into your app's binary
so no one else can
swap it out from you.
Now, if you're building
for multiple architectures,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, if you're building
for multiple architectures,
which you almost certainly will
be if you're building for iOS,
for all the different
arm architectures,
then what you need to do
is build a separate .a,
a separate static library,
for each architecture you want
to support, be it
arm, armv7, arm 64,
whatever you might
need to build for.
Or for x86 underscore
64 in OS X.
Now, when you create those
individual .a static libraries
for each of those architectures,
you use the lipo command tool
to take all of those little
.a architecture slices
and combine them
into one fat binary
that is one single .a static
library that combines all
of our slices for all
of our architectures
we want to support.
And then, you can just drag
that straight into your project,
like we did just before.
Now, when you build OpenSSL,
the first thing you do
is use a configure script
to configure OpenSSL for the
platforms you're building on.
Here's two tips that
you'll find handy.
If you're building for
OS X 64bit, configure it
with the darwind64-x86
underscore 64-cc host type.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with the darwind64-x86
underscore 64-cc host type.
If you see documentation
trying to tell you to use some
of the BASD generic types,
you're going to run
into trouble.
So this is much easier and
it ensures a proper build
of OpenSSL on x86 underscore 64.
But if you're using -- if
you're building for iOS,
try using the iphoneos-cross
host type
in the configure script
to configure it for
iOS accordingly.
But you will definitely not
be the first person ever
that has tried to build
OpenSSL for either OS X or iOS.
We deprecated OpenSSL
a while back in OS X
and we never shipped and OpenSSL
in iOS that developers can use.
So lots of folks have
been through this
for lots of different reasons.
There's lots of examples
available to you online.
Two notes about doing
the verification.
No matter how you do the
verification, be it OpenSSL,
roll your own, or
another offering,
do not check the expiry
date on the certificate.
When you're contacting a web
server via HTTPS, it's important
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that you verify the expiry
date of the signature.
You don't want to be
talking to a web server
out there using a secured
transport with a certificate
that has since expired.
But the reverse is
true here for receipts.
Keep in mind the fact that we
mint this receipt at a point
in time and that comes with your
app when someone downloads it
or makes an in-app purchase.
Now, at that point in time,
there will be certificates
included in that receipt
that have an expiry date.
If that -- if someone buys
your app today and then a year
down the track, that certificate
expires within the receipt,
that does not make
the receipt invalid.
The receipt is just as
valid as it was the day
that you purchased it.
And it would be a terrible
experience for that user if all
of a sudden your app
stopped working only
because the receipt has
a certificate in there
that has since expired.
But what you do absolutely
want to make sure you do
when you do your receipt
validation is evaluate the trust
of the certificates
right up to the Root CA.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of the certificates
right up to the Root CA.
You see, the certificates
exist in a chain.
But at the head of the
chain, at the root,
has to be Apple's Root
Certificate Authority.
That's the only way you can be
absolutely certain that it came
from us and not from someone
else trying to pose as Apple.
Want to take a quick
word, though,
about examples and sample code.
I've mentioned a lot
throughout this session
that there are a lot of samples
out there, there's examples.
I'm sure there's off
the shelf offerings
that you could literally
download and put straight
in your project that
would do this for you.
But that convenience
comes at a price.
You have to keep in mind
that reusing code brings
with it bugs and
vulnerabilities.
No matter how good
the developer is,
no matter how well you
use the sample code,
is no matter how much you might
have paid for it, any code comes
with bugs and possible
vulnerabilities.
And the more people that
use the exact same way
of doing receipt validation,
the more there is the chance
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of doing receipt validation,
the more there is the chance
of one exploit affecting
many, many apps.
You know back in that
jewelry example imagine
if every jewelry shop around
the world used the same lock
on their cabinets, and
then one day someone finds
out a really easy way
to open that lock.
Not going to be good for anyone
owning a jewelry store, right?
Same sort of principle here.
Although this can seem
so convenient to just go
and grab something off
GitHub or somewhere else,
and drop it into your
project, and, "Hey,
I've got receipt validation
and protected revenue."
You need to understand the
risks of what you're bringing
in because that's your revenue
stream at the end of the day,
not the third-party
that wrote that code,
not the nice individual
that put up some sample code
to help you get this done.
It's not their revenue.
It's your revenue.
So make sure you make decisions
that suit your product.
Maybe you're okay with those
sorts of vulnerabilities,
and bugs, and the possibility
of a single exploit
affecting your app.
Maybe that suits the
business model and level
of protection you want.
But either way know the risks
and own those risks yourself.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But either way know the risks
and own those risks yourself.
All right, so we've got a
receipt that we know is trusted
and verified, and
it came from Apple.
Now we need to make
sure that it is intended
for your app on this
device only.
To do that we need to look at
the receipt payload itself.
Now this is a very
high-level look
at how a receipt is structured.
It's structured as a
series of attributes.
You could think of these
attributes as a cross
between an NSArray
and an NSDictionary.
They're like an NSDictionary
because they have things
like a type and a value,
a lot like a dictionary
has a key and an object.
But they're like an Array
because there is a sequence
of these throughout the receipt
that you can loop
over to interpret.
They also have a version on
each of these attributes,
but that's not too
important to us right now.
The key thing we need to know
is inside that body of data
in the receipt is a
series of attributes.
Each attribute has a type
that tells us what it is,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Each attribute has a type
that tells us what it is,
what it means, and then
a value that corresponds
to what this attribute
is telling us.
So the first thing we
need to do is check
if this receipt was actually
intended for our app.
In those attributes
there'll be one called Type 2
and one called Type 3.
All the attributes
are named by a number.
So attribute Type
2 in the receipt,
if you read that attribute's
value it will be the Bundle
Identifier that this
receipt was intended for.
And then Type 3 is
the Bundle Version
that this receipt
was intended for.
So you can check the
bundle identifier,
make sure it's for your app.
You can check the bundle version
if you want an even
higher level of security.
Perhaps you want to
make absolutely certain
that someone's not using a
receipt from an older version
of your app that perhaps
doesn't have the same features
and content.
The one thing to note
is use hardcoded values
for the comparison.
Why? Because if you
simply grab the values
out of your Info.plist and
compare them against the values
in the receipt it's all
too easy for someone to go
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in the receipt it's all
too easy for someone to go
and edit your Info.plist
and make it look exactly
like the receipt that they're
trying to fake you with.
So hardcode those values into
your app that you're expecting,
that way they're part of
the code signed at binary,
much harder to try
and circumvent.
Now that was a very
high-level look
at how this receipt
data is structured
in terms of attributes.
But as I said before,
this is encoded,
as in the bytes are arranged,
using a standard called ASN.1.
There's two other attributes
along with Type 2 and Type 3.
So we use Type 2 and Type 3 to
confirm that this receipt was
for our app, but now
we need to confirm
that it was actually
intended for this device only
because we don't want someone
to simply be able to copy a --
a receipt around
to their friends
so that they can use the
app without paying for it.
So included in the receipt
is an attribute called Type 4
which is an opaque value,
just a blob of bytes,
and Type 5 which
is a SHA-1 hash.
Now if you're not familiar
with hashing, hashing is a way
to take a large bit of data
and boil it down to a unique,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to take a large bit of data
and boil it down to a unique,
smaller data that will
be absolutely unique
to that larger data set that
was the input to the hash.
So this Type 5 attribute is
a SHA-1 hash of 3 key values.
It's a bundle ID, plus a unique
device identifier like the GUI
on OS X or the device
for vendor on iOS,
and also this opaque value.
How does this work to actually
confirm that it's intended
for your device,
for a single device?
You see, the App Store
knows these three key pieces
of information at the time
that purchase is made.
When someone taps the
buy button on your app
or on an in-app purchase
and completes
that transaction the app
store knows three key things.
One, we know the bundle ID of
the app making the purchase.
Two, we know the device
identifier from that device
because it's sent to the store
when that transaction is made.
And three, on service side
we create this opaque value,
this bit of random entropy that
we inject into this process
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
this bit of random entropy that
we inject into this process
to make it harder to attack.
So the app store knows these
three key things at the time
of purchase, creates
the SHA-1 hash,
bakes it into the receipt along
with that opaque
value that we used.
But the great thing is that your
app can know these same three
values at the time of
doing receipt validation.
So because you know
these three values,
and you can string them together
in that long concatenation
of bytes and create the hash,
and the App Store knew
these three key values
and created the same hash
and put it in the receipt,
if the hash you calculate
exactly matches the hash
that the App Store
created and put
in the receipt then
you're certain
that this receipt was intended
for your app on this
device only.
It's completely unique
to this device.
Now about ASN.1,
these high-level --
this high-level look
at the attributes
that explains how we structured
them, and how you work
with the different attributes,
and how we store them
as an array of attributes
with a type and a value.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
as an array of attributes
with a type and a value.
But how are they encoded
at the actual byte level?
How do you work with
this in code?
The way you do that is
by harnessing ASN.1.
ASN.1 is this way in which we
can use a textual definition
that you see here onscreen
to describe how the bytes
are arranged in the receipt.
We provide you with
this ASN.1 definition
of how we've structured the
receipt, and what you're seeing
up onscreen is that you have
a receipt module defined,
and in that receipt
there is a payload,
and that payload is a set
of receipt attributes.
And the receipt attribute
will be part of a sequence,
and it contains a type
which relates to that type
that we just saw before
as well as a value
that is an octet string
or a bunch of bytes.
And that refers to things
like that opaque value
in the SHA-1 hash that
we looked at just before.
Working with ASN.1, the trick
is you need to find a way to go
from that textual
definition that's based
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
from that textual
definition that's based
on the open standard of OpenSSL
to actually being able to read,
and write, and work
with it in a code level.
So there's a few options for you
because of course this
is very widely used.
One option is, again, OpenSSL.
Just as it did the PKCS7
verification it can read ASN.1
just fine.
You can create your own
parser if you wanted to,
to again have full
control over this.
Let's look at an
OpenSSL example.
When we did the first of
verifying the recipe we got back
that Pay 7 data structure.
Remember that?
Now we can use that same data
structure to actually get
that payload of receipt data
that's encoded in ASN.1.
It's buried in there in
the Pay 7 data structure
that we see there.
Once we've got that we can
call this function called ASN1
underscore get underscore object
to start pulling those objects
out of that ASN.1
encoded byte stream.
The objects are things
like there'll be an object
for the top level receipt.
There'll be an object in
there for the Payload.
There'll be an object in there
for the ReceiptAttribute,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
There'll be an object in there
for the ReceiptAttribute,
and then subjects
under there for each
of the type, value, and version.
You see, it's arranged
very hierarchically,
and that follows the same
structure that we just saw
in this ASN.1 textual
definition.
Now this is not perfectly coded,
but it gives you an example
of how in which you would
work with ASN.1 in OpenSSL
if that's how you
choose to do this.
You'd set up a while loop
to essentially move a --
move a pointer throughout
that stream of bytes
that represents the receipt.
We want to basically walk
through the receipt
ad read it as we go.
So we set up the while loop
and start calling ASN1
underscore get underscore object
to pull those attributes and
values out of the receipt.
So I make one call to get
my attribute, for example.
Another call to get the
type, and notice I'm moving
that pointer value around
like a finger following words
in a book.
And I'm calling ASN1
underscore get underscore object
to read each of those words
out and so I can use them.
After a few more calls to ASN1
underscore get underscore object
I'm ready to switch
on that attribute type
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I'm ready to switch
on that attribute type
so I can see what
I'm looking for.
In this case just for a
simple example I'm looking
for Attribute Type 2 which
we know is a BundleID.
And then I can start processing
that by calling ASN1
underscore get underscore object
to actually read the
string value and compare it
against my hardcoded BundleID.
But of course I want to show you
that this is a real world thing
that anyone can do,
so here's my demo.
Now same as before, I've
got a third project here
that builds upon the same two
samples that we've seen so far.
You see here we have
the old familiar loading
up of the receipt,
certificate store,
and then calling PKCS7
underscore verify.
But now when we get
our result of one,
and we know this thing
is valid and unaltered,
and we want to start reading
the purchase information
out I'm going to go ahead and
start using OpenSSL to do that,
to actually read the information
about what someone purchased.
So just like we saw before I
find the actual octet string,
that stream of bytes that
represents the receipt,
buried within my
p7 data structure.
I start calling this ASN1
underscore get underscore object
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I start calling this ASN1
underscore get underscore object
method to start reading those
bits of information out of
that byte stream one at a time.
When I start doing that in my
while loop to walk over each
of those bytes you see
that I set up that pointer,
and each time I call ASN1
underscore get underscore object
here I advance that
pointer along,
just like the finger
following the words in a book.
So I make a few calls to ASN1
underscore get underscore object
because I want to get things
like the attribute type,
attribute version, and then
the actual object itself.
Set up my switch statement, and
I've got a case 2 here looking
for BundleID, and a
final call here looking
for ASN1 underscore get
underscore object that allows me
to form the string which
will be a bundle ID.
Let's run it and see it work.
Notice by the fact that I had
done the OpenSSL integration
before in this project
I don't have to mess
around with that again.
This project is already set
up just like we had before.
So I've got a receipt, great.
We move into our
validateReceipt.
We've gone and called the
PKCS7 underscore verify here
to make sure the
receipt is trusted
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to make sure the
receipt is trusted
and unaltered, and it is.
Now we can start reading it.
So I've got the byte stream.
I start calling ASN1
underscore get underscore object
to read that receipt in.
Then I set up my while loop to
iterate over each of that series
of attributes in the receipt.
And as we see as we sail
down this first attempt
at getting an object out of
the receipt, we get all the way
down to our Switch
statement, and if we pull
up the debug console
here you'll see, in fact,
what found first is
Attribute Type 4.
So as you iterate over this
While loop it's essentially
finding each of those attributes
that we saw in the receipt,
and you set up the Switch
statement here to work
with them accordingly.
So in fact, if we let this
run we would see a bunch
of breakpoints hit here in our
Switch statement, switching
and -- as we looped over each
of those attributes we can see
this time we've got Type 4.
Advance it again and
we get different values
as we go forward.
And we can begin working
with those to pull out things
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And we can begin working
with those to pull out things
like the bundle ID to
confirm that, the hash value,
perform that hash to confirm
that it is intended
for this device.
All right, we're nearly there.
We have a receipt that
we know is trusted.
We've confirmed it's for
our app on this device.
Now we can get down to the
money end of this and find
out what the user has in
fact paid for so we know what
to unlock, and we know what
content to provide them.
Now along with those
Attributes Type 2, 3, 4,
and 5 that we just looked
at you will also see one
or more attributes of Type 17.
Type 17 is a record of an in-app
purchase having been made,
and this is how you verify that
that in-app purchase was real
and authentic and
is going to result
in money making its way to you.
The value of the
Type 17 attribute is
in fact a nested set
of attributes itself.
So you find the Type 17
attribute, get the value,
and that value is going to
be a nested set of attributes
that tell you the
quantity, product identifier,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that tell you the
quantity, product identifier,
and the transaction
identifier and purchase date
for an in-app purchase
that has been made.
And again, we give you the
ASN.1 representation of that
so you know how it's structured.
Now if you have a paid app in
the Store and have been wanting
to make the transition to making
a freemium app, that is free
with in-app purchases,
one thing you don't want
to do is leave those loyal,
paying customers
out in the cold.
If they've already
paid the full price
for your app they shouldn't
have to go and do a bunch
of in-app purchases just
to get back what
they've already paid for,
so here's a trick you can do.
Included in the receipt now is
this Type 19 attribute called
Original Application Version.
That is the application version
that the user originally
purchased.
So if two years ago I purchased
this app the receipt will always
reflect the version that was
in the store at the time I made
that purchase all
those years ago.
And that's your way of knowing
whether to treat the app
as a paid version and give them
what they've paid for or to know
that they purchased the app
when you were a freemium app
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that they purchased the app
when you were a freemium app
to not unlock features
and content
until you see those in-app
purchases being made.
Now in terms of in-app
purchases, different types
of content have different
lifecycles in terms
of what is seen in the receipt.
If you are dealing
with consumable
and non-renewing subscriptions,
so things like gas in a racecar
in a racing game, coins, blocks,
currency, non-consumable --
sorry, consumables are
things that you buy,
use on a single device,
and they get used up.
Those sorts of items,
because they are that purchase
that you can make again
and again and are intended
to be used on a single
device, they only appear once
in the receipt, and only in
the receipt that's issued
at the time of purchase.
So when that purchase is made --
if you have 500 gallons of gas
in the racecar -- you need
to interpret the receipt
at that point in time and
validate it, and then stash
that state -- that is up
the gas tank by 500 gallons
or whatever it might be --
you need to stash
that state yourself.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you need to stash
that state yourself.
That transaction
will not be present
in any subsequent receipts that
we issue, nor will it be present
if you perform the restore
completed transactions.
They're a one-shot deal.
When the transaction is done
you inspect the receipt,
stash that state yourself.
Now the total opposite
to that is non-consumable
and auto-renewable
subscriptions.
If you're selling
non-consumables like levels
in a game, for example,
those are things
that a user buys once, and they
would rightly expect to be able
to use that game level
on other devices.
If I go and buy a new phone and
re-download your app I expect
to get back all the lower levels
that I've already paid for.
So because these non-consumables
and auto-renewable subscriptions
have that sense of permanency
about them they are
always in the receipt.
And you can use the
StoreKit APIs
to restore completed
transactions
which is how the user gets back
all those things they've bought
from you before in
terms of non-consumable
and auto-renewable subscriptions
that are still valid.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and auto-renewable subscriptions
that are still valid.
What we've talked about so far
is all about the happy path
of a receipt being
valid and true.
What if receipts are invalid?
What if when you validate
this it's either missing
or something appears
wrong with it?
The fact is that doesn't mean
something bad has happened.
That does not necessarily
mean someone is trying
to rip you off.
There are many real
world scenarios
that your app will
definitely run
into where a receipt is missing
or invalid through no fault
of the user, not because
anyone's trying to rip you off.
But if you see this
happen what you need
to do is refresh the receipt.
On iOS if the receipt
doesn't exist or appears
to be invalid you'd
refresh it using StoreKit.
StoreKit has the
SKReceipt Refresh operation
which allows you
to -- you can alloc
and init this SKReceiptRefresh
Request object, set a delegate
so you get the callbacks
when the refresh is done,
and then start the refresh.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
When that request completes you
should get back a new receipt
that you can then re-verify.
But note that getting
a new receipt we have
to talk to the App Store.
So of course a network
connection will be required
which your user may
not always have.
Store sign-in will be required
so that we can go and verify
that this person really is them
and check their purchase history
before we issue the receipt.
But what you need to do is avoid
continuous loops of refresh,
validate, refresh
validate, refresh,
validate because each time
you do that the user's going
to get stuck in this horrible
loop of prompt for off.
"Yes, I signed in."
Still bad.
"Oh, sign in again."
And all this network traffic
goes back and forward.
So what you should do is on
launch validate the receipt.
If it's missing or invalid
you can refresh it once.
If it's still missing
and invalid that's it.
Don't call refresh again.
But OS X is of course
a little different.
As we saw before if the receipt
is invalid or missing you exit
with code 173 to tell
the OS and the App Store
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with code 173 to tell
the OS and the App Store
that you need to
get a new receipt.
The OS sees that exit
code, tells the App Store,
and the App Store will go and
get a new receipt for you.
But just like on iOS it will
require a network connection
and store sign-in
will be requested.
So this is definitely
not something you want
to do automatically on launch.
Now when you -- do you receive
validation, and it appears
to be invalid or missing, and
you request a new receipt even
if then you still don't have
a receipt or it still appears
to be invalid what you do
next is entirely up to you.
Invalid or missing
receipts will happen.
Everyone's app will see a
condition where this happens,
and it could be entirely
legitimate.
Here's an example.
If I sync an app from iTunes
to my iOS device it lands
on the iOS device
with no receipt.
Because it wasn't
purchased directly
from the App Store the
App store doesn't have
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
from the App Store the
App store doesn't have
that unique device identifier.
We can't issue a receipt.
So it lands on the
device with no receipt,
and when your app launches
it'll find no receipt and need
to refresh it if you want
to validate the receipt.
So it's a real world scenario
that will happen, but you need
to configure the -- you
need to consider the case
that that refresh of the
receipt may not be possible.
All right, what if I got my
wonderful app from the --
from the iTunes Store or the App
Store, and I'm really excited
about using it on the plane
flight I'm about to get on.
I plug my iOS device in,
sync the app across, unplug,
jump on the plane, no Wi-Fi.
"Ah, I can't get a receipt."
It's up to you now to decide
how you want to handle that.
Okay, you should ideally
match the user experience
to the value of your app.
And this comes back to the
concepts we were talking before.
I mean, you can allow full
access to the app's content
and features even if the
receipt's invalid if you choose.
You could maybe have a -- you
could maybe have a grace period
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
where you will allow
some use of the app.
You could limit access to
certain areas of the app;
perhaps there's one
area of the app
that is particularly valuable
that you wouldn't want
to let them access even if
they can't get a receipt.
Or you could block
functionality entirely.
You could have the
app do nothing
until a valid receipt is seen.
On OS X only you could in
fact force that app to quit
if you wanted to, but that
concept does not exist on iOS.
On iOS the app is
always running,
so it's up to you to
decide what to do.
But again, think about
which business model you are
more like.
Are you like the grocery store
that wants a comfortable,
really happy buying experience
and usage experience every time,
and maybe you don't care if one
or two bananas goes
out the door?
Maybe you don't care too
much if a receipt's invalid
for a little bit of time.
Or maybe you are more like
that high-end jewelry store,
and maybe it's totally not
okay for your app to be used
in any way unless
there's a valid receipt.
It's up to you, but you should
really think about the value
of your product and how you
want the user's experience to be
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of your product and how you
want the user's experience to be
if they don't have a receipt.
Now everything we've talked
about so far is about validating
that receipt on a device,
but you can also do
that same process
on your servers.
If you have servers
issuing content based
on an in-app purchase being
made here's what you can do.
When you request that
content send the receipt
up to your server.
Your server can talk to
Apple's validation servers,
and we will return
back a block of JSON
that will tell you whether
or not the receipt is valid,
and it will also include
the purchase information.
Your server can interpret
the purchase information
and decide only to hand
back that content based
on that real monetary
transaction having taken place.
It's a great way to secure
and lock down the access
to that content that
you're hosting online.
Now for this server --
server-to-server validation it's
only designed for your servers
to validate the receipt
before they issue content.
And it's your app that needs to
send the receipt to your server.
You need to secure
that end-to-end communication
channel,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and then only your
server sends the receipt
to Apple's validation server.
Never, never, never, ever
send the receipt directly
from your app on a device
to the validation servers.
Way too easy for someone
to sit in the middle
and return a false positive,
and you'll also be exposing
your shared secret for using
that validation service.
But the good thing is the
response you get back is in JSON
which is really easy to pass
for virtually any server
platform out there.
Now what you absolutely
have to make sure you do
if you're implementing
receipt validation is test
it thoroughly.
A bug in this area of code
could be disastrous for you
because it could lock
someone out of the features
and content they
really have paid for,
and I guarantee you
that's a one-way trip
to one-star reviews.
So test really thoroughly.
How does your app behave
when there's no receipt?
How does it behave when the
receipt looks to be invalid?
How does it behave
when the app --
when the receipt appears
to be valid after refresh?
What if the refresh failed
and it's still invalid?
How does your app behave?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And if you're selling your
app using the Volume Purchase
Program for business
and education be aware
of the extra fields
that are in the receipt
to tell you whether your
app has been allocated
to a user or revoked.
But the really important message
that I want to make sure gets
through is that these
are not edge cases.
Your app will launch
without a receipt.
Your app will launch with a
receipt that looks invalid.
You know that example
before I gave you
of an iOS app being
synced from iTunes?
When you buy a new Mac and you
move your apps from your old Mac
to your new Mac, guess what?
The receipt moves from the
old Mac to the new Mac.
So when your app launches
it's going to see a receipt
that was not intended
for the new Mac.
So these are real
things that happen.
They're definitely
not edge cases at all.
Now testing this on iOS you need
to use the App Store's
test environment.
To do that you run
the app in Xcode,
perform an in-app purchase
to get the receipt,
but you must have
your app signed
with a development certificate.
OS X, similar but different.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
OS X, similar but different.
Build it in Xcode, run the
app from Finder, remember.
So Finder in the OS, and the
App Store sees that exit code,
and we know you need
to get a receipt.
When you exit with that
receipt you must make sure --
exit with that code,
sorry, you must make sure
that your app is signed with
its development certificate.
Now in case you missed the
very, very deliberate repetition
in those last two slides,
your app must be signed
with your development
certificate.
Why? Well, because
the first thing we do
at the App Store Layer
when your app says, "Hey,
I want a new receipt," or, "Hey,
I want to make an
in-app purchase,"
is we inspect your
code signature.
If the app appears to be signed
with your development
certificate we know you're a
developer testing your app
so we route those requests
to the test environment so that
you can make in-app purchases,
test your receipt validation
without actually
buying something.
But if we see the app is signed
with a production App Store
certificate we know this is an
app that's been purchased;
it's out there in the wheel --
real world, so therefore,
we route those requests
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
real world, so therefore,
we route those requests
to the production store
so that you get paid
for the transactions
the customer is making.
Lastly, a quick word on
the app submission process,
especially as how it
pertains to receipts.
When you're developing your app
Development signed, of course,
and you're working with the
App Store's test environment
to test your in-app purchase
and receipt validation.
The receipts you get back are
test environment receipts.
Now because your app may not
already be on the Store yet,
and you're using the test
environment there'll be some
difference in the
fields that are present.
When your app is on
sale in the App Store
and it's Production signed you
will see production receipts
that have some more fields in
them because your app is real
and live on the Store.
But App Review is different.
App Review uses test receipts
using a Production signed app.
That's not something you can
do, only App Review can do.
But what you need
to be aware of is
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But what you need
to be aware of is
that when you're testing your
app if you try to get too clever
and think, "Oh, I've
found a way to determine
that these receipts are from the
test environment, and I never,
ever, ever, ever want
someone out there
to be getting a receipt using my
app from the test environment,"
if you try and implement code
that rejects a test
environment receipt
because you're Production
signed, guess what?
App Review can't
review your app.
We won't be able to use
your in-app purchases,
and therefore, it'll
get rejected.
So just be aware that you will
have your live Production signed
version of the app
that you submitted.
It will see test receipts
during app review.
So if you'd like more
information you can contact
our Evangelists.
There's also documentation
online
with the Receipt
Validation Programming Guide.
And of course there's the
Apple Developer Forums
which are a great place to
discuss this and ask for help.
Some related sessions, on
Wednesday I gave a session
about optimizing your
in-app purchases.
That was around creating
a trouble-free
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
That was around creating
a trouble-free
and smooth in-app purchase
experience every single time.
And my colleague Rachel gave
an excellent session earlier
that morning that was
about how to design
for a great in-app
purchase experience.
It's great to protect
your revenue,
even better to have a
trouble-free in-app experience
every time, but you've
got to also create
that irresistible
sales experience.
So do go and check
out these sessions.
They were really good.
Thanks very much.
[ Applause ]