Skip to content

feat(cli): Add full PEM encoding support for signing and verifying component versions#2147

Merged
jakobmoellerdev merged 2 commits into
open-component-model:mainfrom
jakobmoellerdev:pem-signing-test-cli
Apr 2, 2026
Merged

feat(cli): Add full PEM encoding support for signing and verifying component versions#2147
jakobmoellerdev merged 2 commits into
open-component-model:mainfrom
jakobmoellerdev:pem-signing-test-cli

Conversation

@jakobmoellerdev

@jakobmoellerdev jakobmoellerdev commented Apr 2, 2026

Copy link
Copy Markdown
Member

What this PR does / why we need it

Adds end-to-end CLI support and documentation for the PEM signature encoding policy (signatureEncodingPolicy: PEM), which embeds an X.509 certificate chain directly in the signature value and validates it against a verifier-supplied trust anchor.

Changes:

  • cli/cmd/sign/component-version/cmd.go — extends the --signer-spec flag description and Example block with a second credential config showing PEM signing (private_key_pem_file + public_key_pem_file for a leaf+intermediate chain) and a second signer spec snippet for signatureEncodingPolicy: PEM
  • cli/cmd/verify/component-version/cmd.go — extends the Example block with a PEM verification credential config (root CA as trust anchor via public_key_pem_file), explains trust anchor isolation, and fixes two copy-paste bugs where examples said sign instead of verify
  • cli/docs/reference/ocm_sign_component-version.md / ocm_verify_component-version.md — regenerated from the above
  • cli/integration/signing_integration_test.go — adds Test_Integration_Signing_PEM: an end-to-end integration test that builds a 3-tier certificate chain (root → intermediate → leaf) at runtime, signs a component version with PEM encoding via the CLI, and verifies it using only the root CA as an isolated trust anchor

Which issue(s) this PR fixes

part of open-component-model/ocm-project#1000

Testing

How to test the changes

Generate a certificate chain and two config files, then sign and verify a component version:

# Generate chain
mkdir -p /tmp/ocm-pem-test && cd /tmp/ocm-pem-test
openssl genrsa -out root.key 2048
openssl req -x509 -new -nodes -key root.key -sha256 -days 365 -subj "/CN=test-root" -out root.crt
openssl genrsa -out leaf.key 2048
openssl req -new -key leaf.key -subj "/CN=test-leaf" -out leaf.csr
openssl x509 -req -in leaf.csr -CA root.crt -CAkey root.key -CAcreateserial -sha256 -days 90 -out leaf.crt
cp leaf.crt chain.pem

sign.ocmconfig

type: generic.config.ocm.software/v1
configurations:
  - type: credentials.config.ocm.software
    consumers:
      - identity:
          type: RSA/v1alpha1
          algorithm: RSASSA-PSS
          signature: default
        credentials:
          - type: Credentials/v1
            properties:
              private_key_pem_file: /tmp/ocm-pem-test/leaf.key
              public_key_pem_file: /tmp/ocm-pem-test/chain.pem

verify.ocmconfig

type: generic.config.ocm.software/v1
configurations:
  - type: credentials.config.ocm.software
    consumers:
      - identity:
          type: RSA/v1alpha1
          algorithm: RSASSA-PSS
          signature: default
        credentials:
          - type: Credentials/v1
            properties:
              public_key_pem_file: /tmp/ocm-pem-test/root.crt

pem-signer.yaml

type: RSASigningConfiguration/v1alpha1
signatureAlgorithm: RSASSA-PSS
signatureEncodingPolicy: PEM
# Sign
ocm sign cv --config sign.ocmconfig --signer-spec pem-signer.yaml --dry-run <repo>//<component>:<version>
ocm sign cv --config sign.ocmconfig --signer-spec pem-signer.yaml <repo>//<component>:<version>

# Verify
ocm verify cv --config verify.ocmconfig <repo>//<component>:<version>

Run the new integration test directly (requires Docker for the OCI registry):

