codesign tool generates "timestamps differ by XXX seconds" error

We have been having unexplained failures with the codesign tool recently on macosx aarch64 and x64 hosts. Every once in a while when signing an app locally using the following command:

/usr/bin/codesign -s - -vvvv --force /home/me/FooBarCalculator.app

results in the following error:

/home/me/FooBarCalculator.app: timestamps differ by 185 seconds - check your system clock

The number of seconds reported in the error message keeps varying (but usually in that range). We have checked the system clock but there isn't anything wrong (from what we can see) with the host. In fact, we have been seeing this error on several hosts now, so it isn't specific to one host.

While looking into this issue, we even printed the details of an already signed binary using the following command:

codesign -dvvv HelloWorld.app

and that prints among other things, similar warning message:

...
Timestamp=12 May 2026 at 5:36:0 AM
HelloWorld.app: timestamp mismatch: internal time 12 May 2026 at 5:32:59 AM (184 seconds apart)

I'm looking for inputs on how we go about debugging this issue and where/how the codesign tool sources these timestamps from (any specific API?) and what value is it comparing against to notice a difference.

These affected hosts have different operating system versions some 15.x and some 26.x.

Answered by DTS Engineer in 887833022
we even printed the details of an already signed binary … and that prints [a] similar warning message

OK. Lemme explain how that message comes about, and I think you’ll be able to extrapolate from there.

The codesign tool calls SecCodeCopySigningInformation and looks at two properties: kSecCodeInfoTime and kSecCodeInfoTimestamp. If the values differ significantly (3 minutes IIRC) you get this message.

While these properties are covered reasonably well by the documentation, the best source of info about them is the doc comments in <Security/SecCode.h>. In summary:

  • kSecCodeInfoTimestamp is secure, having been issued by Apple’s timestamp service. For more on that, see the Certificate expiration section of TN3161 Inside Code Signing: Certificates.
  • kSecCodeInfoTime is based on your Mac’s time setting.

So I suspect that the Mac doing the signing has a clock that’s way of sync with reality, so these two values end up significantly different, and thus this problem.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

we even printed the details of an already signed binary … and that prints [a] similar warning message

OK. Lemme explain how that message comes about, and I think you’ll be able to extrapolate from there.

The codesign tool calls SecCodeCopySigningInformation and looks at two properties: kSecCodeInfoTime and kSecCodeInfoTimestamp. If the values differ significantly (3 minutes IIRC) you get this message.

While these properties are covered reasonably well by the documentation, the best source of info about them is the doc comments in <Security/SecCode.h>. In summary:

  • kSecCodeInfoTimestamp is secure, having been issued by Apple’s timestamp service. For more on that, see the Certificate expiration section of TN3161 Inside Code Signing: Certificates.
  • kSecCodeInfoTime is based on your Mac’s time setting.

So I suspect that the Mac doing the signing has a clock that’s way of sync with reality, so these two values end up significantly different, and thus this problem.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you Quinn, this is helpful. I just read up the contents of the header file you note and the comments on those fields are clear.

So I suspect that the Mac doing the signing has a clock that’s way of sync with reality, so these two values end up significantly different, and thus this problem.

I read through the linked TN3161: Inside Code Signing: Certificates | Apple Developer Documentation and it says that it contacts timestamp.apple.com for getting a secure timestamp. Is there a tool that I could use to directly issue a similar request against that server and see what it returns? That might help us debug this on many of these hosts where this happens intermittently. As far as I can see, there's no option/flag in codesign which would allow us to print verbose logs showing it communicating with the timestamp authority server.

One other question, that doc also says:

Don’t confused the Timestamp and Signed Time fields. The latter is not secured by the Apple timestamp service. Rather, codesign sets this field based on your Mac’s current time.

I think it corresponds to the kSecCodeInfoTime field we are discussing here. Would you happen to know, what API the codesign tool uses in its implementation to obtain the host's current time?

Is there a tool that I could use to directly issue a similar request against that server and see what it returns?

Not as such.

