/* * * 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.commons.security; import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.cert.X509Certificate; import java.util.concurrent.ConcurrentLinkedDeque; import javax.crypto.NoSuchPaddingException; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Hex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xipki.commons.common.ObjectCreationException; import org.xipki.commons.common.util.IoUtil; import org.xipki.commons.common.util.ParamUtil; import org.xipki.commons.common.util.StringUtil; import org.xipki.commons.password.PasswordResolver; import org.xipki.commons.password.PasswordResolverException; import org.xipki.commons.security.exception.P11TokenException; import org.xipki.commons.security.exception.XiSecurityException; import org.xipki.commons.security.pkcs11.P11ContentSignerBuilder; import org.xipki.commons.security.pkcs11.P11CryptService; import org.xipki.commons.security.pkcs11.P11CryptServiceFactory; import org.xipki.commons.security.pkcs11.P11EntityIdentifier; import org.xipki.commons.security.pkcs11.P11Module; import org.xipki.commons.security.pkcs11.P11ObjectIdentifier; import org.xipki.commons.security.pkcs11.P11Slot; import org.xipki.commons.security.pkcs11.P11SlotIdentifier; import org.xipki.commons.security.pkcs12.SoftTokenContentSignerBuilder; import org.xipki.commons.security.util.AlgorithmUtil; /** * @author Lijun Liao * @since 2.0.0 */ public class SignerFactoryRegisterImpl implements SignerFactoryRegister { private static final Logger LOG = LoggerFactory.getLogger(SignerFactoryRegisterImpl.class); private P11CryptServiceFactory p11CryptServiceFactory; private ConcurrentLinkedDeque<SignerFactory> services = new ConcurrentLinkedDeque<SignerFactory>(); public void setP11CryptServiceFactory(final P11CryptServiceFactory p11CryptServiceFactory) { this.p11CryptServiceFactory = p11CryptServiceFactory; } public void bindService(final SignerFactory service) { //might be null if dependency is optional if (service == null) { LOG.info("bindService invoked with null."); return; } boolean replaced = services.remove(service); services.add(service); String action = replaced ? "replaced" : "added"; LOG.info("{} SignerFactory binding for {}", action, service); } public void unbindService(final SignerFactory service) { //might be null if dependency is optional if (service == null) { LOG.info("unbindService invoked with null."); return; } if (services.remove(service)) { LOG.info("removed SignerFactory binding for {}", service); } else { LOG.info("no SignerFactory binding found to remove for '{}'", service); } } @Override public ConcurrentContentSigner newSigner(final SecurityFactory securityFactory, final String type, final SignerConf conf, final X509Certificate[] certificateChain) throws ObjectCreationException { ParamUtil.requireNonBlank("type", type); if ("PKCS12".equalsIgnoreCase(type) || "JKS".equalsIgnoreCase(type)) { return newPkcs12OrJksSigner(securityFactory, type, conf, certificateChain); } if ("PKCS11".equalsIgnoreCase(type)) { return newPkcs11Signer(securityFactory, type, conf, certificateChain); } for (SignerFactory service : services) { if (service.canCreateSigner(type)) { return service.newSigner(type, conf, certificateChain); } } throw new ObjectCreationException( "could not find Factory to create Signer of type '" + type + "'"); } private ConcurrentContentSigner newPkcs12OrJksSigner(final SecurityFactory securityFactory, final String type, final SignerConf conf, final X509Certificate[] certificateChain) throws ObjectCreationException { String str = conf.getConfValue("parallelism"); int parallelism = securityFactory.getDefaultSignerParallelism(); if (str != null) { try { parallelism = Integer.parseInt(str); } catch (NumberFormatException ex) { throw new ObjectCreationException("invalid parallelism " + str); } if (parallelism < 1) { throw new ObjectCreationException("invalid parallelism " + str); } } String passwordHint = conf.getConfValue("password"); char[] password; if (passwordHint == null) { password = null; } else { PasswordResolver passwordResolver = securityFactory.getPasswordResolver(); if (passwordResolver == null) { password = passwordHint.toCharArray(); } else { try { password = passwordResolver.resolvePassword(passwordHint); } catch (PasswordResolverException ex) { throw new ObjectCreationException( "could not resolve password. Message: " + ex.getMessage()); } } } str = conf.getConfValue("keystore"); String keyLabel = conf.getConfValue("key-label"); InputStream keystoreStream; if (StringUtil.startsWithIgnoreCase(str, "base64:")) { keystoreStream = new ByteArrayInputStream( Base64.decode(str.substring("base64:".length()))); } else if (StringUtil.startsWithIgnoreCase(str, "file:")) { String fn = str.substring("file:".length()); try { keystoreStream = new FileInputStream(IoUtil.expandFilepath(fn)); } catch (FileNotFoundException ex) { throw new ObjectCreationException("file not found: " + fn); } } else { throw new ObjectCreationException("unknown keystore content format"); } try { SoftTokenContentSignerBuilder signerBuilder = new SoftTokenContentSignerBuilder( type, keystoreStream, password, keyLabel, password, certificateChain); AlgorithmIdentifier signatureAlgId; if (conf.getHashAlgo() == null) { signatureAlgId = AlgorithmUtil.getSigAlgId(null, conf); } else { PublicKey pubKey = signerBuilder.getCert().getPublicKey(); signatureAlgId = AlgorithmUtil.getSigAlgId(pubKey, conf); } return signerBuilder.createSigner(signatureAlgId, parallelism, securityFactory.getRandom4Sign()); } catch (NoSuchAlgorithmException | NoSuchPaddingException | XiSecurityException ex) { throw new ObjectCreationException(String.format("%s: %s", ex.getClass().getName(), ex.getMessage())); } } public ConcurrentContentSigner newPkcs11Signer(final SecurityFactory securityFactory, final String type, final SignerConf conf, final X509Certificate[] certificateChain) throws ObjectCreationException { if (p11CryptServiceFactory == null) { throw new ObjectCreationException("p11CryptServiceFactory is not set"); } String str = conf.getConfValue("parallelism"); int parallelism = securityFactory.getDefaultSignerParallelism(); if (str != null) { try { parallelism = Integer.parseInt(str); } catch (NumberFormatException ex) { throw new ObjectCreationException("invalid parallelism " + str); } if (parallelism < 1) { throw new ObjectCreationException("invalid parallelism " + str); } } String moduleName = conf.getConfValue("module"); str = conf.getConfValue("slot"); Integer slotIndex = (str == null) ? null : Integer.parseInt(str); str = conf.getConfValue("slot-id"); Long slotId = (str == null) ? null : Long.parseLong(str); if ((slotIndex == null && slotId == null) || (slotIndex != null && slotId != null)) { throw new ObjectCreationException( "exactly one of slot (index) and slot-id must be specified"); } String keyLabel = conf.getConfValue("key-label"); str = conf.getConfValue("key-id"); byte[] keyId = null; if (str != null) { keyId = Hex.decode(str); } if ((keyId == null && keyLabel == null) || (keyId != null && keyLabel != null)) { throw new ObjectCreationException( "exactly one of key-id and key-label must be specified"); } P11CryptService p11Service; P11Slot slot; try { p11Service = p11CryptServiceFactory.getP11CryptService(moduleName); P11Module module = p11Service.getModule(); P11SlotIdentifier p11SlotId; if (slotId != null) { p11SlotId = module.getSlotIdForId(slotId); } else if (slotIndex != null) { p11SlotId = module.getSlotIdForIndex(slotIndex); } else { throw new RuntimeException("should not reach here"); } slot = module.getSlot(p11SlotId); } catch (P11TokenException | XiSecurityException ex) { throw new ObjectCreationException(ex.getMessage(), ex); } P11ObjectIdentifier p11ObjId = (keyId != null) ? slot.getObjectIdForId(keyId) : slot.getObjectIdForLabel(keyLabel); if (p11ObjId == null) { String str2 = (keyId != null) ? "id " + Hex.toHexString(keyId) : "label " + keyLabel; throw new ObjectCreationException("cound not find identity with " + str2); } P11EntityIdentifier entityId = new P11EntityIdentifier(slot.getSlotId(), p11ObjId); try { AlgorithmIdentifier signatureAlgId; if (conf.getHashAlgo() == null) { signatureAlgId = AlgorithmUtil.getSigAlgId(null, conf); } else { PublicKey pubKey = slot.getIdentity(p11ObjId).getPublicKey(); signatureAlgId = AlgorithmUtil.getSigAlgId(pubKey, conf); } P11ContentSignerBuilder signerBuilder = new P11ContentSignerBuilder(p11Service, securityFactory, entityId, certificateChain); return signerBuilder.createSigner(signatureAlgId, parallelism); } catch (P11TokenException | NoSuchAlgorithmException | XiSecurityException ex) { throw new ObjectCreationException(ex.getMessage(), ex); } } }