ACME-Server-ADCS icon indicating copy to clipboard operation
ACME-Server-ADCS copied to clipboard

Could you be tempted to add client support to enable the Apple community?

Open mcnaugha opened this issue 9 months ago • 6 comments

Apologies if this has been asked before. No chance you could provide client authentication support so that this becomes a viable option for the Apple device community?

https://developer.apple.com/documentation/devicemanagement/acmecertificate

Thanks!

mcnaugha avatar Feb 24 '25 22:02 mcnaugha

Last time I tried to look into it, I stopped since Apple didn't really make clear, what the RFC for the server side would be. If that exists now, I'll implement it. Can you by chance provide a link to an RFC or something for the challenge?

glatzert avatar Feb 25 '25 07:02 glatzert

That page says:

"The device issues a new order request using the ClientIdentifier as the permanent-identifier. For compatibility, the ACME server needs to respond with a challenge type of device-attest-01. Then the client replies with a WebAuthn attestation statement."

Is that what you mean? device-attest-01?

mcnaugha avatar Feb 25 '25 12:02 mcnaugha

So it seems to be quite archievable and more or less straight forward to implement, but I have no means of testing it. I neither have a Mac or iOS device nor the infrastructure to roll those settings to a device.

glatzert avatar Mar 07 '25 13:03 glatzert

I am happy to test if you need assistance? I have a few old iOS and macOS devices I can spare.

I'm assuming @mcnaugha that this is for InTune or JAMF deployments?

madtempest avatar Mar 09 '25 10:03 madtempest

I would be happy to test too. If that's all that's needed.

Intune is what I'd use at the moment but the MDM shouldn't matter. It should be pretty agnostic. Joe Bloggs won't get a nice UI yet from Intune or others that haven't built one yet but that's just how it is for now.

Knowing Apple though, there will end up being some quirk that we could only find out about with direct consultancy with Developer Support. I'm game if you are.

mcnaugha avatar Mar 09 '25 11:03 mcnaugha

I'll @ you two, as soon as I have a prototype.
Currently I'm working on ExternalAccountBinding (which is probalby finished now) and will do some code cleanup regarding error handling and hosting code.

After that device-attest-01 is on the list (might be start of April).

glatzert avatar Mar 10 '25 08:03 glatzert

It's not start of april anymore, nevertheless I started working on device-attest-01. But I have to say, there's a little confusing with some of the requirements, and - as I said I neither own an apple device nor servers supporting the use case, so I like to ask if @mcnaugha or @madtempest you could perhaps run the attached server (that will validate the authorization but fail the CSR) and send me the data it stores about the order from it's working directory?

If it would contain data, that you would rather not have online, please send it via mail [email protected]

glatzert avatar Apr 25 '25 19:04 glatzert

Hi @glatzert,

I'll aim to get onto this some time this week. Sorry, just have some other challenges to resolve first but I do have an InTune instance and a spare macOS instance or two that I can use.

Just to confirm, is this a totally separate build that I will need to stand up a new Windows Server with IIS to run against my existing AD CS instance?

madtempest avatar Apr 26 '25 07:04 madtempest

Hi @madtempest,

no worries, I'm not in a hurry :)

The build is taken from https://github.com/glatzert/ACME-Server-ADCS/tree/device-attest-01 and has a stub implementation for device-attest-01. It's not finished, so it'll accept any challenge response, but it will store that response with the order, so I could (together with order and data) use it to validate any implementation I'm doing there - same goes for the CSR, which will be issued by the client, but then ultimately fail, since it cannot understand persitent-identifier and hardware-module SANs, yet.

You don't really need an seperate IIS - it'll play nice on anything running dotnet 8 (to be fair the IIS is not a hard requirement at all, but I have yet to check, if it would just run as a service on kestrel). You don't even need to connect it to your ADCS instance, since it will not communicate with it - at least not for the given scenario. Please be aware, that it's capable of issuing any other certificate for acme clients, if you do connect it to ADCS.

glatzert avatar Apr 28 '25 06:04 glatzert

I finally got to try progressing this. Looks like the MDM side is working and the Mac seems happy with it, however there's an authentication issue when it goes to engage with the ACME server.

