-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #138 from RIPE-NCC/feature/generic-parser-access-c…
…ertificate Access certificate with generic signed object parser
- Loading branch information
Showing
8 changed files
with
165 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,7 +11,7 @@ jobs: | |
runs-on: ubuntu-latest | ||
strategy: | ||
matrix: | ||
java: [ 17 ] | ||
java: [ 21 ] | ||
|
||
steps: | ||
- name: Setup java | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
87 changes: 87 additions & 0 deletions
87
src/main/java/net/ripe/rpki/commons/crypto/util/SignedObjectUtil.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package net.ripe.rpki.commons.crypto.util; | ||
|
||
import lombok.Getter; | ||
import lombok.experimental.UtilityClass; | ||
import net.ripe.rpki.commons.crypto.cms.GenericRpkiSignedObjectParser; | ||
import net.ripe.rpki.commons.crypto.crl.X509Crl; | ||
import net.ripe.rpki.commons.crypto.x509cert.X509ResourceCertificateParser; | ||
import net.ripe.rpki.commons.util.RepositoryObjectType; | ||
import net.ripe.rpki.commons.validation.ValidationResult; | ||
import org.joda.time.Instant; | ||
|
||
import java.net.URI; | ||
|
||
@UtilityClass | ||
public class SignedObjectUtil { | ||
/** | ||
* Extract the creation time from an object. This does <emph>not yet</emph> follow the method described in | ||
* https://datatracker.ietf.org/doc/draft-timbru-sidrops-publication-server-bcp/00/. It differs in that it uses | ||
* the signing time for RPKI signed objects. This is a trade-off: | ||
* * signing-time is more correct when multi-use EE certificates are present. | ||
* * signing-time likely does not match the modification time of the CRL. | ||
* | ||
* This needs to be revisited in 2024. | ||
* | ||
* @param uri URL of the object | ||
* @param decoded object bytes | ||
* @return the file creation time of the object | ||
* @throws NoTimeParsedException if creation time could not be extracted. | ||
*/ | ||
public static Instant getFileCreationTime(URI uri, byte[] decoded) throws NoTimeParsedException { | ||
|
||
final RepositoryObjectType objectType = RepositoryObjectType.parse(uri.toString()); | ||
try { | ||
switch (objectType) { | ||
case Manifest: | ||
case Aspa: | ||
case Roa: | ||
case Gbr: | ||
var signedObjectParser = new GenericRpkiSignedObjectParser(); | ||
|
||
signedObjectParser.parse(ValidationResult.withLocation(uri), decoded); | ||
var signingTime = signedObjectParser.getSigningTime(); | ||
|
||
if (signingTime == null) { | ||
return signedObjectParser.getCertificate().getValidityPeriod().getNotValidBefore().toInstant(); | ||
} | ||
return signingTime.toInstant(); | ||
case Certificate: | ||
X509ResourceCertificateParser x509CertificateParser = new X509ResourceCertificateParser(); | ||
x509CertificateParser.parse(ValidationResult.withLocation(uri), decoded); | ||
final var cert = x509CertificateParser.getCertificate().getCertificate(); | ||
return Instant.ofEpochMilli(cert.getNotBefore().getTime()); | ||
case Crl: | ||
var x509Crl = X509Crl.parseDerEncoded(decoded, ValidationResult.withLocation(uri)); | ||
var crl = x509Crl.getCrl(); | ||
return Instant.ofEpochMilli(crl.getThisUpdate().getTime()); | ||
case Unknown: | ||
default: | ||
throw new NoTimeParsedException(decoded, uri, "Could not determine file type"); | ||
} | ||
} catch (Exception e) { | ||
if (e instanceof NoTimeParsedException) { | ||
throw e; | ||
} | ||
throw new NoTimeParsedException(decoded, uri, "Could not parse object", e); | ||
} | ||
} | ||
|
||
@Getter | ||
public static class NoTimeParsedException extends Exception { | ||
private static final long serialVersionUID = 1L; | ||
|
||
private byte[] decoded; | ||
private URI uri; | ||
public NoTimeParsedException(byte[] decoded, URI uri, String message) { | ||
super(uri.toString() + ": " + message); | ||
this.decoded = decoded; | ||
this.uri = uri; | ||
} | ||
|
||
public NoTimeParsedException(byte[] decoded, URI uri, String message, Throwable cause) { | ||
super(uri.toString() + ": " + message, cause); | ||
this.decoded = decoded; | ||
this.uri = uri; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
src/test/java/net/ripe/rpki/commons/util/SignedObjectUtilTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package net.ripe.rpki.commons.util; | ||
|
||
import com.google.common.io.Resources; | ||
import net.ripe.rpki.commons.crypto.util.SignedObjectUtil; | ||
import org.joda.time.DateTime; | ||
import org.joda.time.Instant; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.CsvSource; | ||
|
||
import java.io.IOException; | ||
import java.net.URI; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
||
public class SignedObjectUtilTest { | ||
@DisplayName("Should parse the file creation time from RPKI objects") | ||
@ParameterizedTest(name = "{index} => {0} filename={1} expected-creation-time={3} path={2}") | ||
@CsvSource({ | ||
"ASPA, sample.asa, interop/aspa/GOOD-profile-15-draft-ietf-sidrops-profile-15-sample.asa, 2023-06-07T09:08:41Z", | ||
// GBR parser has issues | ||
// "GBR, sample.gbr, conformance/root/goodRealGbrNothingIsWrong.gbr, 2023-06-07T09:01:01Z", | ||
// router certificate case is missing due to lack of samples. | ||
"Manifest, sample.mft, conformance/root/root.mft, 2013-10-28T21:24:39Z", | ||
"ROA, sample.roa, interop/rpkid-objects/nI2bsx18I5mlex8lBpY0WSJUYio.roa, 2011-11-11T01:55:18Z", | ||
"'Generic signed object (that does not match object profile)', generic-signed-object.gbr, interop/aspa/BAD-profile-13-AS211321-profile-13.asa, 2021-11-11T11:19:00Z", | ||
}) | ||
void shouldParseObject(String description, String fileName, String path, String modified) throws IOException, SignedObjectUtil.NoTimeParsedException { | ||
Instant creationTime = SignedObjectUtil.getFileCreationTime(URI.create(fileName), Resources.toByteArray(Resources.getResource(path))); | ||
|
||
assertThat(creationTime).isEqualTo(DateTime.parse(modified)); | ||
} | ||
|
||
@Test | ||
void shouldThrowOnUnknown_payload() { | ||
assertThatThrownBy(() -> SignedObjectUtil.getFileCreationTime(URI.create("foo.cer"), new byte[] {(byte) 0xDE, (byte) 0xAD, (byte) 0xBE, (byte) 0xEF})) | ||
.isInstanceOf(SignedObjectUtil.NoTimeParsedException.class); | ||
} | ||
@Test | ||
void shouldThrowOnUnknown_extension() { | ||
assertThatThrownBy(() -> SignedObjectUtil.getFileCreationTime(URI.create("foo.xxx"), Resources.toByteArray(Resources.getResource("interop/aspa/BAD-profile-13-AS211321-profile-13.asa")))) | ||
.isInstanceOf(SignedObjectUtil.NoTimeParsedException.class); | ||
} | ||
} |