diff options
author | Tomasz Wrobel <tomasz.wrobel@nokia.com> | 2021-06-30 16:14:25 +0200 |
---|---|---|
committer | Joanna Jeremicz <joanna.jeremicz@nokia.com> | 2021-07-05 13:11:04 +0200 |
commit | 23de50858f982b986b2e6f3a13ccca4a3bd3980c (patch) | |
tree | 235f754b04192c29569d4820acbd3b80e6c4046e /certService/src/main/java/org | |
parent | 430b63820a2e1807e45ca9fba21d81be8b9fd5ee (diff) |
[OOM-CERT-SERVICE] Add Key Update Request functionality
Issue-ID: OOM-2753
Signed-off-by: Tomasz Wrobel <tomasz.wrobel@nokia.com>
Change-Id: Icecef30b830c38606e17fbc2c502208543d048d2
Diffstat (limited to 'certService/src/main/java/org')
7 files changed, 144 insertions, 16 deletions
diff --git a/certService/src/main/java/org/onap/oom/certservice/api/CertificationController.java b/certService/src/main/java/org/onap/oom/certservice/api/CertificationController.java index d21b1eb5..9f877788 100644 --- a/certService/src/main/java/org/onap/oom/certservice/api/CertificationController.java +++ b/certService/src/main/java/org/onap/oom/certservice/api/CertificationController.java @@ -112,7 +112,7 @@ public class CertificationController { @RequestHeader("PK") String encodedPrivateKey, @RequestHeader("OLD_CERT") String encodedOldCert, @RequestHeader("OLD_PK") String encodedOldPrivateKey - ) throws DecryptionException, CertificateDecryptionException { + ) throws DecryptionException, CmpClientException, CertificateDecryptionException { caName = replaceWhiteSpaceChars(caName); LOGGER.info("Received certificate update request for CA named: {}", caName); CertificateUpdateModel certificateUpdateModel = new CertificateUpdateModel.CertificateUpdateModelBuilder() diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/CertificationModelFactory.java b/certService/src/main/java/org/onap/oom/certservice/certification/CertificationModelFactory.java index d5d9d9b8..a5076a38 100644 --- a/certService/src/main/java/org/onap/oom/certservice/certification/CertificationModelFactory.java +++ b/certService/src/main/java/org/onap/oom/certservice/certification/CertificationModelFactory.java @@ -76,7 +76,7 @@ public class CertificationModelFactory { } public CertificationModel createCertificationModel(CertificateUpdateModel certificateUpdateModel) - throws DecryptionException, CertificateDecryptionException { + throws DecryptionException, CmpClientException, CertificateDecryptionException { LOGGER.info("CSR: " + certificateUpdateModel.getEncodedCsr() + ", old cert: " + certificateUpdateModel.getEncodedOldCert() + ", CA: " + certificateUpdateModel.getCaName()); @@ -87,10 +87,15 @@ public class CertificationModelFactory { final X509CertificateModel certificateModel = x509CertificateModelFactory.createCertificateModel( new StringBase64(certificateUpdateModel.getEncodedOldCert())); + Cmpv2Server cmpv2Server = cmpv2ServerProvider.getCmpv2Server(certificateUpdateModel.getCaName()); + LOGGER.debug("Found server for given CA name: \n{}", cmpv2Server); + LOGGER.info("Sending update request for certification model for CA named: {}, and certificate update request:\n{}", + certificateUpdateModel.getCaName(), csrModel); + if (updateRequestTypeDetector.isKur(csrModel.getCertificateData(), certificateModel.getCertificateData())) { LOGGER.info( "Certificate Signing Request and Old Certificate have the same parameters. Preparing Key Update Request"); - throw new UnsupportedOperationException("TODO: implement KUR in separate MR"); + return certificationProvider.updateCertificate(csrModel, cmpv2Server, certificateUpdateModel); } else { LOGGER.info( "Certificate Signing Request and Old Certificate have different parameters. Preparing Certification Request"); diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/CertificationProvider.java b/certService/src/main/java/org/onap/oom/certservice/certification/CertificationProvider.java index 91148a22..bfa83103 100644 --- a/certService/src/main/java/org/onap/oom/certservice/certification/CertificationProvider.java +++ b/certService/src/main/java/org/onap/oom/certservice/certification/CertificationProvider.java @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * Cert Service * ================================================================================ - * Copyright (C) 2020 Nokia. All rights reserved. + * Copyright (C) 2020-2021 Nokia. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator; import org.bouncycastle.util.io.pem.PemObjectGenerator; import org.bouncycastle.util.io.pem.PemWriter; import org.onap.oom.certservice.certification.configuration.model.Cmpv2Server; +import org.onap.oom.certservice.certification.model.CertificateUpdateModel; import org.onap.oom.certservice.certification.model.CertificationModel; import org.onap.oom.certservice.certification.model.CsrModel; import org.onap.oom.certservice.cmpv2client.api.CmpClient; @@ -59,6 +60,13 @@ public class CertificationProvider { convertFromX509CertificateListToPemList(certificates.getTrustedCertificates())); } + public CertificationModel updateCertificate(CsrModel csrModel, Cmpv2Server cmpv2Server, + CertificateUpdateModel certificateUpdateModel) throws CmpClientException { + Cmpv2CertificationModel certificates = cmpClient.updateCertificate(csrModel, cmpv2Server, certificateUpdateModel); + return new CertificationModel(convertFromX509CertificateListToPemList(certificates.getCertificateChain()), + convertFromX509CertificateListToPemList(certificates.getTrustedCertificates())); + } + private static List<String> convertFromX509CertificateListToPemList(List<X509Certificate> certificates) { return certificates.stream().map(CertificationProvider::convertFromX509CertificateToPem).filter(cert -> !cert.isEmpty()) .collect(Collectors.toList()); @@ -74,5 +82,4 @@ public class CertificationProvider { } return sw.toString(); } - } diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/model/CertificateUpdateModel.java b/certService/src/main/java/org/onap/oom/certservice/certification/model/CertificateUpdateModel.java index 699ffe71..9423af52 100644 --- a/certService/src/main/java/org/onap/oom/certservice/certification/model/CertificateUpdateModel.java +++ b/certService/src/main/java/org/onap/oom/certservice/certification/model/CertificateUpdateModel.java @@ -20,7 +20,16 @@ package org.onap.oom.certservice.certification.model; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; import java.util.Objects; +import org.bouncycastle.util.io.pem.PemObject; +import org.onap.oom.certservice.certification.PemObjectFactory; +import org.onap.oom.certservice.certification.StringBase64; +import org.onap.oom.certservice.certification.exception.KeyDecryptionException; public final class CertificateUpdateModel { @@ -29,6 +38,7 @@ public final class CertificateUpdateModel { private final String encodedOldCert; private final String encodedOldPrivateKey; private final String caName; + private static final PemObjectFactory PEM_OBJECT_FACTORY = new PemObjectFactory(); private CertificateUpdateModel(String encodedCsr, String encodedPrivateKey, String encodedOldCert, String encodedOldPrivateKey, String caName) { @@ -59,6 +69,20 @@ public final class CertificateUpdateModel { return caName; } + public PrivateKey getOldPrivateKeyObject() + throws KeyDecryptionException, InvalidKeySpecException, NoSuchAlgorithmException { + + StringBase64 stringBase64 = new StringBase64(encodedOldPrivateKey); + PemObject pemObject = stringBase64.asString() + .flatMap(PEM_OBJECT_FACTORY::createPemObject) + .orElseThrow( + () -> new KeyDecryptionException("Incorrect Key, decryption failed") + ); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pemObject.getContent()); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return keyFactory.generatePrivate(keySpec); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/certService/src/main/java/org/onap/oom/certservice/cmpv2client/api/CmpClient.java b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/api/CmpClient.java index d525c6e3..5ded3056 100644 --- a/certService/src/main/java/org/onap/oom/certservice/cmpv2client/api/CmpClient.java +++ b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/api/CmpClient.java @@ -1,6 +1,7 @@ /*- * ============LICENSE_START======================================================= * Copyright (C) 2020 Nordix Foundation. + * Copyright (C) 2021 Nokia. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +24,7 @@ package org.onap.oom.certservice.cmpv2client.api; import java.util.Date; import org.onap.oom.certservice.certification.configuration.model.Cmpv2Server; +import org.onap.oom.certservice.certification.model.CertificateUpdateModel; import org.onap.oom.certservice.certification.model.CsrModel; import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException; import org.onap.oom.certservice.cmpv2client.model.Cmpv2CertificationModel; @@ -71,4 +73,19 @@ public interface CmpClient { CsrModel csrModel, Cmpv2Server server) throws CmpClientException; + + /** + * Requests for a External Root CA Certificate to be updated for the passed keyPair wrapped + * in a CSRMeta with common details. Authentication using End Entity Certificate. Old certificate and old privateKey + * are wrapped in CertificateUpdateModel.class + * Exception thrown if verification fails or issue encountered in fetching certificate from CA. + * + * @param csrModel Certificate Signing Request Model. Must not be {@code null}. + * @param cmpv2Server CMPv2 server. Must not be {@code null}. + * @param certificateUpdateModel Model with key update parameters {@code null}. + * @return model for certification containing certificate chain and trusted certificates + * @throws CmpClientException if client error occurs. + */ + Cmpv2CertificationModel updateCertificate(CsrModel csrModel, Cmpv2Server cmpv2Server, + CertificateUpdateModel certificateUpdateModel) throws CmpClientException; } diff --git a/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpClientImpl.java b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpClientImpl.java index 06e785ac..270b5995 100644 --- a/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpClientImpl.java +++ b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpClientImpl.java @@ -29,15 +29,21 @@ import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseValidationHel import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseValidationHelper.verifyPasswordBasedProtection; import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseValidationHelper.verifySignature; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; import java.util.Collections; import java.util.Date; import java.util.Objects; import java.util.Optional; +import org.apache.commons.codec.binary.Base64; import org.apache.http.impl.client.CloseableHttpClient; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.cmp.CMPCertificate; @@ -47,8 +53,12 @@ import org.bouncycastle.asn1.cmp.PKIBody; import org.bouncycastle.asn1.cmp.PKIHeader; import org.bouncycastle.asn1.cmp.PKIMessage; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.Certificate; +import org.bouncycastle.util.io.pem.PemReader; import org.onap.oom.certservice.certification.configuration.model.CaMode; import org.onap.oom.certservice.certification.configuration.model.Cmpv2Server; +import org.onap.oom.certservice.certification.exception.KeyDecryptionException; +import org.onap.oom.certservice.certification.model.CertificateUpdateModel; import org.onap.oom.certservice.certification.model.CsrModel; import org.onap.oom.certservice.cmpv2client.api.CmpClient; import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException; @@ -83,25 +93,19 @@ public class CmpClientImpl implements CmpClient { throws CmpClientException { validate(csrModel, server, httpClient, notBefore, notAfter); - KeyPair keyPair = new KeyPair(csrModel.getPublicKey(), csrModel.getPrivateKey()); final String iak = server.getAuthentication().getIak(); final PkiMessageProtection pkiMessageProtection = new PasswordBasedProtection(iak); final CreateCertRequest certRequest = - CmpMessageBuilder.of(CreateCertRequest::new) - .with(CreateCertRequest::setIssuerDn, server.getIssuerDN()) - .with(CreateCertRequest::setSubjectDn, csrModel.getSubjectData()) - .with(CreateCertRequest::setSansArray, csrModel.getSans()) - .with(CreateCertRequest::setSubjectKeyPair, keyPair) + getCmpMessageBuilderWithCommonRequestValues(csrModel, server) .with(CreateCertRequest::setNotBefore, notBefore) .with(CreateCertRequest::setNotAfter, notAfter) .with(CreateCertRequest::setSenderKid, server.getAuthentication().getRv()) + .with(CreateCertRequest::setCmpRequestType, PKIBody.TYPE_INIT_REQ) .with(CreateCertRequest::setProtection, pkiMessageProtection) .build(); - final PKIMessage pkiMessage = certRequest.generateCertReq(); - Cmpv2HttpClient cmpv2HttpClient = new Cmpv2HttpClient(httpClient); - return retrieveCertificates(csrModel, server, pkiMessage, cmpv2HttpClient); + return executeCmpRequest(csrModel, server, certRequest); } @Override @@ -110,6 +114,66 @@ public class CmpClientImpl implements CmpClient { return createCertificate(csrModel, server, null, null); } + @Override + public Cmpv2CertificationModel updateCertificate(CsrModel csrModel, Cmpv2Server cmpv2Server, + CertificateUpdateModel certificateUpdateModel) throws CmpClientException { + validate(csrModel, cmpv2Server, httpClient, null, null); + + final PkiMessageProtection pkiMessageProtection = getSignatureProtection(certificateUpdateModel); + final CreateCertRequest certRequest = + getCmpMessageBuilderWithCommonRequestValues(csrModel, cmpv2Server) + .with(CreateCertRequest::setCmpRequestType, PKIBody.TYPE_KEY_UPDATE_REQ) + .with(CreateCertRequest::setExtraCerts, getCMPCertificateFromPem(certificateUpdateModel.getEncodedOldCert())) + .with(CreateCertRequest::setProtection, pkiMessageProtection) + .build(); + + return executeCmpRequest(csrModel, cmpv2Server, certRequest); + + } + + private Cmpv2CertificationModel executeCmpRequest(CsrModel csrModel, Cmpv2Server cmpv2Server, + CreateCertRequest certRequest) throws CmpClientException { + final PKIMessage pkiMessage = certRequest.generateCertReq(); + Cmpv2HttpClient cmpv2HttpClient = new Cmpv2HttpClient(httpClient); + return retrieveCertificates(csrModel, cmpv2Server, pkiMessage, cmpv2HttpClient); + } + + private CmpMessageBuilder<CreateCertRequest> getCmpMessageBuilderWithCommonRequestValues(CsrModel csrModel, + Cmpv2Server cmpv2Server) { + KeyPair keyPair = new KeyPair(csrModel.getPublicKey(), csrModel.getPrivateKey()); + return CmpMessageBuilder.of(CreateCertRequest::new) + .with(CreateCertRequest::setIssuerDn, cmpv2Server.getIssuerDN()) + .with(CreateCertRequest::setSubjectDn, csrModel.getSubjectData()) + .with(CreateCertRequest::setSansArray, csrModel.getSans()) + .with(CreateCertRequest::setSubjectKeyPair, keyPair); + } + + private SignatureProtection getSignatureProtection(CertificateUpdateModel certificateUpdateModel) + throws CmpClientException { + try { + PrivateKey oldPrivateKey = certificateUpdateModel.getOldPrivateKeyObject(); + return new SignatureProtection(oldPrivateKey); + } catch (NoSuchAlgorithmException | KeyDecryptionException | InvalidKeySpecException e) { + throw new CmpClientException("Cannot parse old private key ", e); + } + + } + + private CMPCertificate[] getCMPCertificateFromPem(String encodedCertPem) throws CmpClientException { + try { + Certificate certificate = Certificate.getInstance( + new PemReader( + new InputStreamReader( + new ByteArrayInputStream( + Base64.decodeBase64(encodedCertPem)))) + .readPemObject().getContent()); + CMPCertificate cert = new CMPCertificate(certificate); + return new CMPCertificate[]{cert}; + } catch (IOException | NullPointerException e ) { + throw new CmpClientException("Cannot parse old certificate", e); + } + } + private void checkCmpResponse( final PKIMessage respPkiMessage, final PublicKey publicKey, final String initAuthPassword) throws CmpClientException { diff --git a/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CreateCertRequest.java b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CreateCertRequest.java index 0ed493b7..c3283044 100644 --- a/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CreateCertRequest.java +++ b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CreateCertRequest.java @@ -29,6 +29,7 @@ import java.util.Date; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.DERBitString; +import org.bouncycastle.asn1.cmp.CMPCertificate; import org.bouncycastle.asn1.cmp.PKIBody; import org.bouncycastle.asn1.cmp.PKIHeader; import org.bouncycastle.asn1.cmp.PKIMessage; @@ -58,6 +59,8 @@ class CreateCertRequest { private Date notBefore; private Date notAfter; private String senderKid; + private int cmpRequestType; + private CMPCertificate[] extraCerts; private final int certReqId = createRandomInt(Integer.MAX_VALUE); private final AlgorithmIdentifier signingAlgorithm = new DefaultSignatureAlgorithmIdentifierFinder() @@ -95,6 +98,14 @@ class CreateCertRequest { this.senderKid = senderKid; } + public void setCmpRequestType(int requestType) { + this.cmpRequestType = requestType; + } + + public void setExtraCerts(CMPCertificate[] extraCert) { + this.extraCerts = extraCert; + } + /** * Method to create {@link PKIMessage} from {@link CertRequest},{@link ProofOfPossession}, {@link * CertReqMsg}, {@link CertReqMessages}, {@link PKIHeader} and {@link PKIBody}. @@ -127,9 +138,9 @@ class CreateCertRequest { issuerDn, pkiMessageProtection.getAlgorithmIdentifier(), senderKid); - final PKIBody pkiBody = new PKIBody(PKIBody.TYPE_INIT_REQ, certReqMessages); + final PKIBody pkiBody = new PKIBody(cmpRequestType, certReqMessages); final DERBitString messageProtection = this.pkiMessageProtection.generatePkiMessageProtection(pkiHeader, pkiBody); - return new PKIMessage(pkiHeader, pkiBody, messageProtection); + return new PKIMessage(pkiHeader, pkiBody, messageProtection, extraCerts); } } |