/* * Copyright (c) 2011-2012 ICM Uniwersytet Warszawski All rights reserved. * See LICENCE file for licensing information. * * Parts of this class are derived from the glite.security.util-java module, * copyrighted as follows: * * Copyright (c) Members of the EGEE Collaboration. 2004. See * http://www.eu-egee.org/partners/ for details on the copyright holders. */ package eu.emi.security.authn.x509.proxy; import java.io.IOException; import java.math.BigInteger; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.x509.AttributeCertificate; import org.bouncycastle.asn1.x509.KeyUsage; import eu.emi.security.authn.x509.helpers.proxy.ProxyAddressRestrictionData; import eu.emi.security.authn.x509.impl.CertificateUtils; /** * Generic proxy creation parameters useful for all scenarios. * All objects passed to this class are copied. All objects returned by methods of this class are also * copies of the object state. Therefore it is only possible to modify state of this class using its methods. * This class is not thread safe. * * @author J. Hahkala * @author K. Benedyczak */ public abstract class BaseProxyCertificateOptions { static { CertificateUtils.configureSecProvider(); } /** * Key usage value which is used when */ public static final int DEFAULT_KEY_USAGE = KeyUsage.dataEncipherment | KeyUsage.digitalSignature | KeyUsage.keyEncipherment; public static final int DEFAULT_LIFETIME = 12*3600; public static final int UNLIMITED_PROXY_LENGTH = Integer.MAX_VALUE; private final X509Certificate[] parentChain; private int lifetime = DEFAULT_LIFETIME; private Date notBefore; private ProxyType type; private boolean limited = false; private BigInteger serialNumber = null; private int proxyPathLimit = UNLIMITED_PROXY_LENGTH; private int proxyKeyUsageMask = -1; private List<CertificateExtension> extensions; private ProxyPolicy policy = null; private String[] targetRestrictionPermitted; private String[] targetRestrictionExcluded; private String[] sourceRestrictionPermitted; private String[] sourceRestrictionExcluded; private String proxyTracingSubject; private String proxyTracingIssuer; private String samlAssertion; private AttributeCertificate[] attributeCertificates; /** * Create a new proxy cert based on the parent cert chain. * The default type of the proy generation params will be set to the type of the * parent chain if it is an consistent proxy chain. If it is mixed proxy chain, * or EEC certificate chain then by default RFC proxy type is set. * @param parentCertChain chain of the issuer */ protected BaseProxyCertificateOptions(X509Certificate[] parentCertChain) { if (parentCertChain == null || parentCertChain.length == 0) throw new IllegalArgumentException("parent certificate chain must be set"); this.parentChain = Arrays.copyOf(parentCertChain, parentCertChain.length); extensions = new ArrayList<CertificateExtension>(); notBefore = new Date(); if (ProxyUtils.isProxy(parentCertChain)) { ProxyChainType pct; try { pct = new ProxyChainInfo(parentCertChain).getProxyType(); } catch (CertificateException e) { throw new IllegalArgumentException("Can not parse the parentCertChain argument", e); } if (pct == ProxyChainType.RFC3820) type = ProxyType.RFC3820; else if (pct == ProxyChainType.DRAFT_RFC) type = ProxyType.DRAFT_RFC; else if (pct == ProxyChainType.LEGACY) type = ProxyType.LEGACY; else type = ProxyType.RFC3820; } else type = ProxyType.RFC3820; // Removed see issue #64. When creating legacy proxies the requirement to have digSig KU is not formally // enforced (err there is no formal definition) so we can't perform a sanity check here. // Integer parentKU = ProxyGeneratorHelper.getChainKeyUsage(parentCertChain); // if (parentKU != null && ((parentKU & KeyUsage.digitalSignature) == 0)) // throw new IllegalArgumentException("The parent certificate chain has no digital signature" + // " bit set in its Key Usage. This chain can not be used to create proxies."); } /** * Returns the certificate parent chain of the proxy. If only user certificate * was provided then it is returned in a one element array. * @return the parent certificate chain */ public X509Certificate[] getParentCertChain() { return parentChain; } /** * Sets the desired time bounds for the proxy. Note that both arguments are cut to the * seconds precision (this is what goes into certificate). * @param notBefore proxy won't be valid before this date * @param notAfter proxy won't be valid after this date * @since 1.1.0 */ public void setValidityBounds(Date notBefore, Date notAfter) { this.notBefore = new Date(); this.notBefore.setTime((notBefore.getTime()/1000L)*1000); if (notAfter.before(notBefore)) throw new IllegalArgumentException("notBefore argument value must be earlier than notAfter"); this.lifetime = (int)(notAfter.getTime()/1000L - notBefore.getTime()/1000L); } /** * Set the proxy lifetime in seconds. The start of proxy validity is set to the current time. * If not set, the default lifetime is 12h. * * @param lifetime in seconds * @see #setValidityBounds(Date, Date) */ public void setLifetime(int lifetime) { this.notBefore = new Date(); this.lifetime = lifetime; } /** * Set the proxy lifetime using desired unit. The start of proxy validity is set to the current time. * If not set, the default lifetime is 12h. * @param lifetime in unit specified by the 2nd parameter * @param unit the unit of the timeout specified by the first value * @throws IllegalArgumentException if the requested lifetime is larger then * {@link Integer#MAX_VALUE} seconds. * @see #setValidityBounds(Date, Date) * @since 1.1.0 */ public void setLifetime(long lifetime, TimeUnit unit) { long secLifetime = unit.toSeconds(lifetime); if (secLifetime > (long)Integer.MAX_VALUE) throw new IllegalArgumentException("This implementation allows for proxy lifetimes up to " + Integer.MAX_VALUE + " seconds"); setLifetime((int)secLifetime); } /** * * @return proxy lifetime in seconds */ public int getLifetime() { return lifetime; } /** * * @return start of proxy validity */ public Date getNotBefore() { return notBefore; } /** * @return bit mask of KeyUsage flags which was set for the options object or -1 if nothing was set. */ public int getProxyKeyUsageMask() { return proxyKeyUsageMask; } /** * Sets the mask of the KeyUsage for the resulting proxy certificate. Note that the this is a mask, * i.e. the flags from this mask are ANDed with the effective KeyUsage of the parent chain. * <p> * If this method is not called at all (or called with a negative argument), then the default behavior * is applied, and the proxy gets a copy of the effective KeyUsage of the parent chain. If no certificate * in the parent chain has KeyUsage set, then the {@link #DEFAULT_KEY_USAGE} is applied. * @param proxyKeyUsageMask The mask to set. Use constants from the {@link KeyUsage} class. The mask must always * have the {@link KeyUsage#digitalSignature} bit set. * @throws IllegalArgumentException if the argument has no {@link KeyUsage#digitalSignature} bit set */ public void setProxyKeyUsageMask(int proxyKeyUsageMask) throws IllegalArgumentException { if ((proxyKeyUsageMask & KeyUsage.digitalSignature) == 0) throw new IllegalArgumentException("The digital signature bit must be always set for the proxy"); this.proxyKeyUsageMask = proxyKeyUsageMask; } /** * Used to set the type of the proxy. Useful only in case the parent * certificate is user certificate, otherwise the generator will * generate same type of proxy as the parent is. And trying to set * different type here than in the parent will result in * IllegalArgumentException. If the parent certificate is user * certificate and this method is not used, * RFC3820 type will be assumed. * @param type to be set */ public void setType(ProxyType type) throws IllegalArgumentException { this.type = type; } /** * @return the current proxy type */ public ProxyType getType() { return type; } /** * Defines whether the resulting proxy will be a limited proxy. Job * submission with a limited proxy is not possible. * <p> * For legacy proxy this is the only way to control the proxy's application area. * RFC and draft proxies allows for a more rich and extensible semantics using * {@link #setPolicy(ProxyPolicy)}. * <p> * Since version 1.2.0, in case of RFC proxies, usage of this method with argument 'true' is * equivalent to calling <code>setPolicy(new ProxyPolicy(ProxyPolicy.LIMITED_PROXY_OID))</code> * and with argument false to <code>setPolicy(new ProxyPolicy(ProxyPolicy.INHERITALL_POLICY_OID))</code>. * Note that subsequent calls to setPolicy will overwrite the setLimited setting. Therefore the following * code: * <pre> * param.setLimited(true); * param.setPolicy(new ProxyPolicy(ProxyPolicy.INHERITALL_POLICY_OID)); * </pre> * configures the engine to create limited legacy proxies or unlimited rfc proxies. * As this behavior is rather not intended it is strongly advised NOT to mix * setLimited and setPolicy calls in any case. * * @param limited true if proxy shall be limited */ public void setLimited(boolean limited) { this.limited = limited; if (limited) setPolicy(new ProxyPolicy(ProxyPolicy.LIMITED_PROXY_OID)); else setPolicy(new ProxyPolicy(ProxyPolicy.INHERITALL_POLICY_OID)); } /** * Checks if the proxy shall be limited. * @return true if limited proxy shall be created */ public boolean isLimited() { return limited; } /** * Sets the proxy serial number. Only applicable for rfc proxies. * @param sn serial number to be set */ public void setSerialNumber(BigInteger sn) { this.serialNumber = sn; } /** * Gets the proxy serial number. * @return the serial number previously set */ public BigInteger getSerialNumber() { return serialNumber; } /** * Sets the proxy path length limit of this certificate. Only works on * rfc3820 and RFC draft proxies. * Note: this method previously was documented as accepting negative values to mark unlimited * proxy length. The implementation was buggy (see #81). The old approach with negative * value works now, but usage of the constant is preferred in a new code. * @param pathLen path limit, use {@link #UNLIMITED_PROXY_LENGTH} if proxy shall be unlimited. */ public void setProxyPathLimit(int pathLen) { this.proxyPathLimit = (pathLen == Integer.MAX_VALUE || pathLen < 0) ? UNLIMITED_PROXY_LENGTH : pathLen; } /** * Gets the proxy path length limit of this certificate. * @return limit or {@link #UNLIMITED_PROXY_LENGTH} if proxy shall be unlimited */ public int getProxyPathLimit() { return proxyPathLimit; } /////////////////////////////////////////////////////////////////////// //////////// DIRECT EXTENSIONS HANDLING /////////////////////////////// /////////////////////////////////////////////////////////////////////// /** * Add an extension to the proxy certificate to be generated. * @param extension the extension to be set */ public void addExtension(CertificateExtension extension) { extensions.add(extension); } /** * @return Returns a list of extensions including only those which were set via * {@link #addExtension(CertificateExtension)} */ public List<CertificateExtension> getExtensions() { List<CertificateExtension> ret = new ArrayList<CertificateExtension>(extensions.size()); ret.addAll(extensions); return ret; } /** * Set the RFC proxy extension policy OID and octets of the * policy. See RFC3820. Policy can be null in case the OID in it self * defines the behavior, like with "inherit all" policy or * "independent" policy. * <p> * Note: this setting is ignored for legacy proxies. * @param policy to be set */ public void setPolicy(ProxyPolicy policy) { this.policy = policy.clone(); } /** * @return Get the RFC proxy extension policy OID and octets of the * policy. See RFC3820. Policy can be null in case the OID in it self * defines the behavior, like with "inherit all" policy or * "independent" policy. */ public ProxyPolicy getPolicy() { return policy == null ? null : policy.clone(); } /** * Sets a new permitted target IP addressSpace to the Proxy. * * @param addresses The address space to add to the allowed ip address space. * Example of the format: 192.168.0.0/16. * It equals to a network 192.168.0.0 with a net mask 255.255.0.0. * A single IP address can be defined as xxx.xxx.xxx.xxx/32. <br> * See <a href="http://www.ietf.org/rfc/rfc4632.txt"> RFC 4632.</a> * The restriction is of the format used for NameConstraints, * meaning GeneralName with 8 octets for ipv4 and 32 octets for ipv6 addresses. * @throws IllegalArgumentException if the argument does not contain addresses in * the specified format */ public void setTargetRestrictionPermittedAddresses(String[] addresses) throws IllegalArgumentException { targetRestrictionPermitted = addresses.clone(); } /** * Sets a permitted target IP address space to the Proxy. * * @param addresses The array of 8 element arrays of bytes * representation of address spaces defined in this structure. * Each inner 8-elements array must contains IP address and netmask bytes, * e.g. {137,138,0,0,255,255,0,0}. * @throws IllegalArgumentException when inner arrays are not of length 8 * or if does not represent a valid address and netmask combination. */ public void setTargetRestrictionPermittedAddresses(byte[][] addresses) throws IllegalArgumentException { targetRestrictionPermitted = ProxyAddressRestrictionData.convert2strings(addresses); } /** * Returns a permitted target IP address space of the Proxy. * * @return The array of addresses in the CIDR format (address/netmaskBits) * or null if not set */ public String[] getTargetRestrictionPermittedAddresses() { return targetRestrictionPermitted == null ? null : targetRestrictionPermitted.clone(); } /** * Sets a new permitted source IP addressSpace to the Proxy * * @param addresses The address space to add to the allowed ip address space. * Example of the format: 192.168.0.0/16. * It equals a 192.168.0.0 with a net mask 255.255.0.0. * A single IP address can be defined as xxx.xxx.xxx.xxx/32. <br> * See <a href="http://www.ietf.org/rfc/rfc4632.txt"> RFC 4632.</a> * The restriction is of the format used for NameConstraints, * meaning GeneralName with 8 octets for ipv4 and 32 octets for ipv6 addresses. * @throws IllegalArgumentException if the argument does not contain addresses in * the specified format */ public void setSourceRestrictionPermittedAddresses(String[] addresses) throws IllegalArgumentException { sourceRestrictionPermitted = addresses.clone(); } /** * Sets a permitted source IP addressSpace to the Proxy. * * @param addresses The array of 8 element arrays of bytes * representation of address spaces defined in this structure. * Each inner 8-elements array must contains IP address and netmask bytes, * e.g. {137,138,0,0,255,255,0,0}. * @throws IllegalArgumentException when inner arrays are not of length 8 * or if does not represent a valid address and netmask combination. */ public void setSourceRestrictionPermittedAddresses(byte[][] addresses) throws IllegalArgumentException { sourceRestrictionPermitted = ProxyAddressRestrictionData.convert2strings(addresses); } /** * Gets the permitted source IP addressSpace of the Proxy. * * @return The array of addresses in the CIDR format (address/netmaskBits) * or null if not set */ public String[] getSourceRestrictionPermittedAddresses() { return sourceRestrictionPermitted == null ? null : sourceRestrictionPermitted.clone(); } /** * Sets an excluded target IP addressSpace to the data structure. * * @param addresses The address space to add to the allowed ip address space. * Example of the format: 192.168.0.0/16. * It equals a 192.168.0.0 with a net mask 255.255.0.0. * A single IP address can be defined as xxx.xxx.xxx.xxx/32. <br> * See <a href="http://www.ietf.org/rfc/rfc4632.txt"> RFC 4632.</a> * The restriction is of the format used for NameConstraints, * meaning GeneralName with 8 octets for ipv4 and 32 octets for ipv6 addresses. * @throws IllegalArgumentException if the argument does not contain addresses in * the specified format */ public void setTargetRestrictionExcludedAddresses(String[] addresses) throws IllegalArgumentException { targetRestrictionExcluded = addresses.clone(); } /** * Sets an excluded target IP addressSpace to the data structure. * * @param addresses The array of 8 element arrays of bytes * representation of address spaces defined in this structure. * Each inner 8-elements array must contains IP address and netmask bytes, * e.g. {137,138,0,0,255,255,0,0}. * @throws IllegalArgumentException when inner arrays are not of length 8 * or if does not represent a valid address and netmask combination. */ public void setTargetRestrictionExcludedAddresses(byte[][] addresses) throws IllegalArgumentException { targetRestrictionExcluded = ProxyAddressRestrictionData.convert2strings(addresses); } /** * Gets an excluded target IP addressSpace from the data structure. * * @return The array of addresses in the CIDR format (address/netmaskBits) * or null if not set */ public String[] getTargetRestrictionExcludedAddresses() { return targetRestrictionExcluded == null ? null : targetRestrictionExcluded.clone(); } /** * Sets an excluded from source restriction IP addressSpace to the data structure. * * @param addresses The address space to add to the allowed ip address space. * Example of the format: 192.168.0.0/16. * It equals a 192.168.0.0 with a net mask 255.255.0.0. * A single IP address can be defined as xxx.xxx.xxx.xxx/32. <br> * See <a href="http://www.ietf.org/rfc/rfc4632.txt"> RFC 4632.</a> * The restriction is of the format used for NameConstraints, * meaning GeneralName with 8 octets for ipv4 and 32 octets for ipv6 addresses. * @throws IllegalArgumentException if the argument does not contain addresses in * the specified format */ public void setSourceRestrictionExcludedAddresses(String[] addresses) throws IllegalArgumentException { sourceRestrictionExcluded = addresses.clone(); } /** * Sets an excluded from source restriction IP addressSpace to the data structure. * * @param addresses The array of 8 element arrays of bytes * representation of address spaces defined in this structure. * Each inner 8-elements array must contains IP address and netmask bytes, * e.g. {137,138,0,0,255,255,0,0}. * @throws IllegalArgumentException when inner arrays are not of length 8 * or if does not represent a valid address and netmask combination. */ public void setSourceRestrictionExcludedAddresses(byte[][] addresses) throws IllegalArgumentException { sourceRestrictionExcluded = ProxyAddressRestrictionData.convert2strings(addresses); } /** * Gets an excluded from source restriction IP addressSpace from the data structure. * * @return The array of addresses in the CIDR format (address/netmaskBits) * or null if not set */ public String[] getSourceRestrictionExcludedAddresses() { return sourceRestrictionExcluded == null ? null : sourceRestrictionExcluded.clone(); } /** * Sets the issuer URL for the proxy tracing. * * @param url the issuer URL */ public void setProxyTracingIssuer(String url) { this.proxyTracingIssuer = url; } /** * @return Gets the issuer URL for the proxy tracing. */ public String getProxyTracingIssuer() { return proxyTracingIssuer; } /** * Sets the subject URL for the proxy tracing. * @param url the subject URL */ public void setProxyTracingSubject(String url) { this.proxyTracingSubject = url; } /** * @return Gets the subject URL for the proxy tracing. */ public String getProxyTracingSubject() { return proxyTracingSubject; } /** * Gets SAML assertions in a string format. * @return SAML assertions */ public String getSAMLAssertion() { return samlAssertion; } /** * Sets SAML assertions in a string format. * @param saml assertions to be used */ public void setSAMLAssertion(String saml) { samlAssertion = saml; } /** * Sets Attribute certificates, which will be added as the VOMS extensions to the generated proxy. * @param ac to be set * @throws IOException IO exception */ public void setAttributeCertificates(AttributeCertificate[] ac) throws IOException { attributeCertificates = new AttributeCertificate[ac.length]; for (int i=0; i<ac.length; i++) attributeCertificates[i] = AttributeCertificate.getInstance(ac[i].getEncoded(ASN1Encoding.DER)); } /** * * @return Attribute certificates or null if was not set * @throws IOException IO exception */ public AttributeCertificate[] getAttributeCertificates() throws IOException { if (attributeCertificates == null) return null; AttributeCertificate[] ret = new AttributeCertificate[attributeCertificates.length]; for (int i=0; i<attributeCertificates.length; i++) ret[i] = AttributeCertificate.getInstance( attributeCertificates[i].getEncoded(ASN1Encoding.DER)); return ret; } }