cd cli/integration && go test -run Test_Integration_Signing_PEM -v -timeout 120s
Verification
  • I have tested the changes locally by running ocm

…nt versions

Signed-off-by: Jakob Möller <contact@jakob-moeller.com>
@coderabbitai

coderabbitai Bot commented Apr 2, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This pull request updates CLI help text and reference documentation for sign and verify component-version commands to distinguish between Plain (default) and PEM signature encoding policies. It includes credential configuration examples for each encoding type, updates the RSA bindings dependency version, and adds an integration test for PEM-encoded certificate chain signing.

Changes

Cohort / File(s) Summary
Sign Command Help & Documentation
cli/cmd/sign/component-version/cmd.go, cli/docs/reference/ocm_sign_component-version.md
Added distinct examples for Plain and PEM encoding credential configurations; relabeled existing example as "Plain encoding (default)" and introduced new PEM example with private_key_pem_file and public_key_pem_file properties, plus signer spec example with signatureEncodingPolicy: PEM.
Verify Command Help & Documentation
cli/cmd/verify/component-version/cmd.go, cli/docs/reference/ocm_verify_component-version.md
Replaced single credential example with two variants: Plain encoding using public_key_pem (bare RSA key) and PEM encoding using public_key_pem_file (root CA certificate); corrected example command lines from sign to verify.
Dependency Updates
cli/go.mod, cli/integration/go.mod
Updated ocm.software/open-component-model/bindings/go/rsa from v0.0.0-20260402061016-e120070fd921 to v0.0.0-20260402131554-15c6189f2d4a.
PEM Signing Integration Test
cli/integration/signing_integration_test.go
Added comprehensive test Test_Integration_Signing_PEM that generates a certificate chain (root CA → intermediate → leaf), writes credentials to disk, executes sign/verify workflow with PEM encoding policy, and includes helper functions for RSA key/certificate generation and PEM file writing.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

kind/chore

Suggested reviewers

  • matthiasbruns
  • frewilhelm
  • fabianburth

Poem

🐰 A crypto-trail through docs so clear,
Plain meets PEM, encoding dear,
Certificate chains dance, leaf and root,
Signing sealed with PEM-encoded loot!
🔐✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding PEM encoding support for signing and verifying component versions, which aligns with all file changes in the changeset.
Description check ✅ Passed The description is directly related to the changeset, providing clear context about PEM signature encoding policy support, listing specific file changes, and including testing instructions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added size/m Medium kind/feature new feature, enhancement, improvement, extension labels Apr 2, 2026
@jakobmoellerdev jakobmoellerdev marked this pull request as ready for review April 2, 2026 15:06
@jakobmoellerdev jakobmoellerdev requested a review from a team as a code owner April 2, 2026 15:06
@jakobmoellerdev jakobmoellerdev changed the title feat: Add full PEM encoding support for signing and verifying component versions feat(cli): Add full PEM encoding support for signing and verifying component versions Apr 2, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cli/cmd/sign/component-version/cmd.go (1)

136-154: ⚠️ Potential issue | 🟡 Minor

Clarify the defaulting semantics for user-supplied signer specs.

loadSignerSpec injects Plain only when the --signer-spec flag is omitted entirely. When a signer spec file is provided, the raw config passes through unchanged. The binding's code default is also Plain, yet the help text signatureEncodingPolicy: Plain (default) | PEM reads like a field-level default that applies regardless of context.

✏️ Suggested wording
-#   signatureEncodingPolicy: Plain (default) | PEM
+#   signatureEncodingPolicy: Plain | PEM
+#
+# If you omit the entire --signer-spec flag, the CLI defaults to Plain encoding.
+# If you provide a signer spec file, set signatureEncodingPolicy explicitly.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/cmd/sign/component-version/cmd.go` around lines 136 - 154, The help text
is ambiguous about when the Plain default applies; update the documentation
string to clarify that signatureEncodingPolicy's "Plain (default)" is applied
only when no --signer-spec is provided (handled by loadSignerSpec) or by the
binding's own default, and that if a user-supplied signer spec file is provided
the field is passed through unchanged. Reference loadSignerSpec and the
--signer-spec flag in the wording and explicitly state that field-level defaults
in the binding may also be Plain, so users must set signatureEncodingPolicy: PEM
in their signer-spec file to override.
🧹 Nitpick comments (1)
cli/integration/signing_integration_test.go (1)

