| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF 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.apache.hadoop.hdds.security.x509.certificate.authority; |
| |
| import org.apache.hadoop.hdds.security.exception.SCMSecurityException; |
| import org.apache.hadoop.hdds.security.x509.SecurityConfig; |
| import org.apache.hadoop.hdds.security.x509.certificate.authority.PKIProfiles.PKIProfile; |
| import org.apache.hadoop.hdds.security.x509.keys.SecurityUtil; |
| import org.apache.hadoop.util.Time; |
| import org.bouncycastle.asn1.ASN1ObjectIdentifier; |
| import org.bouncycastle.asn1.x500.X500Name; |
| import org.bouncycastle.asn1.x500.style.BCStyle; |
| import org.bouncycastle.asn1.x509.AlgorithmIdentifier; |
| import org.bouncycastle.asn1.x509.Extension; |
| import org.bouncycastle.asn1.x509.Extensions; |
| import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; |
| import org.bouncycastle.cert.X509CertificateHolder; |
| import org.bouncycastle.cert.X509v3CertificateBuilder; |
| import org.bouncycastle.crypto.params.AsymmetricKeyParameter; |
| import org.bouncycastle.crypto.params.RSAKeyParameters; |
| import org.bouncycastle.crypto.util.PrivateKeyFactory; |
| import org.bouncycastle.crypto.util.PublicKeyFactory; |
| import org.bouncycastle.operator.ContentSigner; |
| import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder; |
| import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder; |
| import org.bouncycastle.operator.OperatorCreationException; |
| import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder; |
| import org.bouncycastle.pkcs.PKCS10CertificationRequest; |
| |
| import java.io.IOException; |
| import java.math.BigInteger; |
| import java.security.PrivateKey; |
| import java.util.Date; |
| import java.util.concurrent.CompletableFuture; |
| |
| /** |
| * Default Approver used the by the DefaultCA. |
| */ |
| public class DefaultApprover extends BaseApprover { |
| |
| /** |
| * Constructs the Default Approver. |
| * |
| * @param pkiProfile - PKI Profile to use. |
| * @param config - Security Config |
| */ |
| public DefaultApprover(PKIProfile pkiProfile, SecurityConfig config) { |
| super(pkiProfile, config); |
| } |
| |
| /** |
| * Sign function signs a Certificate. |
| * @param config - Security Config. |
| * @param caPrivate - CAs private Key. |
| * @param caCertificate - CA Certificate. |
| * @param validFrom - Begin Da te |
| * @param validTill - End Date |
| * @param certificationRequest - Certification Request. |
| * @param scmId - SCM id. |
| * @param clusterId - Cluster id. |
| * @return Signed Certificate. |
| * @throws IOException - On Error |
| * @throws OperatorCreationException - on Error. |
| */ |
| @SuppressWarnings("ParameterNumber") |
| @Override |
| public X509CertificateHolder sign( |
| SecurityConfig config, |
| PrivateKey caPrivate, |
| X509CertificateHolder caCertificate, |
| Date validFrom, |
| Date validTill, |
| PKCS10CertificationRequest certificationRequest, |
| String scmId, |
| String clusterId) throws IOException, |
| OperatorCreationException { |
| |
| AlgorithmIdentifier sigAlgId = new |
| DefaultSignatureAlgorithmIdentifierFinder().find( |
| config.getSignatureAlgo()); |
| AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder() |
| .find(sigAlgId); |
| |
| AsymmetricKeyParameter asymmetricKP = PrivateKeyFactory.createKey(caPrivate |
| .getEncoded()); |
| SubjectPublicKeyInfo keyInfo = |
| certificationRequest.getSubjectPublicKeyInfo(); |
| |
| // Get scmId and cluster Id from subject name. |
| X500Name x500Name = certificationRequest.getSubject(); |
| String csrScmId = x500Name.getRDNs(BCStyle.OU)[0].getFirst().getValue(). |
| toASN1Primitive().toString(); |
| String csrClusterId = x500Name.getRDNs(BCStyle.O)[0].getFirst().getValue(). |
| toASN1Primitive().toString(); |
| |
| if (!scmId.equals(csrScmId) || !clusterId.equals(csrClusterId)) { |
| if (csrScmId.equalsIgnoreCase("null") && |
| csrClusterId.equalsIgnoreCase("null")) { |
| // Special case to handle DN certificate generation as DN might not know |
| // scmId and clusterId before registration. In secure mode registration |
| // will succeed only after datanode has a valid certificate. |
| String cn = x500Name.getRDNs(BCStyle.CN)[0].getFirst().getValue() |
| .toASN1Primitive().toString(); |
| x500Name = SecurityUtil.getDistinguishedName(cn, scmId, clusterId); |
| } else { |
| // Throw exception if scmId and clusterId doesn't match. |
| throw new SCMSecurityException("ScmId and ClusterId in CSR subject" + |
| " are incorrect."); |
| } |
| } |
| |
| RSAKeyParameters rsa = |
| (RSAKeyParameters) PublicKeyFactory.createKey(keyInfo); |
| if (rsa.getModulus().bitLength() < config.getSize()) { |
| throw new SCMSecurityException("Key size is too small in certificate " + |
| "signing request"); |
| } |
| X509v3CertificateBuilder certificateGenerator = |
| new X509v3CertificateBuilder( |
| caCertificate.getSubject(), |
| // Serial is not sequential but it is monotonically increasing. |
| BigInteger.valueOf(generateSerialId()), |
| validFrom, |
| validTill, |
| x500Name, keyInfo); |
| |
| Extensions exts = SecurityUtil.getPkcs9Extensions(certificationRequest); |
| for (ASN1ObjectIdentifier extId : getProfile().getSupportedExtensions()) { |
| Extension ext = exts.getExtension(extId); |
| if (ext != null) { |
| certificateGenerator.addExtension(ext); |
| } |
| } |
| |
| ContentSigner sigGen = new BcRSAContentSignerBuilder(sigAlgId, digAlgId) |
| .build(asymmetricKP); |
| |
| return certificateGenerator.build(sigGen); |
| |
| } |
| |
| public long generateSerialId() { |
| // TODO: to make generation of serialId distributed. |
| // This issue will be fixed in HDDS-4999. |
| return Time.monotonicNowNanos(); |
| } |
| |
| @Override |
| public CompletableFuture<X509CertificateHolder> inspectCSR(String csr) |
| throws IOException { |
| return super.inspectCSR(csr); |
| } |
| |
| @Override |
| public CompletableFuture<X509CertificateHolder> |
| inspectCSR(PKCS10CertificationRequest csr) { |
| return super.inspectCSR(csr); |
| } |
| } |