Skip to content

Commit cb3e552

Browse files
committed
ID-39: add support to change key of a secrets file
1 parent d1a50c0 commit cb3e552

File tree

3 files changed

+87
-24
lines changed

3 files changed

+87
-24
lines changed

doc/manual/_config-tool.adoc

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,6 @@ The file has to be created by the `encrypt` tool.
163163
|--secrets-key
164164
|Path to key file to decrypt confidential properties.
165165

166-
This a file containing any arbitrary bytes.
167-
If you edit this file with a text editor be aware of the encoding and the end of line sequence.
168-
169166
The key file has to be the same as on creating the secrets file.
170167

171168
This parameter is required if a secrets file is specified.
@@ -177,13 +174,15 @@ This parameter is required if a secrets file is specified.
177174
The `encrypt` tools is used to generate an initial secrets file and to encrypt the values of the properties.
178175
The script is invoked with the `jython` interpreter provided with the package and deployment tools of the API Gateway.
179176

180-
The tool requires a path to the secrets file and a passphrase to encrypt the values.
177+
The tool requires a path to the secrets file and a key file to encrypt the values.
181178
If the secrets file doesn't exist a new file will be created.
182-
For existing files the given passphrase is checked against the passphrase used on file creation.
179+
For existing files the given key is checked against the key used on file creation.
183180

184181
....
185182
$ encrypt.sh -h
186-
Usage: secrets.py OPTIONS
183+
Usage: encrypt OPTIONS
184+
185+
Encrypt secrets.
187186
188187
Options:
189188
--version show program's version number and exit
@@ -192,9 +191,9 @@ Options:
192191
--secrets-file=FILEPATH
193192
Path of JSON file containing confidential properties
194193
--secrets-key=FILEPATH
195-
Path to key file to decrypt confidential properties
196-
197-
Encrypt credentials.
194+
Path to key file to encrypt confidential properties
195+
--secrets-key-new=FILEPATH
196+
Path to new key file to change key [optional]
198197
....
199198

200199
[cols="2,5a", options="header"]
@@ -206,14 +205,22 @@ Encrypt credentials.
206205
|Path of JSON file containing confidential properties.
207206

208207
|--secrets-key
209-
|Path to key file to decrypt confidential properties.
208+
|Path to key file to encrypt confidential properties.
210209

211-
This a file containing any arbitrary bytes.
210+
.Key File
211+
****
212+
The key file contains any arbitrary sequence of bytes.
213+
It is treated as a binary file.
212214
213215
If you edit this file with a text editor be aware of the encoding and the end of line sequence.
214-
In this case use ASCII characters in a single line (no line feed at the end).
216+
In this case use ASCII characters in a single line (no line feed at the end) to prevent any incompatibility with other line end formats or editor encodings.
217+
****
215218

216-
This parameter is required if a secrets file is specified.
219+
|--secrets-key-new
220+
|Path to new key file.
221+
222+
Use this option to change the key.
223+
All values will be re-encrypted with the new key.
217224
|===
218225

219226
To add new properties tag the values with the `encrypt:` prefix.
@@ -235,6 +242,22 @@ Values having this prefix will be encrypted on running the tool.
235242

236243
NOTE: The `encrypt` tool use the same cipher as the entity store.
237244

245+
*Examples*
246+
247+
Encrypt values with the given key:
248+
249+
.Command Line
250+
....
251+
$ encrypt.sh --secrets-file=gateway.crypt.json --secrets-key=secrets.key
252+
....
253+
254+
Change the key of a secrets file:
255+
256+
.Command Line
257+
....
258+
$ encrypt.sh --secrets-file=gateway.crypt.json --secrets-key=secrets.key --secrets-key-new=new_secrets.key
259+
....
260+
238261
== Configuration Files
239262

240263
For the configuration of the environment specific deployment archive, various configuration files are used.
@@ -532,7 +555,7 @@ All values are encrypted with the same key.
532555
}
533556
}
534557
----
535-
<1> The `secrets` property is requried.
558+
<1> The `secrets` property is required.
536559
<2> Marker to check the key. Don't delete or change it.
537560
<3> The prefix `encrypt:` indicates that the value `changeme` has to be encrypted by the `encrypt` tool.
538561
<4> Values without the prefix are already encrypted.

doc/manual/_usage.adoc

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -393,8 +393,17 @@ $ mvn apigw:tools
393393

394394
=== Encrypt Secrets File
395395
To create an empty secrets file or to add properties to secrets file, the goal `apigw:encrypt` can be used.
396-
A passphrase is used to initially create the secrets file.
397-
The same passphrase as used to create the file has to be used to decrypt values.
396+
A file containing a key is used to initially create the secrets file.
397+
The same key file as used to create the file has to be used to decrypt values.
398+
399+
.Key File
400+
****
401+
The key file contains any arbitrary sequence of bytes.
402+
It is treated as a binary file.
403+
404+
If you edit this file with a text editor be aware of the encoding and the end of line sequence.
405+
In this case use ASCII characters in a single line (no line feed at the end) to prevent any incompatibility with other line end formats or editor encodings.
406+
****
398407

