Skip to content

Commit

Permalink
Merge branch 'development' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
tmberthold committed Feb 17, 2022
2 parents 4b8a7c0 + ed54c5a commit 48ed90e
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 165 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@
# Changelog
All notable changes to this project will be documented in this file.

## Version [6.1.0] 2022-02-17

### Minor Change: New ConnectorFingerprintProvider
- The static call `ConnectorFingerprintProvider.fingerprint` can be used to retrieve the aki/ski connector fingerprint from now on. This fingerprint is determined at the start of the connector based on its certificate. With each reload of the keystoremanager and thus potential change of the connector certificate, the entry is regenerated. If no valid connector certificate with the required aki/ski information is available, the `Optional<String> fingerprint` will be empty. ([PR 431](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/431))

### Patch Change: Infomodel compatibility RejectionMessage refactoring
- If the modelVersion of an inbound message is not compatible with the inbound model versions list in the connector configuration and validation is active, the version of the received message as well as the list of supported versions will now be included in the RejectionMessage. Example: `Infomodel version of incoming Message not in supported inbound model version list! [incoming=(4.2.3), supported=([4.2.0, 4.2.1, 4.2.2])]` ([PR 432](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/432))

### Patch Change: Dependency Maintenance
- Upgrade: org.apache.jena:jena-core 4.3.2 -> 4.4.0 ([PR 426](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/426))
- Upgrade: org.apache.maven.plugins:maven-javadoc-plugin 3.3.1 -> 3.3.2 ([PR 428](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/428))
- Upgrade: maven-compiler-plugin 3.9.0 -> 3.10.0 ([PR 429](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/429))
- Upgrade: org.bitbucket.b_c:jose4j 0.7.9 -> 0.7.10 ([PR 430](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/430))

## Version [6.0.1] 2022-01-31

### Patch Change: Dependency Maintenance
Expand Down
2 changes: 1 addition & 1 deletion core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
<dependency>
<groupId>org.bitbucket.b_c</groupId>
<artifactId>jose4j</artifactId>
<version>0.7.9</version>
<version>0.7.10</version>
</dependency>
</dependencies>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,31 @@
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Date;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import de.fraunhofer.iais.eis.ConfigurationModel;
import de.fraunhofer.ids.messaging.core.config.ssl.truststore.TrustStoreManager;
import de.fraunhofer.ids.messaging.core.config.util.CertificateSubjectCnProvider;
import de.fraunhofer.ids.messaging.core.config.util.ConnectorFingerprintProvider;
import de.fraunhofer.ids.messaging.core.daps.ConnectorMissingCertExtensionException;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x500.style.IETFUtils;
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.jetbrains.annotations.Nullable;
import org.springframework.core.io.ClassPathResource;

import static org.apache.commons.codec.binary.Hex.encodeHexString;

/**
* The KeyStoreManager loads the IDSKeyStore and provides the TrustManager.
*/
Expand Down Expand Up @@ -174,6 +184,7 @@ private void initClassVars(final ConfigurationModel configurationModel,
initTrustManager(trustStorePw);
getPrivateKeyFromKeyStore(keyAlias);
initCertificateSubjectCn(); //requires valid connector certificate (e.g. issued by DAPS)
initCertificateConnectorFingerprint();
}

private void initCertificateSubjectCn() {
Expand All @@ -196,6 +207,27 @@ private void initCertificateSubjectCn() {
}
}

/**
* Initialises the ConnectorFingerprintProvider fingerprint value (aki/ski) of the connector
* by the connector certificate.
*/
private void initCertificateConnectorFingerprint() {
try {
ConnectorFingerprintProvider.fingerprint = Optional.of(getConnectorFingerprint());
if (log.isDebugEnabled()) {
log.debug("Determined AKI/SKI fingerprint of the connector. [code=(IMSCOD0150),"
+ " fingerprint=({})]",
ConnectorFingerprintProvider.fingerprint.get());
}
} catch (Exception exception) {
ConnectorFingerprintProvider.fingerprint = Optional.empty();
if (log.isDebugEnabled()) {
log.debug("Could not determine AKI/SKI fingerprint of the connector."
+ " [code=(IMSCOD0151), error=({})]", exception.getMessage());
}
}
}

private void initTrustManager(final char... trustStorePw)
throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
final var myManager = loadTrustManager(trustStorePw);
Expand Down Expand Up @@ -440,4 +472,113 @@ private void getPrivateKeyFromKeyStore(final String keyAlias)
this.cert = keyStore.getCertificate(keyAlias);
}
}