165-169: Add a negative PEM-chain case next to the happy path.

This locks in the root-only success path, but the new help text also depends on rejecting an embedded self-signed root and on using only the supplied trust anchor. A second case with [leaf, intermediate, root] or an unrelated public_key_pem_file would keep that boundary under regression coverage.

Also applies to: 205-289

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/integration/signing_integration_test.go` around lines 165 - 169, Add a
negative PEM-chain test case alongside the existing happy-path by writing a
chain file that includes the root (e.g., call writePEMCertFile(t, dir,
"chain_with_root.pem", leaf, interm, root) or a chain with an unrelated public
key) and then assert the verifier rejects it — mirror the happy-path flow but
expect failure when using that chainPath (and similarly add the same negative
case for the other ranges mentioned around signing_integration_test.go lines
205-289); ensure you reference the same helpers (writePEMCertFile) and variables
(chainPath/rootCAPath) so the test verifies that only the supplied trust anchor
(rootCAPath) is accepted and embedded self-signed roots are rejected.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@cli/cmd/sign/component-version/cmd.go`:
- Around line 136-154: The help text is ambiguous about when the Plain default
applies; update the documentation string to clarify that
signatureEncodingPolicy's "Plain (default)" is applied only when no
--signer-spec is provided (handled by loadSignerSpec) or by the binding's own
default, and that if a user-supplied signer spec file is provided the field is
passed through unchanged. Reference loadSignerSpec and the --signer-spec flag in
the wording and explicitly state that field-level defaults in the binding may
also be Plain, so users must set signatureEncodingPolicy: PEM in their
signer-spec file to override.

---

Nitpick comments:
In `@cli/integration/signing_integration_test.go`:
- Around line 165-169: Add a negative PEM-chain test case alongside the existing
happy-path by writing a chain file that includes the root (e.g., call
writePEMCertFile(t, dir, "chain_with_root.pem", leaf, interm, root) or a chain
with an unrelated public key) and then assert the verifier rejects it — mirror
the happy-path flow but expect failure when using that chainPath (and similarly
add the same negative case for the other ranges mentioned around
signing_integration_test.go lines 205-289); ensure you reference the same
helpers (writePEMCertFile) and variables (chainPath/rootCAPath) so the test
verifies that only the supplied trust anchor (rootCAPath) is accepted and
embedded self-signed roots are rejected.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 19572e86-1987-49d4-8abe-0833902a4a9c

📥 Commits

Reviewing files that changed from the base of the PR and between 776c99a and c0515f9.

⛔ Files ignored due to path filters (2)
  • cli/go.sum is excluded by !**/*.sum
  • cli/integration/go.sum is excluded by !**/*.sum
📒 Files selected for processing (7)
  • cli/cmd/sign/component-version/cmd.go
  • cli/cmd/verify/component-version/cmd.go
  • cli/docs/reference/ocm_sign_component-version.md
  • cli/docs/reference/ocm_verify_component-version.md
  • cli/go.mod
  • cli/integration/go.mod
  • cli/integration/signing_integration_test.go

