Article
In today's software landscape, securing the software supply chain is no longer optional—it's essential. For Java developers, this means ensuring the integrity and provenance of everything from container images to JAR files. Notary v2 is a cloud-native, open-source project that provides a standardized way to sign and verify container artifacts, bringing crucial security guarantees to your Java CI/CD pipeline.
What is Notary v2?
Notary v2 is a specification and implementation for signing, storing, and verifying container artifacts. It's the modern evolution of Docker Notary, designed specifically for cloud-native environments with support for OCI (Open Container Initiative) registries.
Key improvements in Notary v2:
- OCI-native: Works with any OCI-compliant registry
- Artifact-agnostic: Can sign container images, Helm charts, WASM modules, and even Java JARs
- Cloud-scale: Designed for performance in distributed environments
- Standardized: Part of the CNCF (Cloud Native Computing Foundation) ecosystem
Why Java Developers Need Notary v2
The Java ecosystem faces specific supply chain threats that Notary v2 helps mitigate:
- Tampered Base Images: Ensure your
eclipse-temurinoropenjdkbase images haven't been modified - Malicious Dependencies: Verify the integrity of application containers before deployment
- Build System Compromise: Detect if your CI/CD pipeline has been compromised and is producing malicious artifacts
- Compliance Requirements: Meet regulatory requirements for software provenance and integrity
Notary v2 Architecture and Workflow
Notary v2 operates on a simple but powerful workflow:
Signing: Java App Image → Notary v2 Client → Signature → OCI Registry Verification: Kubernetes/Deployment → Notary v2 Verifier → Check Signature → Allow/Deny Deployment
Signing Java Application Images
Here's how to integrate Notary v2 into your Java application build and deployment process.
Prerequisites:
- Notary v2 CLI installed
- Access to an OCI registry that supports Notary v2 (like Azure Container Registry, Harbor, or ORAS)
- Cryptographic keys for signing
Step 1: Build Your Java Application Container
# Dockerfile FROM eclipse-temurin:17-jre as builder WORKDIR /app COPY target/my-java-app.jar app.jar RUN java -Djarmode=layertools -jar app.jar extract FROM eclipse-temurin:17-jre RUN addgroup --system --gid 1000 appuser && \ adduser --system --uid 1000 --gid 1000 appuser USER appuser COPY --from=builder /app/dependencies/ ./ COPY --from=builder /app/spring-boot-loader/ ./ COPY --from=builder /app/snapshot-dependencies/ ./ COPY --from=builder /app/application/ ./ ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
Build and push the image:
docker build -t myregistry.io/java/my-app:v1.5.0 . docker push myregistry.io/java/my-app:v1.5.0
Step 2: Sign the Container Image with Notary v2
# Generate signing keys (if you don't have them) notary v2 key generate --name java-dev-key --private-key private.key --public-key public.key # Sign the container image notary v2 sign myregistry.io/java/my-app:v1.5.0 \ --key private.key \ --annotations "build-timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ --annotations "build-url=$CI_PIPELINE_URL" \ --annotations "commit-sha=$GIT_COMMIT" \ --annotations "java-version=17"
Step 3: Verify the Signature
# Verify the signature before deployment notary v2 verify myregistry.io/java/my-app:v1.5.0 \ --key public.key \ --output json
Integrating with Java CI/CD Pipelines
Here's how to integrate Notary v2 into common Java build systems:
Maven Integration:
<!-- pom.xml -->
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<to>
<image>myregistry.io/java/${project.artifactId}:${project.version}</image>
</to>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
# GitHub Actions workflow
name: Build, Sign and Push Java App
on:
push:
branches: [main]
jobs:
build-and-sign:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
cache: 'maven'
- name: Build with Maven
run: mvn clean package -DskipTests
- name: Install Notary v2
run: |
curl -sL https://github.com/notaryproject/notary/releases/download/v2.0.0/notary-v2-linux-amd64 -o notary
chmod +x notary
sudo mv notary /usr/local/bin/
- name: Build and push container
run: mvn compile jib:build
env:
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
- name: Sign container image
run: |
notary v2 sign myregistry.io/java/my-app:${{ github.sha }} \
--key ${{ secrets.SIGNING_KEY }} \
--annotations "commit=${{ github.sha }}" \
--annotations "build-run=${{ github.run_id }}"
Gradle Integration:
// build.gradle.kts
plugins {
id("com.google.cloud.tools.jib") version "3.3.1"
}
jib {
to {
image = "myregistry.io/java/${project.name}:${project.version}"
auth {
username = project.findProperty("registryUsername") as String?
password = project.findProperty("registryPassword") as String?
}
}
container {
user = "1000:1000"
jvmFlags = listOf("-Xmx512m", "-Xms256m")
}
}
Verification at Deployment Time
The real security value comes from verifying signatures before deployment:
Kubernetes Admission Controller:
Use projects like Ratify or Gatekeeper to verify signatures automatically:
# Ratify configuration apiVersion: config.ratify.devguru/v1beta1 kind: Verifier metadata: name: notaryv2 spec: name: notaryv2 artifactTypes: application/vnd.oci.image.manifest.v1+json parameters: key: | -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA... -----END PUBLIC KEY-----
GitHub Actions Verification:
name: Verify and Deploy
on:
workflow_dispatch:
inputs:
image-tag:
description: 'Image tag to deploy'
required: true
jobs:
verify-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Verify signature
run: |
notary v2 verify myregistry.io/java/my-app:${{ inputs.image-tag }} \
--key public.key \
--output json
if [ $? -ne 0 ]; then
echo "❌ Signature verification failed"
exit 1
fi
- name: Deploy to Kubernetes
run: kubectl set image deployment/my-app my-app=myregistry.io/java/my-app:${{ inputs.image-tag }}
Signing Maven Artifacts and JAR Files
While Notary v2 primarily focuses on container images, you can also use it to sign other artifacts:
# Sign a Maven artifact using OCI registry
oras push myregistry.io/artifacts/java/my-app/${{ project.version }}/my-app-${{ project.version }}.jar \
--artifact-type application/java-archive
# Sign with Notary v2
notary v2 sign myregistry.io/artifacts/java/my-app/${{ project.version }}/my-app-${{ project.version }}.jar \
--key private.key \
--annotations "groupId=com.example" \
--annotations "artifactId=my-app" \
--annotations "version=${{ project.version }}"
Java-Specific Security Considerations
- JAR Signing vs. Container Signing: Notary v2 complements traditional JAR signing—it secures the entire runtime environment, not just the Java code.
- Dependency Verification: Combine Notary v2 with tools like CycloneDX or Dependency-Track for comprehensive supply chain security:
# Generate SBOM and sign it mvn org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom notary v2 sign myregistry.io/sbom/java/my-app:v1.5.0 \ --key private.key \ --artifact-type application/vnd.cyclonedx+json
- Runtime Verification: Use Java security managers in combination with signed containers for defense in depth.
Best Practices for Java Teams
- Store Keys Securely: Use cloud KMS, HashiCorp Vault, or Kubernetes Secrets for private keys
- Sign Early: Sign images immediately after build in CI pipeline
- Verify Everywhere: Verify signatures in admission controllers, during deployment, and in production
- Use Attestations: Include build metadata as annotations for full provenance
- Rotate Keys: Implement regular key rotation policies
Conclusion
For Java development teams, Notary v2 provides a critical layer of security in the software supply chain. By integrating container signing into your build and deployment processes, you can ensure the integrity of your Java applications from development through production.
The combination of Notary v2 with existing Java security practices creates a robust defense against supply chain attacks, giving developers, operators, and security teams confidence in the artifacts they build, distribute, and deploy. As regulatory requirements tighten and threats evolve, adopting Notary v2 becomes not just a best practice, but a necessity for secure Java application delivery.
Java Logistics, Shipping Integration & Enterprise Inventory Automation (Tracking, ERP, RFID & Billing Systems)
https://macronepal.com/blog/aftership-tracking-in-java-enterprise-package-visibility/
Explains how to integrate AfterShip tracking services into Java applications to provide real-time shipment visibility, delivery status updates, and centralized tracking across multiple courier services.
https://macronepal.com/blog/shipping-integration-using-fedex-api-with-java-for-logistics-automation/
Explains how to integrate the FedEx API into Java systems to automate shipping tasks such as creating shipments, calculating delivery costs, generating shipping labels, and tracking packages.
https://macronepal.com/blog/shipping-and-logistics-integrating-ups-apis-with-java-applications/
Explains UPS API integration in Java to enable automated shipping operations including rate calculation, shipment scheduling, tracking, and delivery confirmation management.
https://macronepal.com/blog/generating-and-reading-qr-codes-for-products-in-java/
Explains how Java applications generate and read QR codes for product identification, tracking, and authentication, supporting faster inventory handling and product verification processes.
https://macronepal.com/blog/designing-a-robust-pick-and-pack-workflow-in-java/
Explains how to design an efficient pick-and-pack workflow in Java warehouse systems, covering order processing, item selection, packaging steps, and logistics preparation to improve fulfillment efficiency.
https://macronepal.com/blog/rfid-inventory-management-system-in-java-a-complete-guide/
Explains how RFID technology integrates with Java applications to automate inventory tracking, reduce manual errors, and enable real-time stock monitoring in warehouses and retail environments.
https://macronepal.com/blog/erp-integration-with-odoo-in-java/
Explains how Java applications connect with Odoo ERP systems to synchronize inventory, orders, customer records, and financial data across enterprise systems.
https://macronepal.com/blog/automated-invoice-generation-creating-professional-excel-invoices-with-apache-poi-in-java/
Explains how to automatically generate professional Excel invoices in Java using Apache POI, enabling structured billing documents and automated financial record creation.
https://macronepal.com/blog/enterprise-financial-integration-using-quickbooks-api-in-java-applications/
Explains QuickBooks API integration in Java to automate financial workflows such as invoice management, payment tracking, accounting synchronization, and financial reporting.