I'm trying to understand how the jwt signature validation works.
This is how I'm doing it at the moment:
1) My app calls the attest api
2) My app sends the jwt to my server
3) My server verify the signature (third field of the jwt) using the certificate provided in the header of the jwt.
I understand that the signature is created by hashing the header and the payload of the jwt and then signing it (encrypting it) with Google's private key.
What I do in my step 3 is that I take the header + payload and decrypt it with
certificate's public key and verify that it matches the signature. (when I say 'I' do I mean a lib does it)
My issue is that, what happens if there is a malware on the user device and amend on the fly the JWT that is sent to my server? The malware would add his own certificate (issued by a trusted CA) in the header, modify the payload as it wishes and created the signature.
Me server side... well I'm going to take the public key provided in the cert valid the signature with it which will matches.
Is this correct? Or I'am confused somewhere? Because in that case it would render this all flow a bit useless no? How doe insure myself 100% that the JWT comes from google?
The key point is verify that the signing certificate is issued to attest.android.com by a trusted Certificate Authority
Any trusted CA will issue a fake certificate to attest.android.com. See what happens if they engage in bad practices https://security.googleblog.com/2016/10/distrusting-wosign-and-startcom.html?m=1
See Google's doc
Verify the compatibility check response
You should take steps to make sure that the compatibility check response actually came from the SafetyNet service and includes data that matches your request data.
Caution: You should send the entire JWS response to your own server, using a secure connection, for verification. We don't recommend that you perform the verification directly in your app because, in that case, there is no guarantee that the verification logic itself hasn't been modified.
Follow these steps to verify the origin of the JWS message:
Extract the SSL certificate chain from the JWS message.
Validate the SSL certificate chain and use SSL hostname matching to verify that the leaf certificate was issued to the hostname attest.android.com.
Use the certificate to verify the signature of the JWS message.
Check the data of the JWS message to make sure it matches the data within your original request. In particular, make sure that the nonce, timestamp, package name, and the SHA-256 hashes match.
The second dot requires to validate the certificate chain. It is assumed that it is used a Trust Manager containing the root certificate of the Certificate Authority
I have inspected Google's sample code in OfflineVerify to ensure the existence of a TrustManager because it is not explictly said, and it is effectively used during JWS verification. It uses the default system TrustManager, but you can use a custom one
Note that is used JWS (Json Web Signature), not JWT. A JWT is usually an authentication token signed with JWS
You did grasp the concept correctly. However something that you overlooked is that the lib you use is probably verifying that the certificate from which it extracts the public keys is a valid and a 'trusted' certificate (AKA comes from a trusted CA)
Thanks to this (and like the doc points it out) you need to verify yourself that the certificate has been issued by a "attest.android.com". No one will be able to forge a certificate to make it comes from this CA because.
This is what I understood at least, please correct me if I am wrong.
Related
When using Google SafetyNet for Android the documentation suggest that you
Validate the SSL certificate chain and use SSL Hostname matching to
ensure the leaf certification was issues to attest.android.com
Now how does this work? I would have assumed that I get the JWS message inspect the certs and signature etc but would validate against a cert grabbed from attest.android.com, but attest.android.com is not a live host.
Does SSL signing cater for validation without previously knowing the public key of the domain? i.e. Can I validate everything from incoming JWS message? I don't see how this is possible, is it?
Unfortunately, the documentation is not very descriptive as what you have to do.
The JWS data includes three sections: the header, the payload and the signature. Simplifying things, the header contains the public key certificates used to sign the payload, and the signature is included at the end.
To validate a SafetyNet JWS, you first need to extract the certificates embedded in the header. These certificates have trust chains that can be validated to a public root certificate, so you should verify that these are indeed valid certificates, and that they are issued to attest.android.com.
Then you take out the signature, and verify it against the embedded certificates.
If you check this, then you can trust the payload. But before looking at basicIntegrity and ctsProfileMatch, please ensure that apkPackageName, apkDigestSha256 and apkCertificateDigestSha256 match those of your app, so you know that the response actually comes from your unmodified app.
Optimally, your programming language should include a JWS library and an SSL library that can do this for you, so you don't have to write this yourself. The public sample includes a Java sample for you to peruse.
I've successfully created backend service and Android client for SafetyNet attestation.
When I send jws token to my server and try to validate it's certificate it turns out that there is no certificate that signed it.
Should I add api key to my Android app?
My attestation result:
eyJhbGciOiJSUzI1NiIsIng1YyI6WyJNSUlFaURDQ0EzQ2dBd0lCQWdJSWNkK3ZTTGZWdWxrd0RRWUpLb1pJaHZjTkFRRUxCUUF3U1RFTE1Ba0dBMVVFQmhNQ1ZWTXhFekFSQmdOVkJBb1RDa2R2YjJkc1pTQkpibU14SlRBakJnTlZCQU1USEVkdmIyZHNaU0JKYm5SbGNtNWxkQ0JCZFhSb2IzSnBkSGtnUnpJd0hoY05NVFl3TnpFeU1UVTBNelUwV2hjTk1UY3dOekV4TURBd01EQXdXakJzTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVXTUJRR0ExVUVCd3dOVFc5MWJuUmhhVzRnVm1sbGR6RVRNQkVHQTFVRUNnd0tSMjl2WjJ4bElFbHVZekViTUJrR0ExVUVBd3dTWVhSMFpYTjBMbUZ1WkhKdmFXUXVZMjl0TUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF3WEtyS0ZRRlBTckthS252NG9MSUhXNlVVMEk2STV2Q1FERFJOUlg2NWQrMVJLUWg0aDNoUHlTQ21MR2thREg0Q3Rud1lCdmRhWmZHUnVJVWx4K21zRjlkbU8rZzR0U2tSaXdrRzBxZ1VVN3docDk2ZHJoZkFrdDdPc2RzUWhmQWlTcmljdDlrTTRISElOVFVadjU0bXhkQmI0SkVBSlAvWlBDL2h5alAwRnlwNFlWVkdGN3RrQ09zUTVzYlB3cTBrbVJ0citobG41TXYwN1oxdkQwTG1MMU8yQy9qQUVCWGlhV3IzTjltenRXK2w3dEllOW1yY3FvUTB1UXdDWTd6YWdjb1czeFRmVExKVGdGTU5ZU0RtaDBEV2NZZUhidjNHQUtTR0hCSWZIWlQranhhZTlwam5pSi8zY3VmOWhVcUZPTGV3blI0TGdiTzNtSlc4a0dtWVFJREFRQUJvNElCVHpDQ0FVc3dIUVlEVlIwbEJCWXdGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUIwR0ExVWRFUVFXTUJTQ0VtRjBkR1Z6ZEM1aGJtUnliMmxrTG1OdmJUQm9CZ2dyQmdFRkJRY0JBUVJjTUZvd0t3WUlLd1lCQlFVSE1BS0dIMmgwZEhBNkx5OXdhMmt1WjI5dloyeGxMbU52YlM5SFNVRkhNaTVqY25Rd0t3WUlLd1lCQlFVSE1BR0dIMmgwZEhBNkx5OWpiR2xsYm5Sek1TNW5iMjluYkdVdVkyOXRMMjlqYzNBd0hRWURWUjBPQkJZRUZON1FsYndMa3BYbUNQbmxLS3FFdDdvc0JpYW5NQXdHQTFVZEV3RUIvd1FDTUFBd0h3WURWUjBqQkJnd0ZvQVVTdDBHRmh1ODltaTFkdldCdHJ0aUdycGFnUzh3SVFZRFZSMGdCQm93R0RBTUJnb3JCZ0VFQWRaNUFnVUJNQWdHQm1lQkRBRUNBakF3QmdOVkhSOEVLVEFuTUNXZ0k2QWhoaDlvZEhSd09pOHZjR3RwTG1kdmIyZHNaUzVqYjIwdlIwbEJSekl1WTNKc01BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQnV4UEpqeE9GcjlqOEJUT0swbGhNNy8zVUN3d2RjSTFFZExmRGx4NmJCWW53SmVPUThKdC9Vd1VkcTFGTWRMM2gydkVTYVFkUVdYQTVCWE9YenI1TXV2V0xYaldKRjRsb0E4VzBTVDQyUDd6MWsyT29qb3JsNXZabkhjY08vZzhFd0ZpRlVINCsydXF1NVFzWmpVZGxMS2FFUWhJZTcvU2x2Qk41eHlZd0RaMlp0R00rdlVnVlFkbXpaV0puTGNHZFVSUGN3N2tlOWlNTFZKcXUrR0Z4c1lJSXYwMW1EQlVlblNUNFRsYWlEUG1nUmwzN3lCQWpUNGVaQlBIRzBGUzhna3FKZzAzMnhjNFdPNE1WMk1ZeEN0NWVzSllaOU04Y09QRFVjOEVYOUlwbC9FT1IrL25Mb2NRSEhIazhJQlhsbDVGM2lnN1RpOGFCNTJUMFdRaEIrIiwiTUlJRDhEQ0NBdGlnQXdJQkFnSURBanFTTUEwR0NTcUdTSWIzRFFFQkN3VUFNRUl4Q3pBSkJnTlZCQVlUQWxWVE1SWXdGQVlEVlFRS0V3MUhaVzlVY25WemRDQkpibU11TVJzd0dRWURWUVFERXhKSFpXOVVjblZ6ZENCSGJHOWlZV3dnUTBFd0hoY05NVFV3TkRBeE1EQXdNREF3V2hjTk1UY3hNak14TWpNMU9UVTVXakJKTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNoTUtSMjl2WjJ4bElFbHVZekVsTUNNR0ExVUVBeE1jUjI5dloyeGxJRWx1ZEdWeWJtVjBJRUYxZEdodmNtbDBlU0JITWpDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBSndxQkhkYzJGQ1JPZ2FqZ3VEWVVFaThpVC94R1hBYWlFWis0SS9GOFluT0llNWEvbUVOdHpKRWlhQjBDMU5QVmFUT2dtS1Y3dXRaWDhiaEJZQVN4RjZVUDd4YlNEajBVL2NrNXZ1UjZSWEV6L1JURGZSSy9KOVUzbjIrb0d0dmg4RFFVQjhvTUFOQTJnaHpVV3gvL3pvOHB6Y0dqcjFMRVFUcmZTVGU1dm44TVhIN2xOVmc4eTVLcjBMU3krckVhaHF5ekZQZEZVdUxIOGdaWVIvTm5hZytZeXVFTldsbGhNZ1p4VVlpK0ZPVnZ1T0FTaERHS3V5Nmx5QVJ4em1aRUFTZzhHRjZsU1dNVGxKMTRyYnRDTW9VL000aWFyTk96MFlEbDVjRGZzQ3gzbnV2UlRQUHVqNXh0OTcwSlNYQ0RUV0puWjM3RGhGNWlSNDN4YStPY21rQ0F3RUFBYU9CNXpDQjVEQWZCZ05WSFNNRUdEQVdnQlRBZXBob2pZbjdxd1ZrREJGOXFuMWx1TXJNVGpBZEJnTlZIUTRFRmdRVVN0MEdGaHU4OW1pMWR2V0J0cnRpR3JwYWdTOHdEZ1lEVlIwUEFRSC9CQVFEQWdFR01DNEdDQ3NHQVFVRkJ3RUJCQ0l3SURBZUJnZ3JCZ0VGQlFjd0FZWVNhSFIwY0RvdkwyY3VjM2x0WTJRdVkyOXRNQklHQTFVZEV3RUIvd1FJTUFZQkFmOENBUUF3TlFZRFZSMGZCQzR3TERBcW9DaWdKb1lrYUhSMGNEb3ZMMmN1YzNsdFkySXVZMjl0TDJOeWJITXZaM1JuYkc5aVlXd3VZM0pzTUJjR0ExVWRJQVFRTUE0d0RBWUtLd1lCQkFIV2VRSUZBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBQ0U0RXA0Qi9FQlpEWGdLdDEwS0E5TENPMHE2ejZ4RjlrSVFZZmVlUUZmdEpmNmlaQlpHN2VzbldQRGNZQ1pxMng1SWdCelV6Q2VRb1kzSU50T0F5bkllWXhCdDJpV2ZCVUZpd0U2b1RHaHN5cGI3cUVaVk1TR05KNlpsZElEZk0vaXBwVVJhVlM2bmVTWUxBRUhEMExQUHN2Q1FrMEU2c3BkbGVIbTJTd2Flc1NEV0IrZVhrbkdWcHpZZWtRVkEvTGxlbGtWRVNXQTZNQ2FHc2VxUVNwU2Z6bWhDWGZWVURCdmRtV0Y5ZlpPR3JYVzJsT1VoMW1Fd3BXanFOMHl2S25GVUV2L1RtRk5XQXJDYnRGNG1tazJ4Y3BNeTQ4R2FPWk9OOW11SUFzMG5INUFxcTNWdUR4M0NRUms2KzBOdFpsbXd1OVJZMjNuSE1BY0lTd1NIR0ZnPT0iXX0.eyJub25jZSI6IlVFMTViamRaVUVNdk4ybFJNRU5UVTJwQ1VXdE1Rak51VEU1WWRtOXZabnBPUm14WkwzcEhibXRWV21oS1ZrSkJTa3R1S3pkWFJHbDBUVnBzYkZORFppOWFMMjlGZUVKa2NEVnllQzlwUTFWUlluVlVlV2M5UFE9PSIsInRpbWVzdGFtcE1zIjoxNDkwNzg3MTQ3MzYxLCJhcGtQYWNrYWdlTmFtZSI6ImNvbS5leGFtcGxlLnNhbXBsZSIsImFwa0RpZ2VzdFNoYTI1NiI6ImtXVWZ3ZlpCSU9ib2UzRGtMM2RkeTFBbm1VdjM3cDFVYlduZ1Fkd1gwZHM9IiwiY3RzUHJvZmlsZU1hdGNoIjp0cnVlLCJleHRlbnNpb24iOiJDUVlZdUQyUmRqQVkiLCJhcGtDZXJ0aWZpY2F0ZURpZ2VzdFNoYTI1NiI6WyJ3ZU93UTZLUWdFZGx2bXJIV0VCRDlyZm96YnF6ZWU0ZlpPT2FXclpoc2NvPSJdLCJiYXNpY0ludGVncml0eSI6dHJ1ZX0.JXOvgIx0PufdyJ72Vo-FvTb_6Dwj7gcSxlXWHt8siVrgK2eMQA3V_eYbXS7NYkNUEXLLMNIfm8qEIwJQGYLUKE9GyaHjjuFt_YOkQMOzC8hh9VpIekS8bc_eRfvVajXzrFSRAigOnfVBrMGrVVpxsqtjqz1Y9ochGHwrjO2b8oZyw06HjzsnuT9YpLXakBg1azOYx9KP-_XkDaKW6_Lkfn6Rmo8hpatadIF5qn54W_UkXvvsG7d4P8h_7uvO66rRhK1OXg3Qhazta3B_XFtiLcmusaqmglopEW1hI07FAvrqzemuY-_4EcvReLKfo84rl_BuJmLFVtQtDyAHtzngxw
You are required to use API key with the latest version of Google Play Services.
See documentation on new SafetyNetClient::attest(byte[], String) method.
As a side note I highly recommend to set the verification quota to minimal value of 1 for a key you generate for your Android app here:
https://console.developers.google.com/projectselector/apis/api/androidcheck.googleapis.com/quotas
This (almost, off by 1 error ;)) stops an attacker from using a key which can be retrieved from your client code like so:
curl --data '{"signedAttestation": "eyJhbGciOiJSUzI1NiIsIng1YyI6WyJNSUlFaURDQ0EzQ2dBd0lCQWdJSWNkK3ZTTGZWdWxrd0RRWUpLb1pJaHZjTkFRRUxCUUF3U1RFTE1Ba0dBMVVFQmhNQ1ZWTXhFekFSQmdOVkJBb1RDa2R2YjJkc1pTQkpibU14SlRBakJnTlZCQU1USEVkdmIyZHNaU0JKYm5SbGNtNWxkQ0JCZFhSb2IzSnBkSGtnUnpJd0hoY05NVFl3TnpFeU1UVTBNelUwV2hjTk1UY3dOekV4TURBd01EQXdXakJzTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVXTUJRR0ExVUVCd3dOVFc5MWJuUmhhVzRnVm1sbGR6RVRNQkVHQTFVRUNnd0tSMjl2WjJ4bElFbHVZekViTUJrR0ExVUVBd3dTWVhSMFpYTjBMbUZ1WkhKdmFXUXVZMjl0TUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF3WEtyS0ZRRlBTckthS252NG9MSUhXNlVVMEk2STV2Q1FERFJOUlg2NWQrMVJLUWg0aDNoUHlTQ21MR2thREg0Q3Rud1lCdmRhWmZHUnVJVWx4K21zRjlkbU8rZzR0U2tSaXdrRzBxZ1VVN3docDk2ZHJoZkFrdDdPc2RzUWhmQWlTcmljdDlrTTRISElOVFVadjU0bXhkQmI0SkVBSlAvWlBDL2h5alAwRnlwNFlWVkdGN3RrQ09zUTVzYlB3cTBrbVJ0citobG41TXYwN1oxdkQwTG1MMU8yQy9qQUVCWGlhV3IzTjltenRXK2w3dEllOW1yY3FvUTB1UXdDWTd6YWdjb1czeFRmVExKVGdGTU5ZU0RtaDBEV2NZZUhidjNHQUtTR0hCSWZIWlQranhhZTlwam5pSi8zY3VmOWhVcUZPTGV3blI0TGdiTzNtSlc4a0dtWVFJREFRQUJvNElCVHpDQ0FVc3dIUVlEVlIwbEJCWXdGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUIwR0ExVWRFUVFXTUJTQ0VtRjBkR1Z6ZEM1aGJtUnliMmxrTG1OdmJUQm9CZ2dyQmdFRkJRY0JBUVJjTUZvd0t3WUlLd1lCQlFVSE1BS0dIMmgwZEhBNkx5OXdhMmt1WjI5dloyeGxMbU52YlM5SFNVRkhNaTVqY25Rd0t3WUlLd1lCQlFVSE1BR0dIMmgwZEhBNkx5OWpiR2xsYm5Sek1TNW5iMjluYkdVdVkyOXRMMjlqYzNBd0hRWURWUjBPQkJZRUZON1FsYndMa3BYbUNQbmxLS3FFdDdvc0JpYW5NQXdHQTFVZEV3RUIvd1FDTUFBd0h3WURWUjBqQkJnd0ZvQVVTdDBHRmh1ODltaTFkdldCdHJ0aUdycGFnUzh3SVFZRFZSMGdCQm93R0RBTUJnb3JCZ0VFQWRaNUFnVUJNQWdHQm1lQkRBRUNBakF3QmdOVkhSOEVLVEFuTUNXZ0k2QWhoaDlvZEhSd09pOHZjR3RwTG1kdmIyZHNaUzVqYjIwdlIwbEJSekl1WTNKc01BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQnV4UEpqeE9GcjlqOEJUT0swbGhNNy8zVUN3d2RjSTFFZExmRGx4NmJCWW53SmVPUThKdC9Vd1VkcTFGTWRMM2gydkVTYVFkUVdYQTVCWE9YenI1TXV2V0xYaldKRjRsb0E4VzBTVDQyUDd6MWsyT29qb3JsNXZabkhjY08vZzhFd0ZpRlVINCsydXF1NVFzWmpVZGxMS2FFUWhJZTcvU2x2Qk41eHlZd0RaMlp0R00rdlVnVlFkbXpaV0puTGNHZFVSUGN3N2tlOWlNTFZKcXUrR0Z4c1lJSXYwMW1EQlVlblNUNFRsYWlEUG1nUmwzN3lCQWpUNGVaQlBIRzBGUzhna3FKZzAzMnhjNFdPNE1WMk1ZeEN0NWVzSllaOU04Y09QRFVjOEVYOUlwbC9FT1IrL25Mb2NRSEhIazhJQlhsbDVGM2lnN1RpOGFCNTJUMFdRaEIrIiwiTUlJRDhEQ0NBdGlnQXdJQkFnSURBanFTTUEwR0NTcUdTSWIzRFFFQkN3VUFNRUl4Q3pBSkJnTlZCQVlUQWxWVE1SWXdGQVlEVlFRS0V3MUhaVzlVY25WemRDQkpibU11TVJzd0dRWURWUVFERXhKSFpXOVVjblZ6ZENCSGJHOWlZV3dnUTBFd0hoY05NVFV3TkRBeE1EQXdNREF3V2hjTk1UY3hNak14TWpNMU9UVTVXakJKTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNoTUtSMjl2WjJ4bElFbHVZekVsTUNNR0ExVUVBeE1jUjI5dloyeGxJRWx1ZEdWeWJtVjBJRUYxZEdodmNtbDBlU0JITWpDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBSndxQkhkYzJGQ1JPZ2FqZ3VEWVVFaThpVC94R1hBYWlFWis0SS9GOFluT0llNWEvbUVOdHpKRWlhQjBDMU5QVmFUT2dtS1Y3dXRaWDhiaEJZQVN4RjZVUDd4YlNEajBVL2NrNXZ1UjZSWEV6L1JURGZSSy9KOVUzbjIrb0d0dmg4RFFVQjhvTUFOQTJnaHpVV3gvL3pvOHB6Y0dqcjFMRVFUcmZTVGU1dm44TVhIN2xOVmc4eTVLcjBMU3krckVhaHF5ekZQZEZVdUxIOGdaWVIvTm5hZytZeXVFTldsbGhNZ1p4VVlpK0ZPVnZ1T0FTaERHS3V5Nmx5QVJ4em1aRUFTZzhHRjZsU1dNVGxKMTRyYnRDTW9VL000aWFyTk96MFlEbDVjRGZzQ3gzbnV2UlRQUHVqNXh0OTcwSlNYQ0RUV0puWjM3RGhGNWlSNDN4YStPY21rQ0F3RUFBYU9CNXpDQjVEQWZCZ05WSFNNRUdEQVdnQlRBZXBob2pZbjdxd1ZrREJGOXFuMWx1TXJNVGpBZEJnTlZIUTRFRmdRVVN0MEdGaHU4OW1pMWR2V0J0cnRpR3JwYWdTOHdEZ1lEVlIwUEFRSC9CQVFEQWdFR01DNEdDQ3NHQVFVRkJ3RUJCQ0l3SURBZUJnZ3JCZ0VGQlFjd0FZWVNhSFIwY0RvdkwyY3VjM2x0WTJRdVkyOXRNQklHQTFVZEV3RUIvd1FJTUFZQkFmOENBUUF3TlFZRFZSMGZCQzR3TERBcW9DaWdKb1lrYUhSMGNEb3ZMMmN1YzNsdFkySXVZMjl0TDJOeWJITXZaM1JuYkc5aVlXd3VZM0pzTUJjR0ExVWRJQVFRTUE0d0RBWUtLd1lCQkFIV2VRSUZBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBQ0U0RXA0Qi9FQlpEWGdLdDEwS0E5TENPMHE2ejZ4RjlrSVFZZmVlUUZmdEpmNmlaQlpHN2VzbldQRGNZQ1pxMng1SWdCelV6Q2VRb1kzSU50T0F5bkllWXhCdDJpV2ZCVUZpd0U2b1RHaHN5cGI3cUVaVk1TR05KNlpsZElEZk0vaXBwVVJhVlM2bmVTWUxBRUhEMExQUHN2Q1FrMEU2c3BkbGVIbTJTd2Flc1NEV0IrZVhrbkdWcHpZZWtRVkEvTGxlbGtWRVNXQTZNQ2FHc2VxUVNwU2Z6bWhDWGZWVURCdmRtV0Y5ZlpPR3JYVzJsT1VoMW1Fd3BXanFOMHl2S25GVUV2L1RtRk5XQXJDYnRGNG1tazJ4Y3BNeTQ4R2FPWk9OOW11SUFzMG5INUFxcTNWdUR4M0NRUms2KzBOdFpsbXd1OVJZMjNuSE1BY0lTd1NIR0ZnPT0iXX0.eyJub25jZSI6IlVFMTViamRaVUVNdk4ybFJNRU5UVTJwQ1VXdE1Rak51VEU1WWRtOXZabnBPUm14WkwzcEhibXRWV21oS1ZrSkJTa3R1S3pkWFJHbDBUVnBzYkZORFppOWFMMjlGZUVKa2NEVnllQzlwUTFWUlluVlVlV2M5UFE9PSIsInRpbWVzdGFtcE1zIjoxNDkwNzg3MTQ3MzYxLCJhcGtQYWNrYWdlTmFtZSI6ImNvbS5leGFtcGxlLnNhbXBsZSIsImFwa0RpZ2VzdFNoYTI1NiI6ImtXVWZ3ZlpCSU9ib2UzRGtMM2RkeTFBbm1VdjM3cDFVYlduZ1Fkd1gwZHM9IiwiY3RzUHJvZmlsZU1hdGNoIjp0cnVlLCJleHRlbnNpb24iOiJDUVlZdUQyUmRqQVkiLCJhcGtDZXJ0aWZpY2F0ZURpZ2VzdFNoYTI1NiI6WyJ3ZU93UTZLUWdFZGx2bXJIV0VCRDlyZm96YnF6ZWU0ZlpPT2FXclpoc2NvPSJdLCJiYXNpY0ludGVncml0eSI6dHJ1ZX0.JXOvgIx0PufdyJ72Vo-FvTb_6Dwj7gcSxlXWHt8siVrgK2eMQA3V_eYbXS7NYkNUEXLLMNIfm8qEIwJQGYLUKE9GyaHjjuFt_YOkQMOzC8hh9VpIekS8bc_eRfvVajXzrFSRAigOnfVBrMGrVVpxsqtjqz1Y9ochGHwrjO2b8oZyw06HjzsnuT9YpLXakBg1azOYx9KP-_XkDaKW6_Lkfn6Rmo8hpatadIF5qn54W_UkXvvsG7d4P8h_7uvO66rRhK1OXg3Qhazta3B_XFtiLcmusaqmglopEW1hI07FAvrqzemuY-_4EcvReLKfo84rl_BuJmLFVtQtDyAHtzngxw"}' -H "Content-Type: application/json" -H "X-Android-Package: com.example" -H "X-Android-Cert: 1111111111111111111111111111111111111111" -X POST "https://www.googleapis.com/androidcheck/v1/attestations/verify?key=AIzaSyCmid3BBzBO0idOTNNRH-7RjCrA3xhvAho"
Substitute X-Android-Package, X-Android-Cert and key values to test on your API key with android app restriction.
I think you missed some details, based on the documentation - Validating the response with Google APIs:
Note: You need an API key to access the Android Device Verification API, and the API is rate-limited. For these reasons, you should use the API only for testing during the initial development stage. You shouldn't use this verification API in a production scenario.
Try adding the API key and check if the behavior change.
Hope this helps.
You can validate the response yourself. Here's some code that will help you interpret the JWT response:
https://github.com/scottyab/safetynethelper/blob/master/safetynetlib/src/main/java/com/scottyab/safetynet/SafetyNetHelper.java
You'll want to validate the signing on your backend. Google does let you know what you need to check, if you're not using their easy service:
Verify the compatibility check response
You should take steps to make
sure that the compatibility check response actually came from the
SafetyNet service and includes data that matches your request data.
Caution: You should send the entire JWS response to your own server,
using a secure connection, for verification. We don't recommend that
you perform the verification directly in your app because, in that
case, there is no guarantee that the verification logic itself hasn't
been modified.
Follow these steps to verify the origin of the JWS message:
Extract the SSL certificate chain from the JWS message.
Validate the
SSL certificate chain and use SSL hostname matching to verify that the
leaf certificate was issued to the hostname attest.android.com.
Use
the certificate to verify the signature of the JWS message.
Check the
data of the JWS message to make sure it matches the data within your
original request. In particular, make sure that the nonce, timestamp,
package name, and the SHA-256 hashes match.
From: https://developer.android.com/training/safetynet/attestation.html
I'm building a app that signs some data and is supposed to send the 3-tuple data, signature, certificate-chain to a server. The server then checks the signature against the certificate and checks whether the provided certificate is signed by a fixed CA. I already installed the certificate on my android device and now I want to use it in my app.
My problem is: how can I retrieve the certificate chain (no private key) to send it to the server for verification? I looked at the keychain stuff but I don't see a way.
Thanks for your help
I want to perform extra validation for SSL connections I make in an android app.
Basically I need to be able to:
See if the certificate of the remote host I am connected to has Extended Validation (EV) status
Find out the root certificate authority for the certificate of the remote end. E.g. I want to know if it is a VeriSign certificate or not.
To elaborate a bit more, I am writing a client that needs a high level of security and our organization is using EV certificates from VeriSign on all servers. I want to prevent any compromised certificate authority, or anyone that can fool a certificate authority to forge a certificate for our domain be able to hijack the application.
Is this doable and if so, how? Is there a way to get more information about the certificate of the remote end from a URLConnection object or a HTTPClient object and so on?
First: you can't possibly 'prevent any compromised certificate authority' from issuing a certificate for your domain. If it is compromised, they can issue whatever they want. What you can do is create a trust store with a limited number of trusted CA certificates, say, VeriSign only. That way, even if an related CA is compromised and issues a cert for your domain, it wouldn't matter since you don't trust it in the first place. That would also take care of second bullet. To have additional checks you need to implement and install your own X509TrustManager. Check the JSSE reference guide for details.
I am developing an Android app which need to consume .Net webservices over SSL which I have no experience in. Now I am looking for some guidance and explanation on SSL handshake and certificates.
Note: the server is using IP address and NOT domain name. It is an intranet application.
So far I have created a certificate(called self-signed?) in web server from IIS 7.
To consume it from Android app, I found two ways of doing it :
1). Embedded the certificate in the app (Which certificate? How do I get it?)
2). Trust all the certificates ( ppl said there is security issue with this approach, could you elaborate more? Does it still do the handshake?)
CERTIFICATES:
How many type of certificates are there in the handshake and what are they?
Does self-signed certificate have root certificate? If yes, how can i get them?
Is it possible to move/copy the self-signed certificate from one server to another?
HANDSHAKE:
First of all, is this process correct?
The SSL handshake process(copied from a website) is described below:
The client initiates the SSL handshake process by sending a URL
starting with the following: https:// to the server.
The client initially sends the Web server a list of each encryption
algorithm which it supports. Algorithms supported by SSL include RC4
and Data Encryption Standard (DES). The client also sends the server
its random challenge string which will be utilized later in the
process.
Will the embedded cert be sent in here?
The Web server next performs the following tasks:
Selects an encryption algorithm from the list of encryption
algorithms supported by, and received from the client.
Sends the client a copy of its server certificate.
Sends the client its random challenge string
The client utilizes the copy of the server certificate received from
the server to authenticate the identity of the server.
The client obtains the public key of the server from the server
certificate.
The client next generates a premaster secret. This is a different
random string which will in turn be utilized to generate the session
key for the SSL session. The client then encrypts a different value
called the premaster secret using the public key of the server, and
returns this encrypted value to the server. This is accompanied with
a keyed hash of the handshake messages, and a master key. The hash
is used to protect the messages exchanged in the handshake process.
The hash is generated from the former two random strings transmitted
between the server and the client.
What is a master key?
The server sends the client a keyed hash of all the handshake
messages exchanged between the two parties so far.
What is this keyed hash made from?
The server and the client then generate the session key from the
different random values and keys, and by applying a mathematical
calculation.
The session key is used as a shared secret key to encrypt and
decrypt data exchanged between the server and the client.
The session key is discarded when the SSL session either times-out or is terminated.
I'll try to answer to the best of my knowledge here
Embedded the certificate in the app (Which certificate? How do I get it?)
This the certificate identifying the client's/app identity. You can either get it through CA or self signed. This certificate will be used by the server to verify the client's/app identity
Trust all the certificates ( ppl said there is security issue with this approach, could you elaborate more? Does it still do the handshake?)
It still does the handshake but it doesn't do the certificate validation which is dangerous unless you are connecting internally (which seems you are). Trusting all certificate means an entity can claim as someone who they are not and thus could obtain confidential information from the users.
How many type of certificates are there in the handshake and what are they? In handshake you have the server's certificate and optionally the client certificate (for two factors authentication)
Does self-signed certificate have root certificate? If yes, how can i get them? Root certificate as far as I know means the ones that identifies by CA itself and thus it has no else to sign it. As your identity can still be verified and needs to be signed by CA, yours would not be classified as root certificate
Is it possible to move/copy the self-signed certificate from one server to another? The short answer is yes though the procedures from one platform to the others are different. Check [this link)(http://www.sslshopper.com/how-to-move-or-copy-an-ssl-certificate-from-one-server-to-another.html), it has instructions to copy certificate for few platforms
Will the embedded cert be sent in here? No, the embedded (client's) certificate is sent after validation of the server's identify is complete
What is a master key? Master key is the key that is used to derived the session key for later communication. It is also used to hash the messages and to verify authenticity of the messages in the next set of stages
What is this keyed hash made from? It's made from the master key sent by the client. In order to verify all messages, the server sent all messages that have been passed and hashed it with the master key. The client will hashed its messages as well with the same key and then compared with the data sent by the server. Only when the hash matches then we could be sure we are still communicating with the same server