/*
*
* Copyright (c) 2013 - 2017 Lijun Liao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3
* as published by the Free Software Foundation with the addition of the
* following permission added to Section 15 as permitted in Section 7(a):
*
* FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
* THE AUTHOR LIJUN LIAO. LIJUN LIAO DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
* OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License.
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial activities involving the XiPKI software without
* disclosing the source code of your own applications.
*
* For more information, please contact Lijun Liao at this
* address: lijun.liao@gmail.com
*/
package org.xipki.pki.scep.message;
import java.io.IOException;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSAbsentContent;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.DefaultSignedAttributeTableGenerator;
import org.bouncycastle.cms.SignerInfoGenerator;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.xipki.commons.common.util.ParamUtil;
import org.xipki.pki.scep.crypto.ScepHashAlgoType;
import org.xipki.pki.scep.exception.MessageEncodingException;
import org.xipki.pki.scep.util.ScepUtil;
/**
* @author Lijun Liao
* @since 2.0.0
*/
public class NextCaMessage {
private X509Certificate caCert;
private List<X509Certificate> raCerts;
public NextCaMessage() {
}
public X509Certificate getCaCert() {
return caCert;
}
public void setCaCert(final X509Certificate caCert) {
this.caCert = caCert;
}
public List<X509Certificate> getRaCerts() {
return raCerts;
}
public void setRaCerts(final List<X509Certificate> raCerts) {
this.raCerts = (raCerts == null || raCerts.isEmpty()) ? null
: Collections.unmodifiableList(new ArrayList<X509Certificate>(raCerts));
}
public ContentInfo encode(final PrivateKey signingKey, final X509Certificate signerCert,
final X509Certificate[] cmsCertSet) throws MessageEncodingException {
ParamUtil.requireNonNull("signingKey", signingKey);
ParamUtil.requireNonNull("signerCert", signerCert);
try {
byte[] degenratedSignedDataBytes;
try {
CMSSignedDataGenerator degenerateSignedData = new CMSSignedDataGenerator();
degenerateSignedData.addCertificate(new X509CertificateHolder(caCert.getEncoded()));
if (raCerts != null && !raCerts.isEmpty()) {
for (X509Certificate m : raCerts) {
degenerateSignedData.addCertificate(
new X509CertificateHolder(m.getEncoded()));
}
}
degenratedSignedDataBytes = degenerateSignedData.generate(
new CMSAbsentContent()).getEncoded();
} catch (CertificateEncodingException ex) {
throw new MessageEncodingException(ex.getMessage(), ex);
}
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
// I don't known which hash algorithm is supported by the client, use SHA-1
String signatureAlgo = getSignatureAlgorithm(signingKey, ScepHashAlgoType.SHA1);
ContentSigner signer = new JcaContentSignerBuilder(signatureAlgo).build(signingKey);
// signerInfo
JcaSignerInfoGeneratorBuilder signerInfoBuilder = new JcaSignerInfoGeneratorBuilder(
new BcDigestCalculatorProvider());
signerInfoBuilder.setSignedAttributeGenerator(
new DefaultSignedAttributeTableGenerator());
SignerInfoGenerator signerInfo = signerInfoBuilder.build(signer, signerCert);
generator.addSignerInfoGenerator(signerInfo);
CMSTypedData cmsContent = new CMSProcessableByteArray(CMSObjectIdentifiers.signedData,
degenratedSignedDataBytes);
// certificateSet
ScepUtil.addCmsCertSet(generator, cmsCertSet);
return generator.generate(cmsContent, true).toASN1Structure();
} catch (CMSException ex) {
throw new MessageEncodingException(ex);
} catch (CertificateEncodingException ex) {
throw new MessageEncodingException(ex);
} catch (IOException ex) {
throw new MessageEncodingException(ex);
} catch (OperatorCreationException ex) {
throw new MessageEncodingException(ex);
}
} // method encode
private static String getSignatureAlgorithm(final PrivateKey key,
final ScepHashAlgoType hashAlgo) {
String algorithm = key.getAlgorithm();
if ("RSA".equalsIgnoreCase(algorithm)) {
return hashAlgo.getName() + "withRSA";
} else {
throw new UnsupportedOperationException(
"getSignatureAlgorithm() for non-RSA is not supported yet.");
}
}
}