How does openiddict select a signing certificate from multiple certificates - openiddict

I looked at the kernel code and didn't find anything similar. Perhaps this function is not provided in openiddict. I would like to know this clearly.

Okay, after a long search, I managed to find an algorithm for selecting a certificate from several.
static int Compare(SecurityKey left, SecurityKey right) => (left, right) switch
{
// If the two keys refer to the same instances, return 0.
(SecurityKey first, SecurityKey second) when ReferenceEquals(first, second) => 0,
// If one of the keys is a symmetric key, prefer it to the other one.
(SymmetricSecurityKey, SymmetricSecurityKey) => 0,
(SymmetricSecurityKey, SecurityKey) => -1,
(SecurityKey, SymmetricSecurityKey) => 1,
// If one of the keys is backed by a X.509 certificate, don't prefer it if it's not valid yet.
(X509SecurityKey first, SecurityKey) when first.Certificate.NotBefore > DateTime.Now => 1,
(SecurityKey, X509SecurityKey second) when second.Certificate.NotBefore > DateTime.Now => 1,
// If the two keys are backed by a X.509 certificate, prefer the one with the furthest expiration date.
(X509SecurityKey first, X509SecurityKey second) => -first.Certificate.NotAfter.CompareTo(second.Certificate.NotAfter),
// If one of the keys is backed by a X.509 certificate, prefer the X.509 security key.
(X509SecurityKey, SecurityKey) => -1,
(SecurityKey, X509SecurityKey) => 1,
// If the two keys are not backed by a X.509 certificate, none should be preferred to the other.
(SecurityKey, SecurityKey) => 0
};
Link

Related

What is actually happening when I call JWT.verify

I've came across two conflicting pieces of information and was wondering if someone could clarify what is happening. As far as I can tell jwt.sign is using a SHA algorithm to create a unique signature which, I saw on computerphile, is not a reversible process. On their video they explained that cryptographic signatures are not the same thing as encryption, as only encryption is a reversible process.
On that note, once I've created this unique signature and then enter it into jwt.verify as an argument, the code example at the bottom seems to reverse it like an encryption and assign the payload to a variable. So is this Bearer token/signature actually encryption? Also what part of the signature is used for verification, is the header and payload decrypted and ran through the signature process again to check it against the attached signature? Can someone please clarify this process because everything online is very wishy washy and or conflicting about the specifics of what is occurring.
function authenticateToken(req, res, next){
const authHeader = req.headers['authorization']
console.log(req.headers)
const token = authHeader && authHeader.split(' ')[1]
if(token == null) return res.sendStatus(401);
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
if(err) return res.sendStatus(403)
console.log('user ', user)
req.user = user;
next();
})
}
Okay so what i've not understood, is that only the last section of the JsonWebToken represents the hash signature. When the format of the token is as follows xxxx.yyyy.zzzz - where x is the header, y is the payload and z is signature - only z actually represents the SHA key.
When you've authorised the login, the signature is created, with the secret. Which is then checked during authentication, by using the base64 data in x and y. I thought the whole thing was SHA.

Yubico / Credential ID length mis-match between Attestation and Assertion

