How to verify a Postback with Skadnetwork/Apple - postback

I'm having issues to verify a postback with Skadnetwork/Apple according to the documentation Apple is giving out.
There are two issues I have (at least I think there are only two issues).
\u2063 - I'm using a PHP to grab all the information from a POST and then create the string for later verification. Sadly I'm not sure if the \u2063 should simply be there in the string or if it should there be in some encoded/decoded way.
The Apple Public Key - How should it be used. The version that is seen in the documentation in some decoded/encoded way. Docs say decode base 64 and then create X.509 standard public key from that.
Does anyone have a working example? At the moment I'm complete lost.

Im using NodeJS and it was pretty simple. I took Apple's public key and wrapped it with -----BEGIN PUBLIC KEY-----\n and \n-----END PUBLIC KEY-----.
-----BEGIN PUBLIC KEY-----
<APPLE_PUBLIC_KEY_BASE_64 (copy paste from Apple's guide)>
-----END PUBLIC KEY-----
Or you can use NodeJs crypto module to load the public key:
const key = Buffer.from(`<APPLE_PUBLIC_KEY_BASE_64 (copy paste from Apple's guide)>`, 'base64');
const publicKey = crypto.createPublicKey({
key,
format: 'der',
type: 'spki',
});
Then I concatenated the parts of the postback that are needed for the verification with \u2063 separator.
// ad network version 2.0/2.1
const message = [
version,
adNetworkId,
campaignId,
appId,
transactionId,
redownload,
].join('\u2063');
Then I used NodeJS crypto module to verify the signature:
const verify = crypto.createVerify('sha256');
verify.update(message);
verify.verify(publicKey, signature, 'base64'); // this returns a boolean
This can be done in a similar way with Singular-SKAdNetwork-App ECDSA wrapper class from here
SEPERATOR = u"\u2063"
postback = {
"version": "2.1",
"ad-network-id": "com.example",
"campaign-id": 42,
"transaction-id": "6aafb7a5-0170-41b5-bbe4-fe71dedf1e28",
"app-id": 525463029,
"attribution-signature": "MEUCID6rbq3qt4GvFaAaynh5/LAcvn1d8CQTRhrZhLIxLKntAiEAo7IrvoMw6u2qDg6Tr5vIsEHXjlLkPlCOL0ojJcEh3Qw=",
"redownload": True,
"source-app-id": 1234567891,
"conversion-value": 20
}
pub_key = """
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWdp8GPcGqmhgzEFj9Z2nSpQVddayaPe4FMzqM9wib1+aHaaIzoHoLN9zW4K8y4SPykE3YVK3sVqW6Af0lfx3gg==
-----END PUBLIC KEY-----
"""
message = (
postback["version"]
+ SEPERATOR
+ postback["ad-network-id"]
+ SEPERATOR
+ str(postback["campaign-id "])
+ SEPERATOR
+ str(postback["app-id "])
+ SEPERATOR
+ postback["transaction-id"]
+ SEPERATOR
+ str(postback["redownload"]).lower()
+ SEPERATOR
+ str(postback["source-app-id"])
)
ecdsa = ECDSA(pub_key)
signature = postback["attribution-signature"]
ecdsa.verify(message, signature) # this returns a boolean
I hope this will help. I don't have any experience with PHP :/

Related

Protecting the service using OAuth2, JWT token not working Spring cloud

I have a micro service architecture. And I am securing the service by using OAuth2 and configured in using the JWT token. I am having a problem regarding securing the services in this micro service.
I have implemented the authorization server successfully. I am able to generate the JWT token and access the protected resource of Authorization server.
For securing the micro sevices I have done the following things:
Added dependency in pom file of the micro service that i want to secure with OAuth2:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
Modified the boostrap application as below:
#SpringBootApplication
#EnableResourceServer
public class Application {
...
}
Created the JwtTokenConfiguration as below:
private String getPublicKeyAsString() {
try {
Resource resource = new ClassPathResource("publickey.pem");
// return IOUtils.toString(oAuth2ConfigProperties.getJwt().getPublicKey().getInputStream(),
// StandardCharsets.UTF_8);
String strPublicKey= ".....";
String verifierKey = String.format("-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----", strPublicKey);
//verifierKey = String.format("-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----", strPublicKey);
System.out.println(verifierKey);
return verifierKey;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
#Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setVerifierKey(getPublicKeyAsString());
return jwtAccessTokenConverter;
}
The error is like below:
... 33 common frames omitted
Caused by: java.lang.IllegalStateException: For MAC signing you do not need to specify the verifier key separately, and if you do it must match the signing key
at org.springframework.util.Assert.state(Assert.java:73) ~[spring-core-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter.afterPropertiesSet(JwtAccessTokenConverter.java:318) ~[spring-security-oauth2-2.3.4.RELEASE.jar:na]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1855) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1792) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
... 53 common frames omitted
The oauthcer.jks is located in the authorization server. I have generate the public from the oauthcer.jks by running the commmand below:
keytool -list -rfc --keystore oauth2cer.jks
The result that i get from running command is like below:
-----BEGIN CERTIFICATE-----
MIIDfDCCAmSgAwIBAgIJAM8UC/xKlIfwMA0GCSqG ...
-----END CERTIFICATE-----
I tested the result in the url : https://8gwifi.org/PemParserFunctions.jsp and it works fine.
I don't know why it is not working.
I have tried many things. I will apriciate any guide.
I have faced a similar problem. What you are doing wrong is you are passing the certificate to the: jwtAccessTokenConverter.setVerifierKey(getPublicKeyAsString());. You need to pass the public key.
To fix this issue you need to extract the public key from the oauth2cer.jks. Execute the command below to extract the public key:
keytool -list -rfc --keystore oauth2cer.jks | openssl x509 -inform pem -pubkey -noout
After executing the command above it will display the result like below:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAdfsdfsdaxzcCAQ8AMIIBCgKCAQEAvJXQdLvlF1d
hx+AAzmNpuD89XPFAcmrvCt7CTGzi0bd/3WzK8dP2clxnVFANh7mbu24U91jK9ZS
3rewr4534tgfdryt54ytry6uyr
-----END PUBLIC KEY-----
The result above needs to be passed to the setVerifierKey method.

Unable to generate OpenSSL::PKey object

I want to generate a openSSL::Pkey using public key but i am getting the following error message "OpenSSL::PKey::RSAError: Neither PUB key nor PRIV key: nested asn1 error"
This is the public key:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAttI6ImgD74PhHWVqnrqSOmStboplyL02DB3/nc2iyDdYLzXoBIQN+NPMBPlsZlEKuKjsg5Ycfp6VjcmncM3CH9MGIr+Lmbj1HZmO/jJGJ84RPhzYOiZuElzs3seIcOtOa3BpFeqRsXJlrf1IVBKVU3erka5ACLVyrsjmp/VXMx5QjPD0qXARMGb6rDewTkyg3pGz07Y7rZgXkTl54ase+XaPegOankxdEQhVjPdrk7eSlIQS5Ni7FAcSyTOtYoPgiP7W0PolOMHvpFg96CHjc8V8xDsFdh0wbHd49WcKxiJMIfL65VUIW5aob9fw05a5FczyASa0iNtRiICy/QnCqQIDAQAB
-----END PUBLIC KEY-----
This the code which is generating the error
OpenSSL::PKey::RSA.new (Base64.decode64(public_key))
Anyone know how to fix it and can tell whats wrong with the key. Thanks
You don't need to decode base64 manually.
Make sure that the key is formatted correctly: BEGIN PUBLIC KEY and END PUBLIC KEY should be on separate lines.
Working example:
require "openssl"
key = "-----BEGIN PUBLIC KEY-----\n" \
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAttI6ImgD74PhHWVqnrqSOmStboplyL02DB3/nc2iyDdYLzXoBIQN+NPMBPlsZlEKuKjsg5Ycfp6VjcmncM3CH9MGIr+Lmbj1HZmO/jJGJ84RPhzYOiZuElzs3seIcOtOa3BpFeqRsXJlrf1IVBKVU3erka5ACLVyrsjmp/VXMx5QjPD0qXARMGb6rDewTkyg3pGz07Y7rZgXkTl54ase+XaPegOankxdEQhVjPdrk7eSlIQS5Ni7FAcSyTOtYoPgiP7W0PolOMHvpFg96CHjc8V8xDsFdh0wbHd49WcKxiJMIfL65VUIW5aob9fw05a5FczyASa0iNtRiICy/QnCqQIDAQAB\n" \
"-----END PUBLIC KEY-----"
p OpenSSL::PKey::RSA.new(key)

Keycloak JWT generation

I have keycloak running in localhost. I am testing it to generate a token. I have used an example I found. The token is the following:
"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJGSjg2R2NGM2pUYk5MT2NvNE52WmtVQ0lVbWZZQ3FvcXRPUWVNZmJoTmxFIn0.eyJqdGkiOiI2YWZlZjBiMC03ZmQ1LTRiOWUtOTk3NC0yOGFjMzBkMGM5OWQiLCJleHAiOjE0OTU2MTA0NTQsIm5iZiI6MCwiaWF0IjoxNDk1NjEwMTU0LCJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjgxMDAvYXV0aC9yZWFsbXMvZXhhbXBsZSIsImF1ZCI6ImpzLWNvbnNvbGUiLCJzdWIiOiJjNGY4NjE0Zi02YjFlLTRlYjItYmYxZC0wOTJmNGYxNWQwYmIiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJqcy1jb25zb2xlIiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiNTQ4NDJjNTgtMzYxYi00MDk2LThhNjgtNGZkZTg5OGUwNzg5IiwiYWNyIjoiMSIsImNsaWVudF9zZXNzaW9uIjoiNDNjMWEzMjAtNGZmNi00NmRmLThmZjUtNTU2ZjgxNGZhYzk1IiwiYWxsb3dlZC1vcmlnaW5zIjpbXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbInVzZXIiXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJuYW1lIjoiU2FtcGxlIFVzZXIiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ1c2VyIiwiZ2l2ZW5fbmFtZSI6IlNhbXBsZSIsImZhbWlseV9uYW1lIjoiVXNlciIsImVtYWlsIjoic2FtcGxlLXVzZXJAZXhhbXBsZSJ9.n1K0KIGYJYPZkjSq1eSg5gWnCAj44mgl4M-GJOlzgCj8y5TGw5OhT7sr84o9Ja9K6WMW3t0ti6feZIgc8mps3RK0HuuXeCNcrN6H2dPEtBphTvfXEUR2iMg83iCmxjhXgXso7oX2vyreJqB6WCCFEPbAQH2e5kHZqv6cfmXRlYU"
For the realm I have created I have an RSA public key and a certificate.
I copy the token in https://jwt.io/ in order to check if it is valid, I also copy this public key VERIFY SIGNATURE section but the token is invalid somehow. Is my token valid or not? How can I produce a valid token?
jwt.io expects a PEM format, so paste your public key from keycloak between -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- and it will work.
You can use this one and replace it with your key :
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoizQyv6WlRw3pPJNvrs9wYoc0hCU2hJ8bdwElevC2Vk/IrnN06mv1AotNpsmkrhNMBHU0iyBDl6ZCdyPDuOrKpuId1eURjfLP7s2V3+KA+x/H6TQFaFGKwEUiTx8oMZh+StBMz8tifHQlsm2VAIl920C1PgDTx7PcpDMz/g0/bnDdq8IDCgAi5rMTblzi+hTh4BO3wcA5EE7Iz1NymlTR0UXgMwS/gNuH+UM1fg8pxcZPM0YtHBBHIXaTqsI6nxfOi2e8JwvS2slKB/I3w66Kl+mNWEdXke5mh724yeQ3oRU/lQAlU8u+zRYFZRuLi06mMv42F+GLCmP9XZCyfFeQQIDAQAB
-----END PUBLIC KEY-----

How to get RSA public key from PEM format

I am trying to get an RSAPublicKey object from a public key file which is PEM encoded . The file is in the format given below
-----BEGIN PUBLIC KEY-----
M67sdjFDSSTRWE%$HGF£$"F£$$£%$ etc...
-----END PUBLIC KEY-----enter code here
I would like to get the modulus and exponent by reading this file. I have tried to do base64 decode and used the following
RSAPublicKey rsakey = (RSAPublicKey)PublicKeyDecoder.decode(decoded, "X509");
but it throws an InvalidKeyEncodingException.
Is there any way I can get an RSAPublicKey object from a PEM encoded public key file?

Ruby equivalent for php function openssl_pkey_get_public

I have the php script where the password encoding done using the
openssl:
$key = openssl_get_publickey($certificate);
openssl_public_encrypt($pass,$userPassCrypted,$key,OPENSSL_PKCS1_PADDING);
openssl_free_key($key);
Now I trying to make the same with ruby
require 'openssl'
cert = OpenSSL::X509::Certificate.new(certificate)
public_key = cert.public_key
passwordSSL = public_key.public_encrypt(password, 1)
The problem here is that these password encoding isn't match
The certificate the same but the public key
in PHP is:
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
in ruby is:
-----BEGIN RSA PUBLIC KEY-----
...
-----END RSA PUBLIC KEY-----
I guess here is the difference (keys body also not the same)...
How can I get inside ruby the same public key as in PHP? Or is it
possible convert RSA key by somehow?
Or maybe I have to do it with another way?
Any suggestions highly appreciated.
You can open this by using the following:
# Public key
public_key = OpenSSL::PKey::RSA.new(File.read("/path/to/public/key"))
# Private key (with password)
private_key = OpenSSL::PKey::RSA.new(File.read("/path/to/private/key), "password")
Hope this helps!
UPDATE: Ah, okay. Yeah, I don't think there is a way to remove the " RSA " from the OpenSSL output without removing it with a regular expression.
cert = OpenSSL::X509::Certificate.new(File.read("/path/to/x509/cert"))
cert.public_key.to_s.gsub(/RSA PUBLIC KEY-----/, "PUBLIC KEY-----")

Resources