Google has introduced Play Integrity for device verification.
It uses nonce as an input string.
There is no sample on how to generate nonce.
There are few blogs like these but there is no example for that can works at production level.
Answering my own question:
Generate Nonce like this
var nonceBytes = ByteArray(40) //you can change the length
SecureRandom().nextBytes(nonceBytes) //randomise the bytes
var nonce = Base64.encodeToString(nonceBytes, Base64.URL_SAFE)
Hope this helps.
Related
We have a physical product with an QRCode, like this:
http://example.com/product/[encryptedProductKey]
We need to check in an app when the qrcode is scanned if the product key is valid and was made by us.
The qrcode will be lasered onto the product and cannot be changed afterwards.
Unfortunately we are a small startup and noone here really knows about encryption. Is there some kind of dummy approach to this?
You probably don't actually need encryption but rather a MAC: Message Authentication Code or a Digital Signature.
If you are both generating and verifying the codes then a MAC is sufficient (I assume this is the case?).
In which case, the product code can be a random set of characters (cryptographically strong random number) and the HMAC (Hash based Message Authentication Code). It could be quite long but would be a simple solution.
The steps would be something like (the below is pseudo code):
Generate a strong secret key that you use to generate and verify codes (don't share this)
Generate a product code:
code = cryptoRandom(16) // 16 strong random bytes
tag = HMAC(code, secretKey)
Encode the key say with Base64
Give the resulting base64 string to your customer
To verify the key:
Split the decoded product customer key string into the code and tag
Generate the tag from the code
tag = HMAC(code, secretKey)
Check that the tag that you generate matches the tag in the customer key
secureCompare(tag, tagFromCustomer)
Note that you must not use == to compare the tags as this will be vulnerable to timing attacks. Your language should have a secure compare library. See What's the difference between a secure compare and a simple ==(=)
As an alternative, if you want to keep the customer key to 32-bytes in length you could take the below approach. However, this would require that you keep a secret key for every customer.
Generate a secret key for the customer
Use that key to generate a customer key by using the HMAC of a zero string (say 16)
code = HMAC("0000000000000000", customerSecretKey)
Give the customer the code
To verify, use the customers secret key to verify
secureCompare(customerCode, HMAC("0000000000000000", customerSecretKey))
For this to be secure, the customer keys must be secret AND unique.
Few days ago, In "Pre-launch report for APK" in Google Play Console, it start to flag me
Unsafe encryption
Detected in APK ???
Your app contains unsafe cryptographic encryption patterns. Please see this Google Help Centre article for details.
Vulnerable classes:
c.j.a.s.J.b
However, since the early day of APK, I do not change anything in encryption code/ description code. Hence, I'm not sure why Google starts to warn me on recent APK?
Any idea how to resolve? As, the information for vulnerable classes c.j.a.s.J.b is not helpful.
I try to use Proguard + mapping.txt to retrace c.j.a.s.J.b but able to figure what class is that.
Any idea how I can get rid of Google security warning?
The google play suggests with vulnerable classes with the function name, you can see in the dialog.
Review your app for statically computed keys, initialization vectors, and/or salts that are used in cryptographic encryption operations and ensure that these values are constructed safely
For example :
public byte[] encryptionUtil(String key, String iv, byte[] plainText) {
Cipher cipher = Cipher.getInstance(“AES/GCM/NoPadding”);
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), “AES”);
GCMParameterSpec paramSpec = new GCMParameterSpec(256, iv.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, keySpec, paramSpec);
return cipher.doFinal(plainText);
}
And you are calling a function as:
byte[] cipherText = encryptionUtil(“abcdef...”, “010203040506”, plainText);
Here your encryption key “abcdef...” is provides as a static string. A statically computed value is a value that is the same on every execution of your app. Statically computed cryptographic values can be extracted from your app and used to attack your app’s encrypted data.
So you can use EncryptedSharedPreferences to store locally data
Reference link https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences
OR
Jetpack Security
For more details:
Remediation for Unsafe Cryptographic Encryption
I think you are using some encryption/decryption code with statically stored key.
A statically computed value is a value that is the same on every execution of your app. Statically computed cryptographic values can be extracted from your app and used to attack your app’s encrypted data.
So Google give this warning to change that stored key with dynamically generated key.
For that you can generate different key on every launch.
To solve this problem generate dynamic encryption/decryption key on every launch.
For that you can find more info here https://developer.android.com/jetpack/androidx/releases/security
I have to get a JWT using the SHA-256 algorithm and a secret key (for example "blablablamysecretkey").
Despite checking SO, several libraries and their documentations I don't know yet how to perform this.
If I use this library https://github.com/jwtk/jjwt (one of the most used) this is the code sample:
Key key = MacProvider.generateKey();
String s = Jwts.builder().setSubject("stringtoencode").signWith(SignatureAlgorithm.HS512, key).compact();
Since I have to use SHA-256 algorithm I guess that I should use:
Key key = MacProvider.generateKey();
String s = Jwts.builder().setSubject("stringtoencode").signWith(SignatureAlgorithm.HS256, key).compact();
My problem is that this sample (and all of the samples I've seen by the way) use Key key = MacProvider.generateKey();, and if I'm not wrong this generates a generic key. In fact this is what the documentation says:
// We need a signing key, so we'll create one just for this example. Usually
// the key would be read from your application configuration instead.
So my problem is how could I convert my secret key (string) into something of Key class?
MacProvider.generateKey() generates a random secret key, which is safer than using a passphrase. Keys need to be chosen at random. Read this post if you want to know how hmac keys have to be generated https://security.stackexchange.com/questions/95972/what-are-requirements-for-hmac-secret-key
// We need a signing key, so we'll create one just for this example. Usually
// the key would be read from your application configuration instead.
The text you have highlighted means that you have to persist the key in your server in order to verify JWT signature when a client sends a token. HMAC keys are symmetric, the key is used both for sign and verify
If you want to generate a Key from a passphrase String use
byte hmacKey[] = passphrase.getBytes(StandardCharsets.UTF8);
Key key = new SecretKeySpec(hmacKey,signatureAlgorithm.getJcaName());
Whenever I include my Google Play Services API key along with my request for Google Directions like in the code below:
private String makeDirectionsURL(double originLat, double originLong, double destLat, double destLong)
{
StringBuilder url = new StringBuilder();
//first part of url//
url.append("https://maps.googleapis.com/maps/api/directions/json?");
//start adding parameters//
//origin coordinates
url.append("origin="+originLat+","+originLong);
//destiniation coordinates
url.append("&destination=");
url.append(destLat+","+destLong);
//api key
url.append("&key=");
url.append(getResources().getString(R.string.google_api_key));
//NOTE: for some reason, the request suceeds when leaving out the api key
return url.toString();
}
When I include the api key as a parameter in the request, the json response shows that my request has been denied. The response reads:
{
"error_message" : "This IP, site or mobile application is not authorized to use this API key.",
"routes" : [],
"status" : "REQUEST_DENIED"
}
Yet the same api key works for my google map view - and my api console registers the accesses for the map view quota limit. Even odder yet, when I leave out the key parameter in the Directions request I get a valid JSON response.
I have a feeling that I have to generate another different api key for just the Google Directions service - but I'm not sure how to. The documentation here:
https://developers.google.com/maps/documentation/directions/#api_key
Says to visit the console and activate the Directions API service - I did.
Then it says my api key "will be available from the API Access page, in the Simple API Access section. Directions API applications use the Key for server apps." Now is where I'm confused - how can I use the key for server apps if I am accessing from a mobile device - I'm assuming this is for any webpages that wish to use the service - but what do I do for an android app. As I said, I already tried using my Simple API Access key for Android apps, which I know works, yet when I pass the same key to Google Directions - it mysteriously doesn't work...
Any help, vague guidance, or links to read up on would be really appreciated.
PS: If I can't figure this out - am I allowed to keep sending requests w/o a api key?
I think the issue is because you are trying to pass a server key which is directly tied to your server's IP address, when you should be passing an android app key.
In your Google APIs console, navigate to API's and Auth. From here you can create a new public API key.
When prompted, select Android Application
You will need to enter your device's SHA1 Certificate Fingerprint
This comes from the keystore used to sign the apk. When running an app in debug, you will most likely be using your IDE's debugging keystore. Google has a nice write up on how to get this information.
When releasing the app, you will need to sign it with your own key. You will either need to edit your API key to the new keystore SHA1 fingerprint or create a new API key.
Additionally, according to Google, you should be using a key.
All Directions API applications should use an API key. Including a key
in your request:
Allows you to monitor your application's API usage in the APIs
Console. Enables per-key instead of per-IP-address quota limits.
Ensures that Google can contact you about your application if necessary.
While I can't comment on how strictly they enforce this, I strongly encourage you to do so to avoid any issues down the road.
no key distribution, public and private keys will be known by users (random key generator will not be used). I have to encrypt hashed message with private key in order to provide signature
message will only 10-20 characters, so system can be as simple as it is possible
For generating a digital signature, you don't need to encrypt the hash. Signing is a separate crypto primitive; the hash is not encrypted raw anyway, there's some padding. That said, the code is:
Signature Signer = Signature.getInstance("SHA1withRSA");
Signer.initSign(MyKey, new SecureRandom()); //Where do you get the key?
byte []Message = MyMessage(); //Initialize somehow
Signer.update(Message, 0, Message.length);
byte [] Signature = Sign.sign();
Okay, back up and tell us what you want. Are you trying to get privacy by protecting the contents of the message, or guarantee authenticity by showing that the message really came from the originator?
If you're looking for privacy, RSA isn't the way to go: use RSA to generate a private/public pair, and then use them to excahnge keys -- or exchange keys out of band. Use a streaming algorithm like AES to encrypt the message.
If you just want signature to show the message was originated by who you think it was, then have a look at the Wiki article on digital signature -- it's reasonably straightforward.