This service isn’t considered API, but some spelunking reveals details about the implementation, namely that it’s HTTP:

% nc timestamp.apple.com 80     
GET / HTTP/1.1
Connection: close
Host: timestamp.apple.com

HTTP/1.1 302 Moved Temporarily
…

As to the request method, path, and payload, I’m gonna give you the opportunity to work those out (-: If you get stuck, you can pull the thread starting from here in Darwin.

there's no option/flag in codesign which would allow us to print verbose logs showing it communicating with the timestamp authority server.

Agreed.

what API the codesign tool uses in its implementation to obtain the host's current time?

It’s gettimeofday [1].

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Actually, it looks like it’s CFAbsoluteTimeGetCurrent, which uses CLOCK_REALTIME, but that’s defined to be the same as gettimeofday.

Thank you Quinn for these additional details.

In the meantime, we have been able to make some progress here. To recap, the error message we have been investigating is the following one when codesign command is executed on a host, let's say H2, to do "adhoc" signing of some binary:

$> /usr/bin/codesign -s - -vvvv --force /home/me/FooBarCalculator.app

/home/me/FooBarCalculator.app: replacing existing signature
/home/me/FooBarCalculator.app: signed app bundle with Mach-O thin (arm64) [Hello]
/home/me/FooBarCalculator.app: timestamps differ by 185 seconds - check your system clock

codesign then exits with a non-zero exit code and thus errors out.

Our investigation so far shows that the message about timestamps differing turns out to be misleading.

What we have been able to narrow down is that, if on some host H1, a binary (FooBarCalculator.app in this case) was signed with a (valid) identity and if for whatever reason, during codesigning there was a timestamp difference, then that difference gets embedded in the signed binary. This part is fine, because you already explained how the timestamp values get stamped within the signature and are stored in the 2 fields. Of course, it's a different matter that we should investigate why that timestamp difference happened during signing.

What's confusing and what has been causing us trouble is that when that signed binary is then copied/downloaded over to some other host (H2 in this case) and when we try to "adhoc" sign that binary using codesign tool (for some specific reasons), then the tool when running on host H2 tries to replace the signature (which is understandable). During that replacement, it notices that the previous signature on that binary has the timestamp difference (embedded in that signature) and thus writes out that "timestamps differ by 185 seconds - check your system clock". This gives the wrong impression that the timestamp difference is on host H2 and happened when the "adhoc" signing was going on, while in reality the tool is merely reporting the difference that was present in the previous signature (on host H1). It might have been slightly more useful if the message included the timestamps that mismatch (like it does when codesign -dvvv is used). So something like the following would have been more useful:

/home/me/FooBarCalculator.app: timestamp mismatch: Timestamp=12 May 2026 at 4:18:27 PM, internal time 12 May 2026 at 4:15:22 PM (185 seconds apart)

Those actual timestamps might have given us hints that the message is about the existing signature.

What's more interesting is that even if codesign reports this message and exits with a non-zero exit code (implying an error), it goes ahead and updates the binary with an adhoc signature. It's not clear if it should be doing that. If it's indeed OK to update the binary with the adhoc signature after reporting this error, then maybe the timestamp difference message should be a warning and the tool should exit with an exit code of zero?

P.S: Investigation is still going on to understand why the original binary which was signed using a valid identity ended up having the timestamp difference. But that issue is a bit more easier to investigate, because we can now actually check the timestamp on the right host(s) and check the timestamp authority server in use, when the codesigning is in progress.

This gives the wrong impression that the timestamp difference is on host H2 … while in reality the tool is merely reporting the difference that was present in the previous signature (on host H1).

Ah, yeah, I can see how that’d happen and also how it’s super confusing. I think you should file a bug against codesign for better diagnostics here; please post your bug number, just for the record.

maybe the timestamp difference message should be a warning and the tool should exit with an exit code of zero?

Agreed. Again, I’d appreciate you filing a second bug about that.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

codesign tool generates "timestamps differ by XXX seconds" error
 
 
Q