Sigstore Rekor in Java

Introduction to Sigstore Rekor

Sigstore Rekor is a transparency log for software supply chain metadata that provides an immutable, tamper-proof ledger of software artifacts, signatures, and attestations. It enables verifiable tracking of software provenance and integrity.

Dependencies and Configuration

Maven Configuration

<properties>
<sigstore.version>0.6.0</sigstore.version>
<rekor-client.version>1.0.0</rekor-client.version>
<fulcio-client.version>1.0.0</fulcio-client.version>
</properties>
<dependencies>
<!-- Sigstore Java SDK -->
<dependency>
<groupId>dev.sigstore</groupId>
<artifactId>sigstore-java</artifactId>
<version>${sigstore.version}</version>
</dependency>
<!-- Rekor Java Client -->
<dependency>
<groupId>dev.sigstore.rekor</groupId>
<artifactId>rekor-client</artifactId>
<version>${rekor-client.version}</version>
</dependency>
<!-- Fulcio Client for code signing -->
<dependency>
<groupId>dev.sigstore.fulcio</groupId>
<artifactId>fulcio-client</artifactId>
<version>${fulcio-client.version}</version>
</dependency>
<!-- JSON Web Token Support -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<!-- Cryptographic Libraries -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>1.77</version>
</dependency>
<!-- HTTP Client -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

Application Configuration

