From a1ea4473296be0f02671ad8152d72327fc53a520 Mon Sep 17 00:00:00 2001 From: Tomasz Wrobel Date: Tue, 29 Jun 2021 11:51:51 +0200 Subject: [OOM-CERT-SERVICE] Add handling cmp response when PBM value is missing. Issue-ID: OOM-2753 Signed-off-by: Tomasz Wrobel Change-Id: I38de28c994b5c83f936b3b5ea47d024a96f4733e --- .../cmpv2client/impl/CmpClientImpl.java | 37 +++++---- .../certservice/cmpv2client/Cmpv2ClientTest.java | 91 ++++++++++++++++++++-- 2 files changed, 106 insertions(+), 22 deletions(-) (limited to 'certService/src') 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 a673869d..7f17260c 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 @@ -39,6 +39,7 @@ import java.util.Date; import java.util.Objects; import java.util.Optional; import org.apache.http.impl.client.CloseableHttpClient; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.cmp.CMPCertificate; import org.bouncycastle.asn1.cmp.CertRepMessage; import org.bouncycastle.asn1.cmp.CertResponse; @@ -67,6 +68,7 @@ public class CmpClientImpl implements CmpClient { private static final String DEFAULT_CA_NAME = "Certification Authority"; private static final String DEFAULT_PROFILE = CaMode.RA.getProfile(); + private static final ASN1ObjectIdentifier PASSWORD_BASED_MAC = new ASN1ObjectIdentifier("1.2.840.113533.7.66.13"); public CmpClientImpl(CloseableHttpClient httpClient) { this.httpClient = httpClient; @@ -112,7 +114,18 @@ public class CmpClientImpl implements CmpClient { final PKIHeader header = respPkiMessage.getHeader(); final AlgorithmIdentifier protectionAlgo = header.getProtectionAlg(); verifySignatureWithPublicKey(respPkiMessage, publicKey); - verifyProtectionWithProtectionAlgo(respPkiMessage, initAuthPassword, header, protectionAlgo); + if (isPasswordBasedMacAlgorithm(protectionAlgo)) { + LOG.info("CMP response is protected by Password Base Mac Algorithm. Attempt to verify protection"); + verifyPasswordBasedMacProtection(respPkiMessage, initAuthPassword, header, protectionAlgo); + } + } + + private boolean isPasswordBasedMacAlgorithm(AlgorithmIdentifier protectionAlgo) throws CmpClientException { + if (Objects.isNull(protectionAlgo)) { + LOG.error("CMP response does not contain Protection Algorithm field"); + throw new CmpClientException("CMP response does not contain Protection Algorithm field"); + } + return PASSWORD_BASED_MAC.equals(protectionAlgo.getAlgorithm()); } private void verifySignatureWithPublicKey(PKIMessage respPkiMessage, PublicKey publicKey) @@ -127,22 +140,12 @@ public class CmpClientImpl implements CmpClient { } } - private void verifyProtectionWithProtectionAlgo( - PKIMessage respPkiMessage, - String initAuthPassword, - PKIHeader header, - AlgorithmIdentifier protectionAlgo) - throws CmpClientException { - if (Objects.nonNull(protectionAlgo)) { - LOG.debug("Verifying PasswordBased Protection of the Response."); - verifyPasswordBasedProtection(respPkiMessage, initAuthPassword, protectionAlgo); - checkImplicitConfirm(header); - } else { - LOG.error( - "Protection Algorithm is not available when expecting PBE protected response containing protection algorithm"); - throw new CmpClientException( - "Protection Algorithm is not available when expecting PBE protected response containing protection algorithm"); - } + private void verifyPasswordBasedMacProtection(PKIMessage respPkiMessage, String initAuthPassword, + PKIHeader header, AlgorithmIdentifier protectionAlgo) + throws CmpClientException { + LOG.debug("Verifying PasswordBased Protection of the Response."); + verifyPasswordBasedProtection(respPkiMessage, initAuthPassword, protectionAlgo); + checkImplicitConfirm(header); } private Cmpv2CertificationModel checkCmpCertRepMessage(final PKIMessage respPkiMessage) diff --git a/certService/src/test/java/org/onap/oom/certservice/cmpv2client/Cmpv2ClientTest.java b/certService/src/test/java/org/onap/oom/certservice/cmpv2client/Cmpv2ClientTest.java index df9699ae..337ed8c1 100644 --- a/certService/src/test/java/org/onap/oom/certservice/cmpv2client/Cmpv2ClientTest.java +++ b/certService/src/test/java/org/onap/oom/certservice/cmpv2client/Cmpv2ClientTest.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2019 Ericsson Software Technology AB. All rights reserved. + * Copyright (C) 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. @@ -16,6 +17,7 @@ package org.onap.oom.certservice.cmpv2client; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; @@ -24,6 +26,7 @@ import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -35,20 +38,30 @@ import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Security; -import java.security.cert.X509Certificate; + import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.Collections; import java.util.Date; -import java.util.List; import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.impl.client.CloseableHttpClient; +import org.bouncycastle.asn1.ASN1GeneralizedTime; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.DERBitString; +import org.bouncycastle.asn1.cmp.PKIBody; +import org.bouncycastle.asn1.cmp.PKIHeader; +import org.bouncycastle.asn1.cmp.PKIHeaderBuilder; +import org.bouncycastle.asn1.cmp.PKIMessage; +import org.bouncycastle.asn1.crmf.CertReqMessages; +import org.bouncycastle.asn1.crmf.CertReqMsg; +import org.bouncycastle.asn1.crmf.CertRequest; +import org.bouncycastle.asn1.crmf.CertTemplateBuilder; +import org.bouncycastle.asn1.crmf.ProofOfPossession; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.X500NameBuilder; import org.bouncycastle.asn1.x500.style.BCStyle; @@ -78,8 +91,6 @@ class Cmpv2ClientTest { private Date notAfter; private X500Name dn; - @Mock - X509Certificate cert; @Mock CloseableHttpClient httpClient; @@ -235,6 +246,47 @@ class Cmpv2ClientTest { () -> cmpClient.createCertificate(csrModel, server, notBefore, notAfter)); } + + @Test + void shouldThrowExceptionWhenResponseNotContainProtectionAlgorithmField() + throws IOException, ParseException { + + Date beforeDate = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").parse("2019/11/11 12:00:00"); + Date afterDate = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").parse("2020/11/11 12:00:00"); + setCsrModelAndServerValues( + "password", + "senderKID", + "http://127.0.0.1/ejbca/publicweb/cmp/cmp", + beforeDate, + afterDate); + + when(httpClient.execute(any())).thenReturn(httpResponse); + when(httpResponse.getEntity()).thenReturn(httpEntity); + + try ( + BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream( + preparePKIMessageWithoutProtectionAlgorithm().getEncoded() + ))) { + + byte[] ba = IOUtils.toByteArray(bis); + doAnswer( + invocation -> { + OutputStream os = invocation.getArgument(0); + os.write(ba); + return null; + }) + .when(httpEntity) + .writeTo(any(OutputStream.class)); + } + + CmpClientImpl cmpClient = new CmpClientImpl(httpClient); + + assertThatExceptionOfType(CmpClientException.class) + .isThrownBy(() -> cmpClient.createCertificate(csrModel, server, notBefore, notAfter)) + .withMessageContaining("CMP response does not contain Protection Algorithm field"); + + } + @Test void shouldThrowIllegalArgumentExceptionWhencreateCertificateCalledWithInvalidCsr() throws ParseException { @@ -287,4 +339,33 @@ class Cmpv2ClientTest { this.notBefore = notBefore; this.notAfter = notAfter; } + + private PKIMessage preparePKIMessageWithoutProtectionAlgorithm() { + + CertTemplateBuilder certTemplateBuilder = new CertTemplateBuilder(); + X500Name issuerDN = getTestIssuerDN(); + + certTemplateBuilder.setIssuer(issuerDN); + certTemplateBuilder.setSerialNumber(new ASN1Integer(0L)); + + CertRequest certRequest = new CertRequest(4, certTemplateBuilder.build(), null); + CertReqMsg certReqMsg = new CertReqMsg(certRequest, new ProofOfPossession(), null); + CertReqMessages certReqMessages = new CertReqMessages(certReqMsg); + + PKIHeaderBuilder pkiHeaderBuilder = new PKIHeaderBuilder(PKIHeader.CMP_2000, new GeneralName(issuerDN), new GeneralName(issuerDN)); + pkiHeaderBuilder.setMessageTime(new ASN1GeneralizedTime(new Date())); + pkiHeaderBuilder.setProtectionAlg(null); + + PKIBody pkiBody = new PKIBody(PKIBody.TYPE_INIT_REQ, certReqMessages); + return new PKIMessage(pkiHeaderBuilder.build(), pkiBody, new DERBitString("test".getBytes())); + } + + private X500Name getTestIssuerDN() { + return new X500NameBuilder() + .addRDN(BCStyle.O, "Test_Organization") + .addRDN(BCStyle.UID, "Test_UID") + .addRDN(BCStyle.CN, "Test_CA") + .build(); + } + } -- cgit 1.2.3-korg