Hi,
the following problem was reproduced with several OpenSSL 1.0.1 versions
and also with a recent build from the OpenSSL_1_0_2-stable branch:
RFC 3161 says in "2.3. Identification of the TSA":
"The corresponding certificate MUST contain only one instance of the
extended key usage field extension as defined in [RFC2459] Section
4.2.1.13 with KeyPurposeID having value:"
id-kp-timeStamping. This extension MUST be critical."
The "openssl ts -verify" command resp. the corresponding function
"check_purpose_timestamp_sign()" in source file v3_purp.c does check
this requirement. However, the check fails to detect a missing critical
flag if the extensions of the TSA certificate are arranged in a specific
order.
How to reproduce:
Save the three attachments into a directory and run the shell script
"tsacritical.sh". The shell scripts creates a CA and TSA certificate
from scratch, creates a time-stamp request, a corresponding time-stamp
reply and verifies the time-stamp reply.
Actual output (only last two lines):
Response has been generated.
Verification: OK
Expected result:
The time-stamp response must not be generated because of the missing
critical flag for the extended key usage field extension.
The expected result actually is produced if the order of the extension
in the TSA certificate is reversed. See file "tsa.extfile":
With this order the missing critical flag is not detected:
extendedKeyUsage = timeStamping
keyUsage = critical,nonRepudiation
When the order is reversed, the missing critical flag is detected:
keyUsage = critical,nonRepudiation
extendedKeyUsage = timeStamping
With this order of extensions the "openssl ts -reply" command will fail
with the following expected error message when running the test script:
Response is not generated.
140735124738912:error:2F083075:time stamp
routines:TS_RESP_CTX_set_signer_cert:invalid signer certificate
purpose:ts_rsp_sign.c:206:
The problematic source code is in function
check_purpose_timestamp_sign() in source file v3_purp.c:
/* Only time stamp key usage is permitted and it's required. */
if (!(x->ex_flags & EXFLAG_XKUSAGE) || x->ex_xkusage != XKU_TIMESTAMP)
return 0;
/* Extended Key Usage MUST be critical */
i_ext = X509_get_ext_by_NID((X509 *) x, NID_ext_key_usage, 0);
if (i_ext >= 0)
{
X509_EXTENSION *ext = X509_get_ext((X509 *) x, i_ext);
if (!X509_EXTENSION_get_critical(ext))
return 0;
}
return 1;
With the "bad" extension order (1) extendedKeyUsage, (2) keyUsage the
function X509_get_ext_by_NID() does not retrieve the extension and
returns a negative return value. Therefore the condition (i_ext >= 0) is
false and 1 is returned.
I was not able to find the root cause why X509_get_ext_by_NID() fails to
retrieve the extension here, but the function
check_purpose_timestamp_sign() should also not return 1 if the extended
key usage extension cannot be retrieved, as the first if statement has
already checked via the flags that the extended key usage exists and the
the value is timeStamping.
Best Regards
Stephan Muehlstrasser