I am using a Yubico security key with an AAGUID of ff8a011f3-8c0a-4d15-8006-17111f9edc7d (Security Key By Yubico v5.1) to perform password-less authentication for my web application. When I create/register a new credential I use the attribute "requireResidentKey = true" in the authenticatorSelection section of the create credential options:
...
authenticatorSelection: {
requireResidentKey: true, // note the use of this attribute set to true
userVerification: options.userVerification,
authenticatorAttachment: options.authenticatorAttachment,
},
...
The Attestation data that is returned contains a credential id of 16 bytes:
return navigator.credentials.create({
publicKey: credentialCreateOptions,
signal: authAbortSignal,
}).then(rawAttestation => {
const attestation = {
id: rawAttestation.id, // returned 16 byte credential id
type: rawAttestation.type,
clientDataJSON: arrayBufferToString(rawAttestation.response.clientDataJSON),
attestationObject: base64encode(rawAttestation.response.attestationObject),
extensionData: base64encode(rawAttestation.getClientExtensionResults()),
}
return attestation
}).catch((error) => {
console.log(error)
if (error === 'AbortError') {
throw new Error('the operation was aborted')
} else {
throw new Error('the operation was cancelled')
}
})
so for example I will receive a 16 byte base64url id looking something like: AUpf0KmNJrRluGG-65D54Q
I then save the resulting credential using this 16 byte id as the key. When I use the Yubico key to sign-in the Assertion data returned contains this same 16 byte credential id:
return navigator.credentials.get({
publicKey: credentialRequestOptions,
}).then(rawAssertion => {
const assertion = {
id: rawAssertion.id, // same 16 byte credential id as returned from create credential
type: rawAssertion.type,
clientDataJSON: arrayBufferToString(rawAssertion.response.clientDataJSON),
authenticatorData: base64encode(rawAssertion.response.authenticatorData),
signature: base64encode(rawAssertion.response.signature),
userHandle: base64encode(rawAssertion.response.userHandle),
}
return assertion
}).catch((error) => {
throw new Error(error.message)
})
I can then use this credential id to retrieve my stored credential data and verify the assertion. So all is good so far...
I was then reading the W3C Editor's Draft on Web Authentication: An API for accessing Public Key Credentials Level 2 and noted that the "requireResidentKey" has been deprecated in favour of a "residentKey" attribute that takes an enum value:
requireResidentKey, of type boolean, defaulting to false
Note: This member is retained for backwards compatibility with WebAuthn Level 1 but is deprecated in favour of residentKey. requireResidentKey is ignored if the caller supplies residentKey and the latter is understood by the client. Otherwise, requireResidentKey's value is used. Note that requireResidentKey's value defaults to false.
If used in absence of residentKey, it describes the Relying Party's requirements regarding resident credentials. If requireResidentKey is set to true, the authenticator MUST create a client-side-resident public key credential source when creating a public key credential.
residentKey, of type ResidentKeyRequirement
Note: This member supersedes requireResidentKey. If both are present and the client understands residentKey, then residentKey is used and requireResidentKey is ignored.
See ResidentKeyRequirement for the description of residentKey's values and semantics.
So I changed the requireResidentKey to residentKey with the enum value of "required" as shown:
authenticatorSelection: {
residentKey: 'required', // now using residentKey attribute
requireResidentKey: true,
userVerification: options.userVerification,
authenticatorAttachment: options.authenticatorAttachment,
},
Now when I create a new credential I get a 64 byte credential ID returned. This would have been fine except that when I use the Yubico security key to sign-in I get a 16 byte credential ID returned which clearly does not match with the 64 byte one I saved during the create credentials stage.
Interestingly, when I tried using both requireResidentKey = true and residentKey = 'required' together I got my 16 byte credential ID returned for both Attestation and Assertion.
Could this be that the new residentKey attribute is not supported? If so why did I get a 64 byte credential id? Is this the length of non-resident credential id's perhaps?
My code is back working using the old requireResidentKey attribute but I would love to know what was going on here and if the residentKey attribute will be supported in newer Yubikeys?

S3 upload image file security issue

