Open tarac99 opened 4 months ago
The first thing that comes to mind is whether the go client is setting any metadata that the server is reading. Can you check what metadata the server receives when queried by the go client?
I added a JWT to the header and that server is able to see.
let headers: HPACKHeaders = ["x-id-token": jwt]
let callOptions = CallOptions(customMetadata: headers, timeLimit: .timeout(.seconds(20)))
...
self.grpcClient = MyActivationServiceNIOClient(channel: channel, defaultCallOptions: callOptions)
When I printed the Identity
object in server side, I see :
&{jdoe@foo.io https://afoo.okta.com/oauth2/asulk9kg0WgF61JKS5d6 map[some-access-group1:{} some-access-group2:{} some-access-group3:{} some-access-group4:{}] <nil> []}
The nil
at the end is the certificate
type. With Go client I get the cert pointer there and it is not nil
.
Does that mean I have to send the x509 client cert as a custom metadata / HPACKHeaders
? Not sure how to do that ?
With Go client I get the cert pointer there and it is not
nil
. Does that mean I have to send the x509 client cert as a custom metadata /HPACKHeaders
? Not sure how to do that ?
I'm not sure to be honest, I don't know how the Go server decides whether extract the certificate. If you can figure that bit out then hopefully we can understand what the Swift client isn't doing.
@glbrntt First of all thanks for looking into it. I looked into the code bit more to see how the Go server looks for certs in the request context.
import (
"context"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/peer"
)
func mTLSFromContext(ctx context.Context) *Certificate {
p, ok := peer.FromContext(ctx)
if !ok {
return nil
}
tlsInfo, _ := p.AuthInfo.(credentials.TLSInfo)
certs := tlsInfo.State.PeerCertificates
if len(certs) == 0 {
return nil
}
return parseCertificate(certs[0])
}
Basically it uses https://pkg.go.dev/google.golang.org/grpc/peer#FromContext to extract the Peer
object which contains the creds. Each cert
is of *x509.Certificate
type. So one thing is clear - the server is not looking the certificate from the request's metadata (which makes sense as it is for some light weight headers). Is there anything we can do from Swift client to add to this request context ? or is this model incompatible with grpc-swift client currently ?
The code in OP looks wrong. Specifically, this call:
let tlsConfig = GRPCTLSConfiguration.makeClientConfigurationBackedByNIOSSL(certificateChain: [certificateChain], trustRoots: trustRoots, certificateVerification: .fullVerification)
You aren't providing a private key here. This cannot successfully present a cert to the server, so presumably you aren't.
@Lukasa Maybe thats the issue. The reason I dont have the private key set is that the client certificate is hardware-backed i.e the private key is in Secure Enclave and not available on disk. I can see in Mac logs, the CryptoTokenKit extension running on the Mac provides the private key ref for signing when the cert is accessed (for example mTLS in browser). I was hoping it can still send the certificate(with the public key) from the Keychain since it can find it with SecItemCopyMatching()
.
It looks like we can’t convert a SecKey
(private key ref from Secure Enclave) to a NIOSSLPrivateKey
. SecKey
represents a key ref in a secure context where the actual key material is not meant to be exported or exposed, providing added security while NIOSSLPrivateKey
requires key material in a format like PEM or DER.
What do you suggest ? In future will hardware-backed client certs be supported ?
You can do this using NIOSSLCustomPrivateKey
.
What are you trying to achieve?
I want to include a client certificate in the gRPC context in the request. My server(in Golang) looks for this certificate along with some custom metadata:
Note: This works fine with a Linux Golang based client gRPC connection.
What have you tried so far?
I included the client certificate in the
TLSConfiguration
but the context in the server end doesn't have any certificate:What am I missing ?