You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@trafficcontrol.apache.org by GitBox <gi...@apache.org> on 2021/06/04 17:57:05 UTC

[GitHub] [trafficcontrol] ocket8888 commented on a change in pull request #5913: Re-encypt Tool for TV Postgres

ocket8888 commented on a change in pull request #5913:
URL: https://github.com/apache/trafficcontrol/pull/5913#discussion_r645746870



##########
File path: traffic_ops/traffic_ops_golang/trafficvault/backends/postgres/encrypt.go
##########
@@ -32,7 +32,7 @@ import (
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres/hashicorpvault"
 )
 
-func aesEncrypt(bytesToEncrypt []byte, aesKey []byte) (string, error) {
+func AesEncrypt(bytesToEncrypt []byte, aesKey []byte) (string, error) {

Review comment:
       exported symbols should have GoDocs (e.g. is that error safe to show to a client, or only for logging server-side?) and nit but as an initialism AES should probably be capitalized

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"

Review comment:
       Standard imports should be first, followed by project-internal, then third party

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)
+	}
+	if newKeyLocation == nil || *newKeyLocation == "" {
+		fmt.Println("newKey flag is required.")
+		os.Exit(0)

Review comment:
       Same as above RE: failure exit code

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)
+	}
+	if newKeyLocation == nil || *newKeyLocation == "" {
+		fmt.Println("newKey flag is required.")
+		os.Exit(0)
+	}
+
+	newKey, err := readKey(*newKeyLocation)
+	if err != nil {
+		fmt.Println("reading newKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	previousKey, err := readKey(*previousKeyLocation)
+	if err != nil {
+		fmt.Println("reading previousKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	dbConfBytes, err := ioutil.ReadFile(PROPERTIES_FILE)
+	if err != nil {
+		fmt.Println("reading db conf '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)

Review comment:
       Same as above RE: exit with failure code on failure

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)
+	}
+	if newKeyLocation == nil || *newKeyLocation == "" {
+		fmt.Println("newKey flag is required.")
+		os.Exit(0)
+	}
+
+	newKey, err := readKey(*newKeyLocation)
+	if err != nil {
+		fmt.Println("reading newKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	previousKey, err := readKey(*previousKeyLocation)
+	if err != nil {
+		fmt.Println("reading previousKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	dbConfBytes, err := ioutil.ReadFile(PROPERTIES_FILE)
+	if err != nil {
+		fmt.Println("reading db conf '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	pgCfg := Config{}
+	err = json.Unmarshal(dbConfBytes, &pgCfg)
+	if err != nil {
+		fmt.Println("unmarshalling '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	sslStr := "require"
+	if !pgCfg.SSL {
+		sslStr = "disable"
+	}
+	db, err := sqlx.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s&fallback_application_name=trafficvault", pgCfg.User, pgCfg.Password, pgCfg.Hostname, pgCfg.Port, pgCfg.DBName, sslStr))
+	if err != nil {
+		fmt.Println("opening database: " + err.Error())
+		os.Exit(0)
+	}
+
+	if err = reEncryptSslKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting SSL Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUrlSigKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URL Sig Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUriSigningKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URI Signing Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptDNSSECKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting DNSSEC Keys: ", err.Error())
+		os.Exit(0)
+	}
+
+	if err = updateKeyFile(previousKey, *previousKeyLocation, newKey); err != nil {
+		fmt.Println("updating the key file: ", err.Error())
+		os.Exit(0)
+	}
+
+	fmt.Println("Successfully re-encrypted keys for SSL Keys, URL Sig Keys, URI Signing Keys, and DNSSEC Keys.")
+}
+
+type Config struct {
+	DBName   string `json:"dbname"`
+	Hostname string `json:"hostname"`
+	User     string `json:"user"`
+	Password string `json:"password"`
+	Port     int    `json:"port"`
+	SSL      bool   `json:"ssl"`
+}
+
+func readKey(keyLocation string) ([]byte, error) {
+	var keyBase64 string
+	keyBase64Bytes, err := ioutil.ReadFile(keyLocation)
+	if err != nil {
+		return []byte{}, errors.New("reading file '" + keyLocation + "':" + err.Error())
+	}
+	keyBase64 = string(keyBase64Bytes)
+
+	key, err := base64.StdEncoding.DecodeString(keyBase64)
+	if err != nil {
+		return []byte{}, errors.New("AES key cannot be decoded from base64")
+	}
+
+	// verify the key works
+	_, err = aes.NewCipher(key)
+	if err != nil {
+		return []byte{}, err
+	}
+
+	return key, nil
+}
+
+func reEncryptSslKeys(db *sqlx.DB, previousKey []byte, newKey []byte) error {
+	tx, err := db.Begin()
+	if err != nil {
+		fmt.Println("transaction begin failed ", err, " ", tx)
+		os.Exit(0)
+	}
+	defer tx.Commit()
+
+	rows, err := tx.Query("SELECT id, data FROM sslkey")
+	if err != nil {
+		fmt.Println("querying: ", err)
+		os.Exit(0)

Review comment:
       Same as above RE: exit with failure code on failure

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)
+	}
+	if newKeyLocation == nil || *newKeyLocation == "" {
+		fmt.Println("newKey flag is required.")
+		os.Exit(0)
+	}
+
+	newKey, err := readKey(*newKeyLocation)
+	if err != nil {
+		fmt.Println("reading newKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	previousKey, err := readKey(*previousKeyLocation)
+	if err != nil {
+		fmt.Println("reading previousKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	dbConfBytes, err := ioutil.ReadFile(PROPERTIES_FILE)
+	if err != nil {
+		fmt.Println("reading db conf '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	pgCfg := Config{}
+	err = json.Unmarshal(dbConfBytes, &pgCfg)
+	if err != nil {
+		fmt.Println("unmarshalling '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	sslStr := "require"
+	if !pgCfg.SSL {
+		sslStr = "disable"
+	}
+	db, err := sqlx.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s&fallback_application_name=trafficvault", pgCfg.User, pgCfg.Password, pgCfg.Hostname, pgCfg.Port, pgCfg.DBName, sslStr))
+	if err != nil {
+		fmt.Println("opening database: " + err.Error())
+		os.Exit(0)
+	}
+
+	if err = reEncryptSslKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting SSL Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUrlSigKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URL Sig Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUriSigningKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URI Signing Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptDNSSECKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting DNSSEC Keys: ", err.Error())
+		os.Exit(0)
+	}
+
+	if err = updateKeyFile(previousKey, *previousKeyLocation, newKey); err != nil {
+		fmt.Println("updating the key file: ", err.Error())
+		os.Exit(0)
+	}
+
+	fmt.Println("Successfully re-encrypted keys for SSL Keys, URL Sig Keys, URI Signing Keys, and DNSSEC Keys.")
+}
+
+type Config struct {
+	DBName   string `json:"dbname"`
+	Hostname string `json:"hostname"`
+	User     string `json:"user"`
+	Password string `json:"password"`
+	Port     int    `json:"port"`
+	SSL      bool   `json:"ssl"`
+}
+
+func readKey(keyLocation string) ([]byte, error) {
+	var keyBase64 string
+	keyBase64Bytes, err := ioutil.ReadFile(keyLocation)
+	if err != nil {
+		return []byte{}, errors.New("reading file '" + keyLocation + "':" + err.Error())
+	}
+	keyBase64 = string(keyBase64Bytes)
+
+	key, err := base64.StdEncoding.DecodeString(keyBase64)
+	if err != nil {
+		return []byte{}, errors.New("AES key cannot be decoded from base64")
+	}
+
+	// verify the key works
+	_, err = aes.NewCipher(key)
+	if err != nil {
+		return []byte{}, err
+	}
+
+	return key, nil
+}
+
+func reEncryptSslKeys(db *sqlx.DB, previousKey []byte, newKey []byte) error {
+	tx, err := db.Begin()
+	if err != nil {
+		fmt.Println("transaction begin failed ", err, " ", tx)
+		os.Exit(0)
+	}
+	defer tx.Commit()
+
+	rows, err := tx.Query("SELECT id, data FROM sslkey")
+	if err != nil {
+		fmt.Println("querying: ", err)
+		os.Exit(0)
+	}
+	defer rows.Close()
+
+	for rows.Next() {
+		updateTx, err := db.Begin()
+		if err != nil {
+			fmt.Println("transaction begin failed ", err, " ", updateTx)
+			os.Exit(0)
+		}
+		defer updateTx.Commit()
+
+		id := 0
+		var encryptedSslKeys []byte
+		if err = rows.Scan(&id, &encryptedSslKeys); err != nil {
+			fmt.Println("getting SSL Keys: ", err)
+			return err
+		}
+		jsonKeys, err := postgres.AesDecrypt(encryptedSslKeys, previousKey)
+		if err != nil {
+			fmt.Println("reading SSL Keys: ", err)
+			return err
+		}
+
+		reencryptedKeys, err := postgres.AesEncrypt(jsonKeys, newKey)
+		if err != nil {
+			fmt.Println("encrypting SSL Keys with new key: ", err)
+			return err
+		}
+
+		res, err := updateTx.Exec(`UPDATE sslkey SET data = $1 WHERE id = $2`, []byte(reencryptedKeys), id)
+		if err != nil {
+			fmt.Println("updating SSL Keys for id ", id, ": ", err)
+			return err
+		}
+		rowsAffected, err := res.RowsAffected()
+		if err != nil {
+			fmt.Println("error determining rows affected for reencrypting SSL Keys with id ", id)
+			return errors.New(fmt.Sprintf("determining rows affected for reencrypting SSL Keys with id %d", id))
+		}
+		if rowsAffected == 0 {
+			fmt.Println("no rows updated for reencrypting SSL Keys for id ", id)
+			return errors.New(fmt.Sprintf("no rows updated for reencrypting SSL Keys for id %d", id))
+		}
+
+	}
+
+	return nil
+}
+
+func reEncryptUrlSigKeys(db *sqlx.DB, previousKey []byte, newKey []byte) error {
+	tx, err := db.Begin()
+	if err != nil {
+		fmt.Println("transaction begin failed ", err, " ", tx)
+		os.Exit(0)

Review comment:
       Same as above RE: exit with failure code on failure and also returning error vs forcibly quitting.

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)
+	}
+	if newKeyLocation == nil || *newKeyLocation == "" {
+		fmt.Println("newKey flag is required.")
+		os.Exit(0)
+	}
+
+	newKey, err := readKey(*newKeyLocation)
+	if err != nil {
+		fmt.Println("reading newKey: ", err.Error())
+		os.Exit(0)

Review comment:
       A failure should be indicated by the exit code, `0` is success.

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"

Review comment:
       Instead of importing Traffic Ops internals, should the function you're using be pulled out into e.g. `lib/go-tc/`? Or some new library for crypto things like `lib/crypto/` maybe?

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)
+	}
+	if newKeyLocation == nil || *newKeyLocation == "" {
+		fmt.Println("newKey flag is required.")
+		os.Exit(0)
+	}

Review comment:
       If these are required options, should they be positional instead of options? Normally options are optional.

##########
File path: docs/source/admin/traffic_vault.rst
##########
@@ -79,6 +79,36 @@ Administration of the PostgreSQL database for Traffic Vault
 
 Similar to administering the Traffic Ops database, the :ref:`admin <database-management>` tool should be used for administering the PostgreSQL Traffic Vault backend.
 
+AES Re-Encryption Tool:
+-----------------------
+
+.. program:: reencrypt
+
+app/db/reencrypt/reencrypt
+=====================================================================
+The :program:`app/db/reencrypt/reencrypt` binary is for use to re-encrypt all keys in the Postgres Traffic Vault.
+
+.. note:: For proper resolution of configuration files, it's recommended that this binary be run from the ``app/db/reencrypt`` directory
+
+Usage
+-----
+``./reencrypt [options]``
+
+Options and Arguments
+---------------------
+.. option:: -newKey

Review comment:
       If an option takes an option-argument it should be shown in the `.. option::` directive's argument, and as long options are a GNU extension it should be shown in GNU standard long form (two `-`s) - according to POSIX guidelines, this single option is equivalent to `-n -e -w -K ey` (ambiguity caused by two `e`s). GNU also recommends that long options be hyphen separated (kebab-case rather than camelCase)
   
   e.g.
   ```rst
   .. option:: --new-key PATH
   
   	The file path for the new base64-encoded AES key.
   ```
   
   I think switching to kebab-case would be an improvement, personally, but that would be a code change and it doesn't really matter much. What's important is that everywhere it's documented it uses the proper `--` to avoid confusion, and indicates that it requires an argument.
   
   
   Excerpt from [the GNU libc manual, Section 25.1.1](https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html):
   
   > POSIX recommends these conventions for command line arguments. getopt (see Getopt) and argp_parse (see Argp) make it easy to implement them.
   >
   >>   * Arguments are options if they begin with a hyphen delimiter (‘-’).
   >>   * Multiple options may follow a hyphen delimiter in a single token if the options do not take arguments. Thus, ‘-abc’ is equivalent to ‘-a -b -c’.
   >>   * Option names are single alphanumeric characters (as for isalnum; see Classification of Characters).
   >>   * Certain options require an argument. For example, the ‘-o’ command of the ld command requires an argument—an output file name.
   >>   * An option and its argument may or may not appear as separate tokens. (In other words, the whitespace separating them is optional.) Thus, ‘-o foo’ and ‘-ofoo’ are equivalent.
   >>   * Options typically precede other non-option arguments.
   >>
   >>    The implementations of getopt and argp_parse in the GNU C Library normally make it appear as if all the option arguments were specified before all the non-option arguments for the purposes of parsing, even if the user of your program intermixed option and non-option arguments. They do this by reordering the elements of the argv array. This behavior is nonstandard; if you want to suppress it, define the _POSIX_OPTION_ORDER environment variable. See Standard Environment.
   >>   * The argument ‘--’ terminates all options; any following arguments are treated as non-option arguments, even if they begin with a hyphen.
   >>   * A token consisting of a single hyphen character is interpreted as an ordinary non-option argument. By convention, it is used to specify input from or output to the standard input and output streams.
   >>   * Options may be supplied in any order, or appear multiple times. The interpretation is left up to the particular application program. 
   >
   > GNU adds long options to these conventions. Long options consist of ‘--’ followed by a name made of alphanumeric characters and dashes. Option names are typically one to three words long, with hyphens to separate words. Users can abbreviate the option names as long as the abbreviations are unique. 

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)
+	}
+	if newKeyLocation == nil || *newKeyLocation == "" {
+		fmt.Println("newKey flag is required.")
+		os.Exit(0)
+	}
+
+	newKey, err := readKey(*newKeyLocation)
+	if err != nil {
+		fmt.Println("reading newKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	previousKey, err := readKey(*previousKeyLocation)
+	if err != nil {
+		fmt.Println("reading previousKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	dbConfBytes, err := ioutil.ReadFile(PROPERTIES_FILE)
+	if err != nil {
+		fmt.Println("reading db conf '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	pgCfg := Config{}
+	err = json.Unmarshal(dbConfBytes, &pgCfg)
+	if err != nil {
+		fmt.Println("unmarshalling '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	sslStr := "require"
+	if !pgCfg.SSL {
+		sslStr = "disable"
+	}
+	db, err := sqlx.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s&fallback_application_name=trafficvault", pgCfg.User, pgCfg.Password, pgCfg.Hostname, pgCfg.Port, pgCfg.DBName, sslStr))
+	if err != nil {
+		fmt.Println("opening database: " + err.Error())
+		os.Exit(0)
+	}
+
+	if err = reEncryptSslKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting SSL Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUrlSigKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URL Sig Keys: ", err.Error())
+		os.Exit(0)

Review comment:
       Same as above RE: exit with failure code on failure

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)

Review comment:
       Improper invocation should exit with a failure code, not success

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)
+	}
+	if newKeyLocation == nil || *newKeyLocation == "" {
+		fmt.Println("newKey flag is required.")
+		os.Exit(0)
+	}
+
+	newKey, err := readKey(*newKeyLocation)
+	if err != nil {
+		fmt.Println("reading newKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	previousKey, err := readKey(*previousKeyLocation)
+	if err != nil {
+		fmt.Println("reading previousKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	dbConfBytes, err := ioutil.ReadFile(PROPERTIES_FILE)
+	if err != nil {
+		fmt.Println("reading db conf '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	pgCfg := Config{}
+	err = json.Unmarshal(dbConfBytes, &pgCfg)
+	if err != nil {
+		fmt.Println("unmarshalling '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	sslStr := "require"
+	if !pgCfg.SSL {
+		sslStr = "disable"
+	}
+	db, err := sqlx.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s&fallback_application_name=trafficvault", pgCfg.User, pgCfg.Password, pgCfg.Hostname, pgCfg.Port, pgCfg.DBName, sslStr))
+	if err != nil {
+		fmt.Println("opening database: " + err.Error())
+		os.Exit(0)

Review comment:
       Same as above RE: exit with failure code on failure

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main

Review comment:
       commands should document their usage in the `main` package's GoDoc, for an example, check out [the GoDoc for `godoc` itself](https://github.com/golang/tools/blob/v0.1.2/cmd/godoc/doc.go).

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)
+	}
+	if newKeyLocation == nil || *newKeyLocation == "" {
+		fmt.Println("newKey flag is required.")
+		os.Exit(0)
+	}
+
+	newKey, err := readKey(*newKeyLocation)
+	if err != nil {
+		fmt.Println("reading newKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	previousKey, err := readKey(*previousKeyLocation)
+	if err != nil {
+		fmt.Println("reading previousKey: ", err.Error())
+		os.Exit(0)

Review comment:
       Same as above RE: exit with failure code on failure

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)
+	}
+	if newKeyLocation == nil || *newKeyLocation == "" {
+		fmt.Println("newKey flag is required.")
+		os.Exit(0)
+	}
+
+	newKey, err := readKey(*newKeyLocation)
+	if err != nil {
+		fmt.Println("reading newKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	previousKey, err := readKey(*previousKeyLocation)
+	if err != nil {
+		fmt.Println("reading previousKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	dbConfBytes, err := ioutil.ReadFile(PROPERTIES_FILE)
+	if err != nil {
+		fmt.Println("reading db conf '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	pgCfg := Config{}
+	err = json.Unmarshal(dbConfBytes, &pgCfg)
+	if err != nil {
+		fmt.Println("unmarshalling '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	sslStr := "require"
+	if !pgCfg.SSL {
+		sslStr = "disable"
+	}
+	db, err := sqlx.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s&fallback_application_name=trafficvault", pgCfg.User, pgCfg.Password, pgCfg.Hostname, pgCfg.Port, pgCfg.DBName, sslStr))
+	if err != nil {
+		fmt.Println("opening database: " + err.Error())
+		os.Exit(0)
+	}
+
+	if err = reEncryptSslKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting SSL Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUrlSigKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URL Sig Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUriSigningKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URI Signing Keys: ", err.Error())
+		os.Exit(0)

Review comment:
       Same as above RE: exit with failure code on failure

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)
+	}
+	if newKeyLocation == nil || *newKeyLocation == "" {
+		fmt.Println("newKey flag is required.")
+		os.Exit(0)
+	}
+
+	newKey, err := readKey(*newKeyLocation)
+	if err != nil {
+		fmt.Println("reading newKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	previousKey, err := readKey(*previousKeyLocation)
+	if err != nil {
+		fmt.Println("reading previousKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	dbConfBytes, err := ioutil.ReadFile(PROPERTIES_FILE)
+	if err != nil {
+		fmt.Println("reading db conf '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	pgCfg := Config{}
+	err = json.Unmarshal(dbConfBytes, &pgCfg)
+	if err != nil {
+		fmt.Println("unmarshalling '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	sslStr := "require"
+	if !pgCfg.SSL {
+		sslStr = "disable"
+	}
+	db, err := sqlx.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s&fallback_application_name=trafficvault", pgCfg.User, pgCfg.Password, pgCfg.Hostname, pgCfg.Port, pgCfg.DBName, sslStr))
+	if err != nil {
+		fmt.Println("opening database: " + err.Error())
+		os.Exit(0)
+	}
+
+	if err = reEncryptSslKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting SSL Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUrlSigKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URL Sig Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUriSigningKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URI Signing Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptDNSSECKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting DNSSEC Keys: ", err.Error())
+		os.Exit(0)

Review comment:
       Same as above RE: exit with failure code on failure

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)
+	}
+	if newKeyLocation == nil || *newKeyLocation == "" {
+		fmt.Println("newKey flag is required.")
+		os.Exit(0)
+	}
+
+	newKey, err := readKey(*newKeyLocation)
+	if err != nil {
+		fmt.Println("reading newKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	previousKey, err := readKey(*previousKeyLocation)
+	if err != nil {
+		fmt.Println("reading previousKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	dbConfBytes, err := ioutil.ReadFile(PROPERTIES_FILE)
+	if err != nil {
+		fmt.Println("reading db conf '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	pgCfg := Config{}
+	err = json.Unmarshal(dbConfBytes, &pgCfg)
+	if err != nil {
+		fmt.Println("unmarshalling '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	sslStr := "require"
+	if !pgCfg.SSL {
+		sslStr = "disable"
+	}
+	db, err := sqlx.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s&fallback_application_name=trafficvault", pgCfg.User, pgCfg.Password, pgCfg.Hostname, pgCfg.Port, pgCfg.DBName, sslStr))
+	if err != nil {
+		fmt.Println("opening database: " + err.Error())
+		os.Exit(0)
+	}
+
+	if err = reEncryptSslKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting SSL Keys: ", err.Error())
+		os.Exit(0)

Review comment:
       Same as above RE: exit with failure code on failure

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)
+	}
+	if newKeyLocation == nil || *newKeyLocation == "" {
+		fmt.Println("newKey flag is required.")
+		os.Exit(0)
+	}
+
+	newKey, err := readKey(*newKeyLocation)
+	if err != nil {
+		fmt.Println("reading newKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	previousKey, err := readKey(*previousKeyLocation)
+	if err != nil {
+		fmt.Println("reading previousKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	dbConfBytes, err := ioutil.ReadFile(PROPERTIES_FILE)
+	if err != nil {
+		fmt.Println("reading db conf '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	pgCfg := Config{}
+	err = json.Unmarshal(dbConfBytes, &pgCfg)
+	if err != nil {
+		fmt.Println("unmarshalling '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	sslStr := "require"
+	if !pgCfg.SSL {
+		sslStr = "disable"
+	}
+	db, err := sqlx.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s&fallback_application_name=trafficvault", pgCfg.User, pgCfg.Password, pgCfg.Hostname, pgCfg.Port, pgCfg.DBName, sslStr))
+	if err != nil {
+		fmt.Println("opening database: " + err.Error())
+		os.Exit(0)
+	}
+
+	if err = reEncryptSslKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting SSL Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUrlSigKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URL Sig Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUriSigningKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URI Signing Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptDNSSECKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting DNSSEC Keys: ", err.Error())
+		os.Exit(0)
+	}
+
+	if err = updateKeyFile(previousKey, *previousKeyLocation, newKey); err != nil {
+		fmt.Println("updating the key file: ", err.Error())
+		os.Exit(0)
+	}
+
+	fmt.Println("Successfully re-encrypted keys for SSL Keys, URL Sig Keys, URI Signing Keys, and DNSSEC Keys.")
+}
+
+type Config struct {
+	DBName   string `json:"dbname"`
+	Hostname string `json:"hostname"`
+	User     string `json:"user"`
+	Password string `json:"password"`
+	Port     int    `json:"port"`
+	SSL      bool   `json:"ssl"`
+}
+
+func readKey(keyLocation string) ([]byte, error) {
+	var keyBase64 string
+	keyBase64Bytes, err := ioutil.ReadFile(keyLocation)
+	if err != nil {
+		return []byte{}, errors.New("reading file '" + keyLocation + "':" + err.Error())
+	}
+	keyBase64 = string(keyBase64Bytes)
+
+	key, err := base64.StdEncoding.DecodeString(keyBase64)
+	if err != nil {
+		return []byte{}, errors.New("AES key cannot be decoded from base64")
+	}
+
+	// verify the key works
+	_, err = aes.NewCipher(key)
+	if err != nil {
+		return []byte{}, err
+	}
+
+	return key, nil
+}
+
+func reEncryptSslKeys(db *sqlx.DB, previousKey []byte, newKey []byte) error {
+	tx, err := db.Begin()
+	if err != nil {
+		fmt.Println("transaction begin failed ", err, " ", tx)
+		os.Exit(0)

Review comment:
       Same as above RE: exit with failure code on failure

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)
+	}
+	if newKeyLocation == nil || *newKeyLocation == "" {
+		fmt.Println("newKey flag is required.")
+		os.Exit(0)
+	}
+
+	newKey, err := readKey(*newKeyLocation)
+	if err != nil {
+		fmt.Println("reading newKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	previousKey, err := readKey(*previousKeyLocation)
+	if err != nil {
+		fmt.Println("reading previousKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	dbConfBytes, err := ioutil.ReadFile(PROPERTIES_FILE)
+	if err != nil {
+		fmt.Println("reading db conf '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	pgCfg := Config{}
+	err = json.Unmarshal(dbConfBytes, &pgCfg)
+	if err != nil {
+		fmt.Println("unmarshalling '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)

Review comment:
       Same as above RE: exit with failure code on failure

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)
+	}
+	if newKeyLocation == nil || *newKeyLocation == "" {
+		fmt.Println("newKey flag is required.")
+		os.Exit(0)
+	}
+
+	newKey, err := readKey(*newKeyLocation)
+	if err != nil {
+		fmt.Println("reading newKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	previousKey, err := readKey(*previousKeyLocation)
+	if err != nil {
+		fmt.Println("reading previousKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	dbConfBytes, err := ioutil.ReadFile(PROPERTIES_FILE)
+	if err != nil {
+		fmt.Println("reading db conf '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	pgCfg := Config{}
+	err = json.Unmarshal(dbConfBytes, &pgCfg)
+	if err != nil {
+		fmt.Println("unmarshalling '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	sslStr := "require"
+	if !pgCfg.SSL {
+		sslStr = "disable"
+	}
+	db, err := sqlx.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s&fallback_application_name=trafficvault", pgCfg.User, pgCfg.Password, pgCfg.Hostname, pgCfg.Port, pgCfg.DBName, sslStr))
+	if err != nil {
+		fmt.Println("opening database: " + err.Error())
+		os.Exit(0)
+	}
+
+	if err = reEncryptSslKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting SSL Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUrlSigKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URL Sig Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUriSigningKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URI Signing Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptDNSSECKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting DNSSEC Keys: ", err.Error())
+		os.Exit(0)
+	}
+
+	if err = updateKeyFile(previousKey, *previousKeyLocation, newKey); err != nil {
+		fmt.Println("updating the key file: ", err.Error())
+		os.Exit(0)
+	}
+
+	fmt.Println("Successfully re-encrypted keys for SSL Keys, URL Sig Keys, URI Signing Keys, and DNSSEC Keys.")
+}
+
+type Config struct {
+	DBName   string `json:"dbname"`
+	Hostname string `json:"hostname"`
+	User     string `json:"user"`
+	Password string `json:"password"`
+	Port     int    `json:"port"`
+	SSL      bool   `json:"ssl"`
+}
+
+func readKey(keyLocation string) ([]byte, error) {
+	var keyBase64 string
+	keyBase64Bytes, err := ioutil.ReadFile(keyLocation)
+	if err != nil {
+		return []byte{}, errors.New("reading file '" + keyLocation + "':" + err.Error())
+	}
+	keyBase64 = string(keyBase64Bytes)
+
+	key, err := base64.StdEncoding.DecodeString(keyBase64)
+	if err != nil {
+		return []byte{}, errors.New("AES key cannot be decoded from base64")
+	}
+
+	// verify the key works
+	_, err = aes.NewCipher(key)
+	if err != nil {
+		return []byte{}, err
+	}
+
+	return key, nil
+}
+
+func reEncryptSslKeys(db *sqlx.DB, previousKey []byte, newKey []byte) error {
+	tx, err := db.Begin()
+	if err != nil {
+		fmt.Println("transaction begin failed ", err, " ", tx)
+		os.Exit(0)
+	}
+	defer tx.Commit()
+
+	rows, err := tx.Query("SELECT id, data FROM sslkey")
+	if err != nil {
+		fmt.Println("querying: ", err)
+		os.Exit(0)
+	}
+	defer rows.Close()
+
+	for rows.Next() {
+		updateTx, err := db.Begin()
+		if err != nil {
+			fmt.Println("transaction begin failed ", err, " ", updateTx)
+			os.Exit(0)

Review comment:
       Same as above RE: exit with failure code on failure

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)
+	}
+	if newKeyLocation == nil || *newKeyLocation == "" {
+		fmt.Println("newKey flag is required.")
+		os.Exit(0)
+	}
+
+	newKey, err := readKey(*newKeyLocation)
+	if err != nil {
+		fmt.Println("reading newKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	previousKey, err := readKey(*previousKeyLocation)
+	if err != nil {
+		fmt.Println("reading previousKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	dbConfBytes, err := ioutil.ReadFile(PROPERTIES_FILE)
+	if err != nil {
+		fmt.Println("reading db conf '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	pgCfg := Config{}
+	err = json.Unmarshal(dbConfBytes, &pgCfg)
+	if err != nil {
+		fmt.Println("unmarshalling '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	sslStr := "require"
+	if !pgCfg.SSL {
+		sslStr = "disable"
+	}
+	db, err := sqlx.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s&fallback_application_name=trafficvault", pgCfg.User, pgCfg.Password, pgCfg.Hostname, pgCfg.Port, pgCfg.DBName, sslStr))
+	if err != nil {
+		fmt.Println("opening database: " + err.Error())
+		os.Exit(0)
+	}
+
+	if err = reEncryptSslKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting SSL Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUrlSigKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URL Sig Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUriSigningKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URI Signing Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptDNSSECKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting DNSSEC Keys: ", err.Error())
+		os.Exit(0)
+	}
+
+	if err = updateKeyFile(previousKey, *previousKeyLocation, newKey); err != nil {
+		fmt.Println("updating the key file: ", err.Error())
+		os.Exit(0)
+	}
+
+	fmt.Println("Successfully re-encrypted keys for SSL Keys, URL Sig Keys, URI Signing Keys, and DNSSEC Keys.")
+}
+
+type Config struct {
+	DBName   string `json:"dbname"`
+	Hostname string `json:"hostname"`
+	User     string `json:"user"`
+	Password string `json:"password"`
+	Port     int    `json:"port"`
+	SSL      bool   `json:"ssl"`
+}
+
+func readKey(keyLocation string) ([]byte, error) {
+	var keyBase64 string
+	keyBase64Bytes, err := ioutil.ReadFile(keyLocation)
+	if err != nil {
+		return []byte{}, errors.New("reading file '" + keyLocation + "':" + err.Error())
+	}
+	keyBase64 = string(keyBase64Bytes)
+
+	key, err := base64.StdEncoding.DecodeString(keyBase64)
+	if err != nil {
+		return []byte{}, errors.New("AES key cannot be decoded from base64")
+	}
+
+	// verify the key works
+	_, err = aes.NewCipher(key)
+	if err != nil {
+		return []byte{}, err
+	}
+
+	return key, nil
+}
+
+func reEncryptSslKeys(db *sqlx.DB, previousKey []byte, newKey []byte) error {
+	tx, err := db.Begin()
+	if err != nil {
+		fmt.Println("transaction begin failed ", err, " ", tx)
+		os.Exit(0)
+	}
+	defer tx.Commit()
+
+	rows, err := tx.Query("SELECT id, data FROM sslkey")
+	if err != nil {
+		fmt.Println("querying: ", err)
+		os.Exit(0)
+	}
+	defer rows.Close()
+
+	for rows.Next() {
+		updateTx, err := db.Begin()
+		if err != nil {
+			fmt.Println("transaction begin failed ", err, " ", updateTx)
+			os.Exit(0)
+		}
+		defer updateTx.Commit()
+
+		id := 0
+		var encryptedSslKeys []byte
+		if err = rows.Scan(&id, &encryptedSslKeys); err != nil {
+			fmt.Println("getting SSL Keys: ", err)
+			return err
+		}
+		jsonKeys, err := postgres.AesDecrypt(encryptedSslKeys, previousKey)
+		if err != nil {
+			fmt.Println("reading SSL Keys: ", err)
+			return err
+		}
+
+		reencryptedKeys, err := postgres.AesEncrypt(jsonKeys, newKey)
+		if err != nil {
+			fmt.Println("encrypting SSL Keys with new key: ", err)
+			return err
+		}
+
+		res, err := updateTx.Exec(`UPDATE sslkey SET data = $1 WHERE id = $2`, []byte(reencryptedKeys), id)
+		if err != nil {
+			fmt.Println("updating SSL Keys for id ", id, ": ", err)
+			return err
+		}
+		rowsAffected, err := res.RowsAffected()
+		if err != nil {
+			fmt.Println("error determining rows affected for reencrypting SSL Keys with id ", id)
+			return errors.New(fmt.Sprintf("determining rows affected for reencrypting SSL Keys with id %d", id))
+		}
+		if rowsAffected == 0 {
+			fmt.Println("no rows updated for reencrypting SSL Keys for id ", id)
+			return errors.New(fmt.Sprintf("no rows updated for reencrypting SSL Keys for id %d", id))
+		}
+
+	}
+
+	return nil
+}
+
+func reEncryptUrlSigKeys(db *sqlx.DB, previousKey []byte, newKey []byte) error {
+	tx, err := db.Begin()
+	if err != nil {
+		fmt.Println("transaction begin failed ", err, " ", tx)
+		os.Exit(0)
+	}
+	defer tx.Commit()
+
+	rows, err := tx.Query("SELECT deliveryservice, data FROM url_sig_key")
+	if err != nil {
+		fmt.Println("querying: ", err)
+		os.Exit(0)
+	}
+	defer rows.Close()
+
+	for rows.Next() {
+		updateTx, err := db.Begin()
+		if err != nil {
+			fmt.Println("transaction begin failed ", err, " ", updateTx)
+			os.Exit(0)
+		}
+		defer updateTx.Commit()
+
+		ds := ""
+		var encryptedSslKeys []byte
+		if err = rows.Scan(&ds, &encryptedSslKeys); err != nil {
+			fmt.Println("getting URL Sig Keys: ", err)
+			return err
+		}
+		jsonKeys, err := postgres.AesDecrypt(encryptedSslKeys, previousKey)
+		if err != nil {
+			fmt.Println("reading URL Sig Keys: ", err)
+			return err
+		}
+
+		reencryptedKeys, err := postgres.AesEncrypt(jsonKeys, newKey)
+		if err != nil {
+			fmt.Println("encrypting URL Sig Keys with new key: ", err)
+			return err
+		}
+
+		res, err := updateTx.Exec(`UPDATE url_sig_key SET data = $1 WHERE deliveryservice = $2`, []byte(reencryptedKeys), ds)
+		if err != nil {
+			fmt.Println("updating URL Sig Keys for deliveryservice ", ds, ": ", err)
+			return err
+		}
+		rowsAffected, err := res.RowsAffected()
+		if err != nil {
+			fmt.Println("error determining rows affected for reencrypting URL Sig Keys with deliveryservice ", ds)
+			return errors.New(fmt.Sprintf("determining rows affected for reencrypting URL Sig Keys with deliveryservice %d", ds))
+		}
+		if rowsAffected == 0 {
+			fmt.Println("no rows updated for reencrypting URL Sig Keys for id ", ds)
+			return errors.New(fmt.Sprintf("no rows updated for reencrypting URL Sig Keys for deliveryservice %d", ds))
+		}
+
+	}
+
+	return nil
+}
+
+func reEncryptUriSigningKeys(db *sqlx.DB, previousKey []byte, newKey []byte) error {
+	tx, err := db.Begin()
+	if err != nil {
+		fmt.Println("transaction begin failed ", err, " ", tx)
+		os.Exit(0)

Review comment:
       Same as above RE: exit with failure code on failure and also returning an error versus forcibly quitting

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)
+	}
+	if newKeyLocation == nil || *newKeyLocation == "" {
+		fmt.Println("newKey flag is required.")
+		os.Exit(0)
+	}
+
+	newKey, err := readKey(*newKeyLocation)
+	if err != nil {
+		fmt.Println("reading newKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	previousKey, err := readKey(*previousKeyLocation)
+	if err != nil {
+		fmt.Println("reading previousKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	dbConfBytes, err := ioutil.ReadFile(PROPERTIES_FILE)
+	if err != nil {
+		fmt.Println("reading db conf '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	pgCfg := Config{}
+	err = json.Unmarshal(dbConfBytes, &pgCfg)
+	if err != nil {
+		fmt.Println("unmarshalling '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	sslStr := "require"
+	if !pgCfg.SSL {
+		sslStr = "disable"
+	}
+	db, err := sqlx.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s&fallback_application_name=trafficvault", pgCfg.User, pgCfg.Password, pgCfg.Hostname, pgCfg.Port, pgCfg.DBName, sslStr))
+	if err != nil {
+		fmt.Println("opening database: " + err.Error())
+		os.Exit(0)
+	}
+
+	if err = reEncryptSslKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting SSL Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUrlSigKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URL Sig Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUriSigningKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URI Signing Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptDNSSECKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting DNSSEC Keys: ", err.Error())
+		os.Exit(0)
+	}
+
+	if err = updateKeyFile(previousKey, *previousKeyLocation, newKey); err != nil {
+		fmt.Println("updating the key file: ", err.Error())
+		os.Exit(0)
+	}
+
+	fmt.Println("Successfully re-encrypted keys for SSL Keys, URL Sig Keys, URI Signing Keys, and DNSSEC Keys.")
+}
+
+type Config struct {
+	DBName   string `json:"dbname"`
+	Hostname string `json:"hostname"`
+	User     string `json:"user"`
+	Password string `json:"password"`
+	Port     int    `json:"port"`
+	SSL      bool   `json:"ssl"`
+}
+
+func readKey(keyLocation string) ([]byte, error) {
+	var keyBase64 string
+	keyBase64Bytes, err := ioutil.ReadFile(keyLocation)
+	if err != nil {
+		return []byte{}, errors.New("reading file '" + keyLocation + "':" + err.Error())
+	}
+	keyBase64 = string(keyBase64Bytes)
+
+	key, err := base64.StdEncoding.DecodeString(keyBase64)
+	if err != nil {
+		return []byte{}, errors.New("AES key cannot be decoded from base64")
+	}
+
+	// verify the key works
+	_, err = aes.NewCipher(key)
+	if err != nil {
+		return []byte{}, err
+	}
+
+	return key, nil
+}
+
+func reEncryptSslKeys(db *sqlx.DB, previousKey []byte, newKey []byte) error {
+	tx, err := db.Begin()
+	if err != nil {
+		fmt.Println("transaction begin failed ", err, " ", tx)
+		os.Exit(0)

Review comment:
       Also, sometimes this function returns an error, and sometimes it forcibly exits the entire program - why not just always return the error and have the caller handle exiting or not?

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)
+	}
+	if newKeyLocation == nil || *newKeyLocation == "" {
+		fmt.Println("newKey flag is required.")
+		os.Exit(0)
+	}
+
+	newKey, err := readKey(*newKeyLocation)
+	if err != nil {
+		fmt.Println("reading newKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	previousKey, err := readKey(*previousKeyLocation)
+	if err != nil {
+		fmt.Println("reading previousKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	dbConfBytes, err := ioutil.ReadFile(PROPERTIES_FILE)
+	if err != nil {
+		fmt.Println("reading db conf '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	pgCfg := Config{}
+	err = json.Unmarshal(dbConfBytes, &pgCfg)
+	if err != nil {
+		fmt.Println("unmarshalling '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	sslStr := "require"
+	if !pgCfg.SSL {
+		sslStr = "disable"
+	}
+	db, err := sqlx.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s&fallback_application_name=trafficvault", pgCfg.User, pgCfg.Password, pgCfg.Hostname, pgCfg.Port, pgCfg.DBName, sslStr))
+	if err != nil {
+		fmt.Println("opening database: " + err.Error())
+		os.Exit(0)
+	}
+
+	if err = reEncryptSslKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting SSL Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUrlSigKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URL Sig Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUriSigningKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URI Signing Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptDNSSECKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting DNSSEC Keys: ", err.Error())
+		os.Exit(0)
+	}
+
+	if err = updateKeyFile(previousKey, *previousKeyLocation, newKey); err != nil {
+		fmt.Println("updating the key file: ", err.Error())
+		os.Exit(0)

Review comment:
       Same as above RE: exit with failure code on failure

##########
File path: traffic_ops/app/db/reencrypt/reencrypt.go
##########
@@ -0,0 +1,406 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres"
+
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+
+	"crypto/aes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+const PROPERTIES_FILE = "./reencrypt.conf"
+
+func main() {
+	previousKeyLocation := flag.String("previousKey", "", "The file path for the previous base64 encoded AES key.")
+	newKeyLocation := flag.String("newKey", "", "The file path for the new base64 encoded AES key.")
+	flag.Parse()
+
+	if previousKeyLocation == nil || *previousKeyLocation == "" {
+		fmt.Println("previousKey flag is required.")
+		os.Exit(0)
+	}
+	if newKeyLocation == nil || *newKeyLocation == "" {
+		fmt.Println("newKey flag is required.")
+		os.Exit(0)
+	}
+
+	newKey, err := readKey(*newKeyLocation)
+	if err != nil {
+		fmt.Println("reading newKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	previousKey, err := readKey(*previousKeyLocation)
+	if err != nil {
+		fmt.Println("reading previousKey: ", err.Error())
+		os.Exit(0)
+	}
+
+	dbConfBytes, err := ioutil.ReadFile(PROPERTIES_FILE)
+	if err != nil {
+		fmt.Println("reading db conf '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	pgCfg := Config{}
+	err = json.Unmarshal(dbConfBytes, &pgCfg)
+	if err != nil {
+		fmt.Println("unmarshalling '", PROPERTIES_FILE, "': ", err.Error())
+		os.Exit(0)
+	}
+
+	sslStr := "require"
+	if !pgCfg.SSL {
+		sslStr = "disable"
+	}
+	db, err := sqlx.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s&fallback_application_name=trafficvault", pgCfg.User, pgCfg.Password, pgCfg.Hostname, pgCfg.Port, pgCfg.DBName, sslStr))
+	if err != nil {
+		fmt.Println("opening database: " + err.Error())
+		os.Exit(0)
+	}
+
+	if err = reEncryptSslKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting SSL Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUrlSigKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URL Sig Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptUriSigningKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting URI Signing Keys: ", err.Error())
+		os.Exit(0)
+	}
+	if err = reEncryptDNSSECKeys(db, previousKey, newKey); err != nil {
+		fmt.Println("re-encrypting DNSSEC Keys: ", err.Error())
+		os.Exit(0)
+	}
+
+	if err = updateKeyFile(previousKey, *previousKeyLocation, newKey); err != nil {
+		fmt.Println("updating the key file: ", err.Error())
+		os.Exit(0)
+	}
+
+	fmt.Println("Successfully re-encrypted keys for SSL Keys, URL Sig Keys, URI Signing Keys, and DNSSEC Keys.")
+}
+
+type Config struct {
+	DBName   string `json:"dbname"`
+	Hostname string `json:"hostname"`
+	User     string `json:"user"`
+	Password string `json:"password"`
+	Port     int    `json:"port"`
+	SSL      bool   `json:"ssl"`
+}
+
+func readKey(keyLocation string) ([]byte, error) {
+	var keyBase64 string
+	keyBase64Bytes, err := ioutil.ReadFile(keyLocation)
+	if err != nil {
+		return []byte{}, errors.New("reading file '" + keyLocation + "':" + err.Error())
+	}
+	keyBase64 = string(keyBase64Bytes)
+
+	key, err := base64.StdEncoding.DecodeString(keyBase64)
+	if err != nil {
+		return []byte{}, errors.New("AES key cannot be decoded from base64")
+	}
+
+	// verify the key works
+	_, err = aes.NewCipher(key)
+	if err != nil {
+		return []byte{}, err
+	}
+
+	return key, nil
+}
+
+func reEncryptSslKeys(db *sqlx.DB, previousKey []byte, newKey []byte) error {
+	tx, err := db.Begin()
+	if err != nil {
+		fmt.Println("transaction begin failed ", err, " ", tx)
+		os.Exit(0)
+	}
+	defer tx.Commit()
+
+	rows, err := tx.Query("SELECT id, data FROM sslkey")
+	if err != nil {
+		fmt.Println("querying: ", err)
+		os.Exit(0)
+	}
+	defer rows.Close()
+
+	for rows.Next() {
+		updateTx, err := db.Begin()
+		if err != nil {
+			fmt.Println("transaction begin failed ", err, " ", updateTx)
+			os.Exit(0)
+		}
+		defer updateTx.Commit()
+
+		id := 0
+		var encryptedSslKeys []byte
+		if err = rows.Scan(&id, &encryptedSslKeys); err != nil {
+			fmt.Println("getting SSL Keys: ", err)
+			return err
+		}
+		jsonKeys, err := postgres.AesDecrypt(encryptedSslKeys, previousKey)
+		if err != nil {
+			fmt.Println("reading SSL Keys: ", err)
+			return err
+		}
+
+		reencryptedKeys, err := postgres.AesEncrypt(jsonKeys, newKey)
+		if err != nil {
+			fmt.Println("encrypting SSL Keys with new key: ", err)
+			return err
+		}
+
+		res, err := updateTx.Exec(`UPDATE sslkey SET data = $1 WHERE id = $2`, []byte(reencryptedKeys), id)
+		if err != nil {
+			fmt.Println("updating SSL Keys for id ", id, ": ", err)
+			return err
+		}
+		rowsAffected, err := res.RowsAffected()
+		if err != nil {
+			fmt.Println("error determining rows affected for reencrypting SSL Keys with id ", id)
+			return errors.New(fmt.Sprintf("determining rows affected for reencrypting SSL Keys with id %d", id))
+		}
+		if rowsAffected == 0 {
+			fmt.Println("no rows updated for reencrypting SSL Keys for id ", id)
+			return errors.New(fmt.Sprintf("no rows updated for reencrypting SSL Keys for id %d", id))
+		}
+
+	}
+
+	return nil
+}
+
+func reEncryptUrlSigKeys(db *sqlx.DB, previousKey []byte, newKey []byte) error {
+	tx, err := db.Begin()
+	if err != nil {
+		fmt.Println("transaction begin failed ", err, " ", tx)
+		os.Exit(0)
+	}
+	defer tx.Commit()
+
+	rows, err := tx.Query("SELECT deliveryservice, data FROM url_sig_key")
+	if err != nil {
+		fmt.Println("querying: ", err)
+		os.Exit(0)
+	}
+	defer rows.Close()
+
+	for rows.Next() {
+		updateTx, err := db.Begin()
+		if err != nil {
+			fmt.Println("transaction begin failed ", err, " ", updateTx)
+			os.Exit(0)
+		}
+		defer updateTx.Commit()
+
+		ds := ""
+		var encryptedSslKeys []byte
+		if err = rows.Scan(&ds, &encryptedSslKeys); err != nil {
+			fmt.Println("getting URL Sig Keys: ", err)
+			return err
+		}
+		jsonKeys, err := postgres.AesDecrypt(encryptedSslKeys, previousKey)
+		if err != nil {
+			fmt.Println("reading URL Sig Keys: ", err)
+			return err
+		}
+
+		reencryptedKeys, err := postgres.AesEncrypt(jsonKeys, newKey)
+		if err != nil {
+			fmt.Println("encrypting URL Sig Keys with new key: ", err)
+			return err
+		}
+
+		res, err := updateTx.Exec(`UPDATE url_sig_key SET data = $1 WHERE deliveryservice = $2`, []byte(reencryptedKeys), ds)
+		if err != nil {
+			fmt.Println("updating URL Sig Keys for deliveryservice ", ds, ": ", err)
+			return err
+		}
+		rowsAffected, err := res.RowsAffected()
+		if err != nil {
+			fmt.Println("error determining rows affected for reencrypting URL Sig Keys with deliveryservice ", ds)
+			return errors.New(fmt.Sprintf("determining rows affected for reencrypting URL Sig Keys with deliveryservice %d", ds))
+		}
+		if rowsAffected == 0 {
+			fmt.Println("no rows updated for reencrypting URL Sig Keys for id ", ds)
+			return errors.New(fmt.Sprintf("no rows updated for reencrypting URL Sig Keys for deliveryservice %d", ds))
+		}
+
+	}
+
+	return nil
+}
+
+func reEncryptUriSigningKeys(db *sqlx.DB, previousKey []byte, newKey []byte) error {
+	tx, err := db.Begin()
+	if err != nil {
+		fmt.Println("transaction begin failed ", err, " ", tx)
+		os.Exit(0)
+	}
+	defer tx.Commit()
+
+	rows, err := tx.Query("SELECT deliveryservice, data FROM uri_signing_key")
+	if err != nil {
+		fmt.Println("querying: ", err)
+		os.Exit(0)
+	}
+	defer rows.Close()
+
+	for rows.Next() {
+		updateTx, err := db.Begin()
+		if err != nil {
+			fmt.Println("transaction begin failed ", err, " ", updateTx)
+			os.Exit(0)
+		}
+		defer updateTx.Commit()
+
+		ds := ""
+		var encryptedSslKeys []byte
+		if err = rows.Scan(&ds, &encryptedSslKeys); err != nil {
+			fmt.Println("getting URI Signing Keys: ", err)
+			return err
+		}
+		jsonKeys, err := postgres.AesDecrypt(encryptedSslKeys, previousKey)
+		if err != nil {
+			fmt.Println("reading URI Signing Keys: ", err)
+			return err
+		}
+
+		reencryptedKeys, err := postgres.AesEncrypt(jsonKeys, newKey)
+		if err != nil {
+			fmt.Println("encrypting URI Signing Keys with new key: ", err)
+			return err
+		}
+
+		res, err := updateTx.Exec(`UPDATE uri_signing_key SET data = $1 WHERE deliveryservice = $2`, []byte(reencryptedKeys), ds)
+		if err != nil {
+			fmt.Println("updating URI Signing Keys for deliveryservice ", ds, ": ", err)
+			return err
+		}
+		rowsAffected, err := res.RowsAffected()
+		if err != nil {
+			fmt.Println("error determining rows affected for reencrypting URI Signing Keys with deliveryservice ", ds)
+			return errors.New(fmt.Sprintf("determining rows affected for reencrypting URI Signing Keys with deliveryservice %d", ds))
+		}
+		if rowsAffected == 0 {
+			fmt.Println("no rows updated for reencrypting URI Signing Keys for id ", ds)
+			return errors.New(fmt.Sprintf("no rows updated for reencrypting URI Signing Keys for deliveryservice %d", ds))
+		}
+
+	}
+
+	return nil
+}
+
+func reEncryptDNSSECKeys(db *sqlx.DB, previousKey []byte, newKey []byte) error {
+	tx, err := db.Begin()
+	if err != nil {
+		fmt.Println("transaction begin failed ", err, " ", tx)
+		os.Exit(0)

Review comment:
       Same as above RE: exit with failure code on failure and also returning an error versus forcibly quitting




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org