@jakobmoellerdev jakobmoellerdev enabled auto-merge (squash) April 2, 2026 15:56
@jakobmoellerdev jakobmoellerdev merged commit 8803afa into open-component-model:main Apr 2, 2026
22 checks passed
morri-son pushed a commit to morri-son/open-component-model that referenced this pull request Apr 14, 2026
…mponent versions (open-component-model#2147)

<!-- markdownlint-disable MD041 -->
#### What this PR does / why we need it

Adds end-to-end CLI support and documentation for the **PEM signature
encoding policy** (`signatureEncodingPolicy: PEM`), which embeds an
X.509 certificate chain directly in the signature value and validates it
against a verifier-supplied trust anchor.

**Changes:**

- `cli/cmd/sign/component-version/cmd.go` — extends the `--signer-spec`
flag description and `Example` block with a second credential config
showing PEM signing (`private_key_pem_file` + `public_key_pem_file` for
a leaf+intermediate chain) and a second signer spec snippet for
`signatureEncodingPolicy: PEM`
- `cli/cmd/verify/component-version/cmd.go` — extends the `Example`
block with a PEM verification credential config (root CA as trust anchor
via `public_key_pem_file`), explains trust anchor isolation, and fixes
two copy-paste bugs where examples said `sign` instead of `verify`
- `cli/docs/reference/ocm_sign_component-version.md` /
`ocm_verify_component-version.md` — regenerated from the above
- `cli/integration/signing_integration_test.go` — adds
`Test_Integration_Signing_PEM`: an end-to-end integration test that
builds a 3-tier certificate chain (root → intermediate → leaf) at
runtime, signs a component version with PEM encoding via the CLI, and
verifies it using only the root CA as an isolated trust anchor

#### Which issue(s) this PR fixes

part of open-component-model/ocm-project#1000

<!--
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->

#### Testing

##### How to test the changes

Generate a certificate chain and two config files, then sign and verify
a component version:

```bash
# Generate chain
mkdir -p /tmp/ocm-pem-test && cd /tmp/ocm-pem-test
openssl genrsa -out root.key 2048
openssl req -x509 -new -nodes -key root.key -sha256 -days 365 -subj "/CN=test-root" -out root.crt
openssl genrsa -out leaf.key 2048
openssl req -new -key leaf.key -subj "/CN=test-leaf" -out leaf.csr
openssl x509 -req -in leaf.csr -CA root.crt -CAkey root.key -CAcreateserial -sha256 -days 90 -out leaf.crt
cp leaf.crt chain.pem
```

**`sign.ocmconfig`**
```yaml
type: generic.config.ocm.software/v1
configurations:
  - type: credentials.config.ocm.software
    consumers:
      - identity:
          type: RSA/v1alpha1
          algorithm: RSASSA-PSS
          signature: default
        credentials:
          - type: Credentials/v1
            properties:
              private_key_pem_file: /tmp/ocm-pem-test/leaf.key
              public_key_pem_file: /tmp/ocm-pem-test/chain.pem
```

**`verify.ocmconfig`**
```yaml
type: generic.config.ocm.software/v1
configurations:
  - type: credentials.config.ocm.software
    consumers:
      - identity:
          type: RSA/v1alpha1
          algorithm: RSASSA-PSS
          signature: default
        credentials:
          - type: Credentials/v1
            properties:
              public_key_pem_file: /tmp/ocm-pem-test/root.crt
```

**`pem-signer.yaml`**
```yaml
type: RSASigningConfiguration/v1alpha1
signatureAlgorithm: RSASSA-PSS
signatureEncodingPolicy: PEM
```

```bash
# Sign
ocm sign cv --config sign.ocmconfig --signer-spec pem-signer.yaml --dry-run <repo>//<component>:<version>
ocm sign cv --config sign.ocmconfig --signer-spec pem-signer.yaml <repo>//<component>:<version>

# Verify
ocm verify cv --config verify.ocmconfig <repo>//<component>:<version>
```

Run the new integration test directly (requires Docker for the OCI
registry):

```bash
cd cli/integration && go test -run Test_Integration_Signing_PEM -v -timeout 120s
```

##### Verification

- [x] I have tested the changes locally by running `ocm`

Signed-off-by: Jakob Möller <contact@jakob-moeller.com>

Signed-off-by: Gerald Morrison (SAP) <gerald.morrison@sap.com>
morri-son pushed a commit to morri-son/open-component-model that referenced this pull request Apr 15, 2026
…mponent versions (open-component-model#2147)

<!-- markdownlint-disable MD041 -->
#### What this PR does / why we need it

Adds end-to-end CLI support and documentation for the **PEM signature
encoding policy** (`signatureEncodingPolicy: PEM`), which embeds an
X.509 certificate chain directly in the signature value and validates it
against a verifier-supplied trust anchor.

**Changes:**

- `cli/cmd/sign/component-version/cmd.go` — extends the `--signer-spec`
flag description and `Example` block with a second credential config
showing PEM signing (`private_key_pem_file` + `public_key_pem_file` for
a leaf+intermediate chain) and a second signer spec snippet for
`signatureEncodingPolicy: PEM`
- `cli/cmd/verify/component-version/cmd.go` — extends the `Example`
block with a PEM verification credential config (root CA as trust anchor
via `public_key_pem_file`), explains trust anchor isolation, and fixes
two copy-paste bugs where examples said `sign` instead of `verify`
- `cli/docs/reference/ocm_sign_component-version.md` /
`ocm_verify_component-version.md` — regenerated from the above
- `cli/integration/signing_integration_test.go` — adds
`Test_Integration_Signing_PEM`: an end-to-end integration test that
builds a 3-tier certificate chain (root → intermediate → leaf) at
runtime, signs a component version with PEM encoding via the CLI, and
verifies it using only the root CA as an isolated trust anchor

#### Which issue(s) this PR fixes

part of open-component-model/ocm-project#1000

<!--
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->

#### Testing

##### How to test the changes

Generate a certificate chain and two config files, then sign and verify
a component version:

```bash
# Generate chain
mkdir -p /tmp/ocm-pem-test && cd /tmp/ocm-pem-test
openssl genrsa -out root.key 2048
openssl req -x509 -new -nodes -key root.key -sha256 -days 365 -subj "/CN=test-root" -out root.crt
openssl genrsa -out leaf.key 2048
openssl req -new -key leaf.key -subj "/CN=test-leaf" -out leaf.csr
openssl x509 -req -in leaf.csr -CA root.crt -CAkey root.key -CAcreateserial -sha256 -days 90 -out leaf.crt
cp leaf.crt chain.pem
```

**`sign.ocmconfig`**
```yaml
type: generic.config.ocm.software/v1
configurations:
  - type: credentials.config.ocm.software
    consumers:
      - identity:
          type: RSA/v1alpha1
          algorithm: RSASSA-PSS
          signature: default
        credentials:
          - type: Credentials/v1
            properties:
              private_key_pem_file: /tmp/ocm-pem-test/leaf.key
              public_key_pem_file: /tmp/ocm-pem-test/chain.pem
```

**`verify.ocmconfig`**
```yaml
type: generic.config.ocm.software/v1
configurations:
  - type: credentials.config.ocm.software
    consumers:
      - identity:
          type: RSA/v1alpha1
          algorithm: RSASSA-PSS
          signature: default
        credentials:
          - type: Credentials/v1
            properties:
              public_key_pem_file: /tmp/ocm-pem-test/root.crt
```

**`pem-signer.yaml`**
```yaml
type: RSASigningConfiguration/v1alpha1
signatureAlgorithm: RSASSA-PSS
signatureEncodingPolicy: PEM
```

```bash
# Sign
ocm sign cv --config sign.ocmconfig --signer-spec pem-signer.yaml --dry-run <repo>//<component>:<version>
ocm sign cv --config sign.ocmconfig --signer-spec pem-signer.yaml <repo>//<component>:<version>

# Verify
ocm verify cv --config verify.ocmconfig <repo>//<component>:<version>
```

Run the new integration test directly (requires Docker for the OCI
registry):

```bash
cd cli/integration && go test -run Test_Integration_Signing_PEM -v -timeout 120s
```

##### Verification

- [x] I have tested the changes locally by running `ocm`

Signed-off-by: Jakob Möller <contact@jakob-moeller.com>
Signed-off-by: Gerald Morrison (SAP) <gerald.morrison@sap.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/feature new feature, enhancement, improvement, extension size/m Medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants