Temporarily store relevant public and private keys (after a successful key-exchange) in a WebAPI scenario - asp.net-mvc

I have written an API to implement the Diffie Hellman key exchange and now wanted to store my "server" private/public keys in a secure and expirable way so that I can access them on the subsequent api calls.
The idea is to have the client initiate the exchange of the keys, then have the client send me an encrypted message based on his privatekey that I can decrypt on the server because I have my related privateKey.
My problem is where to store securely the list of various public/private server keys in a way that I could reuse it when referenced at later stage.
My initial idea would be to have some sort of static list that contains
KeyID (Guid - autogenerated)
Expiration Date (DateTime - now + 20 seconds)
ServerPublicKey (byte[] - resulted from successful key exchange)
ServerPrivateKey (byte[] - resulted from successful key exchange)
On completion of the KeyExchange I could return a KeyID to the client caller and then on subsequent API calls, he should pass an encrypted message using his clientPrivate key. By also passing me the KeyID, I could quickly identify what is the related serverPrivateKey and Decrypt the message.
Is this the right and most secure approach?

Related

How to protect SecIdentity with biometrics/kSecAttrAccessControl?

Here's the repo where this can be easily reproduced: https://github.com/haemi/SecIdentityBiometrics
What I try to achieve is generating a RSA 2048bit key pair, where the public key is sent to the server. The server sends a x.509 certificate back.
With my private key and the certificate, I need to create a SecIdentity (used for URLAuthChallenge). This works when I do not use biometrics to protect the private key.
But as soon as I add biometrics, I'm unable to fetch the SecIdentity. I tried with two variants:
enum Variant {
case writeIdentityWithBiometrics
case writePrivateKeyWithBiometrics
}
writeIdentityWithBiometrics
Here I add kSecAttrAccessControl to the private key directly and try to read the SecIdentity in the end.
writePrivateKeyWithBiometrics
Here I first add the keypair, then the certificate to the keychain - everything without biometrics. Then I read the SecIdentity and try to add it with kSecAttrAccessControl.

How to create a "challenge" for my Cloud Functions server

I'm trying use Apple's new DeviceCheck API to verify that network calls in my app are actually coming from an uncompromised version of my app.
Documentation
After successfully verifying a key’s attestation, your server can
require the app to assert its legitimacy for any or all future server
requests. The app does this by signing the request. In the app, first
obtain a unique, one-time challenge from the server. You use a
challenge here, like for attestation, to avoid replay attacks. Then
combine the challenge with the server request to create a hash:
let challenge = <# A string from your server #>
let request = [ "action": "getGameLevel",
"levelId": "1234",
"challenge": challenge ]
guard let clientData = try? JSONEncoder().encode(request) else { return }
let clientDataHash = Data(SHA256.hash(data: clientData))
Use this hash and the key identifier that you generated earlier to
create an assertion object by calling the
generateAssertion(_:clientDataHash:completionHandler:) method:
service.generateAssertion(keyId, clientDataHash: clientDataHash) { assertion, error in
guard error == nil else { /* Handle the error. */ }
// Send the assertion and request to your server.
}
I'm trying to add this assertion functionality to my Swift function, which is a helper function that calls a Firebase Cloud Function.
I want the assertion object to be passed as data to the Cloud Function, to verify that the Cloud Function is being called from an uncompromised version of my app:
func callFunction(name: String, data: [String:Any?], completion: #escaping (HTTPSCallableResult?, Error?)->()){
var functions = Functions.functions()
functions.httpsCallable(name).call(data){ (result, error) in
completion(result, error)
}
}
(Example of callFunction() being used below):
let data: [String:Any?] = [
"gameId": self.game?.id,
"answer": answer,
"answeredAt": Date().millisecondsSince1970
]
callFunction(name: "answerQuestion", data: data){ res, err in
print("Submitted answer: \(res.debugDescription) | Error: \(err)")
if let err = err {
self.game?.question?.state = .initial
}
}
To generate the assertion object to send to my server (cloud function), it requires me to generate a challenge as stated above. However I'm not sure how to generate this challenge.
Apple says it should be "A string from your server". But I'm not sure what the string should be. Is it meant to be a dynamic string based on the user's UID? A Base64-encoded string of the user ID and a static secret string? And when I try to retrieve this string from the server, the user will just be able to read it as they can see incoming network JSON (I presume I would retrieve the string with a Cloud Function call) - so it seems pointless as it's not a secret string anymore?
Any idea how I can make the challenge work securely?
The point of the challenge is to avoid replay attacks, so it can be any randomised string. A UUID would be fine. It doesn't need to be a secret.
The challenge string is combined with the transaction data and a hash is generated. You send the hash to and you send that to generateAssertion and receive the assertion object. You then send this to your server along with the request data.
Now your server can combine the received request data with the challenge (which it knows, since it sent it to the client initially), generate the same hash and validate the attestation.
The server-side attestation article provides detail on the challenge data:
Provide a Challenge
Every time your app needs to communicate attestation data to your server, the app first asks the server for a unique, one-time challenge. App Attest integrates this challenge into the objects that it provides, and that your app sends back to your server for validation. This makes it harder for an attacker to implement a replay attack.
When asked for a challenge, provide your app with a randomized data value, and remember the value for use when verifying the corresponding attestation or assertion objects sent by the client. How you use the challenge data depends on the kind of object that you need to validate.

Incomplete Linkedin OAuth 2.0 access token response

