Skip to content

Commit

Permalink
fix(apple): include the root CA in signatures
Browse files Browse the repository at this point in the history
Normally X509 doesn't need this, but it seems that AMFI gets upset if
it's missing.

Signed-off-by: Michael Tharp <Michael.Tharp@sas.com>
  • Loading branch information
mtharp committed Oct 30, 2024
1 parent 52391ee commit 9b4549b
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 16 deletions.
66 changes: 54 additions & 12 deletions doc/macos.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,42 @@

relic has preliminary support for signing MacOS and iOS binaries.

For example, to sign a Mac binary for offline distribution, you will need a DevID certificate.
For example, to sign a Mac binary for offline distribution,
you will need a Dev ID certificate:

```sh
# Create a signing request:
openssl genrsa -out devid.key 2048
openssl req -new -subj /CN=devid -key devid.key -out devid.csr
# Submit the CSR to https://developer.apple.com/account/resources/certificates/add
# and retrieve the CER, then:
openssl x509 -inform DER -in devid.cer -out devid.crt

# Identify the intermdiate certificate needed:
openssl x509 -in devid.crt -noout -issuer
# e.g. issuer=CN = Developer ID Certification Authority, OU = G2, ...

# Navigate to https://www.apple.com/certificateauthority/
# and find the matching certificate.
# Then fetch and reformat it:
curl -Ls https://www.apple.com/certificateauthority/DeveloperIDG2CA.cer \
| openssl x509 -inform DER -out intermediate.crt
# Repeat for the root CA:
curl -Ls https://www.apple.com/appleca/AppleIncRootCertificate.cer \
| openssl x509 -inform DER -out root.crt

# Confirm that the chain is complete:
openssl verify -ignore_critical -CAfile root.crt \
-untrusted intermediate.crt devid.crt
# devid.crt: OK

# Finally, append the chain to the signing cert:
cat intermediate.crt root.crt >>devid.crt
```

Note that the root CA certificate **must** be included,
otherwise verification errors may occur.

Configure relic to use the cert with Apple's timestamp servers:

```yaml
Expand All @@ -22,18 +57,25 @@ timestamp:
- http://timestamp.apple.com/ts01
```
relic sign -k devid -f foo-darwin-amd64
Finally, sign a binary or package:
```sh
relic sign -k devid -f foo-darwin-amd64
```

Binaries are signed with the `hardened-runtime` flag by default, which is required for notarization to succeed.
If this is not desired then it can be disabled with `--hardened-runtime=false`.
Binaries are signed with the `hardened-runtime` flag by default,
which is required for notarization to succeed.
If this is not desired then it can be disabled
with `--hardened-runtime=false`.

Note also that relic currently does not support signing multi-arch ("fat") binaries, although it can verify them.
Note also that relic currently does not support signing multi-arch
("fat") binaries, although it can verify them.
Sign each arch separately and then combine them afterwards:

go install github.com/randall77/makefat@latest
relic sign -f foo-darwin-amd64
relic sign -f foo-darwin-arm64
makefat foo foo-darwin-amd64 foo-darwin-arm64
relic verify foo

The signed binary can then be placed into a regular zip file and uploaded for notarization.
```sh
go install github.com/randall77/makefat@latest
relic sign -f foo-darwin-amd64
relic sign -f foo-darwin-arm64
makefat foo foo-darwin-amd64 foo-darwin-arm64
relic verify foo
```
6 changes: 4 additions & 2 deletions lib/fruit/csblob/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func (p *SignatureParams) DefaultsFromBundle(cert *certloader.Certificate) error
p.TeamIdentifier = TeamID(cert.Leaf)
}
if p.Requirements == nil {
req, err := DefaultRequirement(p.SigningIdentity, cert.Chain())
req, err := DefaultRequirement(p.SigningIdentity, cert.Certificates)
if err != nil {
return fmt.Errorf("computing default designated requirement: %w", err)
}
Expand Down Expand Up @@ -202,7 +202,9 @@ func Sign(ctx context.Context, cert *certloader.Certificate, params *SignaturePa
}
items = append(items, hashedItems...)
// sign
builder := pkcs7.NewBuilder(cert.Signer(), cert.Chain(), params.HashFunc)
// IMPORTANT: make sure to include the root CA, or AMFI gets mad:
// "does not pass CT evaluation"
builder := pkcs7.NewBuilder(cert.Signer(), cert.Certificates, params.HashFunc)
if err := builder.SetContentData(firstCD); err != nil {
return nil, nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions lib/fruit/xar/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func Sign(ctx context.Context, r io.Reader, cert *certloader.Certificate, hashTy
}
origSigSize := removeSigs(toc)
// reserve space for new signatures and insert elements into TOC
newSigSize := reserveSignatures(toc, hashType, cert.Chain())
newSigSize := reserveSignatures(toc, hashType, cert.Certificates)
// verify and discard remaining input files
heap := &streamReaderAt{r: r}
if err := checkFiles(toc, heap); err != nil {
Expand Down Expand Up @@ -230,7 +230,7 @@ func appendSignatures(ctx context.Context, out *bytes.Buffer, ztoc []byte, uncom
usedSigSize += int64(len(classicBytes))
}
// CMS signature
builder := pkcs7.NewBuilder(cert.Signer(), cert.Chain(), hashType)
builder := pkcs7.NewBuilder(cert.Signer(), cert.Certificates, hashType)
if err := builder.SetContentData(ztocHash); err != nil {
return nil, err
}
Expand Down

0 comments on commit 9b4549b

Please sign in to comment.