I'm using softHSM (FWIW with a go library https://github.com/ThalesIgnite/crypto11, documentation here https://pkg.go.dev/github.com/ThalesIgnite/crypto11?tab=doc).
My goal is to store a 'master key' (AES256) for encrypting objects similarly to how AWS S3 does into the HSM device (because it's more secure). From that key, just derive any other key that I need to encrypt my objects (or decrypt them).
I'm failing at understanding how a generated secret key in a HSM can later be retrieved by the same software program. I see that the API mentions of a context..
rephrased: when I generate a secret key in the HSM like this:
func TestFindingAllKeys(t *testing.T) {
withContext(t, func(ctx *Context) {
for i := 0; i < 10; i++ {
id := randomBytes()
key, err := ctx.GenerateSecretKey(id, 128, CipherAES)
require.NoError(t, err)
defer func(k *SecretKey) { _ = k.Delete() }(key)
}
keys, err := ctx.FindAllKeys()
require.NoError(t, err)
require.NotNil(t, keys)
require.Len(t, keys, 10)
})
}
how do I 'associate' one of those secret keys with my program data (e.g. a S3 bucket or customer)?
How do I retrieve that same secret key again (even if I can't dump it out of the HSM) to decrypt the data at a later time?
I'm missing this apparently stupid, but crucial connection: how does one retrieve a previously generated secret key again at a later time?
You can use pkcs#11 token labels, or equivalent to tag symmetric keys.
You could also use the slot concept, keeping a local database mapping users/customers to keys.
For asymmetric primitives you can export the public key and map this object to a customer/user.
Related
I've been creating a gateway for a legacy service, this legacy service needs a signature as a body parameter of a PUT request, in order to create this sign I need to follow the following steps:
Create a hash with certain text as data, this hash needs to be SHA256.
Encrypt the result of the hash using RSA with a PEM key
Encode the result of the RSA to Base64
Following the previous steps I create the following code
export class Signature {
// class body
public static sign(text: string){
const key = readFileSync('key.pem')
const passphrase = '12345678'
const createdSign = createSign('RSA-SHA256')
createdSign.write(text)
createdSign.end()
return createdSign.sign({ key, passphrase }).toString('base64')
}
}
But I'm not sure if this the correct implementation, taking into consideration the previous steps, and the existence of the hash API in NodeJS.
If someone could tell me if I'm correctly implementing this algorithm.
For some reason, I've been asked to implement some basic encryption to secure a specific value transmitted to the client (depending on the client).
Context is: we are the one who generate the encrypted key, we pass that encrypted key to the client, and the client will never have to decrypt it (but we will have to in backend)
Example => we give the encrypted key "123ABCDE==" to the client. The client calls our API passing data + that encrypted key, like:
{
"encKey": "123ABCDE==",
"payload": "somedatahere"
}
Then we decrypt the key, if it matches a specific value in DB (again, depending on client), we continue with some other operations.
So, I decided to go with AES encryption. Following is what I have for now.
definition of the key information:
public class KeyInfo
{
public byte[] Key { get; }
public byte[] Iv { get; }
public KeyInfo()
{
using (var myAes = Aes.Create())
{
Key = myAes.Key;
Iv = myAes.IV;
}
}
public KeyInfo(string key, string iv)
{
Key = Convert.FromBase64String(key);
Iv = Convert.FromBase64String(iv);
}
}
the encryption method
private static byte[] Encrypt_AES(string plainText, byte[] key, byte[] iv)
{
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException(nameof(plainText));
if (key == null || key.Length <= 0)
throw new ArgumentNullException(nameof(key));
if (iv == null || iv.Length <= 0)
throw new ArgumentNullException(nameof(iv));
byte[] encrypted;
using (var aesAlgo = Aes.Create())
{
aesAlgo.Key = key;
aesAlgo.IV = iv;
var encryptor = aesAlgo.CreateEncryptor(aesAlgo.Key, aesAlgo.IV);
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (var swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
return encrypted;
}
the decryption method:
private static string Decrypt_AES(byte[] cipherText, byte[] key, byte[] iv)
{
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException(nameof(cipherText));
if (key == null || key.Length <= 0)
throw new ArgumentNullException(nameof(key));
if (iv == null || iv.Length <= 0)
throw new ArgumentNullException(nameof(iv));
string plaintext;
using (var aesAlgo = Aes.Create())
{
aesAlgo.Key = key;
aesAlgo.IV = iv;
var decryptor = aesAlgo.CreateDecryptor(aesAlgo.Key, aesAlgo.IV);
using (var msDecrypt = new MemoryStream(cipherText))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt))
{
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
}
the key and IV stored in appsettings.json:
"EncryptionKey": "myEncryptionKeyHere",
"EncryptionInitialVector": "myInitialVectorHere",
the registration in Satrtup.cs
services.AddTransient(ec => new EncryptionService(new KeyInfo(appSettings.EncryptionKey, appSettings.EncryptionInitialVector)));
I have a few question about all of this.
is AES rh right choice for my needs?
is the way it is implemented here correct?
where should I store the Key and IV ? (not sure appsettings.json is okay)
how do I generate the Key and IV ? Is there tools for that?
Thanks for reading!
EDIT:
You say "we are the one who generate the encrypted key, we pass that
encrypted key to the client" - how does that happen securely?
-> The client will have to connect to his account where he could access that encKey.
So, reading #vcsjones answer, AES may not be the right thing to implement here. Since I don't want to store the IV on database, if the client loses it, it means he would have to generate another key, with another IV, and change the encKey in all applications.
Would an asymetric encryption be better? If I understood correctly, it would mean to encrypt the value with a private key, give that encrypted value to the client + the public key (which would be the same for every client?)
is AES the right choice for my needs?
By itself, no it is not. AES is a cryptographic primitive - a building block - and such building blocks are not usually useful by themselves. For example with AES-CBC (the mode you are using), this is currently vulnerable to a padding oracle attack and lacks authentication. AES might be the right choice when combined with other primitives that provide authentication, like an HMAC.
The best way to solve this problem is to treat primitives for what they are - primitives that are insufficient for use on their own. There are other libraries, like libsodium, that are more abstracted concepts of cryptography, and provides simple APIs that "do the right thing".
You say "we are the one who generate the encrypted key, we pass that encrypted key to the client" - how does that happen securely?
Barring using something like libsodium, there are some issues to address.
There is no authentication of the ciphertext ("authentication" in cryptography has its own meaning, not like sign-on authentication).
The initialization vector should never be used more than once with the same key. You appear to be using a fixed IV. Every thing you encrypt should use its own random IV. The IV is not a secret. It should be authenticated however along with the cipher text as per point 1.
where should I store the Key and IV ?
The IV, since there should be a 1:1 of them with each cipher text (encrypted output) should be stored with the cipher text. When it's time to decrypt, the caller will need to provide the IV again.
The Key is the real secret. Storing them securely is important. If you store it in appsettings.json, then the security of your key is the same as the security of that JSON file. A more common approach in a cloud environment is to use a managed service. Azure has Azure Key Vault, AWS has KMS and Secret Manager.
how do I generate the Key and IV ? Is there tools for that?
They should be generated with a CSPRNG. For example, to do so programmatically:
byte[] iv = new byte[128 / 8];
RandomNumberGenerator.Fill(iv);
//iv now contains random bytes
You can do the same for generating a key.
I want to implement the Proof of Possession with an Asymmetric Key specs.
I would like to store the RSA in secure place in the browser - I will be able to sign the part of the request with private key and with public key as part of JWT verify the request.
I don't know how import RSA into my browser - where can I store RSA private key secure in browser?
Look at the WebCrypto API and IndexedDB. There are some WebCrypto examples here - https://github.com/diafygi/webcrypto-examples.
This will allow you to import the key in a way that it cannot be extracted - only used to sign and verify signatures. This will however be wiped when browser data is cleared with the "clear application data" (/similar - depends on browser) flag is ticked.
For example to import a key and store it in an IndexedDB (this code hasn't been tested / _openDb would have to be implemented) -
window.crypto.subtle.importKey(
"jwk", //can be "jwk" (public or private), "spki" (public only), or "pkcs8" (private only)
{ //this is an example jwk key, other key types are Uint8Array objects
kty: "RSA",
e: "AQAB",
n: "vGO3eU16ag9zRkJ4AK8ZUZrjbtp5xWK0LyFMNT8933evJoHeczexMUzSiXaLrEFSyQZortk81zJH3y41MBO_UFDO_X0crAquNrkjZDrf9Scc5-MdxlWU2Jl7Gc4Z18AC9aNibWVmXhgvHYkEoFdLCFG-2Sq-qIyW4KFkjan05IE",
alg: "PS256",
ext: true,
},
{ //these are the algorithm options
name: "RSA-PSS",
hash: {name: "SHA-256"}, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
},
false, //whether the key is extractable (i.e. can be used in exportKey)
["verify"] //"verify" for public key import, "sign" for private key imports
).then(function(key){
((_db_handle === null) ?
_openDb() :
Promise.resolve()
).then(function() {
let tx = _db_handle.transaction("KeyStore", "readwrite");
let store = tx.objectStore("KeyStore");
let putKey = store.put({id: "Key 1", key: key});
putKey.onsuccess = function() {
resolve();
};
putKey.onerror = function() {
reject(putKey.error);
};
});
})
Then to read it back out -
let tx = _db_handle.transaction("KeyStore", "readwrite");
let store = tx.objectStore("KeyStore");
let getKey = store.get("Key 1");
getKey.onsuccess = function() {
resolve(getKey.result ? getKey.result.key : null);
};
getKey.onerror = function() {
reject(getKey.error);
};
Maybe you can create an extension for holding keys. Consider that if you need to store private keys in a browser, you should only allow to store encrypted keys per default and in addition if you want/need to store more keys don't allow the same password on different key files.
Also consider using a master key and/or OTPs to decrypt the store.
Looking for an extension where these things are available could save some work
I have some experience using Go, but now I don't really understand the complexity in security of what I am doing, so I need to ask.
I am creating an RSA private key, converting to PEM and then encryping it with a passphrase.
So, how secure is to store it in a public place?
I'm not looking for answers like "it's ok, just change the passphrase over time", I really want to know which mechanism of cypher Golang is using to do it and if is safe to leave the encrypted PEM in, for example, a public blockchain and why I can do it or why I cannot.
I'm leaving here the code I am using right now:
func New(passphrase string)(*pem.Block, error){
pk, err := createPrivateKey(2048)
if err != nil {
return false, err
}
pem := getPemFromPK(pk)
block, err := EncryptPEMBlock(pem,passphrase)
if err != nil {
return false, err
}
return block,nil
}
func createPrivateKey(bits int) (*rsa.PrivateKey, error){
pk, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return nil, err
}
return pk,nil
}
func getPemFromPK(pk *rsa.PrivateKey) (*pem.Block){
block := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(pk),
}
return block
}
func EncryptPEMBlock(block *pem.Block, passphrase string) (*pem.Block, error){
block, err := x509.EncryptPEMBlock(rand.Reader, block.Type, block.Bytes, []byte(passphrase), x509.PEMCipherAES256)
if err != nil {
return nil, err
}
return block,nil
}
Thank you very much.
Edit:
As an answer here and other forums, it is not recommended to publish in public any type of private key, even if encrypted.
This topic is answered.
You are making a mistake in your thinking about what a private key is and what a passphrase is. The passphrase is used to encrypt and unencrypt your private key - if you are storing a key file which needs a passphrase to be used, then that file contains your encrypted key.
If you store the "private key" as you say, it sounds like you wish to publicly store the unencrypted key. However, even if you publish an encrypted private key on a public online repository, there's many ways to crack a passphrase. If the passphrase is short or unsecure in other ways, the attacker now has your private key. If they target you and gain access to a machine of yours that has used this key in an application (i.e. bash), then they can just access bash history log to find the passphrase.
Sometimes actually, it's trivial to keylog someone in a targeted attack.
There are many many things that can go wrong if you store an unencrypted private key online.
I'm doing some tests with HMAC by using a time-window mechanism based on UTC+0 synced time. The server has a special public API call http://myserver.com/api/servertime/ that will return the server's exact UTC+0 time. This way the API users can sync their requesting client so it will be able to match the time window my API allows for secure calls. I built in a 30 minute timeslot (-15min - +15min).
My code looks like this:
func GenerateHmac512(message []byte, key []byte) []byte {
h := hmac.New(sha512.New, key)
h.Write(message)
return []byte(base64.StdEncoding.EncodeToString(h.Sum(nil)))
}
func ValidateHmac512(message, messageMAC, key []byte) bool {
var err error
decryptedMessageMAC, err := base64.StdEncoding.DecodeString(string(messageMAC))
if err != nil {
log.Fatalln(err.Error())
return false
}
mac := hmac.New(sha512.New, key)
mac.Write(message)
expectedMAC := mac.Sum(nil)
return hmac.Equal(decryptedMessageMAC, expectedMAC)
}
func main() {
timestamp := time.Now().Unix()
key := []byte("afad9411468602782fb62d904f623d87")
message := []byte(fmt.Sprintf("SecretHash,Value1,Value2,Value3,TimeStamp:%d", time.Now().Unix()))
hmacHash := GenerateHmac512(message, key)
hmacValid := ValidateHmac512(message, hmacHash, key)
log.Println(timestamp)
log.Println(string(hmacHash))
log.Println(hmacValid)
requestValid := false
if timestamp > time.Now().Unix()-(60*15) && timestamp < time.Now().Unix()+(60+15) {
requestValid = true
}
log.Println(requestValid)
}
I'm hashing the timestamp that will be publicly provided in the call in my HMAC hash, combined with the secret hash. I'm wondering if this is fool-proof enough, or it would need more work to make it totally solid? The call would be something like this:
POST http://myserver.com/api/users/
Value1 : Data1
Value2 : Data2
Value3 : Data3
Timestamp : 1420497639
Eventually when this is all OK I'm gonna send this data over SSL/TLS. I know SSL is more than enough and HMAC wouldn't be needed, but I like to have these 3 layers of security. And I want to benchmark variations of these layers to see what the performance impact is and how I can tweak it to have a good balance between performance and security.
There's not much to answer here, an HMAC authenticates a message and verifies integrity, and that seems to be what you want. Also, TLS is only "more than enough" if you're authenticating the client. If this is an unauthenticated call, then HMAC is still reasonable to prove the knowledge of a shared secret.
Note that SecretHash is superfluous. You already have a secret shared key.