399408
.gateway.crypt.json
400409
[source,json]
@@ -412,9 +421,10 @@ The same passphrase as used to create the file has to be used to decrypt values.
412421

413422
.Command Line
414423
....
415-
$ mvn apigw:encrypt -Daxway.config.secrets.file=gateway.crypt.json -Daxway.config.secrets.passphrase=changeme
424+
$ mvn apigw:encrypt -Daxway.config.secrets.file=gateway.crypt.json -Daxway.config.secrets.key=secrets.key
416425
....
417426

427+
418428
== Maven Goals
419429
This section provides a short overview of the goals supported by the Maven plugin.
420430

src/main/resources/scripts/lib/secrets.py

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,16 @@ def __init__(self, secrets_key_file, secrets_file_path, create_if_not_exists=Fal
6161
raise ValueError("Secrets file '%s' doesn't exist!" % (self.__file_path))
6262
return
6363

64-
def __encrypt_value(self, value):
64+
def __encrypt_value(self, value, cipher=None):
65+
if not cipher:
66+
cipher = self.__cipher
67+
6568
# add salt
6669
salt = "#{:06d}:".format(random.randint(0,999999))
6770
value = salt + value
6871

6972
# encrypt salted value
70-
value_bytes = self.__cipher.encrypt(value.encode('utf-8'))
73+
value_bytes = cipher.encrypt(value.encode('utf-8'))
7174
value_b64 = base64.b64encode(value_bytes)
7275

7376
return value_b64
@@ -114,6 +117,28 @@ def get_secret(self, key):
114117

115118
return value
116119

120+
def change_key(self, secrets_key_file_new):
121+
# create new cipher using new key file
122+
if not os.path.isfile(secrets_key_file_new):
123+
raise ValueError("Key file not found: %s" % (secrets_key_file_new))
124+
125+
key = None
126+
with open(secrets_key_file_new, mode='rb') as skf:
127+
key = skf.read()
128+
129+
new_cipher = PasswordCipher(key)
130+
131+
# re-encrypt values
132+
for s in self.__secrets_json["secrets"]:
133+
v = self.get_secret(s)
134+
v = self.__encrypt_value(v, new_cipher)
135+
136+
self.__secrets_json["secrets"][s] = v
137+
138+
logging.info("Key changed for property '%s'." % (s))
139+
140+
self.__cipher = new_cipher
141+
return
117142

118143
def write(self):
119144
json_str = json.dumps(self.__secrets_json, sort_keys=True, indent=2)
@@ -123,15 +148,17 @@ def write(self):
123148
return
124149

125150
def main_encrypt():
126-
prog = sys.argv[0]
151+
prog = "encrypt"
152+
description="Encrypt secrets."
127153
version = "%prog 1.0.0"
128154
usage = "%prog OPTIONS"
129-
epilog = "Encrypt secrets."
155+
epilog = ""
130156

131-
parser = OptionParser(usage=usage, version=version, epilog=epilog)
157+
parser = OptionParser(usage=usage, version=version, epilog=epilog, prog=prog, description=description)
132158
parser.add_option("-v", "--verbose", dest="verbose", help="Enable verbose messages [optional]", action="store_true")
133-
parser.add_option("--secrets-file", dest="secrets_file", help="Path of JSON file containing confidential propertiers", metavar="FILEPATH")
134-
parser.add_option("--secrets-key", dest="secrets_key_file", help="Path to key file to decrypt confidential properties", metavar="FILEPATH")
159+
parser.add_option("--secrets-file", dest="secrets_file", help="Path of JSON file containing confidential properties", metavar="FILEPATH")
160+
parser.add_option("--secrets-key", dest="secrets_key_file", help="Path to key file to encrypt confidential properties", metavar="FILEPATH")
161+
parser.add_option("--secrets-key-new", dest="secrets_key_file_new", help="Path to new key file to change key [optional]", metavar="FILEPATH")
135162

136163
(options, args) = parser.parse_args()
137164

@@ -150,6 +177,9 @@ def main_encrypt():
150177
try:
151178
secrets = Secrets(options.secrets_key_file, options.secrets_file, create_if_not_exists=True)
152179
secrets.encrypt()
180+
if options.secrets_key_file_new:
181+
logging.info("Re-encrypt values to change key.")
182+
secrets.change_key(options.secrets_key_file_new)
153183
secrets.write()
154184

155185
except Exception as e:

0 commit comments

Comments
 (0)