My MDM configuration profile:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
	<dict>
		<key>PayloadContent</key>
		<array>
			<dict>
				<key>AllowAllAppsAccess</key>
				<true />
				<key>Attest</key>
				<true />
				<key>ClientIdentifier</key>
				<string>{{deviceid}}</string>
				<key>DirectoryURL</key>
				<string>https://url-redacted</string>
				<key>ExtendedKeyUsage</key>
				<array>
					<string>1.3.6.1.5.5.7.3.2</string>
					<string>1.3.6.1.5.5.7.3.1</string>
				</array>
				<key>HardwareBound</key>
				<true />
				<key>KeySize</key>
				<integer>384</integer>
				<key>KeyType</key>
				<string>ECSECPrimeRandom</string>
				<key>PayloadDisplayName</key>
				<string>ACME Certificate #1</string>
				<key>PayloadIdentifier</key>
				<string>com.apple.security.acme.6567cc31-7f4f-4384-a289-213af0f862ed</string>
				<key>PayloadType</key>
				<string>com.apple.security.acme</string>
				<key>PayloadUUID</key>
				<string>6567cc31-7f4f-4384-a289-213af0f862ed</string>
				<key>PayloadVersion</key>
				<integer>1</integer>
				<key>Subject</key>
				<array>
					<array>
						<array>
							<string>CN</string>
							<string>Mac-{{serialnumber}}<string />
						</array>
					</array>
					<array>
						<array>
							<string>2.5.4.3</string>
							<string>Mac-{{serialnumber}}<string />
						</array>
					</array>
				</array>
				<key>SubjectAltName</key>
				<dict>
					<key>dNSName</key>
					<string>Mac-{{serialnumber}}.domain.redacted</string>
					<key>uniformResourceIdentifier</key>
					<string>ID:Microsoft Endpoint Manager:GUID:{{deviceid}}</string>
				</dict>
			</dict>
		</array>
		<key>PayloadDescription</key>
		<string>Certificate obtained via the ACME protocol to allow for hardware-bound certificates.</string>
		<key>PayloadDisplayName</key>
		<string>Mac ACME Certificate</string>
		<key>PayloadIdentifier</key>
		<string>domain.redacted.acme</string>
		<key>PayloadOrganization</key>
		<string>Org Name Redacted</string>
		<key>PayloadRemovalDisallowed</key>
		<true />
		<key>PayloadScope</key>
		<string>System</string>
		<key>PayloadType</key>
		<string>Configuration</string>
		<key>PayloadUUID</key>
		<string>92472c03-0cce-4298-aa17-befad5c75c45</string>
		<key>PayloadVersion</key>
		<integer>1</integer>
		<key>TargetDeviceType</key>
		<integer>5</integer>
	</dict>
</plist>

The ACME server has logged just this:

{"@t":"2025-05-02T16:15:17.7919775Z","@m":"\"JWSProtectedPayload\" was not authenticated. Failure message: \"No JWSToken found to authenticate\"","@i":"48071232","AuthenticationScheme":"JWSProtectedPayload","FailureMessage":"No JWSToken found to authenticate","EventId":{"Id":7,"Name":"AuthenticationSchemeNotAuthenticatedWithFailure"},"SourceContext":"Th11s.ACMEServer.AspNetCore.Authentication.JWSAuthenticationHandler","RequestId":"400000d8-0003-fb00-b63f-84710c7967bb","RequestPath":"/"}

I'm assuming something is amiss with authentication. I did think that the server seemed a little free-and-easy. Do I have something else to configure server-side?

mcnaugha avatar May 02 '25 12:05 mcnaugha

@mcnaugha we established the missing forwarding as headers to be a problem here. The attached build allows you to use a forward proxy. Add something like this to your appsettings.json:

//If you use a load balancer, that has another public dns name than your application server, you might need to activate forwarded headers:
  // We're using AspNetCore's ForwardedHeaders Middleware to support this: https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-8.0#forwarded-headers-middleware-options and expose the options to json
  "ForwardedHeaders": {
    "KnownNetworks": [],
    "KnownProxies": ["127.0.0.1"]
  },

Here's the build, that will support the headers. It should log a short info message about activating the module, if it found the config.

glatzert avatar May 03 '25 19:05 glatzert

The forwarding stuff didn't seem to be working and I suspect it's because Microsoft tries to make their application proxy as transparent as possible... possibly by not sending any headers that give away that a proxy is involved.

I realised the next best thing to try is turning off the header translation that they have been doing. It has been staring me in the face:

"For applications that require the original host header in the request, uncheck Translate URL in Headers."

^So, I turned this off and within a few minutes, the ACME server initial response changed to show the external URL's instead of the internal ones. Finally, Apple would be happy to talk to us some more.

Some more interesting IIS log entries for a change:

