GordianKnot Asymmetric Agreements
Overview
Agreements are supported via the GordianAgreementFactory interface.
A GordianAgreement is created at the client by the factory using a GordianAgreementParams object and this will produce a ClientHello message to be sent to the server. The receiving factory will take the ClientHello message and create a second GordianAgreement object that will process the client message. For non-anonymous agreements, a further ServerHello message is generated to be sent back to the client for processing. In turn, for confirm agreements, another ClientConfirm message is created for sending back to the server for processing. The result will be available from the agreement once all processing is complete for that object.
Agreements are always targeted by a Client towards a Server.
The resultTypes can be any of the following
| ResultType | Description |
|---|---|
| GordianFactoryType | The underlying agreement is used as the input to the HKDF algorithm to generate a new personalised GordianFactory |
| GordianKeySetSpec | The underlying agreement is used as the input to the HKDF algorithm to generate a new GordianKeySet of the specified type, belonging to an agreed BC factory. |
| GordianSymCipherSpec | The underlying agreement is used as the input to the HKDF algorithm to generate a new GordianSymCipher Key and InitVector (if required), belonging to an agreed BC factory. A pair of GordianSymCiphers are returned as the result, one for encryption and one for decryption |
| GordianStreamCipherSpec | Similar to GordianSymKeySpec. |
| Integer | The underlying agreement is used as the input to the HKDF algorithm to generate a byte array of the desired length |
Parameters
The following parameters are available
| Parameter | Description |
|---|---|
| ServerCertificate | The certificate of the server keyPair |
| ClientCertificate | The certificate of the client keyPair |
| SignerCertificate | The certificate of the signing keyPair |
| SignerAlgorithm | The signatureAlgorithm to be used by the signer. If not specified, then the default signature algorithm for the signer keyPair is used |
| AdditionalData | AdditionalData to be added to personalize the agreement. This is not transmitted and must be supplied independently and identically by client and server. It is only available via the KDF and therefore the KDF must not be NONE for this parameter to be accepted. |
MiniCertificates
Agreements are driven by certificates, but these are not available for newly created keyPairs, and in any case are heavyweight to generate. Therefore miniCertificates are available to provide lightweight certificates that can drive agreements.
The only contents or mini-certificates are the keyPair, the defined usage and the X500Name. The Name does not need to have any meaning, but can convey whatever information is required by the application.
Anonymous Agreement
An anonymous agreement is an agreement where the only certificate required is that of the server. It must be specified in the parameters prior to the agreement being created. If a keyPair is required at the server, then it is an ephemeral anonymous keyPair.
There are two type of anonymous agreements, which produce different types of messages. The KEM agreement produces an encapsulated secret, whereas the ANON agreement produces an ephemeral keyPair.
- The client creates a ClientHello message to be sent to the server, and immediately enters the RESULT_AVAILABLE state and makes the result available to the caller.
- The server will parse the incoming ClientHello message and create a corresponding GordianAgreementParams object. It cannot immediately continue, since it does not know the privateKey of the server, so it enters the AWAITING_SERVERPRIVATE state.
- The server is expected to look at the serverCertificate contained in the parameters, and replace it with a certificate that contains the privateKey of the server, before updating the agreement.
- The server can now calculate the result and enters the RESULT_AVAILABLE state making the result available to the caller
- A random client InitVector is included in the process to randomize the result
Sample
/* Access factory */
final GordianFactory myBaseFactory = GordianGenerator.createFactory();
final GordianAsyncFactory myAsyncFactory = myBase.getAsyncFactory();
final GordianKeyPairFactory myKeyPairFactory = myAsyncFactory.getKeyPairFactory();
final GordianAgreementFactory myAgreementFactory = myAsyncFactory.getAgreementFactory();
/* Access keyPairGenerator and create pair */
final GordianKeyPairSpec mySpec = GordianKeyPairSpecBuilder.dh(GordianDHGroup.FFDHE2048);
final GordianKeyPairGenerator myGenerator = myKeyPairFactory.getKeyPairGenerator(mySpec);
final GordianKeyPair myTarget = myGenerator.generateKeyPair();
/* Create mini-certificates */
final X500Name myServerName = ...
final GordianCertificate myTargetCert = myAgreementFactory.newMiniCertificate(myServerName, myTarget,
new GordianKeyPairUsage(GordianKeyPairUse.AGREEMENT));
/* Create the client hello */
final GordianAgreementSpec myAgreeSpec = GordianAgreementSpecBuilder.anon(mySpec, GordianKDFType.SHA256KDF);
final GordianKeySetSpec myKeySetSpec = new GordianKeySetSpec(GordianLength.LEN_256);
GordianAgreementParams myParams = myAgreementFactory.newAgreementParams(mySpec, myKeySetSpec)
.setServerCertificate(myTargetCert);
final GordianAgreement mySender = myAgrees.createAgreement(myParams);
final byte[] myClientHello = mySender.nextMessage();
final GordianKeySet myClientKeySet = mySender.getKeySetResult();
/* Handle receipt at server */
final GordianAgreement myResponder = myAgreementFactory.parseAgreementMessage(myClientHello);
myParams = myResponder.getAgreementParams()
.setServerCertificate(myTargetCert);
myResponder.updateParams(myParams);
final GordianKeySet myServerKeySet = myResponder.getKeySetResult();
Handshake Agreement
A handshake agreement is an agreement where certificates are required for both the client and the server. Both certificates must be specified in the parameters prior to the agreement being created. The clientCertificate must contain the private key of the keyPair.
A BASIC agreement, just utilizes the keyPairs from the certificates. Three further agreement types - UNIFIED, MQV and SM2 use additional ephemeral keyPairs, but differ in internal implementation
- The client creates a ClientHello message to be sent to the server, and immediately enters the AWAITING_SERVERHELLO state. The agreement is added to the factory cache.
- The server will parse the incoming ClientHello message as per the Anonymous case. The only difference is that a ServerHello message is created to send to the client.
- It is possible for the server to reject the agreement, perhaps because it did not recognize the serverCertificate or accept the clientCertificate. It this case it can set an errorMessage which will cause the result to be an Exception object. The error will be transmitted in the ServerHello message so that it becomes the result at the client as well.
- The client will parse the incoming ServerHello message and will look up the agreement in the cache. If no matching agreement is found then the message is rejected.
- The client can now calculate the result and deletes the agreement from the cache and enters the RESULT_AVAILABLE state making the result available to the caller
- Random client and server InitVectors are included in the process to randomize the result
Sample
/* Access factory */
final GordianFactory myBaseFactory = GordianGenerator.createFactory();
final GordianAsyncFactory myAsyncFactory = myBase.getAsyncFactory();
final GordianKeyPairFactory myKeyPairFactory = myAsyncFactory.getKeyPairFactory();
final GordianAgreementFactory myAgreementFactory = myAsyncFactory.getAgreementFactory();
/* Access keyPairGenerator and create pairs */
final GordianKeyPairSpec mySpec = GordianKeyPairSpecBuilder.dh(GordianDHGroup.FFDHE2048);
final GordianKeyPairGenerator myGenerator = myKeyPairFactory.getKeyPairGenerator(mySpec);
final GordianKeyPair mySource = myGenerator.generateKeyPair();
final GordianKeyPair myTarget = myGenerator.generateKeyPair();
/* Create mini-certificates */
final X500Name myClientName = ...
final GordianCertificate mySourceCert = myAgreementFactory.newMiniCertificate(myClientName, mySource,
new GordianKeyPairUsage(GordianKeyPairUse.AGREEMENT));
final X500Name myServerName = ...
final GordianCertificate myTargetCert = myAgreementFactory.newMiniCertificate(myServerName, myTarget,
new GordianKeyPairUsage(GordianKeyPairUse.AGREEMENT));
/* Create the client hello */
final GordianAgreementSpec myAgreeSpec = GordianAgreementSpecBuilder.unified(mySpec, GordianKDFType.SHA256KDF);
final GordianKeySetSpec myKeySetSpec = new GordianKeySetSpec(GordianLength.LEN_256);
GordianAgreementParams myParams = myAgreementFactory.newAgreementParams(mySpec, myKeySetSpec)
.setClientCertificate(mySourceCert)
.setServerCertificate(myTargetCert);
final GordianAgreement mySender = myAgrees.createAgreement(myParams);
final byte[] myClientHello = mySender.nextMessage();
/* Handle receipt at server */
final GordianAgreement myResponder = myAgreementFactory.parseAgreementMessage(myClientHello);
myParams = myResponder.getAgreementParams()
.setServerCertificate(myTargetCert);
myResponder.updateParams(myParams);
final byte[] myServerHello = myResponder.nextMessage();
final GordianKeySet myServerKeySet = myResponder.getKeySetResult();
/* Process ServerHello at Client (will receive existing agreement) */
final GordianAgreement myClient = myAgreementFactory.parseAgreementMessage(myServerHello);
final GordianKeySet myClientKeySet = myClient.getKeySetResult();
Confirmation Agreement
A confirmation agreement is simply a handshake agreement with an additional step. It requires the use of ephemeral keyPairs, so is not available for BASIC agreements
- The client creates a ClientHello message the same as for a handshake agreement.
- The server processes the ClientHello as before but rather than making the result available, it enters the AWAITING_CLIENTCONFIRM state. The agreement is added to the factory cache.
- The client will parse the incoming ServerHello message as per a handshake agreement, with the addition that a confirmation process is performed and a ClientConfirm message is created to send to the server. If the server rejected the message then no ClientConfirmis created.
- If confirmation fails at the client then the result will be an exception object. The error will be transmitted in the ClientConfirm message so that it becomes the result at the server as well.
- The server will parse the incoming ClientConfirm message and will look up the agreement in the cache. If no matching agreement is found then the message is rejected.
- The server can now process the confirmation and calculate the result and deletes the agreement from the cache and enters the RESULT_AVAILABLE state making the result available to the caller
- If confirmation fails at the server then the result will be an exception object.
- The SM2 confirm is defined by the SM2 algorithm. For UNIFIED and MQV agreements the confirmation tags are calculated over the well-known and ephemeral keyPairs using an hMac keyed by the agreed result.
Sample
/* Access factory */
final GordianFactory myBaseFactory = GordianGenerator.createFactory();
final GordianAsyncFactory myAsyncFactory = myBase.getAsyncFactory();
final GordianKeyPairFactory myKeyPairFactory = myAsyncFactory.getKeyPairFactory();
final GordianAgreementFactory myAgreementFactory = myAsyncFactory.getAgreementFactory();
/* Access keyPairGenerator and create pairs */
final GordianKeyPairSpec mySpec = GordianKeyPairSpecBuilder.dh(GordianDHGroup.FFDHE2048);
final GordianKeyPairGenerator myGenerator = myKeyPairFactory.getKeyPairGenerator(mySpec);
final GordianKeyPair mySource = myGenerator.generateKeyPair();
final GordianKeyPair myTarget = myGenerator.generateKeyPair();
/* Create mini-certificates */
final X500Name myClientName = ...
final GordianCertificate mySourceCert = myAgreementFactory.newMiniCertificate(myClientName, mySource,
new GordianKeyPairUsage(GordianKeyPairUse.AGREEMENT));
final X500Name myServerName = ...
final GordianCertificate myTargetCert = myAgreementFactory.newMiniCertificate(myServerName, myTarget,
new GordianKeyPairUsage(GordianKeyPairUse.AGREEMENT));
/* Create the client hello */
final GordianAgreementSpec myAgreeSpec = GordianAgreementSpecBuilder.unifiedConfirm(mySpec, GordianKDFType.SHA256KDF);
final GordianKeySetSpec myKeySetSpec = new GordianKeySetSpec(GordianLength.LEN_256);
GordianAgreementParams myParams = myAgreementFactory.newAgreementParams(mySpec, myKeySetSpec)
.setClientCertificate(mySourceCert)
.setServerCertificate(myTargetCert);
final GordianAgreement mySender = myAgrees.createAgreement(myParams);
final byte[] myClientHello = mySender.nextMessage();
/* Handle receipt at server */
final GordianAgreement myResponder = myAgreementFactory.parseAgreementMessage(myClientHello);
myParams = myResponder.getAgreementParams()
.setServerCertificate(myTargetCert);
myResponder.updateParams(myParams);
final byte[] myServerHello = myResponder.nextMessage();
/* Process ServerHello at Client (will receive existing agreement) */
final GordianAgreement myClient = myAgrees.parseAgreementMessage(myServerHello);
final byte[] myClientConfirm = myClient.nextMessage();
final GordianKeySet myClientKeySet = myClient.getKeySetResult();
/* Process ClientConfirm at Server (will receive existing agreement) */
final GordianAgreement myServer = myAgreementFactory.parseAgreementMessage(myClientConfirm);
final GordianKeySet myServerKeySet = myServer.getKeySetResult();
Signed Agreement
A signed agreement is simply a handshake agreement used with ephemeral keyPairs with the results signed by a known signer. No client/server certificates are required. It is signified by an agreement type of SIGNED.
- The client creates a ClientHello message the same as for a handshake agreement.
- The server processes the ClientHello but rather than waiting for a serverCertificate with a private key, it waits for signerCertificate with a private key.
- It is possible to specify a default signerCertificate and signatureAlgorithm in the factory, and this will be set as a default in the parameters, so that the server does not have to override on a per-message basis.
- The server will then sign the results and include the signature and signerCertificate in the ServerHello message to be sent to the client.
- The client will verify the incoming ServerHello message using the signature and signerCertificate, and will proceed as per the handshake agreement.
- If the signature fails, then the result is an Exception object.
- The signature is calculated over the two ephemeral keys and the initVectors
Sample
/* Access factory */
final GordianFactory myBaseFactory = GordianGenerator.createFactory();
final GordianAsyncFactory myAsyncFactory = myBase.getAsyncFactory();
final GordianKeyPairFactory myKeyPairFactory = myAsyncFactory.getKeyPairFactory();
final GordianAgreementFactory myAgreementFactory = myAsyncFactory.getAgreementFactory();
/* Access keyPairGenerator and create pair */
final GordianKeyPairSpec mySpec = GordianKeyPairSpecBuilder.ed25519();
final GordianKeyPairGenerator myGenerator = myKeyPairFactory.getKeyPairGenerator(mySpec);
final GordianKeyPair mySigner = myGenerator.generateKeyPair();
/* Create mini-certificates */
final X500Name myClientName = ...
final GordianCertificate mySignerCert = myAgreementFactory.newMiniCertificate(myClientName, mySigner,
new GordianKeyPairUsage(GordianKeyPairUse.SIGNATURE));
/* Create the client hello */
final GordianAgreementSpec myAgreeSpec = GordianAgreementSpecBuilder.unified(mySpec, GordianKDFType.SHA256KDF);
final GordianKeySetSpec myKeySetSpec = new GordianKeySetSpec(GordianLength.LEN_256);
GordianAgreementParams myParams = myAgreementFactory.newAgreementParams(mySpec, myKeySetSpec);
final GordianAgreement mySender = myAgrees.createAgreement(myParams);
final byte[] myClientHello = mySender.nextMessage();
/* Handle receipt at server */
final GordianAgreement myResponder = myAgreementFactory.parseAgreementMessage(myClientHello);
myParams = myResponder.getAgreementParams()
.setSigner(mySignerCert);
myResponder.updateParams(myParams);
final byte[] myServerHello = myResponder.nextMessage();
final GordianKeySet myServerKeySet = myResponder.getKeySetResult();
/* Process ServerHello at Client (will receive existing agreement) */
final GordianAgreement myClient = myAgreementFactory.parseAgreementMessage(myServerHello);
final GordianKeySet myClientKeySet = myClient.getKeySetResult();
Composite Agreements
Composite agreements may be created by a composite keyPair, as long as each element of the composite keyPair is capable of supporting the desired AgreementSpec. There are three small variations.
- Confirm is not supported for SM2 agreements
- The various individual agreements are used as inputs to the HKDF algorithm to extract a combined agreement secret
- Signed agreements are implemented as a set of Basic Agreements with the combined results signed.
Sample
/* Access factory */
final GordianFactory myBaseFactory = GordianGenerator.createFactory();
final GordianAsyncFactory myAsyncFactory = myBase.getAsyncFactory();
final GordianKeyPairFactory myKeyPairFactory = myAsyncFactory.getKeyPairFactory();
final GordianAgreementFactory myAgreementFactory = myAsyncFactory.getAgreementFactory();
/* Access keyPairGenerator and create pair */
final GordianKeyPairSpec mySpec = GordianKeyPairSpecBuilder.composite(GordianKeyPairSpec.dh(GordianDGroup.FFDHE2048),
GordianKeyPairSpec.ec(GordianDSAElliptic.SECP256R1));
final GordianKeyPairGenerator myGenerator = myKeyPairFactory.getKeyPairGenerator(mySpec);
final GordianKeyPair myTarget = myGenerator.generateKeyPair();
/* Create mini-certificates */
final X500Name myServerName = ...
final GordianCertificate myTargetCert = myAgreementFactory.newMiniCertificate(myServerName, myTarget,
new GordianKeyPairUsage(GordianKeyPairUse.AGREEMENT));
/* Handle as standard */
....
Algorithms
The following agreement algorithms are supported.
| KeyPairType | AgreementType | Notes |
|---|---|---|
| RSA | KEM | Not available in JCA |
| EC, GOST2012, SM2 | KEM, ANON, BASIC, SIGNED, MQV, UNIFIED, SM2 | KEM and SM2 not available in JCA |
| DSTU4145 | KEM, ANON, BASIC, SIGNED, MQV, UNIFIED | KEM not available in JCA |
| DH | ANON, BASIC, SIGNED, MQV, UNIFIED | |
| XDH | ANON, BASIC, SIGNED, UNIFIED | |
| BIKE, CMCE, Frodo, HQC, MLKEM, NEWHOPE, NTRU, NTRUPRIME, SABER | KEM |
