-
Notifications
You must be signed in to change notification settings - Fork 3
Description
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
Obtain the message digestWrap 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)
Perform type 01 encryption-block formattingA 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)
Perform the classic RSA computationy = 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.