2025-05-03 21:38:13 ::1 GET / - 444 - ::1 com.apple.security.acmeclient/1.0 - 200 0 0 8
2025-05-03 21:38:13 ::1 HEAD /new-nonce - 444 - ::1 com.apple.security.acmeclient/1.0 - 200 0 0 30
2025-05-03 21:38:13 ::1 POST /new-account - 444 - ::1 com.apple.security.acmeclient/1.0 - 201 0 0 446
2025-05-03 21:38:14 ::1 POST /new-order - 444 - ::1 com.apple.security.acmeclient/1.0 - 400 0 0 194
2025-05-03 21:38:30 ::1 GET / - 444 - ::1 com.apple.security.acmeclient/1.0 - 200 0 0 2
2025-05-03 21:38:30 ::1 HEAD /new-nonce - 444 - ::1 com.apple.security.acmeclient/1.0 - 200 0 0 2
2025-05-03 21:38:30 ::1 POST /new-account - 444 - ::1 com.apple.security.acmeclient/1.0 - 201 0 0 46
2025-05-03 21:38:30 ::1 POST /new-order - 444 - ::1 com.apple.security.acmeclient/1.0 - 400 0 0 43

At the server end however, less interesting (to me anyway). Nothing in the orders folder but there was data in the nonce and accounts folders. Here is that data:

ACME-WorkingFolder-2025-05-03-2238.zip

^Hopefully nothing dangerously private in there. I didn't see anything anyone could use.

Some kind of domain error (Code=400 "bad request") at the end of the Mac logs. I'm hoping that's normal because of this special build... but it could be further indication that ACME isn't going to work well with the private host having a different name to its external one.

mcnaugha avatar May 03 '25 22:05 mcnaugha

I shouldn't have done it tired yesterday .... During the current rebuilding of the server, I added more identifiers that are supported and forgot to allow hardware-module and persistent-identifier to go through. They'll now be passed through unchecked.

I attached a build, that will ignore those checks, so we're seeing which identifiers are submitted. I hope, this will be the last step so far. When the order is created, there might be PII in the working folder.

Besides that, I improved the logging of acme errors, so the outgoing error will be logged in full (at least while logging is set to Debug level)

glatzert avatar May 04 '25 08:05 glatzert

For what it's worth, Apple's CertificateService process declares what it's about to send over to the server like this:

