/* * Copyright (c) 2011-2012 ICM Uniwersytet Warszawski All rights reserved. * See LICENCE file for licensing information. */ package eu.emi.security.authn.x509.proxy; import java.io.IOException; import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.DERSet; import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; import org.bouncycastle.asn1.pkcs.Attribute; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.util.PrivateKeyFactory; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder; import org.bouncycastle.pkcs.PKCS10CertificationRequest; import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; import eu.emi.security.authn.x509.helpers.proxy.DraftRFCProxyCertInfoExtension; import eu.emi.security.authn.x509.helpers.proxy.ProxyAddressRestrictionData; import eu.emi.security.authn.x509.helpers.proxy.ProxyCSRImpl; import eu.emi.security.authn.x509.helpers.proxy.ProxyCertInfoExtension; import eu.emi.security.authn.x509.helpers.proxy.ProxyGeneratorHelper; import eu.emi.security.authn.x509.helpers.proxy.ProxySAMLExtension; import eu.emi.security.authn.x509.helpers.proxy.ProxyTracingExtension; import eu.emi.security.authn.x509.helpers.proxy.RFCProxyCertInfoExtension; import eu.emi.security.authn.x509.impl.CertificateUtils; /** * Generates a proxy certificate signing request. The request parameters may contain * extensions which are passed in the generated Certificate Signing Request. * Of course the peer issuing the proxy certificate may ignore them. * <p> * The following rules are applied basing on the parameters object: * <ul> * <li> [RFC proxy only] If the serial number is set then it is used as requested CN part of the proxy. * Otherwise the CN part is set to the serial number of the issuing certificate. Note that * this value most probably will be ignored anyway by the part which issues the actual proxy, * as this is this party responsibility to guarantee uniqueness of serial numbers across all proxies * issued. * <li> All additional extensions, SAML, tracing and address restrictions are added as Attributes * of extensionRequest type (PKCS 9) if are set. * <li> Proxy path limit and policy (if set) are wrapped into the proxy extension and then included in * the Attributes list (as above). If only one of the values is set then the second receives the default * value. If the type is set to the legacy proxy then those parameters are ignored. * <li> There is no way to request a validity time of the generated proxy, therefore the lifetime * parameter is ignored. * </ul> * * @author K. Benedyczak */ public class ProxyCSRGenerator { static { CertificateUtils.configureSecProvider(); } /** * Generate the proxy certificate object. Use this method if you want to sign * the proxy with the key which will be autogenerated together with the public part * which will be included in the proxy itself. This method will throw an exception * if used with parameter which has public key manually set. * * @param param request creation parameters * @return Proxy certificate signing request * @throws InvalidKeyException invalid key exception * @throws SignatureException signature exception * @throws NoSuchAlgorithmException no such algorithm exception * @throws CertificateEncodingException certificate encoding exception * @throws IllegalArgumentException when signingKey is null and public key was manully set */ public static ProxyCSR generate(ProxyCertificateOptions param) throws InvalidKeyException, SignatureException, NoSuchAlgorithmException, CertificateEncodingException { return generate(param, null); } /** * Generate the proxy certificate object. Use this method if you want to manually * specify the CSR signing key. This is normally the case when * the {@link ProxyCertificateOptions} parameter contains a manually set public key. * * @param param request creation parameters * @param signingKey private key * @return Proxy certificate signing request * @throws InvalidKeyException invalid key exception * @throws SignatureException signature exception * @throws NoSuchAlgorithmException no such algorithm exception * @throws CertificateEncodingException certificate encoding exception * @throws IllegalArgumentException when signingKey is null and public key was manually set */ public static ProxyCSR generate(ProxyCertificateOptions param, PrivateKey signingKey) throws InvalidKeyException, SignatureException, NoSuchAlgorithmException, CertificateEncodingException { PublicKey pubKey = param.getPublicKey(); KeyPair keyPair; if (pubKey == null) keyPair = ProxyGeneratorHelper.generateKeyPair(param.getKeyLength()); else keyPair = new KeyPair(pubKey, null); if (signingKey == null) signingKey = keyPair.getPrivate(); if (signingKey == null) throw new IllegalArgumentException("Signing (private) key can not be null " + "when using a manually set public key"); X509Certificate []chain = param.getParentCertChain(); ProxyType type = param.getType(); BigInteger serial = ProxyGeneratorHelper.establishSerial(param); X500Name proxySubjectName = ProxyGeneratorHelper.generateDN(chain[0].getSubjectX500Principal(), type, param.isLimited(), serial); List<Attribute> attributes = generateAttributes(param); PKCS10CertificationRequest req; try { ASN1InputStream is = new ASN1InputStream(keyPair.getPublic().getEncoded()); SubjectPublicKeyInfo subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(is.readObject()); is.close(); PKCS10CertificationRequestBuilder builder = new PKCS10CertificationRequestBuilder( proxySubjectName, subjectPublicKeyInfo); for (Attribute attribute: attributes) builder.addAttribute(attribute.getAttrType(), attribute.getAttributeValues()); AlgorithmIdentifier signatureAi = new AlgorithmIdentifier(OIWObjectIdentifiers.sha1WithRSA); AlgorithmIdentifier hashAi = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1); BcRSAContentSignerBuilder csBuilder = new BcRSAContentSignerBuilder(signatureAi, hashAi); AsymmetricKeyParameter pkParam = PrivateKeyFactory.createKey(signingKey.getEncoded()); ContentSigner signer = csBuilder.build(pkParam); req = builder.build(signer); } catch (IOException e) { throw new InvalidKeyException("Probelm with the proxy CSR private key", e); } catch (OperatorCreationException e) { throw new SignatureException("Problem signing the proxy CSR", e); } return new ProxyCSRImpl(req, keyPair.getPrivate()); } private static List<Attribute> generateAttributes(ProxyCertificateOptions param) { List<Attribute> attributes = new ArrayList<Attribute>(); List<CertificateExtension> additionalExts = param.getExtensions(); for (CertificateExtension ext: additionalExts) addAttribute(attributes, ext); ProxyPolicy policy = param.getPolicy(); int pathLimit = param.getProxyPathLimit(); if (param.getType() != ProxyType.LEGACY && (policy != null || pathLimit != -1)) { if (policy == null) policy = new ProxyPolicy(ProxyPolicy.INHERITALL_POLICY_OID); String oid = param.getType() == ProxyType.DRAFT_RFC ? DraftRFCProxyCertInfoExtension.DRAFT_EXTENSION_OID : RFCProxyCertInfoExtension.RFC_EXTENSION_OID; ProxyCertInfoExtension extValue = param.getType() == ProxyType.DRAFT_RFC ? new DraftRFCProxyCertInfoExtension(pathLimit, policy) : new RFCProxyCertInfoExtension(pathLimit, policy); CertificateExtension ext = new CertificateExtension(oid, extValue, true); addAttribute(attributes, ext); } if (param.getProxyTracingIssuer() != null) { ProxyTracingExtension extValue = new ProxyTracingExtension(param.getProxyTracingIssuer()); CertificateExtension ext = new CertificateExtension( ProxyTracingExtension.PROXY_TRACING_ISSUER_EXTENSION_OID, extValue, false); addAttribute(attributes, ext); } if (param.getProxyTracingSubject() != null) { ProxyTracingExtension extValue = new ProxyTracingExtension(param.getProxyTracingSubject()); CertificateExtension ext = new CertificateExtension( ProxyTracingExtension.PROXY_TRACING_SUBJECT_EXTENSION_OID, extValue, false); addAttribute(attributes, ext); } if (param.getSAMLAssertion() != null) { ProxySAMLExtension extValue = new ProxySAMLExtension(param.getSAMLAssertion()); CertificateExtension ext = new CertificateExtension( ProxySAMLExtension.SAML_OID, extValue, false); addAttribute(attributes, ext); } String[] srcExcl = param.getSourceRestrictionExcludedAddresses(); String[] srcPerm = param.getSourceRestrictionPermittedAddresses(); if (srcExcl != null || srcPerm != null) { ProxyAddressRestrictionData extValue = new ProxyAddressRestrictionData(); if (srcExcl != null) { for (String addr: srcExcl) extValue.addExcludedIPAddressWithNetmask(addr); } if (srcPerm != null) { for (String addr: srcPerm) extValue.addPermittedIPAddressWithNetmask(addr); } CertificateExtension ext = new CertificateExtension( ProxyAddressRestrictionData.SOURCE_RESTRICTION_OID, extValue, false); addAttribute(attributes, ext); } String[] tgtExcl = param.getTargetRestrictionExcludedAddresses(); String[] tgtPerm = param.getTargetRestrictionPermittedAddresses(); if (tgtExcl != null || tgtPerm != null) { ProxyAddressRestrictionData extValue = new ProxyAddressRestrictionData(); if (tgtExcl != null) { for (String addr: tgtExcl) extValue.addExcludedIPAddressWithNetmask(addr); } if (tgtPerm != null) { for (String addr: tgtPerm) extValue.addPermittedIPAddressWithNetmask(addr); } CertificateExtension ext = new CertificateExtension( ProxyAddressRestrictionData.TARGET_RESTRICTION_OID, extValue, false); addAttribute(attributes, ext); } return attributes; } private static void addAttribute(List<Attribute> attributes, ASN1Encodable ext) { Attribute a = new Attribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, new DERSet(ext)); attributes.add(a); } }