[Fleet] fix message signing service encryption bug (#154934)

## Summary

fixes: adding an encryption key after generating a key pair would cause
the private key to become unable to decrypt.


### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios


### For maintainers

- [x] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
This commit is contained in:
Joey F. Poon 2023-04-19 07:34:21 -07:00 committed by GitHub
parent fa7bde1296
commit ddf9ab6c13
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 62 additions and 56 deletions

View file

@ -153,7 +153,8 @@ describe('MessageSigningService', () => {
MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE,
keyPairObj.id,
{
passphrase: expect.any(String),
...keyPairObj.attributes,
passphrase: keyPairObj.attributes.passphrase_plain,
passphrase_plain: '',
}
);

View file

@ -8,7 +8,10 @@
import { generateKeyPairSync, createSign, randomBytes } from 'crypto';
import type { KibanaRequest } from '@kbn/core-http-server';
import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
import type {
SavedObjectsClientContract,
SavedObjectsFindResult,
} from '@kbn/core-saved-objects-api-server';
import type { EncryptedSavedObjectsClient } from '@kbn/encrypted-saved-objects-plugin/server';
import { SECURITY_EXTENSION_ID } from '@kbn/core-saved-objects-server';
@ -45,35 +48,12 @@ export class MessageSigningService implements MessageSigningServiceInterface {
publicKey: string;
passphrase: string;
}> {
let passphrase = providedPassphrase || this.generatePassphrase();
const currentKeyPair = await this.getCurrentKeyPair();
if (
currentKeyPair.privateKey &&
currentKeyPair.publicKey &&
(currentKeyPair.passphrase || currentKeyPair.passphrasePlain)
) {
passphrase = currentKeyPair.passphrase || currentKeyPair.passphrasePlain;
// newly configured encryption key, encrypt the passphrase
if (currentKeyPair.passphrasePlain && this.isEncryptionAvailable) {
await this.soClient.update<MessageSigningKeys>(
MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE,
currentKeyPair.id,
{
passphrase,
passphrase_plain: '',
}
);
}
return {
privateKey: currentKeyPair.privateKey,
publicKey: currentKeyPair.publicKey,
passphrase,
};
const existingKeyPair = await this.checkForExistingKeyPair();
if (existingKeyPair) {
return existingKeyPair;
}
const passphrase = providedPassphrase || this.generatePassphrase();
const keyPair = generateKeyPairSync('ec', {
namedCurve: 'prime256v1',
privateKeyEncoding: {
@ -176,13 +156,9 @@ export class MessageSigningService implements MessageSigningServiceInterface {
return this._soClient;
}
private async getCurrentKeyPair(): Promise<{
id: string;
privateKey: string;
publicKey: string;
passphrase: string;
passphrasePlain: string;
}> {
private async getCurrentKeyPairObj(): Promise<
SavedObjectsFindResult<MessageSigningKeys> | undefined
> {
const finder =
await this.esoClient.createPointInTimeFinderDecryptedAsInternalUser<MessageSigningKeys>({
type: MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE,
@ -190,30 +166,59 @@ export class MessageSigningService implements MessageSigningServiceInterface {
sortField: 'created_at',
sortOrder: 'desc',
});
let keyPair = {
id: '',
privateKey: '',
publicKey: '',
passphrase: '',
passphrasePlain: '',
};
let soDoc: SavedObjectsFindResult<MessageSigningKeys> | undefined;
for await (const result of finder.find()) {
const savedObject = result.saved_objects[0];
const attributes = savedObject?.attributes;
if (!attributes?.private_key) {
break;
}
keyPair = {
id: savedObject.id,
privateKey: attributes.private_key,
publicKey: attributes.public_key,
passphrase: attributes.passphrase,
passphrasePlain: attributes.passphrase_plain,
};
soDoc = result.saved_objects[0];
break;
}
finder.close();
return keyPair;
return soDoc;
}
private async checkForExistingKeyPair(): Promise<
| {
privateKey: string;
publicKey: string;
passphrase: string;
}
| undefined
> {
const currentKeyPair = await this.getCurrentKeyPairObj();
if (!currentKeyPair) {
return;
}
const { attributes } = currentKeyPair;
if (!attributes) {
return;
}
const {
private_key: privateKey,
public_key: publicKey,
passphrase: passphraseEncrypted,
passphrase_plain: passphrasePlain,
} = attributes;
const passphrase = passphraseEncrypted || passphrasePlain;
if (!privateKey || !publicKey || !passphrase) {
return;
}
// newly configured encryption key, encrypt the passphrase
if (passphrasePlain && this.isEncryptionAvailable) {
await this.soClient.update<MessageSigningKeys>(
MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE,
currentKeyPair?.id,
{ ...attributes, passphrase, passphrase_plain: '' }
);
}
return {
privateKey,
publicKey,
passphrase,
};
}
private generatePassphrase(): string {