/**
* Generates the fingerprint of the Connector (UID) using the KeyStoreManager.
*
* @return The generated connector fingerprint (UID).
* @throws ConnectorMissingCertExtensionException Thrown if either AKI
* or SKI are not valid within the connector certificate.
*/
private String getConnectorFingerprint()
throws ConnectorMissingCertExtensionException {
final var certificate = (X509Certificate) this.getCert();
final var authorityKeyIdentifier = getCertificateAKI(certificate);
final var subjectKeyIdentifier = getCertificateSKI(certificate);

return generateConnectorFingerprint(authorityKeyIdentifier, subjectKeyIdentifier);
}

/**
* Generates the fingerprint of the connector (UID).
*
* @param authorityKeyIdentifier The connector certificate AKI.
* @param subjectKeyIdentifier The connector certificate SKI.
* @return The generated fingerprint of the connector (UID).
*/
private String generateConnectorFingerprint(final byte[] authorityKeyIdentifier,
final byte[] subjectKeyIdentifier) {
final var akiResult = beautifyHex(encodeHexString(authorityKeyIdentifier).toUpperCase());
final var skiResult = beautifyHex(encodeHexString(subjectKeyIdentifier).toUpperCase());

return skiResult + "keyid:" + akiResult.substring(0, akiResult.length() - 1);
}

/**
* Get the SKI of the certificate.
*
* @param cert The X509Certificate-Certificate
* @return The SKI-KeyIdentifier of the certificate
* @throws ConnectorMissingCertExtensionException thrown if SKI of certificate is empty
*/
private byte[] getCertificateSKI(final X509Certificate cert)
throws ConnectorMissingCertExtensionException {
if (log.isDebugEnabled()) {
log.debug("Get SKI from certificate... [code=(IMSCOD0110)]");
}

final var skiOid = Extension.subjectKeyIdentifier.getId();
final var rawSubjectKeyIdentifier = cert.getExtensionValue(skiOid);

if (rawSubjectKeyIdentifier == null) {
throw new ConnectorMissingCertExtensionException(
"SKI of the Connector Certificate is null!");
}

final var ski0c = ASN1OctetString.getInstance(rawSubjectKeyIdentifier);
final var ski = SubjectKeyIdentifier.getInstance(ski0c.getOctets());

return ski.getKeyIdentifier();
}

/**
* Get the AKI of the certificate.
*
* @param cert The X509Certificate-Certificate
* @return The AKI-KeyIdentifier of the Certificate
* @throws ConnectorMissingCertExtensionException thrown if AKI of certificate is empty
*/
private byte[] getCertificateAKI(final X509Certificate cert)
throws ConnectorMissingCertExtensionException {
if (log.isDebugEnabled()) {
log.debug("Get AKI from certificate... [code=(IMSCOD0111)]");
}

final var akiOid = Extension.authorityKeyIdentifier.getId();
final var rawAuthorityKeyIdentifier = cert.getExtensionValue(akiOid);

checkEmptyRawAKI(rawAuthorityKeyIdentifier); //can throw exception

final var akiOc = ASN1OctetString.getInstance(rawAuthorityKeyIdentifier);
final var aki = AuthorityKeyIdentifier.getInstance(akiOc.getOctets());

return aki.getKeyIdentifier();
}

/**
* Checks if AKI is empty.
*
* @param rawAuthorityKeyIdentifier The AKI to check
* @throws ConnectorMissingCertExtensionException thrown if AKI of certificate is null
*/
private void checkEmptyRawAKI(final byte[] rawAuthorityKeyIdentifier)
throws ConnectorMissingCertExtensionException {
if (rawAuthorityKeyIdentifier == null) {
throw new ConnectorMissingCertExtensionException(
"AKI of the Connector Certificate is null!");
}
}

/**
* Beautifies Hex strings and will generate a result later used to
* create the client id (XX:YY:ZZ).
*
* @param hexString HexString to be beautified
* @return beautifiedHex result
*/
private static String beautifyHex(final String hexString) {
return Arrays.stream(hexString.split("(?<=\\G..)"))
.map(s -> s + ":")
.collect(Collectors.joining());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.fraunhofer.ids.messaging.core.config.util;

import java.util.Optional;

/**
* This class provides static access to the aki/ski fingerprint of the connector,
* by its certificate.
*/
public final class ConnectorFingerprintProvider {
/**
* AKI/SKI connector fingerprint. Value is initialized by the KeyStoreManager and reset at
* each update (e.g. connector certificate change). Will be empty if no fingerprint could be
* determined (e.g. invalid or no connector certificate).
*/
public static Optional<String> fingerprint;

private ConnectorFingerprintProvider() {
//Nothing to do here.
}
}
Loading

0 comments on commit 48ed90e

Please sign in to comment.