[0:Cert_PI:ACME:<0x146955f>] ACME options: {
    AllowAllAppsAccess = 1;
    Attest = 1;
    ClientIdentifier = "<NON-EMPTY STRING>";
    DirectoryURL = "https://url.redacted";
    ExtendedKeyUsage =     (
        "1.3.6.1.5.5.7.3.2",
        "1.3.6.1.5.5.7.3.1"
    );
    HardwareBound = 1;
    KeySize = 384;
    KeyType = ECSECPrimeRandom;
    Subject =     (
                (
                        (
                CN,
                "Mac-redacted"
            )
        ),
                (
                        (
                "2.5.4.3",
                "Mac-redacted"
            )
        )
    );
    SubjectAltName =     {
        dNSName = "redacted.fqdn";
        uniformResourceIdentifier = "ID:Microsoft Endpoint Manager:GUID:redacted-guid";
    };
}
[0:Cert_PI:ACME:<0x146955f>] Calling SecRequestClientIdentity with: {
    acmeServerURL = "https://url.redacted";
    attestationOids =     (
        "1.2.840.113635.100.8.10.2",
        "1.2.840.113635.100.8.10.3",
        "1.2.840.113635.100.8.10.1",
        "1.2.840.113635.100.8.9.4",
        "1.2.840.113635.100.8.13.2",
        "1.2.840.113635.100.8.13.1",
        "1.2.840.113635.100.8.13.3",
        "1.2.840.113635.100.8.9.1",
        "1.2.840.113635.100.8.9.2",
        "1.2.840.113635.100.8.11.1"
    );
    bsiz = 384;
    certificateEKUs =     (
        "1.3.6.1.5.5.7.3.2",
        "1.3.6.1.5.5.7.3.1"
    );
    clientIdentifier = "<NON-EMPTY STRING>";
    hardwareBound = 1;
    kSecCMSSignHashAlgorithm = kSecCMSHashingAlgorithmSHA256;
    keyUsage = 1;
    nleg = 1;
    private =     {
        accc = "<SecAccessControlRef: dku;ock(true);osgn(true);odel(true);okd(true);oa(true)>";
        agrp = "com.apple.identities";
        atag = {length = 16, bytes = 0xd3f8ddc1bd3b40<…>

mcnaugha avatar May 04 '25 10:05 mcnaugha

The latest working folder:

ACME-ADCS-2025-05-04-1128.zip

mcnaugha avatar May 04 '25 10:05 mcnaugha

This is what I'd added for the forwardedheaders section of the settings. Not sure if this is still worth keeping?

  "ForwardedHeaders": {
    "AllowedHosts": [ "redacted.external.hostname", "redacted.private.hostname" ],
    "KnownNetworks": [ "127.0.0.1/8", "*.*.0.0/16", "*.*.0.0/16" ],
    "KnownProxies": [ "127.0.0.1", "<host private ip>" ]
  },

I noticed there is also:

 "AllowedHosts": "*",

Is this a different setting or is it the same as the forwardedheaders one? I'm thinking it'll need to come out if it's the same one.

mcnaugha avatar May 04 '25 11:05 mcnaugha

I think the Forwarded headers take "allowed hosts" from the generic config section, since the latter one is AFAIR independend of the ForwardedHeaders Module, but I would have to validate that.

Nevertheless - about the identifier - I should just have written a test and not bother you running it until, I made sure, it'll take the unknown identifiers. I have now writte a test (and it fails, which means, it'll take the -as of now invalid - identifier). So sorry for that, but attached find a version, that will (fingers crossed) give us the data we're looking for.

glatzert avatar May 04 '25 16:05 glatzert

Here is the latest working folder:

ACME-ADCS-2025-05-04-1809.zip

mcnaugha avatar May 04 '25 17:05 mcnaugha

Ah - we're getting somewhere - it now discarded the order, since it was not able to create all necessary challenges - that's something I cannot create a testcase currently, since the acme-client library, I'm using only supports dns names.

Alas here's the next build.

ACMEServer.ADCS-validation-build-device-attest-01.zip

glatzert avatar May 04 '25 19:05 glatzert

ACME-ADCS-2025-05-04-2018.zip

Event Viewer showing things like:

Log Name:      Application
Source:        .NET Runtime
Date:          04/05/2025 20:21:15
Event ID:      1000
Task Category: None
Level:         Error
Keywords:      Classic
User:          N/A
Computer:      
Description:
Category: Th11s.ACMEServer.Services.Processors.OrderValidationProcessor
EventId: 0

Error processing orders for validation.

Exception: 
System.InvalidOperationException: No validator found for challenge type device-attest-01 and identifier type permanent-identifier
   at Th11s.ACMEServer.Services.DefaultChallengeValidatorFactory.GetValidator(Challenge challenge) in E:\Dev\ACME-Server-ADCS\src\ACMEServer\Services\DefaultChallangeValidatorFactory.cs:line 13
   at Th11s.ACMEServer.Services.Processors.OrderValidationProcessor.ValidateOrder(ValidationContext context, IOrderStore orderStore, IChallengeValidatorFactory challengeValidatorFactory, CancellationToken cancellationToken) in E:\Dev\ACME-Server-ADCS\src\ACMEServer\Services\Processors\OrderValidationProcessor.cs:line 80
   at Th11s.ACMEServer.Services.Processors.OrderValidationProcessor.ProcessOrdersAsync(CancellationToken cancellationToken) in E:\Dev\ACME-Server-ADCS\src\ACMEServer\Services\Processors\OrderValidationProcessor.cs:line 48

mcnaugha avatar May 04 '25 19:05 mcnaugha

There's the first payload i've been looking for - it contains the response from the apple device :) Thanks a lot so far - I've now to dissect it and will report back with an updated build, that might already be able to create certificates.

glatzert avatar May 04 '25 20:05 glatzert

@mcnaugha I looked into the challenge payload, that the order contained and I have to say it's a little confusing. It contains an attObj, as it should and inside there's CBOR data that resembles part of an attestation statement as it would be expected. It containes two certificates (Apples Root cert and an Intermediate), but I cannot find anything beyond that 'metadata', so it seems to be incomplete. There's no signed data nor a hint which key is actually used to sign the data.

glatzert avatar May 05 '25 17:05 glatzert

So, I don't really know what I'm talking about but are you saying the client didn't POST the CSR?

I can see it attempting to POST the finalize in the IIS log but IIS is returning a 409 error.

In the client OS logs I see:

ACME request flow failed at step 7: Error Domain=NSURLErrorDomain Code=409 "conflict" UserInfo={NSLocalizedDescription=conflict}

"HTTP 409 Conflict, means the server can't complete the request because it's in conflict with the current state of the resource. This usually happens when the client attempts to update a resource that has been changed or deleted by another user or process since the client last accessed it."

Seems to suggest that it wasn't able to deliver the CSR because of a server side issue. IIS couldn't write to the finalize destination???

mcnaugha avatar May 05 '25 22:05 mcnaugha

In the morning, I may try and reconfigure IIS and the Entra App Proxy to use HTTP instead of HTTPS for the private part of the journey. That way I should be able to capture the finalise data that the client may have attempted to POST. Right now, all I get is TLS encrypted data obviously. I believe the client won't complain because it'll still be HTTPS to and from the App Proxy and what happens next should be unseen by it.

I meant to suggest, if it makes things easier, you could email me with the "directory" URL for a server you're in control of and I'll point the Mac to use that. That way you can see the errors as they happen and try fixes at your leisure. The only thing you can't do, of course, is stimulate the client to attempt the process. It should be doing that automatically roughly every 8 hours I think. You can certainly post here or email to trigger me to kick the MDM agent.

mcnaugha avatar May 05 '25 22:05 mcnaugha

Good morning, you're probably right - it would be easier to have a machine on my end - at least to collect the data to go forward. But currently I think there's a problem with the configuration your MDM is using (or something along anywhere here).

I'll try to explain and since I don't know how deep you're involved with either protocol at play here, I'm just describe it from the clients perspective:

  1. Create account at ACME server
  2. Send Order, containing identifiers.
  3. Pick a challenge and prepare the response
  4. Signal to server which challenge to use and await validation
  5. Send CSR to the server
  6. Retreive certificate.

I'm currently struggeling in the server side of 3. Device-attest-01 says, the challenge will contain a payload (that's new - the other methods don't have a payload), which will contain a WebAuthN attestation Statement, that uses a KeyAuthroization (esentially an account identifier + a token from the challenge) as WebAuthN-challenge

That WebAuthN attestation statement was stored with the challenge, you provided in the zip file. The payload is base64Url encoded json and resolved to {"attObj" = "<base64url-encoded-blob>"} The blob itself is CBOR and contains the following:

a2                                           # map(2)
   63                                        #   text(3)
      666d74                                 #     "fmt"
   65                                        #   text(5)
      6170706c65                             #     "apple"
   67                                        #   text(7)
      61747453746d74                         #     "attStmt"
   a1                                        #   map(1)
      63                                     #     text(3)
         783563                              #       "x5c"
      82                                     #     array(2)
         59 0348                             #       bytes(840)
            30820344308202cba003020102020601 #         "0\x82\x03D0\x82\x02\xcb\xa0\x03\x02\x01\x02\x02\x06\x01"
            969cbc2daf300a06082a8648ce3d0403 #         "\x96\x9c\xbc-\xaf0\n\x06\x08*\x86H\xce=\x04\x03"
            033052312e302c06035504030c254170 #         "\x030R1.0,\x06\x03U\x04\x03\x0c%Ap"
            706c6520456e74657270726973652041 #         "ple Enterprise A"
            74746573746174696f6e205375622043 #         "ttestation Sub C"
            41203131133011060355040a0c0a4170 #         "A 11\x130\x11\x06\x03U\x04\n\x0c\nAp"
            706c6520496e632e310b300906035504 #         "ple Inc.1\x0b0\t\x06\x03U\x04"
            0613025553301e170d32353035303331 #         "\x06\x13\x02US0\x1e\x17\r2505031"
            39313832305a170d3235303830323139 #         "91820Z\x17\r25080219"
            313832305a3081913149304706035504 #         "1820Z0\x81\x911I0G\x06\x03U\x04"
            030c4032636637646133353134353964 #         "\x03\x0c@2cf7da351459d"
            65363631613831663962313639376464 #         "e661a81f9b1697dd"
            64376564613734613537383431653735 #         "d7eda74a57841e75"
            65376361653661383065616231393766 #         "e7cae6a80eab197f"
            303863311a3018060355040b0c114141 #         "08c1\x1a0\x18\x06\x03U\x04\x0b\x0c\x11AA"
            412043657274696669636174696f6e31 #         "A Certification1"
            133011060355040a0c0a4170706c6520 #         "\x130\x11\x06\x03U\x04\n\x0c\nApple "
            496e632e3113301106035504080c0a43 #         "Inc.1\x130\x11\x06\x03U\x04\x08\x0c\nC"
            616c69666f726e69613076301006072a #         "alifornia0v0\x10\x06\x07*"
            8648ce3d020106052b81040022036200 #         "\x86H\xce=\x02\x01\x06\x05+\x81\x04\x00\"\x03b\x00"
            048d05c24cadb26049c463eeea887d3d #         "\x04\x8d\x05\xc2L\xad\xb2`I\xc4c\xee\xea\x88}="
            800ee941cdb0eb69e9977a692795564d #         "\x80\x0e\xe9A\xcd\xb0\xebi\xe9\x97zi\'\x95VM"
            86812a862a0459cb7516513fc1b7644f #         "\x86\x81*\x86*\x04Y\xcbu\x16Q?\xc1\xb7dO"
            01848c3e690f8ddaa9387fd3186a2351 #         "\x01\x84\x8c>i\x0f\x8d\xda\xa98\x7f\xd3\x18j#Q"
            1c52d8a1ddc889fce7e2072a46f5598d #         "\x1cR\xd8\xa1\xdd\xc8\x89\xfc\xe7\xe2\x07*F\xf5Y\x8d"
            7f84abf833678929bba225fae5a6c026 #         "\x7f\x84\xab\xf83g\x89)\xbb\xa2%\xfa\xe5\xa6\xc0&"
            cea382012e3082012a300c0603551d13 #         "\xce\xa3\x82\x01.0\x82\x01*0\x0c\x06\x03U\x1d\x13"
            0101ff04023000300e0603551d0f0101 #         "\x01\x01\xff\x04\x020\x000\x0e\x06\x03U\x1d\x0f\x01\x01"
            ff0404030204f03012060a2a864886f7 #         "\xff\x04\x04\x03\x02\x04\xf00\x12\x06\n*\x86H\x86\xf7"
            6364080a02040431352e353012060a2a #         "cd\x08\n\x02\x04\x0415.50\x12\x06\n*"
            864886f76364080a03040431352e3530 #         "\x86H\x86\xf7cd\x08\n\x03\x04\x0415.50"
            12060a2a864886f76364080a01040431 #         "\x12\x06\n*\x86H\x86\xf7cd\x08\n\x01\x04\x041"
            352e353015060a2a864886f763640809 #         "5.50\x15\x06\n*\x86H\x86\xf7cd\x08\t"
            0404074a333136734150301e060a2a86 #         "\x04\x04\x07J316sAP0\x1e\x06\n*\x86"
            4886f76364080d020410526564756365 #         "H\x86\xf7cd\x08\r\x02\x04\x10Reduce"
            642053656375726974793011060a2a86 #         "d Security0\x11\x06\n*\x86"
            4886f76364080d010403020100301106 #         "H\x86\xf7cd\x08\r\x01\x04\x03\x02\x01\x000\x11\x06"
            0a2a864886f76364080d03040302010a #         "\n*\x86H\x86\xf7cd\x08\r\x03\x04\x03\x02\x01\n"
            3018060a2a864886f76364080901040a #         "0\x18\x06\n*\x86H\x86\xf7cd\x08\t\x01\x04\n"
            5032344d57334a3650323027060a2a86 #         "P24MW3J6P20\'\x06\n*\x86"
            4886f763640809020419303030303630 #         "H\x86\xf7cd\x08\t\x02\x04\x19000060"
            30302d30303143303443313341443838 #         "00-001C04C13AD88"
            303145302e060a2a864886f76364080b #         "01E0.\x06\n*\x86H\x86\xf7cd\x08\x0b"
            0104201c2c9898cf82809389d4259739 #         "\x01\x04 \x1c,\x98\x98\xcf\x82\x80\x93\x89\xd4%\x979"
            2a3ef3dab5c454a195fce6ad8830bb50 #         "*>\xf3\xda\xb5\xc4T\xa1\x95\xfc\xe6\xad\x880\xbbP"
            4b613f300a06082a8648ce3d04030303 #         "Ka?0\n\x06\x08*\x86H\xce=\x04\x03\x03\x03"
            67003064023035a9a910e58483eab798 #         "g\x000d\x0205\xa9\xa9\x10\xe5\x84\x83\xea\xb7\x98"
            9a376fd234c0bf629df8c4041f8b4c6d #         "\x9a7o\xd24\xc0\xbfb\x9d\xf8\xc4\x04\x1f\x8bLm"
            f650a8717f4f45d5954c82eeacc260c0 #         "\xf6P\xa8q\x7fOE\xd5\x95L\x82\xee\xac\xc2`\xc0"
            72bb6745f3ee0230201ddce8ea6ff736 #         "r\xbbgE\xf3\xee\x020 \x1d\xdc\xe8\xeao\xf76"
            81d7102808c771123ae5e305c281c32a #         "\x81\xd7\x10(\x08\xc7q\x12:\xe5\xe3\x05\xc2\x81\xc3*"
            63f8cdc6eceeb406b1fdae1ba708e851 #         "c\xf8\xcd\xc6\xec\xee\xb4\x06\xb1\xfd\xae\x1b\xa7\x08\xe8Q"
            4459875de5494228                 #         "DY\x87]\xe5IB("
         59 024d                             #       bytes(589)
            30820249308201cea003020102021470 #         "0\x82\x02I0\x82\x01\xce\xa0\x03\x02\x01\x02\x02\x14p"
            31e040a6e6b46bf0e2f34d556a8454d0 #         "1\xe0@\xa6\xe6\xb4k\xf0\xe2\xf3MUj\x84T\xd0"
            425e71300a06082a8648ce3d04030330 #         "B^q0\n\x06\x08*\x86H\xce=\x04\x03\x030"
            51312d302b06035504030c244170706c #         "Q1-0+\x06\x03U\x04\x03\x0c$Appl"
            6520456e746572707269736520417474 #         "e Enterprise Att"
            6573746174696f6e20526f6f74204341 #         "estation Root CA"
            31133011060355040a0c0a4170706c65 #         "1\x130\x11\x06\x03U\x04\n\x0c\nApple"
            20496e632e310b300906035504061302 #         " Inc.1\x0b0\t\x06\x03U\x04\x06\x13\x02"
            5553301e170d32323032313631393034 #         "US0\x1e\x17\r2202161904"
            34375a170d3332303231383030303030 #         "47Z\x17\r32021800000"
            305a3052312e302c06035504030c2541 #         "0Z0R1.0,\x06\x03U\x04\x03\x0c%A"
            70706c6520456e746572707269736520 #         "pple Enterprise "
            4174746573746174696f6e2053756220 #         "Attestation Sub "
            4341203131133011060355040a0c0a41 #         "CA 11\x130\x11\x06\x03U\x04\n\x0c\nA"
            70706c6520496e632e310b3009060355 #         "pple Inc.1\x0b0\t\x06\x03U"
            0406130255533076301006072a8648ce #         "\x04\x06\x13\x02US0v0\x10\x06\x07*\x86H\xce"
            3d020106052b81040022036200042c3f #         "=\x02\x01\x06\x05+\x81\x04\x00\"\x03b\x00\x04,?"
            521febf1756baed8c0b48f15aa5897cd #         "R\x1f\xeb\xf1uk\xae\xd8\xc0\xb4\x8f\x15\xaaX\x97\xcd"
            95a4ec2e37075ab3ebc112ed7eebbb7c #         "\x95\xa4\xec.7\x07Z\xb3\xeb\xc1\x12\xed~\xeb\xbb|"
            866fab5049b20846d215327be7856911 #         "\x86o\xabPI\xb2\x08F\xd2\x152{\xe7\x85i\x11"
            16eb053dbdc1a65bfee13acf242b1496 #         "\x16\xeb\x05=\xbd\xc1\xa6[\xfe\xe1:\xcf$+\x14\x96"
            fba5778486b407ae710cc9a9209e33c4 #         "\xfb\xa5w\x84\x86\xb4\x07\xaeq\x0c\xc9\xa9 \x9e3\xc4"
            5ae7598343f4e622d4c968e0fa20a366 #         "Z\xe7Y\x83C\xf4\xe6\"\xd4\xc9h\xe0\xfa \xa3f"
            306430120603551d130101ff04083006 #         "0d0\x12\x06\x03U\x1d\x13\x01\x01\xff\x04\x080\x06"
            0101ff020100301f0603551d23041830 #         "\x01\x01\xff\x02\x01\x000\x1f\x06\x03U\x1d#\x04\x180"
            168014f36a4d019df26b81a55e5dbbe5 #         "\x16\x80\x14\xf3jM\x01\x9d\xf2k\x81\xa5^]\xbb\xe5"
            086d56e2f9deba301d0603551d0e0416 #         "\x08mV\xe2\xf9\xde\xba0\x1d\x06\x03U\x1d\x0e\x04\x16"
            04141985dbcf786fa5b2ca38ac664b44 #         "\x04\x14\x19\x85\xdb\xcfxo\xa5\xb2\xca8\xacfKD"
            5a7561646566300e0603551d0f0101ff #         "Zuadef0\x0e\x06\x03U\x1d\x0f\x01\x01\xff"
            040403020106300a06082a8648ce3d04 #         "\x04\x04\x03\x02\x01\x060\n\x06\x08*\x86H\xce=\x04"
            030303690030660231009bf5c39badfe #         "\x03\x03\x03i\x000f\x021\x00\x9b\xf5\xc3\x9b\xad\xfe"
            d080a6951a9a9a247f5c2b06e7fda986 #         "\xd0\x80\xa6\x95\x1a\x9a\x9a$\x7f\\+\x06\xe7\xfd\xa9\x86"
            b2b28ce0990ecb7b52b09daf0af7d9cf #         "\xb2\xb2\x8c\xe0\x99\x0e\xcb{R\xb0\x9d\xaf\n\xf7\xd9\xcf"
            c098d890e4bf2358dbfa023100f28c43 #         "\xc0\x98\xd8\x90\xe4\xbf#X\xdb\xfa\x021\x00\xf2\x8cC"
            7d71c1e6fd383c66455a6c86fc5e40b7 #         "}q\xc1\xe6\xfd8<fEZl\x86\xfc^@\xb7"
            fafe3f3221cec735e9a183b3c0631adf #         "\xfa\xfe?2!\xce\xc75\xe9\xa1\x83\xb3\xc0c\x1a\xdf"
            41116451414b72901da308ed18       #         "A\x11dQAKr\x90\x1d\xa3\x08\xed\x18"

Those two X509 certificates, are the aforementioned root and intermediate certificate from apple,. What I fail to see here is the signed WebAuthN-challenge, which makes me think something is off in the MDM configuration.

Regarding the CSR - that would be the next step, but without being able to validate the challenge, the CSR doesn't matter much.

EDIT: I misinterpreted the certficate output - the first should contain the data I'm looking for, but it seems to be missing OID 1.2.840.113635.100.8.2, which should be present, if I understand this here correctly: https://www.w3.org/TR/webauthn-2/#sctn-apple-anonymous-attestation

glatzert avatar May 06 '25 06:05 glatzert

Perhaps I'm trying to get us to run before we can walk. Maybe we should see if we can get this to work without attestation first?

Here is a capture after a request without attestation:

ACME-ADCS-2025-05-06-0923.zip

mcnaugha avatar May 06 '25 08:05 mcnaugha

I'm looking through the Apple Developer forums to see if any of the conversations there might help. I do see someone who had gotten to step 9 of the process. Suggests they got past this part.

I do fear there was more for the MDM server to do and that Intune simply isn't ready for this but my impression was that the attestation is entirely self-contained within the Apple device and that it's Apple providing the trust. I also see a discussion from an Apple engineer saying that the model doesn't rely on the MDM server providing any part of this and how they view that as aligning with Zero Trust principles.

I will look at sending the attestation config to an iPhone as I do see someone saying that their payload worked fine with iOS; just not macOS because they were running a beta. Confession time... I'm running the latest beta. It would be an awful coincidence if they broke ACME attestation in this release.

I don't know if you know, if you have or work for a non-profit organisation, you can get a fee waiver for the Apple Developer membership, which entitles you to developer support requests. I could consider registering my non-profit.

mcnaugha avatar May 06 '25 09:05 mcnaugha

Here is the working folder after an iPhone request:

ACME-ADCS-2025-05-06-1118.zip

mcnaugha avatar May 06 '25 10:05 mcnaugha

I extracted the Certificate and put it in openssl - what I see is the Phone Model and OS Version, but the attestation itself is missing.

X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Key Usage: critical
                Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment
            1.2.840.113635.100.8.10.2: 
                18.5
            1.2.840.113635.100.8.10.3: 
                18.5
            1.2.840.113635.100.8.10.1: 
                18.5
            1.2.840.113635.100.8.9.4: 
                iPhone16,2
            1.2.840.113635.100.8.9.1: 
                K122W7774L
            1.2.840.113635.100.8.9.2: 
                0000..001C
            1.2.840.113635.100.8.11.1: 
                VM.G.

The extensions should contain a OID 1.2.840.113635.100.8.2, which should contain a value from the challenge. So something is still not working as I would expect it.

Did you disable attestation in the profile now or is it "active"?

glatzert avatar May 06 '25 10:05 glatzert

Attestation should be active in this profile for the iPhone.

mcnaugha avatar May 06 '25 10:05 mcnaugha