I'm reading the following tutorial:
https://devcenter.heroku.com/articles/s3-upload-node#uploading-directly-to-s3
The first step is when a user chooses an image
function s3_upload(){
var s3upload = new S3Upload({
file_dom_selector: '#files',
s3_sign_put_url: '/sign_s3',
onProgress: function(percent, message) {
// some code
},
onFinishS3Put: function(public_url) {
// some cde
},
onError: function(status) {
// somecode
}
});
}
Now the s3_sign_put_url refers to a server side function that returns
app.get('/sign_s3', function(req, res){
...
// calculates signature (signature variable)
// sets expiration time (expires variable)
var credentials = {
signed_request: url+"?AWSAccessKeyId="+AWS_ACCESS_KEY+"&Expires="+expires+"&Signature="+signature,
url: url
};
...
}
If I already calculated a signature as function of (AWS_SECRET_KEY) like this:
var signature = crypto.createHmac('sha1', AWS_SECRET_KEY).update(put_request).digest('base64');
signature = encodeURIComponent(signature.trim());
signature = signature.replace('%2B','+');
Question:
Why do I have to pass the AWS_SECRET_KEY value as part of the credentials object returned by s3_sign function? why isn't the signature enough to be returned? isn't this a security issue?
You aren't doing that.
The returned credentials contain the AWS_ACCESS_KEY, not the AWS_SECRET_KEY.
The access key is analogous to a username... it's needed by S3 so that it knows who created the signature. From this, S3 looks up the associated secret key internally, creates a signature for the request, and if it's the same signature as the one you generated and the supplied access key is associated with a user with permission to perform the operation, it succeeds.
The access key and secret key work as a pair, and one can't reasonably be derived from the other; the access key is not considered private, while the secret key is.

load SSL CA's from string as opposed to from file

I'd like to store my CAs in a string inside my binary instead of loading it in via SSL_CTX_load_verify_locations, which takes in file paths and folder paths.
However, I can't find a method that lets me take in a string.
I don't know of a documented way to do this. The only way I know of is to roll your own verification, e.g. create a store using X509_STORE_CTX_new, add the trusted CAs with X509_STORE_CTX_trusted_stack, add the certificate with X509_STORE_CTX_set_cert add some other chain certificates and CRLs with similar function and finally call X509_verify_cert on the X509_STORE_CTX.
OK I figured out how to do it. OpenSSL has a bunch of ways to deal with loading certs, and many of them add the certs to the non-trusted chain. You must use SSL_CTX_get_cert_store and X509_STORE_add_cert in conjunction. These two functions take in X509 pointers, which can be created from a raw c string. Since the documentation is pretty much non-existent for these two functions, so I figured I'd share the code here:
Edit: this is basically Steffen Ulrich's method except using X509_STORE_add_cert.
#include <openssl/ssl.h>
#include <openssl/bio.h>
#include <openssl/x509.h>
void read_cert_into_ctx(istream &some_stream, SSL_CTX *ctx) {
// Add a stream of PEM formatted certificate strings to the trusted store
// of the ctx.
string line;
string buffer;
while(getline(some_stream, line)) {
buffer.append(line);
buffer.append("\n");
if(line == "-----END CERTIFICATE-----") {
BIO *bio;
X509 *certificate;
bio = BIO_new(BIO_s_mem());
BIO_puts(bio, buffer.c_str());
certificate = PEM_read_bio_X509(bio, NULL, 0, NULL);
if(certificate == NULL)
throw std::runtime_error("could not add certificate to trusted\
CAs");
X509_STORE* store = SSL_CTX_get_cert_store(ctx);
int result = X509_STORE_add_cert(store, certificate);
BIO_free(bio);
buffer = "";
}
}
}

Custom X509CertificateValidator Check Requestor Against CN

I have a custom X509CertificateValidator that currently validates a series of rules against a certificate presented for a WCF SOAP message.
There is a requirement to check the CN name on the certificate against the domain the certificate is being presented by, but I'm not aware that I have access to the request from within the X509CertificateValidator.
Is there any way to check that the certificate matches the request domain?
I haven't found any way to do this from within the X509CertificateValidator, but it is possible within the service.
Here is my first cut - I will be refining it to make it more elegant, but this works.
private static void ValidateRequestIsFromCertificateDomain()
{
RemoteEndpointMessageProperty endpointProperty = OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
var claimSet = OperationContext.Current.ServiceSecurityContext.AuthorizationContext.ClaimSets[0] as X509CertificateClaimSet;
string domain = claimSet.X509Certificate.GetNameInfo(X509NameType.DnsName, false);
var resolvedAddress = System.Net.Dns.GetHostAddresses(domain);
if (resolvedAddress.Count() == 0 || endpointProperty.Address != resolvedAddress[0].ToString())
{
throw new SecurityException("Client address mismatch");
}
}
This isn't really required because the client encrypts data with its private key that can only be decrypted with its public key - so you know the certificate is being presented by the real client.
However, if you are given this as an integration requirement as I have been, this may be useful to you.