# application.yml
sigstore:
enabled: true
# Rekor configuration
rekor:
server-url: ${REKOR_SERVER_URL:https://rekor.sigstore.dev}
api-version: v1
timeout: 30
retry:
max-attempts: 3
backoff-delay: 1000
# Local cache configuration
cache:
enabled: true
directory: ${user.home}/.sigstore/rekor
max-age-hours: 24
# Fulcio configuration (for code signing)
fulcio:
server-url: ${FULCIO_SERVER_URL:https://fulcio.sigstore.dev}
oidc:
issuer: https://oauth2.sigstore.dev/auth
client-id: sigstore
# Signing configuration
signing:
enabled: true
identity-provider: github
identity-token: ${GITHUB_TOKEN}
private-key-store: ${SIGSTORE_KEY_STORE:file://${user.home}/.sigstore/keys}
# Verification configuration
verification:
enabled: true
require-proof-of-possession: true
require-timestamp-authority: true
policy:
allowed-identities:
- "https://github.com/.*"
max-age-days: 90
# Monitoring configuration
monitoring:
enabled: true
metrics-prefix: sigstore
health-check-interval: 60
# Logging configuration for Sigstore
logging:
level:
dev.sigstore: INFO
org.apache.http: WARN

Core Rekor Client

Rekor Service Implementation

/**
* Rekor Service for interacting with Sigstore transparency log
*/
@Service
@Slf4j
public class RekorService {
private final RekorClient rekorClient;
private final FulcioClient fulcioClient;
private final SigningService signingService;
private final VerificationService verificationService;
private final ObjectMapper objectMapper;
@Value("${sigstore.rekor.server-url}")
private String rekorServerUrl;
@Value("${sigstore.rekor.api-version}")
private String apiVersion;
public RekorService(RestTemplateBuilder restTemplateBuilder,
SigningService signingService,
VerificationService verificationService) {
this.signingService = signingService;
this.verificationService = verificationService;
this.objectMapper = new ObjectMapper();
configureObjectMapper();
// Initialize Rekor client
this.rekorClient = createRekorClient(restTemplateBuilder);
// Initialize Fulcio client
this.fulcioClient = createFulcioClient();
}
/**
* Enters artifact metadata into Rekor transparency log
*/
public RekorEntry enterArtifact(ArtifactMetadata artifact, byte[] signature, 
String publicKey, String signatureFormat) {
try {
log.info("Entering artifact into Rekor: {}", artifact.getArtifactHash());
// Create Rekor entry
RekorEntryRequest request = createRekorEntryRequest(
artifact, signature, publicKey, signatureFormat);
// Submit to Rekor
RekorEntry entry = rekorClient.createEntry(request);
log.info("Artifact entered into Rekor. UUID: {}, LogIndex: {}", 
entry.getUuid(), entry.getLogIndex());
// Verify the entry was properly recorded
verifyRekorEntry(entry.getUuid());
return entry;
} catch (Exception e) {
log.error("Failed to enter artifact into Rekor: {}", artifact.getArtifactHash(), e);
throw new RekorException("Failed to enter artifact into Rekor", e);
}
}
/**
* Signs and enters an artifact into Rekor
*/
public SignedArtifact signAndEnterArtifact(ArtifactMetadata artifact, 
PrivateKey privateKey) {
try {
// Sign the artifact
SignatureResult signature = signingService.signArtifact(
artifact, privateKey);
// Get public key
String publicKey = signingService.getPublicKeyPem(privateKey);
// Enter into Rekor
RekorEntry entry = enterArtifact(
artifact, 
signature.getSignatureBytes(),
publicKey,
signature.getSignatureFormat()
);
return new SignedArtifact(artifact, signature, entry);
} catch (Exception e) {
log.error("Failed to sign and enter artifact: {}", artifact.getArtifactHash(), e);
throw new RekorException("Failed to sign and enter artifact", e);
}
}
/**
* Retrieves an entry from Rekor by UUID
*/
public RekorEntry getEntry(String uuid) {
try {
log.debug("Retrieving Rekor entry: {}", uuid);
return rekorClient.getEntry(uuid);
} catch (Exception e) {
log.error("Failed to retrieve Rekor entry: {}", uuid, e);
throw new RekorException("Failed to retrieve Rekor entry: " + uuid, e);
}
}
/**
* Retrieves entries by artifact hash
*/
public List<RekorEntry> getEntriesByArtifactHash(String artifactHash, String hashAlgorithm) {
try {
log.debug("Searching Rekor entries for artifact hash: {}", artifactHash);
SearchQuery query = new SearchQuery()
.hash(artifactHash)
.hashAlgorithm(hashAlgorithm);
return rekorClient.searchLog(query);
} catch (Exception e) {
log.error("Failed to search Rekor entries for hash: {}", artifactHash, e);
throw new RekorException("Failed to search Rekor entries", e);
}
}
/**
* Retrieves entries by public key
*/
public List<RekorEntry> getEntriesByPublicKey(String publicKey) {
try {
log.debug("Searching Rekor entries for public key");
SearchQuery query = new SearchQuery().publicKey(publicKey);
return rekorClient.searchLog(query);
} catch (Exception e) {
log.error("Failed to search Rekor entries by public key", e);
throw new RekorException("Failed to search Rekor entries by public key", e);
}
}
/**
* Verifies a Rekor entry
*/
public VerificationResult verifyEntry(String uuid) {
try {
log.debug("Verifying Rekor entry: {}", uuid);
// Get the entry
RekorEntry entry = getEntry(uuid);
// Verify inclusion proof
boolean inclusionVerified = verifyInclusionProof(entry);
// Verify signature
boolean signatureVerified = verificationService.verifySignature(
entry.getBody(),
entry.getSignature(),
entry.getPublicKey()
);
// Verify timestamp
boolean timestampVerified = verifyTimestamp(entry);
VerificationResult result = new VerificationResult();
result.setUuid(uuid);
result.setInclusionVerified(inclusionVerified);
result.setSignatureVerified(signatureVerified);
result.setTimestampVerified(timestampVerified);
result.setFullyVerified(inclusionVerified && signatureVerified && timestampVerified);
result.setVerificationTime(Instant.now());
return result;
} catch (Exception e) {
log.error("Failed to verify Rekor entry: {}", uuid, e);
throw new RekorException("Failed to verify Rekor entry", e);
}
}
/**
* Gets Rekor log info
*/
public LogInfo getLogInfo() {
try {
return rekorClient.getLogInfo();
} catch (Exception e) {
log.error("Failed to get Rekor log info", e);
throw new RekorException("Failed to get Rekor log info", e);
}
}
/**
* Gets Rekor public key
*/
public String getRekorPublicKey() {
try {
return rekorClient.getPublicKey();
} catch (Exception e) {
log.error("Failed to get Rekor public key", e);
throw new RekorException("Failed to get Rekor public key", e);
}
}
/**
* Monitors Rekor log for new entries
*/
public List<RekorEntry> monitorLog(long sinceLogIndex, int maxEntries) {
try {
LogInfo logInfo = getLogInfo();
long currentSize = logInfo.getTreeSize();
if (sinceLogIndex >= currentSize) {
return Collections.emptyList();
}
long endIndex = Math.min(sinceLogIndex + maxEntries, currentSize);
List<RekorEntry> newEntries = new ArrayList<>();
for (long i = sinceLogIndex; i < endIndex; i++) {
try {
// Note: Rekor API might have limits on batch retrieval
// This is a simplified implementation
RekorEntry entry = rekorClient.getEntryByIndex(i);
newEntries.add(entry);
} catch (Exception e) {
log.warn("Failed to retrieve entry at index {}", i, e);
}
}
return newEntries;
} catch (Exception e) {
log.error("Failed to monitor Rekor log", e);
throw new RekorException("Failed to monitor Rekor log", e);
}
}
/**
* Creates a signed attestation and enters it into Rekor
*/
public AttestationResult createAndEnterAttestation(Attestation attestation, 
PrivateKey privateKey) {
try {
// Serialize attestation
byte[] attestationBytes = objectMapper.writeValueAsBytes(attestation);
// Create artifact metadata
ArtifactMetadata metadata = ArtifactMetadata.builder()
.artifactHash(calculateHash(attestationBytes, "SHA-256"))
.hashAlgorithm("sha256")
.artifactType("attestation")
.contentType("application/json")
.created(Instant.now())
.build();
// Sign and enter
SignedArtifact signedArtifact = signAndEnterArtifact(metadata, privateKey);
return new AttestationResult(attestation, signedArtifact);
} catch (Exception e) {
log.error("Failed to create and enter attestation", e);
throw new RekorException("Failed to create attestation", e);
}
}
private RekorClient createRekorClient(RestTemplateBuilder restTemplateBuilder) {
return new DefaultRekorClient.Builder()
.serverUrl(rekorServerUrl)
.apiVersion(apiVersion)
.restTemplate(restTemplateBuilder.build())
.build();
}
private FulcioClient createFulcioClient() {
return new DefaultFulcioClient.Builder()
.serverUrl("https://fulcio.sigstore.dev")
.build();
}
private RekorEntryRequest createRekorEntryRequest(ArtifactMetadata artifact, 
byte[] signature, 
String publicKey,
String signatureFormat) {
RekorEntryRequest request = new RekorEntryRequest();
// Set artifact information
request.setApiVersion("0.0.1");
request.setKind("hashedrekord");
// Build spec
HashedRekordSpec spec = new HashedRekordSpec();
spec.setData(HashedRekordData.builder()
.hash(Hash.builder()
.algorithm(artifact.getHashAlgorithm())
.value(artifact.getArtifactHash())
.build())
.build());
spec.setSignature(HashedRekordSignature.builder()
.content(Base64.getEncoder().encodeToString(signature))
.publicKey(HashedRekordPublicKey.builder()
.content(publicKey)
.build())
.format(signatureFormat)
.build());
request.setSpec(spec);
return request;
}
private void verifyRekorEntry(String uuid) {
try {
// Wait a moment for the entry to be processed
Thread.sleep(1000);
VerificationResult result = verifyEntry(uuid);
if (!result.isFullyVerified()) {
throw new RekorException("Rekor entry verification failed for UUID: " + uuid);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RekorException("Verification interrupted", e);
}
}
private boolean verifyInclusionProof(RekorEntry entry) {
try {
// Verify Merkle tree inclusion proof
InclusionProof proof = entry.getInclusionProof();
if (proof == null) {
log.warn("No inclusion proof found for entry: {}", entry.getUuid());
return false;
}
// Verify the proof
return rekorClient.verifyInclusionProof(
entry.getLogIndex(),
entry.getBodyHash(),
proof.getRootHash(),
proof.getHashes(),
proof.getTreeSize()
);
} catch (Exception e) {
log.error("Failed to verify inclusion proof for entry: {}", entry.getUuid(), e);
return false;
}
}
private boolean verifyTimestamp(RekorEntry entry) {
try {
// Verify integrated timestamp signature
if (entry.getIntegratedTime() == null) {
log.warn("No timestamp found for entry: {}", entry.getUuid());
return false;
}
// Check timestamp is not too far in the future
Instant entryTime = Instant.ofEpochSecond(entry.getIntegratedTime());
Instant now = Instant.now();
if (entryTime.isAfter(now.plus(Duration.ofDays(1)))) {
log.warn("Entry timestamp is in the future: {}", entryTime);
return false;
}
// Verify timestamp signature if available
if (entry.getTimestampSignature() != null) {
return verificationService.verifyTimestamp(
entry.getBodyHash(),
entry.getTimestampSignature(),
entry.getIntegratedTime()
);
}
return true; // Some entries might not have timestamp signatures
} catch (Exception e) {
log.error("Failed to verify timestamp for entry: {}", entry.getUuid(), e);
return false;
}
}
private String calculateHash(byte[] data, String algorithm) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance(algorithm);
byte[] hash = digest.digest(data);
return Hex.encodeHexString(hash);
}
private void configureObjectMapper() {
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
}
/**
* Rekor data models
*/
@Data
@Builder
public class ArtifactMetadata {
private String artifactHash;
private String hashAlgorithm;
private String artifactType;
private String contentType;
private String description;
private Map<String, String> annotations;
private Instant created;
public static class ArtifactMetadataBuilder {
public ArtifactMetadataBuilder annotation(String key, String value) {
if (this.annotations == null) {
this.annotations = new HashMap<>();
}
this.annotations.put(key, value);
return this;
}
}
}
@Data
public class SignedArtifact {
private ArtifactMetadata artifact;
private SignatureResult signature;
private RekorEntry rekorEntry;
private Instant signedAt;
public SignedArtifact(ArtifactMetadata artifact, SignatureResult signature, RekorEntry rekorEntry) {
this.artifact = artifact;
this.signature = signature;
this.rekorEntry = rekorEntry;
this.signedAt = Instant.now();
}
}
@Data
public class Attestation {
private String predicateType;
private Map<String, Object> predicate;
private Map<String, Object> subject;
private Map<String, String> headers;
private Instant generatedAt;
}
@Data
public class AttestationResult {
private Attestation attestation;
private SignedArtifact signedArtifact;
public AttestationResult(Attestation attestation, SignedArtifact signedArtifact) {
this.attestation = attestation;
this.signedArtifact = signedArtifact;
}
}
@Data
public class VerificationResult {
private String uuid;
private boolean inclusionVerified;
private boolean signatureVerified;
private boolean timestampVerified;
private boolean fullyVerified;
private Instant verificationTime;
private String verificationMessage;
}
@Data
public class LogInfo {
private Long treeSize;
private String rootHash;
private String signedTreeHead;
private String treeId;
private Instant timestamp;
}
@Data
@Builder
public class HashedRekordSpec {
private HashedRekordData data;
private HashedRekordSignature signature;
@Data
@Builder
public static class HashedRekordData {
private Hash hash;
@Data
@Builder
public static class Hash {
private String algorithm;
private String value;
}
}
@Data
@Builder
public static class HashedRekordSignature {
private String content;
private HashedRekordPublicKey publicKey;
private String format;
@Data
@Builder
public static class HashedRekordPublicKey {
private String content;
}
}
}
public class RekorException extends RuntimeException {
public RekorException(String message) {
super(message);
}
public RekorException(String message, Throwable cause) {
super(message, cause);
}
}

Signing Service

Artifact Signing Service

/**
* Service for signing artifacts with cryptographic signatures
*/
@Service
@Slf4j
public class SigningService {
private final KeyPairGenerator keyPairGenerator;
private final Signature signatureAlgorithm;
private final SecureRandom secureRandom;
@Value("${sigstore.signing.signature-algorithm:RSA}")
private String signatureAlgorithmName;
@Value("${sigstore.signing.key-size:2048}")
private int keySize;
@Value("${sigstore.signing.hash-algorithm:SHA256withRSA}")
private String hashAlgorithm;
public SigningService() throws NoSuchAlgorithmException {
this.keyPairGenerator = KeyPairGenerator.getInstance(signatureAlgorithmName);
this.keyPairGenerator.initialize(keySize);
this.signatureAlgorithm = Signature.getInstance(hashAlgorithm);
this.secureRandom = new SecureRandom();
}
/**
* Generates a new key pair for signing
*/
public KeyPair generateKeyPair() {
try {
log.info("Generating new key pair for signing");
return keyPairGenerator.generateKeyPair();
} catch (Exception e) {
log.error("Failed to generate key pair", e);
throw new SigningException("Failed to generate key pair", e);
}
}
/**
* Signs an artifact with the given private key
*/
public SignatureResult signArtifact(ArtifactMetadata artifact, PrivateKey privateKey) {
try {
log.debug("Signing artifact: {}", artifact.getArtifactHash());
// Create signing data
byte[] dataToSign = createSigningData(artifact);
// Initialize signature
signatureAlgorithm.initSign(privateKey, secureRandom);
signatureAlgorithm.update(dataToSign);
// Generate signature
byte[] signatureBytes = signatureAlgorithm.sign();
// Create signature result
SignatureResult result = new SignatureResult();
result.setSignatureBytes(signatureBytes);
result.setSignatureFormat(getSignatureFormat());
result.setHashAlgorithm(artifact.getHashAlgorithm());
result.setArtifactHash(artifact.getArtifactHash());
result.setSigningTime(Instant.now());
log.info("Artifact signed successfully: {}", artifact.getArtifactHash());
return result;
} catch (Exception e) {
log.error("Failed to sign artifact: {}", artifact.getArtifactHash(), e);
throw new SigningException("Failed to sign artifact", e);
}
}
/**
* Signs an artifact with OIDC token (for Sigstore Fulcio)
*/
public SignatureResult signWithOIDC(ArtifactMetadata artifact, String oidcToken) {
try {
// This would integrate with Fulcio for code signing certificates
// For now, we'll use a simplified approach
log.info("Signing artifact with OIDC token: {}", artifact.getArtifactHash());
// Generate ephemeral key pair
KeyPair keyPair = generateKeyPair();
// Get signing certificate from Fulcio (simplified)
X509Certificate signingCertificate = getSigningCertificate(
keyPair.getPublic(), oidcToken);
// Sign the artifact
SignatureResult signature = signArtifact(artifact, keyPair.getPrivate());
// Add certificate to signature
signature.setCertificate(signingCertificate);
signature.setSigningMethod("fulcio-oidc");
return signature;
} catch (Exception e) {
log.error("Failed to sign with OIDC: {}", artifact.getArtifactHash(), e);
throw new SigningException("Failed to sign with OIDC", e);
}
}
/**
* Gets public key in PEM format
*/
public String getPublicKeyPem(PrivateKey privateKey) {
try {
// In a real implementation, we'd extract public key from private key
// For RSA keys
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(
rsaPrivateKey.getModulus(), 
rsaPrivateKey.getPrivateExponent());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
return convertToPem(publicKey);
} catch (Exception e) {
log.error("Failed to get public key PEM", e);
throw new SigningException("Failed to get public key PEM", e);
}
}
/**
* Creates a detached signature for an artifact
*/
public DetachedSignature createDetachedSignature(byte[] artifactData, PrivateKey privateKey) {
try {
log.debug("Creating detached signature for artifact data");
// Calculate hash
String hash = calculateHash(artifactData, "SHA-256");
// Create artifact metadata
ArtifactMetadata metadata = ArtifactMetadata.builder()
.artifactHash(hash)
.hashAlgorithm("sha256")
.artifactType("binary")
.contentType("application/octet-stream")
.created(Instant.now())
.build();
// Sign the metadata
SignatureResult signature = signArtifact(metadata, privateKey);
return new DetachedSignature(artifactData, signature);
} catch (Exception e) {
log.error("Failed to create detached signature", e);
throw new SigningException("Failed to create detached signature", e);
}
}
/**
* Signs multiple artifacts in batch
*/
public Map<String, SignatureResult> signBatch(List<ArtifactMetadata> artifacts, 
PrivateKey privateKey) {
Map<String, SignatureResult> results = new HashMap<>();
for (ArtifactMetadata artifact : artifacts) {
try {
SignatureResult signature = signArtifact(artifact, privateKey);
results.put(artifact.getArtifactHash(), signature);
} catch (Exception e) {
log.error("Failed to sign artifact in batch: {}", artifact.getArtifactHash(), e);
// Continue with other artifacts
}
}
return results;
}
/**
* Creates a timestamped signature
*/
public TimestampedSignature createTimestampedSignature(ArtifactMetadata artifact,
PrivateKey privateKey) {
try {
// Create standard signature
SignatureResult signature = signArtifact(artifact, privateKey);
// Get timestamp from timestamp authority
Timestamp timestamp = getTimestampFromAuthority(
signature.getSignatureBytes());
return new TimestampedSignature(signature, timestamp);
} catch (Exception e) {
log.error("Failed to create timestamped signature", e);
throw new SigningException("Failed to create timestamped signature", e);
}
}
private byte[] createSigningData(ArtifactMetadata artifact) throws JsonProcessingException {
// Create canonical JSON representation for signing
Map<String, Object> signingData = new LinkedHashMap<>();
signingData.put("artifactHash", artifact.getArtifactHash());
signingData.put("hashAlgorithm", artifact.getHashAlgorithm());
signingData.put("artifactType", artifact.getArtifactType());
signingData.put("contentType", artifact.getContentType());
signingData.put("created", artifact.getCreated().toString());
if (artifact.getAnnotations() != null) {
signingData.put("annotations", artifact.getAnnotations());
}
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsBytes(signingData);
}
private String getSignatureFormat() {
return "x509";
}
private X509Certificate getSigningCertificate(PublicKey publicKey, String oidcToken) {
// This would make a request to Fulcio to get a signing certificate
// For now, return a mock certificate
try {
// Create a self-signed certificate for demonstration
return createSelfSignedCertificate(publicKey);
} catch (Exception e) {
log.error("Failed to get signing certificate", e);
throw new SigningException("Failed to get signing certificate", e);
}
}
private X509Certificate createSelfSignedCertificate(PublicKey publicKey) 
throws CertificateException, NoSuchAlgorithmException, 
InvalidKeyException, NoSuchProviderException, SignatureException {
// Create certificate builder
X500Name issuer = new X500Name("CN=Sigstore Demo, O=Demo Org");
X500Name subject = new X500Name("CN=Sigstore Demo, O=Demo Org");
BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());
Date notBefore = new Date();
Date notAfter = new Date(notBefore.getTime() + 365L * 24 * 60 * 60 * 1000); // 1 year
X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(
issuer, serial, notBefore, notAfter, subject, 
SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
// Sign the certificate
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA")
.build(generateKeyPair().getPrivate());
X509CertificateHolder certHolder = certBuilder.build(signer);
return new JcaX509CertificateConverter()
.getCertificate(certHolder);
}
private String convertToPem(PublicKey publicKey) throws IOException {
StringWriter writer = new StringWriter();
try (PemWriter pemWriter = new PemWriter(writer)) {
PemObject pemObject = new PemObject("PUBLIC KEY", publicKey.getEncoded());
pemWriter.writeObject(pemObject);
}
return writer.toString();
}
private String calculateHash(byte[] data, String algorithm) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance(algorithm);
byte[] hash = digest.digest(data);
return Hex.encodeHexString(hash);
}
private Timestamp getTimestampFromAuthority(byte[] signature) {
// This would request a timestamp from a timestamp authority
// For now, create a mock timestamp
return new Timestamp(Instant.now(), "mock-authority", "mock-token");
}
}
/**
* Signing data models
*/
@Data
public class SignatureResult {
private byte[] signatureBytes;
private String signatureFormat;
private String hashAlgorithm;
private String artifactHash;
private Instant signingTime;
private X509Certificate certificate;
private String signingMethod;
private Map<String, String> metadata;
}
@Data
@AllArgsConstructor
public class DetachedSignature {
private byte[] artifactData;
private SignatureResult signature;
private Instant created;
public DetachedSignature(byte[] artifactData, SignatureResult signature) {
this.artifactData = artifactData;
this.signature = signature;
this.created = Instant.now();
}
}
@Data
public class TimestampedSignature {
private SignatureResult signature;
private Timestamp timestamp;
private Instant timestampedAt;
public TimestampedSignature(SignatureResult signature, Timestamp timestamp) {
this.signature = signature;
this.timestamp = timestamp;
this.timestampedAt = Instant.now();
}
}
@Data
public class Timestamp {
private Instant timestamp;
private String authority;
private String token;
private byte[] signature;
public Timestamp(Instant timestamp, String authority, String token) {
this.timestamp = timestamp;
this.authority = authority;
this.token = token;
}
}
public class SigningException extends RuntimeException {
public SigningException(String message) {
super(message);
}
public SigningException(String message, Throwable cause) {
super(message, cause);
}
}

Verification Service

Signature Verification Service

```java
/**

  • Service for verifying signatures and Rekor entries
    */
    @Service
    @Slf4j
    public class VerificationService { private final Signature signatureVerifier;
    private final CertificateFactory certificateFactory;
    private final TrustManagerFactory trustManagerFactory; @Value("${sigstore.verification.require-proof-of-possession:true}")
    private boolean requireProofOfPossession; @Value("${sigstore.verification.allowed-signature-algorithms:RSA,ECDSA}")
    private List allowedSignatureAlgorithms; public VerificationService() throws NoSuchAlgorithmException, CertificateException,
    KeyStoreException, IOException { this.signatureVerifier = Signature.getInstance("SHA256withRSA"); this.certificateFactory = CertificateFactory.getInstance("X.509"); // Initialize trust store with Sigstore roots this.trustManagerFactory = initializeTrustManagerFactory(); } /**
    • Verifies a signature against public key
      */
      public boolean verifySignature(byte[] data, byte[] signature, String publicKeyPem) {
      try {
      log.debug("Verifying signature with public key"); // Parse public key from PEM PublicKey publicKey = parsePublicKey(publicKeyPem); // Initialize verifier signatureVerifier.initVerify(publicKey); signatureVerifier.update(data); // Verify signature return signatureVerifier.verify(signature); } catch (Exception e) {
      log.error("Signature verification failed", e);
      return false;
      }
      }
    /**
    • Verifies a signature with X.509 certificate
      */
      public CertificateVerificationResult verifySignatureWithCertificate(
      byte[] data, byte[] signature, X509Certificate certificate) { try {
      log.debug("Verifying signature with X.509 certificate"); CertificateVerificationResult result = new CertificateVerificationResult(); // Verify certificate chain boolean certificateValid = verifyCertificateChain(certificate); result.setCertificateValid(certificateValid); if (!certificateValid) { result.setVerificationMessage("Certificate validation failed"); return result; } // Verify certificate is not expired boolean certificateNotExpired = verifyCertificateNotExpired(certificate); result.setCertificateNotExpired(certificateNotExpired); if (!certificateNotExpired) { result.setVerificationMessage("Certificate has expired"); return result; } // Verify signature signatureVerifier.initVerify(certificate.getPublicKey()); signatureVerifier.update(data); boolean signatureValid = signatureVerifier.verify(signature); result.setSignatureValid(signatureValid); if (!signatureValid) { result.setVerificationMessage("Signature verification failed"); return result; } // Verify proof of possession if required if (requireProofOfPossession) { boolean popVerified = verifyProofOfPossession(certificate, data); result.setProofOfPossessionVerified(popVerified);if (!popVerified) { result.setVerificationMessage("Proof of possession verification failed"); return result; }} // Extract and verify identity IdentityVerification identity = verifyCertificateIdentity(certificate); result.setIdentityVerified(identity.isVerified()); result.setIdentity(identity.getIdentity()); result.setFullyVerified(true); result.setVerificationMessage("Signature and certificate verified successfully"); return result; } catch (Exception e) {
      log.error("Certificate signature verification failed", e);
      CertificateVerificationResult result = new CertificateVerificationResult();
      result.setVerificationMessage("Verification failed: " + e.getMessage());
      return result;
      }
      }
    /**
    • Verifies timestamp signature
      */
      public boolean verifyTimestamp(String dataHash, byte[] timestampSignature, long timestamp) {
      try {
      log.debug("Verifying timestamp signature"); // Create timestamp verification data String verificationData = String.format("%s:%d", dataHash, timestamp); // This would verify against timestamp authority's public key // For now, return true for demonstration return true; } catch (Exception e) {
      log.error("Timestamp verification failed", e);
      return false;
      }
      }
    /**
    • Verifies a complete Rekor entry chain
      */
      public ChainVerificationResult verifyEntryChain(RekorEntry entry) {
      try {
      log.debug("Verifying Rekor entry chain: {}", entry.getUuid()); ChainVerificationResult result = new ChainVerificationResult(); result.setEntryUuid(entry.getUuid()); // Verify entry signature boolean entrySignatureVerified = verifySignature( entry.getBody().getBytes(StandardCharsets.UTF_8), Base64.getDecoder().decode(entry.getSignature()), entry.getPublicKey() ); result.setEntrySignatureVerified(entrySignatureVerified); // Verify timestamp boolean timestampVerified = verifyTimestamp( entry.getBodyHash(), entry.getTimestampSignature(), entry.getIntegratedTime() ); result.setTimestampVerified(timestampVerified); // Verify inclusion proof if available if (entry.getInclusionProof() != null) { // This would verify Merkle tree inclusion // Implementation depends on Rekor client capabilities result.setInclusionVerified(true); // Simplified } else { result.setInclusionVerified(false); result.addWarning("No inclusion proof available"); } // Verify certificate chain if certificate is present if (entry.getCertificate() != null) { CertificateVerificationResult certResult = verifySignatureWithCertificate( entry.getBody().getBytes(StandardCharsets.UTF_8), Base64.getDecoder().decode(entry.getSignature()), entry.getCertificate() ); result.setCertificateVerified(certResult.isFullyVerified()); result.setCertificateIdentity(certResult.getIdentity()); } // Check log consistency boolean logConsistent = verifyLog
    • Advanced Java Supply Chain Security, Kubernetes Hardening & Runtime Threat Detection
    • Sigstore Rekor in Java – https://macronepal.com/blog/sigstore-rekor-in-java/
    • Explains integrating Sigstore Rekor into Java systems to create a transparent, tamper-proof log of software signatures and metadata for verifying supply chain integrity.
    • Securing Java Applications with Chainguard Wolfi – https://macronepal.com/blog/securing-java-applications-with-chainguard-wolfi-a-comprehensive-guide/
    • Explains using Chainguard Wolfi minimal container images to reduce vulnerabilities and secure Java applications with hardened, lightweight runtime environments.
    • Cosign Image Signing in Java Complete Guide – https://macronepal.com/blog/cosign-image-signing-in-java-complete-guide/
    • Explains how to digitally sign container images using Cosign in Java-based workflows to ensure authenticity and prevent unauthorized modifications.
    • Secure Supply Chain Enforcement Kyverno Image Verification for Java Containers – https://macronepal.com/blog/secure-supply-chain-enforcement-kyverno-image-verification-for-java-containers/
    • Explains enforcing Kubernetes policies with Kyverno to verify container image signatures and ensure only trusted Java container images are deployed.
    • Pod Security Admission in Java Securing Kubernetes Deployments for JVM Applications – https://macronepal.com/blog/pod-security-admission-in-java-securing-kubernetes-deployments-for-jvm-applications/
    • Explains Kubernetes Pod Security Admission policies that enforce security rules like restricted privileges and safe configurations for Java workloads.
    • Securing Java Applications at Runtime Kubernetes Security Context – https://macronepal.com/blog/securing-java-applications-at-runtime-a-guide-to-kubernetes-security-context/
    • Explains how Kubernetes security contexts control runtime permissions, user IDs, and access rights for Java containers to improve isolation.
    • Process Anomaly Detection in Java Behavioral Monitoring – https://macronepal.com/blog/process-anomaly-detection-in-java-comprehensive-behavioral-monitoring-2/
    • Explains detecting abnormal runtime behavior in Java applications to identify potential security threats using process monitoring techniques.
    • Achieving Security Excellence CIS Benchmark Compliance for Java Applications – https://macronepal.com/blog/achieving-security-excellence-implementing-cis-benchmark-compliance-for-java-applications/
    • Explains applying CIS security benchmarks to Java environments to standardize hardening and improve overall system security posture.
    • Process Anomaly Detection in Java Behavioral Monitoring – https://macronepal.com/blog/process-anomaly-detection-in-java-comprehensive-behavioral-monitoring/
    • Explains behavioral monitoring of Java processes to detect anomalies and improve runtime security through continuous observation and analysis.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper