/*
* 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;
}
}