My question is about OAuth2 access token response from Linkedin api. When I'm trying to get this token I recieve the following response:
{"access_token":"...","expires_in":...}
But the thing is that according to OAuth2 documentation (in 5.1 paragraph) there should be at least one more required parameter - "token_type".
So the question is: could it be somehow customized so the linkedin API will return this parameter with access token response or it is just a departure from the rule and this parameter won't be returned?
Thanks in advance.
I have run into the same issue. According to LinkedIn Docs:
A successful Access Token request will return a JSON object containing the following fields:
access_token — The access token for the user. This value must be kept secure, as per your agreement to the API Terms of Use.
expires_in — The number of seconds remaining, from the time it was requested, before the token will expire. Currently, all access tokens are issued with a 60 day lifespan.
they respond with
{"access_token":"...","expires_in":...}
which violates the standard.
Currently I am using Spring Security 5.0.3 and to fix the issue, I had to monkeypatch one class:
com.nimbusds.oauth2.sdk.token.BearerAccessToken
I will not post the whole class, only a significant part:
public static BearerAccessToken parse(final JSONObject jsonObject)
throws ParseException {
// Parse and verify type
AccessTokenType tokenType;
try {
tokenType = new AccessTokenType(JSONObjectUtils.getString(jsonObject, "token_type"));
} catch (ParseException ex) {
tokenType = AccessTokenType.BEARER;
}
if (!tokenType.equals(AccessTokenType.BEARER))
throw new ParseException("Token type must be \"Bearer\"");
//...
}
I hoped to get answer from Linkedin member since they stated on their site that stackoverflow is a proper place for asking such questions. But since there is no answer from them and I didn't find any relevant information regarding this question I believe that it is just the way they implemented OAuth 2.0 protocol.

OVER_QUERY_LIMIT in simple SPGooglePlacesAutocompleteQuery request

I'm trying to create simple autocomplete UI widget:
self.autocompleteQuery = [[SPGooglePlacesAutocompleteQuery alloc] initWithApiKey:[GlobalConfig sharedInstance].kGoogleBrowserKey];
self.autocompleteQuery.language = kFFAutocompleteQueryLanguage;
self.autocompleteQuery.types = SPPlaceTypeAddress;
self.autocompleteQuery.location = [FFAppDataHelper coordinatesForMoscow];
self.autocompleteQuery.radius = [GlobalConfig sharedInstance].kMoscowRadius;
self.autocompleteQuery.countryCode = #"RU";
Then I want to get all values for input string from UITextField on 'valueChanged' event:
self.autocompleteQuery.input = [streetName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
[self.autocompleteQuery fetchPlaces:^(NSArray *places, NSError *error) {
[self loaderStopAnimating];
if (places) {
//do some stuff
} else {
FFError *detectedError = [FFError errorWithNSError:error];
[self showErrorMessage:[detectedError errorMessage]];
}
}];
So when I type 'k' I get response with streets and if I add next char to my text field I receive OVER_QUERY_LIMIT every time. I've tried it on simulator and devices with the same result. And it starts working again after 10-20 sec. I don't use loops or smth similar, I just want to get suggestions for input string in real time, but I can't get it because of error. What should I do to avoid it?
For the web service request, use a key.
Follow this :https://developers.google.com/maps/documentation/geocoding/start#api_key
Doc says:
Note: Maps for Work users must include client and signature parameters with their requests instead of a key.
All Geocoding API applications should use an API key. Including a key in your request:
Allows you to monitor your application's API usage in the Google Developers Console. Enables per-key instead of per-IP-address quota limits. Ensures that Google can contact you about your application if necessary. The Geocoding API uses an API key to identify your application. API keys are managed through the Google APIs console. To create your key:
Visit the APIs console at Google Developers Console and log in with your Google Account. Click the Services link from the left-hand menu in the APIs Console, then activate the Geocoding API service. Once the service has been activated, your API key is available from the API > Access page, in the Simple API Access section. Geocoding API applications use the Key for server apps. To specify a key in your request, include it as the value of a key parameter.
Note: By default, a key can be used from any server. We strongly recommend that you restrict the use of your key by IP address to servers that you administer. You can specify which IP addresses are allowed to use your API key by clicking the Edit allowed referers... link in the API console.
Note: HTTPS is enforced for requests that include an API key.

What is the cryptogram in the ApplePay token?

I'm currently working on ApplePay and we are decrypting the token on our own server.
The decryption of token is done but there is a few things I don't quite understand. From the Getting-Started-with-Apple-Pay we know that:
"The payment token encapsulates the
information needed to complete a payment
transaction, including the device-specific
account number, the amount, and a unique,
one-time-use cryptogram."
But from the Payment Token Format Reference, there are 8 things contained in a token:
applicationPrimaryAccountNumber
applicationExpirationDate
currencyCode
transactionAmount
cardholderName
deviceManufacturerIdentifier
paymentDataType
paymentData
We get the accountNumber and the amount, but which one of those is the cryptogram?
Is it the last one, paymentData, since the other 7 really don't look like cryptogram? If not, how could we get this cryptogram?
I'd also like to ask what should we do after we get the cryptogram? Should we send the cryptogram and accountNumber to the acquirer?
Thank you!
If you look at the Payment Token Format
You will see that the token contains a paymentDataType string and a paymentData dictionary.
If the paymentDataType is "3DSecure" then the paymentData dictionary will contain a key onlinePaymentCryptogram which is the cryptogram string.
This must be submitted to your payment gateway if you are submitting a 3-D Secure transaction.

Resources