-
Notifications
You must be signed in to change notification settings - Fork 14
US92112: Encrypt and Store adapter client-secret in ACS db #97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
sanjeevchopra
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you rebase from the branch which has the ConnectorService, so you can invoke the encryption/decryption ? or we can wait for that branch to merge to develop.
| byte[] result = Bytes.concat(ivBytes, encrypted); | ||
|
|
||
| return Base64.encodeBase64String(result); | ||
| } catch (Exception ex) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- catch
Throwableto be safe - pls remove the printStackTrace
- log a error (excluding the encryption key)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created custom exceptions based on the error and added log statements.
|
|
||
| public String encrypt(final String value) { | ||
| try { | ||
| if (this.key.length() != 24) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are there any other requirements on the key ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not that I know of. We are doing 192 bit encryption, the key just needs to have that number of bits and we should be good to go.
| @Configuration | ||
| public class EncryptionConfig { | ||
|
|
||
| @Value("${ENCRYPTION_KEY}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wondering if we should use a obscure name for obfuscation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Obfuscation from who? If someone is able to see our environment variables in cf, they are already a privileged user.
| } | ||
| } | ||
|
|
||
| public String decrypt(final String encrypted) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comments from encrypt apply here as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed.
| SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES"); | ||
|
|
||
| Cipher cipher = Cipher.getInstance(ALGO); | ||
| cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can the key and cipher setup be done once in the constructor ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call. Moved.
|
|
||
| import com.google.common.primitives.Bytes; | ||
|
|
||
| public class AcsEncryption { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AESEncrypter ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Went with Encryptor
| @@ -0,0 +1,9 @@ | |||
| package com.ge.predix.acs.encryption; | |||
|
|
|||
| public interface Encryptor { | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To make this generally usable, we could build this as a credential store interface. It has 2 functions storage and encryption/decryption. This service could be used as-is with audit or any other credential which ACS needs to store.
`public interface CredentialStore {
String encryptAndStore(String id, String key, String value);
String findAndDecrypt(String id, String key, String encrypted);
}`
- id - will be used for lookup and storage
- key - for encryption/decryption OR authN token in case of vault)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
discussed with Frank. Doesnt quite work because when using a store, you dont really have a 'encrypted value'. Probably best as 2 separate services - Encryptor and SecureStorage. You only need one or the other (Encryptor + regular storage OR SecureStorage)
a506d69 to
13189f0
Compare
| Set<AttributeAdapterConnection> attributeAdapterConnections = new HashSet<>(); | ||
| connector.getAdapters().parallelStream().forEach(a -> { | ||
| a.setUaaClientSecret(this.encryptor.encrypt(a.getUaaClientSecret())); | ||
| attributeAdapterConnections.add(a); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- why do you need a new set of connections ? the secret is already updated by reference
- pls make sure HashSet indeed supports parallel streams
- code will read better if you rename
atoadapterConnection
| return this.connectorConverter.toConnector(zoneEntity.getResourceAttributeConnector()); | ||
| AttributeConnector connector = this.connectorConverter.toConnector(zoneEntity.getResourceAttributeConnector()); | ||
| if (null != connector) { | ||
| Set<AttributeAdapterConnection> attributeAdapterConnections = new HashSet<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comments as upsert
| private String adapterClientId; | ||
|
|
||
| @Transient | ||
| @Column(name = "adapter_client_secret", nullable = false, length = 128) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does the encryption algo produce a value of the same length ? Just making sure if 128 is the appropriate lenght
| import com.ge.predix.acs.encryption.Encryptor; | ||
|
|
||
| @Configuration | ||
| public class EncryptionConfig { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove this class and make Encryptor a spring bean.
- Also AttributeAdatperConnectionEntity should have a transient getUaaClientSecret() which returns the decrypted secret
- The persistent field can be renamed to something like getEncryptedUaaClientSecret() (the whole entity is changing to transient now based on our json change)
| this.cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); | ||
|
|
||
| byte[] encrypted = this.cipher.doFinal(value.getBytes()); | ||
| byte[] result = Bytes.concat(ivBytes, encrypted); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is limited value to concat'ing a salt here, given we our code is open source. If we keep it, pls add some comments to explain the steps here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IV is required by CBC cipher mode. We could switch to using ECB instead and then IV will not be required. ECB is considered weaker than CBC since the same blocks of plain text encrypted with ECB result in the same encrypted blocks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Frank, explained that this is useful to prevent against dictionary attacks. Lets keep it and add some comments here 👍
| @Test | ||
| public void testEncryptCompleteFlow() { | ||
| Encryptor encryption = Encryptor.getInstance(); | ||
| ReflectionTestUtils.setField(encryption, ENCRYPTION_KEY_VAR_NAME, "FooBarFooBarFooB"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assert.asertNotEquals(encryption.encrypt(VALUE_TO_ENCRYPT), VALUE_TO_ENCRYPT)
13189f0 to
3c6e36d
Compare
| this.cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); | ||
|
|
||
| byte[] encrypted = this.cipher.doFinal(value.getBytes()); | ||
| byte[] result = Bytes.concat(ivBytes, encrypted); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Frank, explained that this is useful to prevent against dictionary attacks. Lets keep it and add some comments here 👍
|
|
||
| private String encryptionKey; | ||
|
|
||
| private static final Encryptor INSTANCE = new Encryptor(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this will work, because the object instance being created here will not be spring managed, and hence not receive the ENCRYPTION_KEY value from the env var. Is there a integration test for this yet ?
- On 2nd thought the cleanest way to solve this would be to have AttributeConnectorService decrypt the client secret. Since it is spring managed, it can autowire the Encryptor to it. You could do the json serialization/des-ser there too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I pushed a commit to make this change.
|
I also pushed a commit with some refactoring on how to handle json serialization/de-ser' . If it looks better - please keep it. Tests are clean with this change. Also - if you move the ser/de-ser to the service, the refactoring still applies - should be easily portable. |
|
This branch is ready to merge, imo. |
|
PR build passed for predix-cloud - https://predix1.jenkins.build.ge.com/job/Predix-Security/job/ACS/job/ACS-PullRequest/job/acs-integration-pullrequest/291/ (issue seems to be postgres resources - it cannot create 4 db instances at a time. I had to remove some db instances to allow the above build to go through and remove 'cf login' command from the jenkins job) Filed https://gesoftware.service-now.com/nav_to.do?uri=incident.do?sys_id=3bc1cd164f25f600b3d87acd0210c7d5 for this |
Signed-off-by: Sanjeev Chopra <[email protected]> Signed-off-by: Irina Epshteyn
No description provided.