diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/pom.xml b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/pom.xml index ff9717a267d..2cafab4584b 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/pom.xml +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/pom.xml @@ -69,8 +69,10 @@ org.bouncycastle.operator, org.bouncycastle.operator.jcajce, org.bouncycastle.pkcs, + org.bouncycastle.openssl, org.bouncycastle.util, org.jscep.message, + org.wso2.carbon.context, org.jscep.transaction, org.w3c.dom, org.xml.sax, diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/bean/Certificate.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/bean/Certificate.java new file mode 100644 index 00000000000..5ced778afaa --- /dev/null +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/bean/Certificate.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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 org.wso2.carbon.certificate.mgt.core.bean; + +import java.security.cert.X509Certificate; + +public class Certificate { + String serial; + X509Certificate certificate; + int tenantId; + String tenantDomain; + + public int getTenantId() { + return tenantId; + } + + public void setTenantId(int tenantId) { + this.tenantId = tenantId; + } + + public String getSerial() { + return serial; + } + + public void setSerial(String serial) { + this.serial = serial; + } + + public X509Certificate getCertificate() { + return certificate; + } + + public void setCertificate(X509Certificate certificate) { + this.certificate = certificate; + } + + public String getTenantDomain() { + return tenantDomain; + } + + public void setTenantDomain(String tenantDomain) { + this.tenantDomain = tenantDomain; + } +} diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/dao/CertificateDAO.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/dao/CertificateDAO.java index e6ce3ef65a0..53dc1a2b030 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/dao/CertificateDAO.java +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/dao/CertificateDAO.java @@ -18,7 +18,11 @@ package org.wso2.carbon.certificate.mgt.core.dao; +import org.wso2.carbon.device.mgt.common.PaginationRequest; +import org.wso2.carbon.device.mgt.common.PaginationResult; + import java.io.ByteArrayInputStream; +import java.util.List; /** * This class represents the key operations associated with persisting certificate related @@ -30,21 +34,34 @@ public interface CertificateDAO { * This can be used to store a certificate in the database, where it will be stored against the serial number * of the certificate. * - * @param byteArrayInputStream Holds the certificate. - * @param serialNumber Serial number of the certificate. + * @param certificate Holds the certificate and relevant details. * @throws CertificateManagementDAOException */ - void addCertificate(ByteArrayInputStream byteArrayInputStream, String serialNumber - ) throws CertificateManagementDAOException; + void addCertificate(List certificate) + throws CertificateManagementDAOException; /** - * Usage is to obtain a certificate stored in the database by providing the serial number. + * Usage is to obtain a certificate stored in the database by providing the common name. * * @param serialNumber Serial number of the certificate. * @return representation of the certificate. * @throws CertificateManagementDAOException */ - byte[] retrieveCertificate(String serialNumber + org.wso2.carbon.certificate.mgt.core.dto.CertificateResponse retrieveCertificate(String serialNumber ) throws CertificateManagementDAOException; + /** + * Get all the certificates in a paginated manner. + * @param request Request mentioning pagination details such as length and stating index. + * @return Pagination result with data and the count of results. + * @throws CertificateManagementDAOException + */ + PaginationResult getAllCertificates(PaginationRequest request) throws CertificateManagementDAOException; + + /** + * Delete a certificate identified by a serial number() + * @param serialNumber serial number + * @return whether the certificate was removed or not. + */ + boolean removeCertificate(String serialNumber) throws CertificateManagementDAOException; } diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/dao/impl/GenericCertificateDAOImpl.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/dao/impl/GenericCertificateDAOImpl.java index 166b1333afd..a7fcc01c82d 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/dao/impl/GenericCertificateDAOImpl.java +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/dao/impl/GenericCertificateDAOImpl.java @@ -18,58 +18,172 @@ package org.wso2.carbon.certificate.mgt.core.dao.impl; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.certificate.mgt.core.bean.Certificate; import org.wso2.carbon.certificate.mgt.core.dao.CertificateDAO; import org.wso2.carbon.certificate.mgt.core.dao.CertificateManagementDAOException; import org.wso2.carbon.certificate.mgt.core.dao.CertificateManagementDAOFactory; import org.wso2.carbon.certificate.mgt.core.dao.CertificateManagementDAOUtil; +import org.wso2.carbon.certificate.mgt.core.dto.CertificateResponse; +import org.wso2.carbon.certificate.mgt.core.exception.KeystoreException; +import org.wso2.carbon.certificate.mgt.core.impl.CertificateGenerator; +import org.wso2.carbon.certificate.mgt.core.util.CertificateManagerUtil; +import org.wso2.carbon.certificate.mgt.core.util.Serializer; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.mgt.common.PaginationRequest; +import org.wso2.carbon.device.mgt.common.PaginationResult; +import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManagementException; +import org.wso2.carbon.device.mgt.core.operation.mgt.dao.OperationManagementDAOUtil; import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; import java.sql.*; +import java.util.ArrayList; +import java.util.List; public class GenericCertificateDAOImpl implements CertificateDAO { + private static final Log log = LogFactory.getLog(GenericCertificateDAOImpl.class); + @Override - public void addCertificate(ByteArrayInputStream byteArrayInputStream, String serialNumber) + public void addCertificate(List certificates) throws CertificateManagementDAOException { Connection conn; PreparedStatement stmt = null; try { conn = this.getConnection(); - stmt = conn.prepareStatement("INSERT INTO DM_DEVICE_CERTIFICATE (SERIAL_NUMBER, CERTIFICATE) VALUES (?,?)"); - stmt.setString(1, serialNumber); - stmt.setObject(2, byteArrayInputStream); - stmt.execute(); - } catch (SQLException e) { - throw new CertificateManagementDAOException("Error occurred while saving certificate with serial " + - serialNumber, e); + stmt = conn.prepareStatement( + "INSERT INTO DM_DEVICE_CERTIFICATE (SERIAL_NUMBER, CERTIFICATE, TENANT_ID) VALUES (?,?,?)"); + for (Certificate certificate : certificates) { + String serialNumber = certificate.getSerial(); + if (serialNumber == null || serialNumber.isEmpty()) { + serialNumber = String.valueOf(certificate.getCertificate().getSerialNumber()); + } + byte[] bytes = Serializer.serialize(certificate.getCertificate()); + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); + + stmt.setString(1, serialNumber); + stmt.setObject(2, byteArrayInputStream); + stmt.setInt(3, certificate.getTenantId()); + stmt.addBatch(); + } + stmt.executeBatch(); + } catch (SQLException | IOException e) { + throw new CertificateManagementDAOException("Error occurred while saving certificates. " + , e); } finally { CertificateManagementDAOUtil.cleanupResources(stmt, null); } } @Override - public byte[] retrieveCertificate(String serialNumber) + public CertificateResponse retrieveCertificate(String serialNumber) throws CertificateManagementDAOException { Connection conn; PreparedStatement stmt = null; ResultSet resultSet = null; - byte[] binaryStream = null; + CertificateResponse certificateResponse = null; + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); try { conn = this.getConnection(); - String query = "SELECT CERTIFICATE FROM DM_DEVICE_CERTIFICATE WHERE SERIAL_NUMBER = ?"; + String query = + "SELECT CERTIFICATE, SERIAL_NUMBER, TENANT_ID FROM DM_DEVICE_CERTIFICATE WHERE SERIAL_NUMBER = ?" + + " AND TENANT_ID = ? "; stmt = conn.prepareStatement(query); stmt.setString(1, serialNumber); + stmt.setInt(2, tenantId); + resultSet = stmt.executeQuery(); + + while (resultSet.next()) { + certificateResponse = new CertificateResponse(); + byte [] certificateBytes = resultSet.getBytes("CERTIFICATE"); + certificateResponse.setCertificate(certificateBytes); + certificateResponse.setSerialNumber(resultSet.getString("SERIAL_NUMBER")); + certificateResponse.setTenantId(resultSet.getInt("TENANT_ID")); + CertificateGenerator.extractCertificateDetails(certificateBytes, certificateResponse); + break; + } + } catch (SQLException e) { + String errorMsg = + "Unable to get the read the certificate with serial" + serialNumber; + log.error(errorMsg, e); + throw new CertificateManagementDAOException(errorMsg, e); + } finally { + CertificateManagementDAOUtil.cleanupResources(stmt, resultSet); + } + return certificateResponse; + } + + @Override + public PaginationResult getAllCertificates(PaginationRequest request) throws CertificateManagementDAOException { + PreparedStatement stmt = null; + ResultSet resultSet = null; + CertificateResponse certificateResponse; + List certificates = new ArrayList<>(); + PaginationResult paginationResult; + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); + try { + Connection conn = this.getConnection(); + String sql = "SELECT CERTIFICATE, SERIAL_NUMBER, TENANT_ID FROM DM_DEVICE_CERTIFICATE WHERE TENANT_ID = ? " + + "ORDER BY ID DESC LIMIT ?,?"; + stmt = conn.prepareStatement(sql); + stmt.setInt(1, tenantId); + stmt.setInt(2, request.getStartIndex()); + stmt.setInt(3, request.getRowCount()); resultSet = stmt.executeQuery(); + int resultCount = 0; while (resultSet.next()) { - binaryStream = resultSet.getBytes("CERTIFICATE"); + certificateResponse = new CertificateResponse(); + byte [] certificateBytes = resultSet.getBytes("CERTIFICATE"); + certificateResponse.setSerialNumber(resultSet.getString("SERIAL_NUMBER")); + certificateResponse.setTenantId(resultSet.getInt("TENANT_ID")); + CertificateGenerator.extractCertificateDetails(certificateBytes, certificateResponse); + certificates.add(certificateResponse); + resultCount++; + } + paginationResult = new PaginationResult(); + paginationResult.setData(certificates); + paginationResult.setRecordsTotal(resultCount); + } catch (SQLException e) { + String errorMsg = "SQL error occurred while retrieving the certificates."; + log.error(errorMsg, e); + throw new CertificateManagementDAOException(errorMsg, e); + } finally { + OperationManagementDAOUtil.cleanupResources(stmt, resultSet); + } + return paginationResult; + } + + @Override + public boolean removeCertificate(String serialNumber) throws CertificateManagementDAOException { + Connection conn; + PreparedStatement stmt = null; + ResultSet resultSet = null; + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); + try { + conn = this.getConnection(); + String query = + "DELETE FROM DM_DEVICE_CERTIFICATE WHERE SERIAL_NUMBER = ?" + + " AND TENANT_ID = ? "; + stmt = conn.prepareStatement(query); + stmt.setString(1, serialNumber); + stmt.setInt(2, tenantId); + + if(stmt.executeUpdate() > 0) { + return true; } + return false; } catch (SQLException e) { - throw new CertificateManagementDAOException( - "Unable to get the read the certificate with serial" + serialNumber, e); + String errorMsg = + "Unable to get the read the certificate with serial" + serialNumber; + log.error(errorMsg, e); + throw new CertificateManagementDAOException(errorMsg, e); } finally { CertificateManagementDAOUtil.cleanupResources(stmt, resultSet); } - return binaryStream; } private Connection getConnection() throws SQLException { diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/dto/CertificateResponse.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/dto/CertificateResponse.java new file mode 100644 index 00000000000..5ffb8270f37 --- /dev/null +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/dto/CertificateResponse.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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 org.wso2.carbon.certificate.mgt.core.dto; + +import java.math.BigInteger; + +public class CertificateResponse { + + byte[] certificate; + String serialNumber; + int tenantId; + String commonName; + long notAfter; + long notBefore; + BigInteger certificateserial; + String issuer; + String subject; + int certificateVersion; + + public long getNotAfter() { + return notAfter; + } + + public void setNotAfter(long notAfter) { + this.notAfter = notAfter; + } + + public long getNotBefore() { + return notBefore; + } + + public void setNotBefore(long notBefore) { + this.notBefore = notBefore; + } + + public BigInteger getCertificateserial() { + return certificateserial; + } + + public void setCertificateserial(BigInteger certificateserial) { + this.certificateserial = certificateserial; + } + + public String getIssuer() { + return issuer; + } + + public void setIssuer(String issuer) { + this.issuer = issuer; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public int getCertificateVersion() { + return certificateVersion; + } + + public void setCertificateVersion(int certificateVersion) { + this.certificateVersion = certificateVersion; + } + + public String getCommonName() { + return commonName; + } + + public void setCommonName(String commonName) { + this.commonName = commonName; + } + + public byte[] getCertificate() { + return certificate; + } + + public void setCertificate(byte[] certificate) { + this.certificate = certificate; + } + + public String getSerialNumber() { + return serialNumber; + } + + public void setSerialNumber(String serialNumber) { + this.serialNumber = serialNumber; + } + + public int getTenantId() { + return tenantId; + } + + public void setTenantId(int tenantId) { + this.tenantId = tenantId; + } +} diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/impl/CertificateGenerator.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/impl/CertificateGenerator.java index 3dfe16854f1..d1882b30965 100755 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/impl/CertificateGenerator.java +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/impl/CertificateGenerator.java @@ -39,6 +39,7 @@ import org.bouncycastle.cms.CMSException; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.CMSSignedDataGenerator; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMWriter; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; @@ -52,16 +53,19 @@ import org.wso2.carbon.certificate.mgt.core.dao.CertificateDAO; import org.wso2.carbon.certificate.mgt.core.dao.CertificateManagementDAOException; import org.wso2.carbon.certificate.mgt.core.dao.CertificateManagementDAOFactory; import org.wso2.carbon.certificate.mgt.core.dto.CAStatus; +import org.wso2.carbon.certificate.mgt.core.dto.CertificateResponse; import org.wso2.carbon.certificate.mgt.core.dto.SCEPResponse; import org.wso2.carbon.certificate.mgt.core.exception.KeystoreException; import org.wso2.carbon.certificate.mgt.core.util.CommonUtil; import org.wso2.carbon.certificate.mgt.core.util.ConfigurationUtil; import org.wso2.carbon.certificate.mgt.core.util.Serializer; +import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.device.mgt.common.TransactionManagementException; import javax.security.auth.x500.X500Principal; import javax.xml.bind.DatatypeConverter; import java.io.*; +import java.math.BigInteger; import java.security.*; import java.security.cert.Certificate; import java.security.cert.*; @@ -154,7 +158,13 @@ public class CertificateGenerator { certificate.verify(certificate.getPublicKey()); - saveCertInKeyStore(certificate); + List certificates = new ArrayList<>(); + org.wso2.carbon.certificate.mgt.core.bean.Certificate certificateToStore = + new org.wso2.carbon.certificate.mgt.core.bean.Certificate(); + certificateToStore.setTenantId(PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId()); + certificateToStore.setCertificate(certificate); + certificates.add(certificateToStore); + saveCertInKeyStore(certificates); return certificate; } catch (NoSuchAlgorithmException e) { @@ -269,6 +279,57 @@ public class CertificateGenerator { return (certificate != null); } + public CertificateResponse verifyPEMSignature(X509Certificate requestCertificate) throws KeystoreException { + KeyStoreReader keyStoreReader = new KeyStoreReader(); + CertificateResponse lookUpCertificate; + + String commonNameExtracted = getCommonName(requestCertificate); + lookUpCertificate = keyStoreReader.getCertificateBySerial(commonNameExtracted); + return lookUpCertificate; + } + + public static String getCommonName(X509Certificate requestCertificate) { + String distinguishedName = requestCertificate.getSubjectDN().getName(); + if(distinguishedName != null && !distinguishedName.isEmpty()) { + String[] dnSplits = distinguishedName.split(","); + for (String dnSplit : dnSplits) { + if (dnSplit.contains("CN=")) { + String[] cnSplits = dnSplit.split("="); + if (cnSplits[1] != null) { + return cnSplits[1]; + } + } + } + } + return null; + } + + public X509Certificate pemToX509Certificate(String pem) + throws KeystoreException { + InputStream inputStream = null; + X509Certificate x509Certificate = null; + try { + + inputStream = new ByteArrayInputStream(Base64.decodeBase64(pem.getBytes())); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + x509Certificate = (X509Certificate) cf.generateCertificate(inputStream); + + } catch (CertificateException e) { + String errorMsg = "Certificate issue occurred when generating converting PEM to x509Certificate"; + log.error(errorMsg, e); + throw new KeystoreException(errorMsg, e); + } finally { + try { + if (inputStream != null) { + inputStream.close(); + } + } catch (IOException e) { + log.error("Error closing Certificate input stream", e); + } + } + return x509Certificate; + } + public X509Certificate extractCertificateFromSignature(String headerSignature) throws KeystoreException { if (headerSignature == null || headerSignature.isEmpty()) { @@ -364,8 +425,13 @@ public class CertificateGenerator { issuedCert = new JcaX509CertificateConverter().setProvider( ConfigurationUtil.PROVIDER).getCertificate( certificateBuilder.build(sigGen)); - - saveCertInKeyStore(issuedCert); + org.wso2.carbon.certificate.mgt.core.bean.Certificate certificate = + new org.wso2.carbon.certificate.mgt.core.bean.Certificate(); + List certificates = new ArrayList<>(); + certificate.setTenantId(PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId()); + certificate.setCertificate(issuedCert); + certificates.add(certificate); + saveCertInKeyStore(certificates); } catch (CertIOException e) { String errorMsg = "Certificate Input output issue occurred when generating generateCertificateFromCSR"; log.error(errorMsg, e); @@ -522,7 +588,7 @@ public class CertificateGenerator { } } - private void saveCertInKeyStore(X509Certificate certificate) + public void saveCertInKeyStore(List certificate) throws KeystoreException { if (certificate == null) { @@ -530,18 +596,10 @@ public class CertificateGenerator { } try { - String serialNumber = certificate.getSerialNumber().toString(); - byte[] bytes = Serializer.serialize(certificate); - ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); CertificateDAO certificateDAO = CertificateManagementDAOFactory.getCertificateDAO(); CertificateManagementDAOFactory.beginTransaction(); - certificateDAO.addCertificate(byteArrayInputStream, serialNumber); + certificateDAO.addCertificate(certificate); CertificateManagementDAOFactory.commitTransaction(); - } catch (IOException e) { - String errorMsg = "IOException occurred when saving the generated certificate"; - log.error(errorMsg, e); - CertificateManagementDAOFactory.rollbackTransaction(); - throw new KeystoreException(errorMsg, e); } catch (CertificateManagementDAOException e) { String errorMsg = "Error occurred when saving the generated certificate"; log.error(errorMsg, e); @@ -557,6 +615,8 @@ public class CertificateGenerator { } } + + public String extractChallengeToken(X509Certificate certificate) { byte[] challengePassword = certificate.getExtensionValue( @@ -617,4 +677,28 @@ public class CertificateGenerator { certCA.getIssuerX500Principal().getName()); return signedCertificate; } + + public static void extractCertificateDetails(byte[] certificateBytes, CertificateResponse certificateResponse) + throws CertificateManagementDAOException { + try { + if (certificateBytes != null) { + java.security.cert.Certificate x509Certificate = + (java.security.cert.Certificate) Serializer.deserialize(certificateBytes); + if (x509Certificate instanceof X509Certificate) { + X509Certificate certificate = (X509Certificate) x509Certificate; + certificateResponse.setNotAfter(certificate.getNotAfter().getTime()); + certificateResponse.setNotBefore(certificate.getNotBefore().getTime()); + certificateResponse.setCertificateserial(certificate.getSerialNumber()); + certificateResponse.setIssuer(certificate.getIssuerDN().getName()); + certificateResponse.setSubject(certificate.getSubjectDN().getName()); + certificateResponse.setCertificateVersion(certificate.getVersion()); + } + } + } catch (ClassNotFoundException | IOException e) { + String errorMsg = "Error while deserializing the certificate."; + log.error(errorMsg, e); + throw new CertificateManagementDAOException(errorMsg, e); + } + + } } \ No newline at end of file diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/impl/KeyStoreReader.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/impl/KeyStoreReader.java index 29074251def..c7d5978fa67 100755 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/impl/KeyStoreReader.java +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/impl/KeyStoreReader.java @@ -21,6 +21,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.certificate.mgt.core.dao.CertificateManagementDAOException; import org.wso2.carbon.certificate.mgt.core.dao.CertificateManagementDAOFactory; +import org.wso2.carbon.certificate.mgt.core.dto.CertificateResponse; import org.wso2.carbon.certificate.mgt.core.util.ConfigurationUtil; import org.wso2.carbon.certificate.mgt.core.exception.KeystoreException; import org.wso2.carbon.certificate.mgt.core.util.Serializer; @@ -30,13 +31,10 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.UnrecoverableKeyException; +import java.security.*; import java.security.cert.Certificate; import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; import java.sql.SQLException; public class KeyStoreReader { @@ -213,8 +211,11 @@ public class KeyStoreReader { Certificate raCertificate = null; try { CertificateManagementDAOFactory.openConnection(); - byte[] certificateBytes = CertificateManagementDAOFactory.getCertificateDAO().retrieveCertificate(alias); - raCertificate = (Certificate) Serializer.deserialize(certificateBytes); + CertificateResponse certificateResponse = CertificateManagementDAOFactory.getCertificateDAO(). + retrieveCertificate(alias); + if(certificateResponse != null) { + raCertificate = (Certificate) Serializer.deserialize(certificateResponse.getCertificate()); + } } catch (CertificateManagementDAOException e) { String errorMsg = "Error when retrieving certificate the the database for the alias " + alias; log.error(errorMsg, e); @@ -261,4 +262,39 @@ public class KeyStoreReader { return raPrivateKey; } + + public CertificateResponse getCertificateBySerial(String serialNumber) throws KeystoreException { + + CertificateResponse certificateResponse = null; + try { + CertificateManagementDAOFactory.openConnection(); + certificateResponse = CertificateManagementDAOFactory.getCertificateDAO(). + retrieveCertificate(serialNumber); + if(certificateResponse != null && certificateResponse.getCertificate() != null) { + Certificate certificate = (Certificate) Serializer.deserialize(certificateResponse.getCertificate()); + if (certificate instanceof X509Certificate) { + X509Certificate x509cert = (X509Certificate) certificate; + String commonName = CertificateGenerator.getCommonName(x509cert); + certificateResponse.setCommonName(commonName); + } + } + + } catch (CertificateManagementDAOException e) { + String errorMsg = "Error when retrieving certificate from the the database for the serial number: " + + serialNumber; + log.error(errorMsg, e); + throw new KeystoreException(errorMsg, e); + } catch (SQLException e) { + String errorMsg = "Error when making a connection to the database."; + log.error(errorMsg, e); + throw new KeystoreException(errorMsg, e); + } catch (ClassNotFoundException | IOException e) { + String errorMsg = "Error when deserializing saved certificate."; + log.error(errorMsg, e); + throw new KeystoreException(errorMsg, e); + } finally { + CertificateManagementDAOFactory.closeConnection(); + } + return certificateResponse; + } } diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/service/CertificateManagementService.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/service/CertificateManagementService.java index 24d805c8f18..7fd89c9f6fd 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/service/CertificateManagementService.java +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/service/CertificateManagementService.java @@ -18,8 +18,12 @@ package org.wso2.carbon.certificate.mgt.core.service; import org.bouncycastle.pkcs.PKCS10CertificationRequest; +import org.wso2.carbon.certificate.mgt.core.dao.CertificateManagementDAOException; +import org.wso2.carbon.certificate.mgt.core.dto.CertificateResponse; import org.wso2.carbon.certificate.mgt.core.dto.SCEPResponse; import org.wso2.carbon.certificate.mgt.core.exception.KeystoreException; +import org.wso2.carbon.device.mgt.common.PaginationRequest; +import org.wso2.carbon.device.mgt.common.PaginationResult; import java.io.InputStream; import java.security.PrivateKey; @@ -50,9 +54,24 @@ public interface CertificateManagementService { boolean verifySignature(String headerSignature) throws KeystoreException; + public CertificateResponse verifyPEMSignature(X509Certificate requestCertificate) throws KeystoreException; + public X509Certificate extractCertificateFromSignature(String headerSignature) throws KeystoreException; String extractChallengeToken(X509Certificate certificate); X509Certificate getSignedCertificateFromCSR(String binarySecurityToken) throws KeystoreException; + + public CertificateResponse getCertificateBySerial(String serial) throws KeystoreException; + + public void saveCertificate(List certificate) + throws KeystoreException; + + public X509Certificate pemToX509Certificate(String pem) throws KeystoreException; + + public CertificateResponse retrieveCertificate(String serialNumber) throws CertificateManagementDAOException; + + public PaginationResult getAllCertificates(PaginationRequest request) throws CertificateManagementDAOException; + + boolean removeCertificate(String serialNumber) throws CertificateManagementDAOException; } diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/service/CertificateManagementServiceImpl.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/service/CertificateManagementServiceImpl.java index f3c355c6f19..80b865ebecf 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/service/CertificateManagementServiceImpl.java +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/service/CertificateManagementServiceImpl.java @@ -20,16 +20,24 @@ package org.wso2.carbon.certificate.mgt.core.service; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.bouncycastle.pkcs.PKCS10CertificationRequest; +import org.wso2.carbon.certificate.mgt.core.dao.CertificateDAO; +import org.wso2.carbon.certificate.mgt.core.dao.CertificateManagementDAOException; +import org.wso2.carbon.certificate.mgt.core.dao.CertificateManagementDAOFactory; +import org.wso2.carbon.certificate.mgt.core.dto.CertificateResponse; import org.wso2.carbon.certificate.mgt.core.dto.SCEPResponse; import org.wso2.carbon.certificate.mgt.core.exception.KeystoreException; import org.wso2.carbon.certificate.mgt.core.impl.CertificateGenerator; import org.wso2.carbon.certificate.mgt.core.impl.KeyStoreReader; import org.wso2.carbon.certificate.mgt.core.util.ConfigurationUtil; +import org.wso2.carbon.device.mgt.common.PaginationRequest; +import org.wso2.carbon.device.mgt.common.PaginationResult; +import org.wso2.carbon.device.mgt.core.operation.mgt.dao.OperationManagementDAOFactory; import java.io.InputStream; import java.security.PrivateKey; import java.security.cert.Certificate; import java.security.cert.X509Certificate; +import java.sql.SQLException; import java.util.List; public class CertificateManagementServiceImpl implements CertificateManagementService { @@ -94,6 +102,10 @@ public class CertificateManagementServiceImpl implements CertificateManagementSe return certificateGenerator.verifySignature(headerSignature); } + public CertificateResponse verifyPEMSignature(X509Certificate requestCertificate) throws KeystoreException { + return certificateGenerator.verifyPEMSignature(requestCertificate); + } + public X509Certificate extractCertificateFromSignature(String headerSignature) throws KeystoreException { return certificateGenerator.extractCertificateFromSignature(headerSignature); } @@ -106,4 +118,63 @@ public class CertificateManagementServiceImpl implements CertificateManagementSe return certificateGenerator.getSignedCertificateFromCSR(binarySecurityToken); } + public CertificateResponse getCertificateBySerial(String serial) throws KeystoreException { + return keyStoreReader.getCertificateBySerial(serial); + } + + public void saveCertificate(List certificate) + throws KeystoreException { + certificateGenerator.saveCertInKeyStore(certificate); + } + + public X509Certificate pemToX509Certificate(String pem) throws KeystoreException { + return certificateGenerator.pemToX509Certificate(pem); + } + + public CertificateResponse retrieveCertificate(String serialNumber) + throws CertificateManagementDAOException { + CertificateDAO certificateDAO; + try { + CertificateManagementDAOFactory.openConnection(); + certificateDAO = CertificateManagementDAOFactory.getCertificateDAO(); + return certificateDAO.retrieveCertificate(serialNumber); + } catch (SQLException e) { + String errorMsg = "Error when opening connection"; + log.error(errorMsg, e); + throw new CertificateManagementDAOException(errorMsg, e); + } finally { + CertificateManagementDAOFactory.closeConnection(); + } + } + + public PaginationResult getAllCertificates(PaginationRequest request) + throws CertificateManagementDAOException { + try { + CertificateManagementDAOFactory.openConnection(); + CertificateDAO certificateDAO = CertificateManagementDAOFactory.getCertificateDAO(); + return certificateDAO.getAllCertificates(request); + } catch (SQLException e) { + String errorMsg = "Error when opening connection"; + log.error(errorMsg, e); + throw new CertificateManagementDAOException(errorMsg, e); + } finally { + CertificateManagementDAOFactory.closeConnection(); + } + } + + @Override + public boolean removeCertificate(String serialNumber) throws CertificateManagementDAOException { + try { + CertificateManagementDAOFactory.openConnection(); + CertificateDAO certificateDAO = CertificateManagementDAOFactory.getCertificateDAO(); + return certificateDAO.removeCertificate(serialNumber); + } catch (SQLException e) { + String errorMsg = "Error when opening connection"; + log.error(errorMsg, e); + throw new CertificateManagementDAOException(errorMsg, e); + } finally { + CertificateManagementDAOFactory.closeConnection(); + } + } + } diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/util/TenantUtil.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/util/TenantUtil.java new file mode 100644 index 00000000000..f452360bd89 --- /dev/null +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/util/TenantUtil.java @@ -0,0 +1,15 @@ +package org.wso2.carbon.certificate.mgt.core.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.certificate.mgt.core.exception.CertificateManagementException; +import org.wso2.carbon.context.PrivilegedCarbonContext; + +public class TenantUtil { + + private static final Log log = LogFactory.getLog(TenantUtil.class); + + public static int getTenanntId(String tenantDomain) throws CertificateManagementException { + return PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); + } +} diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/pom.xml b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/pom.xml index c8f549c316a..c59f1a1b515 100644 --- a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/pom.xml +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/pom.xml @@ -95,8 +95,7 @@ javax.xml, org.apache.axis2.transport.http, org.wso2.carbon.apimgt.impl, - org.wso2.carbon.certificate.mgt.core.service, - org.wso2.carbon.certificate.mgt.core.exception, + org.wso2.carbon.certificate.mgt.core.*, org.wso2.carbon.device.mgt.core.permission.mgt, org.wso2.carbon.device.mgt.common, org.wso2.carbon.device.mgt.common.permission.mgt, diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/Utils/Utils.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/Utils/Utils.java index 92dcaee3aa6..96326492772 100644 --- a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/Utils/Utils.java +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/Utils/Utils.java @@ -21,9 +21,12 @@ package org.wso2.carbon.webapp.authenticator.framework.Utils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.base.MultitenantConstants; +import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.user.api.TenantManager; import org.wso2.carbon.user.api.UserStoreException; +import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.utils.multitenancy.MultitenantUtils; import org.wso2.carbon.webapp.authenticator.framework.AuthenticationException; @@ -47,4 +50,28 @@ public class Utils { } return tenantId; } + + public static String getTenantDomain(int tenantId) throws AuthenticationException { + try { + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + + RealmService realmService = (RealmService) ctx.getOSGiService(RealmService.class, null); + if (realmService == null) { + String msg = "RealmService is not initialized"; + log.error(msg); + throw new AuthenticationException(msg); + } + + return realmService.getTenantManager().getDomain(tenantId); + + } catch (UserStoreException e) { + String msg = "User store not initialized"; + log.error(msg); + throw new AuthenticationException(msg, e); + } finally { + PrivilegedCarbonContext.endTenantFlow(); + } + } + } diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/CertificateAuthenticator.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/CertificateAuthenticator.java index f747c6d30b9..879efecd805 100644 --- a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/CertificateAuthenticator.java +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/CertificateAuthenticator.java @@ -4,6 +4,7 @@ import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.certificate.mgt.core.dto.CertificateResponse; import org.wso2.carbon.certificate.mgt.core.exception.KeystoreException; import org.wso2.carbon.device.mgt.common.DeviceIdentifier; import org.wso2.carbon.device.mgt.common.DeviceManagementConstants; @@ -11,8 +12,10 @@ import org.wso2.carbon.device.mgt.common.EnrolmentInfo; import org.wso2.carbon.device.mgt.core.scep.SCEPException; import org.wso2.carbon.device.mgt.core.scep.SCEPManager; import org.wso2.carbon.device.mgt.core.scep.TenantedDeviceWrapper; +import org.wso2.carbon.webapp.authenticator.framework.AuthenticationException; import org.wso2.carbon.webapp.authenticator.framework.AuthenticatorFrameworkDataHolder; import org.wso2.carbon.webapp.authenticator.framework.AuthenticationInfo; +import org.wso2.carbon.webapp.authenticator.framework.Utils.Utils; import java.security.cert.X509Certificate; import java.util.Properties; @@ -24,7 +27,9 @@ public class CertificateAuthenticator implements WebappAuthenticator { private static final Log log = LogFactory.getLog(CertificateAuthenticator.class); private static final String CERTIFICATE_AUTHENTICATOR = "CertificateAuth"; + private static final String MUTUAL_AUTH_HEADER = "mutual-auth-header"; private static final String CERTIFICATE_VERIFICATION_HEADER = "certificate-verification-header"; + private static final String CLIENT_CERTIFICATE_ATTRIBUTE = "javax.servlet.request.X509Certificate"; @Override public void init() { @@ -33,10 +38,9 @@ public class CertificateAuthenticator implements WebappAuthenticator { @Override public boolean canHandle(Request request) { - String certVerificationHeader = request.getContext().findParameter(CERTIFICATE_VERIFICATION_HEADER); - if (certVerificationHeader != null && !certVerificationHeader.isEmpty()) { - String certHeader = request.getHeader(certVerificationHeader); - return certHeader != null; + if (request.getHeader(CERTIFICATE_VERIFICATION_HEADER) != null || request.getHeader(MUTUAL_AUTH_HEADER) != + null) { + return true; } return false; } @@ -52,7 +56,38 @@ public class CertificateAuthenticator implements WebappAuthenticator { String certVerificationHeader = request.getContext().findParameter(CERTIFICATE_VERIFICATION_HEADER); try { - if (certVerificationHeader != null && !certVerificationHeader.isEmpty()) { + + if (request.getHeader(MUTUAL_AUTH_HEADER) != null) { + X509Certificate[] clientCertificate = (X509Certificate[]) request. + getAttribute(CLIENT_CERTIFICATE_ATTRIBUTE); + if (clientCertificate[0] != null) { + CertificateResponse certificateResponse = AuthenticatorFrameworkDataHolder.getInstance(). + getCertificateManagementService().verifyPEMSignature(clientCertificate[0]); + if (certificateResponse == null) { + authenticationInfo.setStatus(Status.FAILURE); + authenticationInfo.setMessage("Certificate sent doesn't match any certificate in the store." + + " Unauthorized access attempt."); + } else if (certificateResponse.getCommonName() != null && !certificateResponse.getCommonName(). + isEmpty()) { + authenticationInfo.setTenantId(certificateResponse.getTenantId()); + authenticationInfo.setStatus(Status.CONTINUE); + authenticationInfo.setUsername(certificateResponse.getCommonName()); + try { + authenticationInfo.setTenantDomain(Utils. + getTenantDomain( + certificateResponse.getTenantId())); + } catch (AuthenticationException e) { + authenticationInfo.setStatus(Status.FAILURE); + authenticationInfo.setMessage("Could not identify tenant domain."); + } + } else { + authenticationInfo.setStatus(Status.FAILURE); + authenticationInfo.setMessage("A matching certificate is found, " + + "but the serial number is missing in the database."); + } + + } + } else if (request.getHeader(CERTIFICATE_VERIFICATION_HEADER) != null) { String certHeader = request.getHeader(certVerificationHeader); if (certHeader != null && @@ -76,8 +111,8 @@ public class CertificateAuthenticator implements WebappAuthenticator { authenticationInfo.setTenantDomain(tenantedDeviceWrapper.getTenantDomain()); authenticationInfo.setTenantId(tenantedDeviceWrapper.getTenantId()); - if(tenantedDeviceWrapper.getDevice() != null && - tenantedDeviceWrapper.getDevice().getEnrolmentInfo() != null) { + if (tenantedDeviceWrapper.getDevice() != null && + tenantedDeviceWrapper.getDevice().getEnrolmentInfo() != null) { EnrolmentInfo enrolmentInfo = tenantedDeviceWrapper.getDevice().getEnrolmentInfo(); authenticationInfo.setUsername(enrolmentInfo.getOwner()); diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/h2.sql b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/h2.sql index d172e2f5d27..3a268960d14 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/h2.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/h2.sql @@ -8,6 +8,7 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_CERTIFICATE ( ID INTEGER auto_increment NOT NULL, SERIAL_NUMBER VARCHAR(500) DEFAULT NULL, CERTIFICATE BLOB DEFAULT NULL, + TENANT_ID INTEGER DEFAULT 0, PRIMARY KEY (ID) );