Skip to content

Add support for managing CA keys in Key Vault instead of as secrets #4

@thenewwazoo

Description

@thenewwazoo

At present, the CA signing key is stored as a PEM-encoded plaintext secret in the Key Vault. Instead, do RSASSA-PKCS1-v1_5 with SHA1 using AKV RSNULL support. From correspondence:

Here's an example of SHA-1 signing with Key Vault using RSNULL, along with some details on what our RSNULL algorithm does below.

    static async Task<bool> KeyVaultSignAndVerifyWithSHA1(KeyVaultClient client, string keyIdentifier)
    {
        // Create a SHA1 hash
        var digest = SHA1Managed.Create().ComputeHash(new byte[] { 1, 2, 3 } );

        // Create a DigestInfo structure and sign it with RSNULL
        var digestInfo = GetDigestInfoForSHA1Digest(digest);
        var result = await client.SignAsync(keyIdentifier, "RSNULL", digestInfo);

        // ****************
        // * Verification *
        // ****************
        // Get the public key, and use it to validate the signature
        var rsaKey = (await client.GetKeyAsync(keyIdentifier)).Key.ToRSA(false);
        var isValid = rsaKey.VerifyHash(digest, "SHA1", result.Result);
        if (!isValid)
            throw new Exception("Signature was invalid");

        return true;
    }

    static byte[] GetDigestInfoForSHA1Digest(byte[] digest)
    {
        // This function constructs an ASN.1 DigestInfo structure for the caller-specified hash. 
        // For SHA-1 this is 35 bytes of ASN.1 encoded data where the first 15 bytes are predetermined and the last 20 bytes are the SHA1 digest
        // ASN.1 data:
        // 30 21                   SEQUENCE DigestInfo (33 bytes) (13 remaining of header data + 20 bytes of digest)
        // |  30 09                  SEQUENCE AlgorithmIdentifier (9 bytes)
        // |  |  06 05                 OBJECT IDENTIFIER algorithm (5 bytes)
        // |  |  |  2b 0e 03 02 1a       OID of SHA1 (1.3.14.3.2.26)
        // |  |  05 00                 NUL algorithm parameters (05 00 is the DER encoding for NUL)
        // |  04 14                  OCTET STRING digest (20 bytes)
        // |  |  {hash}                Caller-specified SHA1 digest
        byte[] digestInfo = new byte[] { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 };
        digest.CopyTo(digestInfo, 15);
        return digestInfo;
    }

RSNULL
There are essentially 4 steps to PKCS #1 v 1.5 signing

  1.   Obtain the message digest
    
  2.   Wrap the message digest in an ASN.1 encoded DigestInfo structure:
    

DigestInfo ::= SEQUENCE {
digestAlgorithm DigestAlgorithmIdentifier,
digest Digest }
DigestAlgorithmIdentifier ::= AlgorithmIdentifier
Digest ::= OCTET STRING (From https://www.ietf.org/rfc/rfc2313.txt)

  1.   Perform type 01 encryption-block formatting
    

A block type BT, a padding string PS, and the data D shall be
formatted into an octet string EB, the encryption block.
EB = 00 || BT || PS || 00 || D . (1)
The block type BT shall be a single octet indicating the structure of
the encryption block.

The padding string PS shall consist of k-3-||D|| octets. For block type 01, they
shall have value FF. This makes the length of the
encryption block EB equal to k. (From https://www.ietf.org/rfc/rfc2313.txt)

  1.   Perform the classic RSA computation
    

y = x^c mod n, 0 <= y < n (From https://www.ietf.org/rfc/rfc2313.txt)

In our RS256, RS384, and RS512 implementations, we take the output of step 1 (the hash) as input, and perform the remaining 3 steps.

RSNULL is a non-JWA-standard algorithm we support which takes the output of step 2 and performs steps 3 and 4. It was added to support TLS pre-1.2 RSA signing (which uses a concatenation of SHA1 and MD5 digests), but it can also be used for normal PKCS #1 v1.5 signing with hash algorithms we don't support directly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions