diff --git a/jams-ca/src/main/java/net/jami/jams/ca/JamsCA.java b/jams-ca/src/main/java/net/jami/jams/ca/JamsCA.java index 21bb6cfd40b61cc7ad1e62e5bac3eef0e4662eb1..00fb29f0f9237b375ce890e6615cb09038bfd3ca 100644 --- a/jams-ca/src/main/java/net/jami/jams/ca/JamsCA.java +++ b/jams-ca/src/main/java/net/jami/jams/ca/JamsCA.java @@ -41,6 +41,7 @@ import org.bouncycastle.cert.ocsp.OCSPResp; import org.bouncycastle.jcajce.provider.asymmetric.X509; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import java.security.PrivateKey; import java.security.Security; import java.security.cert.X509Certificate; import java.util.Base64; @@ -130,8 +131,8 @@ public class JamsCA implements CertificateAuthority { } } - public static OCSPResp getOCSPResponse(OCSPReq ocspRequest) throws OCSPException { - return ocspWorker.getOCSPResponse(ocspRequest); + public static OCSPResp getOCSPResponse(OCSPReq ocspRequest, X509Certificate certificate, PrivateKey privateKey, Boolean unknown) throws OCSPException { + return ocspWorker.getOCSPResponse(ocspRequest, certificate, privateKey, unknown); } @Override diff --git a/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/builders/DeviceBuilder.java b/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/builders/DeviceBuilder.java index c15443567e8711b42740bccea0f61bb186e66e9d..345eedf144d2710f5d67ee9b7e9ca02c144b616e 100644 --- a/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/builders/DeviceBuilder.java +++ b/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/builders/DeviceBuilder.java @@ -29,6 +29,7 @@ import net.jami.jams.ca.workers.csr.utils.ExtensionLibrary; import net.jami.jams.common.objects.devices.Device; import net.jami.jams.common.objects.user.User; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; @@ -36,6 +37,11 @@ import java.math.BigInteger; import java.security.SecureRandom; import java.util.Date; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.AccessDescription; +import org.bouncycastle.asn1.x509.AuthorityInformationAccess; +import org.bouncycastle.asn1.x509.Extension; + import static net.jami.jams.ca.workers.csr.CertificateWorker.SHIFT; @Slf4j @@ -54,6 +60,22 @@ public class DeviceBuilder { device.getCertificationRequest().getSubject(), device.getCertificationRequest().getSubjectPublicKeyInfo() ); + + /* + * + * This extension library configuration is done at this point in order to add the issuer id (here the jami Id) + * to the certificate to help retrieve the device certificate issuer in more suitable manner during the call to the OCSP endpoint + * + * */ + + //Pre-Define the AIA Point + AccessDescription accessDescription = new AccessDescription( + AccessDescription.id_ad_ocsp, + new GeneralName(GeneralName.uniformResourceIdentifier, JamsCA.serverDomain + "/api/ocsp/" + user.getJamiId()) + ); + + ExtensionLibrary.deviceExtensions.getExtensions().add(new Object[]{Extension.authorityInfoAccess, false, new AuthorityInformationAccess(accessDescription)}); + device.setCertificate(CertificateSigner.signCertificate(user.getPrivateKey(),builder, ExtensionLibrary.deviceExtensions)); boolean deviceIdDetected = false; for(int i=0; i < device.getCertificationRequest().getSubject().getRDNs().length;i++) { diff --git a/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/utils/ExtensionLibrary.java b/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/utils/ExtensionLibrary.java index fca44100a99d68b81ef283656f915ddfbb286988..3d021d61862436cbbc0ef56c2efb5b5d8636833c 100644 --- a/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/utils/ExtensionLibrary.java +++ b/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/utils/ExtensionLibrary.java @@ -78,7 +78,6 @@ public class ExtensionLibrary { deviceExtensions.getExtensions().add(new Object[]{Extension.basicConstraints, true, new BasicConstraints(false)}); deviceExtensions.getExtensions().add(new Object[]{Extension.keyUsage, false, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.dataEncipherment | KeyUsage.keyAgreement | KeyUsage.nonRepudiation)}); deviceExtensions.getExtensions().add(new Object[]{Extension.cRLDistributionPoints, false, new CRLDistPoint(distPoints)}); - deviceExtensions.getExtensions().add(new Object[]{Extension.authorityInfoAccess, false, new AuthorityInformationAccess(accessDescription)}); } diff --git a/jams-ca/src/main/java/net/jami/jams/ca/workers/ocsp/OCSPWorker.java b/jams-ca/src/main/java/net/jami/jams/ca/workers/ocsp/OCSPWorker.java index 0b407e0df6ca5dcac60887fa8061acf71d725cdb..b77ea1885a6860710506270ef0c1a6d3de1b20f6 100644 --- a/jams-ca/src/main/java/net/jami/jams/ca/workers/ocsp/OCSPWorker.java +++ b/jams-ca/src/main/java/net/jami/jams/ca/workers/ocsp/OCSPWorker.java @@ -23,12 +23,13 @@ package net.jami.jams.ca.workers.ocsp; import lombok.extern.slf4j.Slf4j; -import net.jami.jams.ca.JamsCA; import net.jami.jams.ca.workers.X509Worker; import net.jami.jams.ca.workers.crl.CRLWorker; import net.jami.jams.common.cryptoengineapi.ocsp.CertificateStatus; import net.jami.jams.common.cryptoengineapi.ocsp.CertificateSummary; import net.jami.jams.common.cryptoengineapi.ocsp.OCSPCertificateStatusMapper; +import net.jami.jams.common.objects.devices.Device; +import net.jami.jams.common.objects.user.User; import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.Extensions; @@ -36,15 +37,7 @@ import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.cert.X509CRLEntryHolder; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; -import org.bouncycastle.cert.ocsp.BasicOCSPResp; -import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder; -import org.bouncycastle.cert.ocsp.CertificateID; -import org.bouncycastle.cert.ocsp.OCSPException; -import org.bouncycastle.cert.ocsp.OCSPReq; -import org.bouncycastle.cert.ocsp.OCSPResp; -import org.bouncycastle.cert.ocsp.OCSPRespBuilder; -import org.bouncycastle.cert.ocsp.Req; -import org.bouncycastle.cert.ocsp.RespID; +import org.bouncycastle.cert.ocsp.*; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder; import org.bouncycastle.operator.DigestCalculatorProvider; @@ -55,16 +48,13 @@ import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import java.math.BigInteger; import java.security.PrivateKey; import java.security.cert.X509Certificate; -import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.ArrayList; import java.util.Collection; import java.util.Date; -import static net.jami.jams.ca.JamsCA.CA; -import static net.jami.jams.ca.JamsCA.OCSP; -import static net.jami.jams.ca.JamsCA.crlLifetime; +import static net.jami.jams.ca.JamsCA.*; @Slf4j public class OCSPWorker extends X509Worker<String> { @@ -81,11 +71,11 @@ public class OCSPWorker extends X509Worker<String> { DigestCalculatorProvider digestCalculatorProvider = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build(); // only SHA-1 is supported for responder IDs. this.responderID = new RespID(SubjectPublicKeyInfo.getInstance(CA.getCertificate().getPublicKey().getEncoded()), digestCalculatorProvider.get(new DefaultDigestAlgorithmIdentifierFinder().find ("SHA-1"))); - this.contentSigner = new JcaContentSignerBuilder("SHA256withRSA").setProvider("BC").build(OCSP.getPrivateKey()); + this.contentSigner = new JcaContentSignerBuilder("SHA256withRSA").setProvider("BC").build(CA.getPrivateKey()); log.info("Instantiated OCSP Worker..."); } - public OCSPResp getOCSPResponse(OCSPReq ocspRequest) throws OCSPException { + public OCSPResp getOCSPResponse(OCSPReq ocspRequest, X509Certificate certificate, PrivateKey privateKey, Boolean unknown) throws OCSPException { try { if(validateRequest(ocspRequest) != null) throw new OCSPException("Request is not valid"); //this means the request is invalid and we should notify the client. //If the request was valid, we move on to other things. @@ -97,12 +87,13 @@ public class OCSPWorker extends X509Worker<String> { if (nonceExtension != null) responseExtensions.add(nonceExtension); Extension[] extensions = responseExtensions.toArray(new Extension[responseExtensions.size()]); responseBuilder.setResponseExtensions(new Extensions(extensions)); - for (Req request : ocspRequest.getRequestList()) { - addResponse(responseBuilder, request); - } + + for (Req request : ocspRequest.getRequestList()) + addResponse(responseBuilder, request, unknown); + BasicOCSPResp basicResponse = responseBuilder.build( - contentSigner, - new X509CertificateHolder[]{new JcaX509CertificateHolder(OCSP.getCertificate())}, + new JcaContentSignerBuilder("SHA256withRSA").setProvider("BC").build(privateKey), + new X509CertificateHolder[]{new JcaX509CertificateHolder(certificate)}, new Date() ); return new OCSPRespBuilder().build(OCSPRespBuilder.SUCCESSFUL, basicResponse); @@ -122,7 +113,15 @@ public class OCSPWorker extends X509Worker<String> { } - private CertificateSummary getCertificateSummary(BigInteger serial) { + private CertificateSummary getCertificateSummary(BigInteger serial, Boolean unknown) { + + if(unknown) { + return CertificateSummary.newBuilder() + .withStatus(CertificateStatus.UNKNOWN) + .withSerialNumber(serial) + .build(); + } + X509CRLEntryHolder x509CRLEntryHolder = crlWorker.getExistingCRL().get().getRevokedCertificate(serial); if(x509CRLEntryHolder != null) return CertificateSummary.newBuilder() @@ -137,7 +136,7 @@ public class OCSPWorker extends X509Worker<String> { } - private void addResponse(BasicOCSPRespBuilder responseBuilder, Req request) throws OCSPException{ + private void addResponse(BasicOCSPRespBuilder responseBuilder, Req request, Boolean unknown) throws OCSPException{ CertificateID certificateID = request.getCertID(); // Build Extensions Extensions extensions = new Extensions(new Extension[]{}); @@ -147,7 +146,7 @@ public class OCSPWorker extends X509Worker<String> { if (nonceExtension != null) extensions = new Extensions(nonceExtension); } responseBuilder.addResponse(certificateID, - OCSPCertificateStatusMapper.getStatus(getCertificateSummary(request.getCertID().getSerialNumber())), + OCSPCertificateStatusMapper.getStatus(getCertificateSummary(request.getCertID().getSerialNumber(), unknown)), new Date(), new Date(new Date().getTime() + crlLifetime), extensions); diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/x509/OCSPServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/x509/OCSPServlet.java index 00969d454176b33f250f7de51ffb45efb19538ae..3a94f3905202cb0fc719079ac0ea38c8e53c32be 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/x509/OCSPServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/x509/OCSPServlet.java @@ -27,46 +27,96 @@ import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import net.jami.jams.ca.JamsCA; -import net.jami.jams.common.utils.X509Utils; -import org.bouncycastle.asn1.DEROctetString; -import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers; -import org.bouncycastle.asn1.x509.Extension; -import org.bouncycastle.asn1.x509.Extensions; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; -import org.bouncycastle.cert.ocsp.CertificateID; +import net.jami.jams.common.dao.StatementElement; +import net.jami.jams.common.dao.StatementList; +import net.jami.jams.common.objects.devices.Device; +import net.jami.jams.common.objects.user.User; import org.bouncycastle.cert.ocsp.OCSPReq; -import org.bouncycastle.cert.ocsp.OCSPReqBuilder; import org.bouncycastle.cert.ocsp.OCSPResp; -import org.bouncycastle.operator.DigestCalculator; -import org.bouncycastle.operator.DigestCalculatorProvider; -import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; +import org.bouncycastle.cert.ocsp.Req; import java.io.IOException; -import java.security.cert.X509Certificate; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; -@WebServlet("/api/ocsp") +import static net.jami.jams.server.Server.dataStore; + +@WebServlet("/api/ocsp/*") public class OCSPServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { - resp.sendError(404); -// resp.setContentType("application/ocsp-response"); -// byte[] content = new byte[Integer.parseInt(req.getHeader("Content-Length"))]; -// try { -// for(int i=0;i<content.length;i++) -// req.getInputStream().read(content); -// -// OCSPReq ocspReq = new OCSPReq(content); -// OCSPResp response = JamsCA.getOCSPResponse(ocspReq); -// if (response != null) { -// byte[] respBytes = response.getEncoded(); -// resp.getOutputStream().write(respBytes); -// } else resp.setStatus(404); -// -// } -// catch (Exception e) { -// resp.sendError(404, "Could not find the requested certificate!"); -// } + resp.setContentType("application/ocsp-response"); + byte[] content = new byte[Integer.parseInt(req.getHeader("Content-Length"))]; + try { + for(int i=0;i<content.length;i++) + req.getInputStream().read(content); + + OCSPReq ocspReq = new OCSPReq(content); + List<BigInteger> ids = new ArrayList<>(); + + for (Req request : ocspReq.getRequestList()) + ids.add(request.getCertID().getSerialNumber()); + + String issuerId = req.getPathInfo().replace("/",""); + + /* + * If there is no issue id we are dealing with a certificate signed byt the CA root + * Else the certificate is a device certificate signed by a user with and the issuerId is his Jami Id + */ + + if(issuerId == ""){ + OCSPResp response = JamsCA.getOCSPResponse(ocspReq, JamsCA.CA.getCertificate(), JamsCA.CA.getPrivateKey(), false); + + if (response != null) { + byte[] respBytes = response.getEncoded(); + resp.getOutputStream().write(respBytes); + } else resp.setStatus(404); + } + else { + User targetUser = null; + StatementList userStatementsList = new StatementList(); + StatementElement usernameStatement = new StatementElement("jamiId", "=", issuerId, ""); + userStatementsList.addStatement(usernameStatement); + + try { + targetUser = dataStore.getUserDao().getObjects(userStatementsList).get(0); + StatementList deviceStatementsList = new StatementList(); + StatementElement deviceOwnerStatement = new StatementElement("owner", "=", targetUser.getUsername(), ""); + deviceStatementsList.addStatement(deviceOwnerStatement); + List<Device> devices = dataStore.getDeviceDao().getObjects(deviceStatementsList); + + boolean deviceDoesNotExist = true; + for (Device d: devices) { + for (BigInteger id: ids) { + if (d.getCertificate().getSerialNumber().equals(id)) { + deviceDoesNotExist= false; + } + } + } + OCSPResp response = null; + + if(deviceDoesNotExist){ + response = JamsCA.getOCSPResponse(ocspReq, targetUser.getCertificate(), targetUser.getPrivateKey(), true); + } + else{ + response = JamsCA.getOCSPResponse(ocspReq, targetUser.getCertificate(), targetUser.getPrivateKey(), false); + } + + if (response != null) { + byte[] respBytes = response.getEncoded(); + resp.getOutputStream().write(respBytes); + } else resp.setStatus(404); + } + catch (Exception e) { + resp.sendError(404, "Could not find the requested certificate!"); + } + } + + } + catch (Exception e) { + resp.sendError(404, "Could not find the requested certificate!"); + } } } \ No newline at end of file