/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.security.cert;
import java.io.IOException;
import java.security.PublicKey;
import javax.security.auth.x500.X500Principal;
import org.apache.harmony.security.utils.Array;
import org.apache.harmony.security.x509.NameConstraints;
/**
* This class represents a trust anchor for validation of X.509 certification
* path.
* <p>
* It is a <i>trusted</i> certificate authority (CA) and includes the public key
* of the CA, the CA's name and the constraints for the validation of
* certification paths. The constructor also allows to specify a binary
* representation of a so called "Name Constraints" extension as a byte array.
*/
public class TrustAnchor {
// Most trusted CA as a X500Principal
private final X500Principal caPrincipal;
// Most trusted CA name
private final String caName;
// Most trusted CA public key
private final PublicKey caPublicKey;
// Most trusted CA certificate
private final X509Certificate trustedCert;
// Name constraints extension
private final byte[] nameConstraints;
/**
* Creates a new {@code TrustAnchor} with the specified certificate and name
* constraints.
* <p>
* The name constraints will be used as additional constraints during the
* validation of certification paths.
*
* @param trustedCert
* the trusted certificate
* @param nameConstraints
* the ASN.1 DER encoded form of the name constraints or {@code
* null} if none.
* @throws IllegalArgumentException
* if the decoding of the name constraints fail.
*/
public TrustAnchor(X509Certificate trustedCert, byte[] nameConstraints) {
if (trustedCert == null) {
throw new NullPointerException("trustedCert == null");
}
this.trustedCert = trustedCert;
// copy nameConstraints if not null
if (nameConstraints != null) {
this.nameConstraints = new byte[nameConstraints.length];
System.arraycopy(nameConstraints, 0,
this.nameConstraints, 0, this.nameConstraints.length);
processNameConstraints();
} else {
this.nameConstraints = null;
}
this.caName = null;
this.caPrincipal = null;
this.caPublicKey = null;
}
/**
* Creates a new {@code TrustAnchor} with the specified certificate
* authority name, its public key and the specified name constraints.
* <p>
* The name constraints will be used as additional constraints during the
* validation of certification paths.
*
* @param caName
* the X.500 name of the certificate authority in RFC 2253
* {@code String} format.
* @param caPublicKey
* the public key of the certificate authority
* @param nameConstraints
* the ASN.1 DER encoded form of the name constraints or {@code
* null} if none.
* @throws IllegalArgumentException
* if the {@code caName} is empty or if decoding of the name
* constraints fail.
*/
public TrustAnchor(String caName, PublicKey caPublicKey,
byte[] nameConstraints) {
if (caName == null) {
throw new NullPointerException("caName == null");
}
this.caName = caName;
if (caPublicKey == null) {
throw new NullPointerException("caPublicKey == null");
}
this.caPublicKey = caPublicKey;
// copy nameConstraints if not null
if (nameConstraints != null) {
this.nameConstraints = new byte[nameConstraints.length];
System.arraycopy(nameConstraints, 0,
this.nameConstraints, 0, this.nameConstraints.length);
processNameConstraints();
} else {
this.nameConstraints = null;
}
this.trustedCert = null;
// X500Principal checks caName validity
if (caName.isEmpty()) {
throw new IllegalArgumentException("caName.isEmpty()");
}
this.caPrincipal = new X500Principal(this.caName);
}
/**
* Creates a new {@code TrustAnchor} with the specified certificate
* authority name as principal, its public key and the specified name
* constraints.
* <p>
* The name constraints will be used as additional constraints during the
* validation of certification paths.
*
* @param caPrincipal
* the name of the certificate authority as X500 principal.
* @param caPublicKey
* the public key of the certificate authority.
* @param nameConstraints
* the ASN.1 DER encoded form of the name constraints or {@code
* null} if none.
* @throws IllegalArgumentException
* if decoding of the name constraints fail.
*/
public TrustAnchor(X500Principal caPrincipal,
PublicKey caPublicKey, byte[] nameConstraints) {
if (caPrincipal == null) {
throw new NullPointerException("caPrincipal == null");
}
this.caPrincipal = caPrincipal;
if (caPublicKey == null) {
throw new NullPointerException("caPublicKey == null");
}
this.caPublicKey = caPublicKey;
// copy nameConstraints if not null
if (nameConstraints != null) {
this.nameConstraints = new byte[nameConstraints.length];
System.arraycopy(nameConstraints, 0,
this.nameConstraints, 0, this.nameConstraints.length);
processNameConstraints();
} else {
this.nameConstraints = null;
}
this.trustedCert = null;
this.caName = caPrincipal.getName();
}
/**
* Returns a copy of the name constraints in ASN.1 DER encoded form.
*
* @return a copy of the name constraints in ASN.1 DER encoded form.
*/
public final byte[] getNameConstraints() {
if (nameConstraints == null) {
return null;
}
byte[] ret = new byte[nameConstraints.length];
System.arraycopy(nameConstraints, 0,
ret, 0, nameConstraints.length);
return ret;
}
/**
* Returns the certificate of this <i>trusted</i> certificate authority.
*
* @return the certificate of this CA or {@code null}, if the trust anchor
* of this instance was not created with a certificate.
*/
public final X509Certificate getTrustedCert() {
return trustedCert;
}
/**
* Returns the name of the certificate authority as {@code X500Principal}.
*
* @return the name of the certificate authority or {@code null} if the
* trust anchor of this instance was not created with a {@code
* X500Principal}.
*/
public final X500Principal getCA() {
return caPrincipal;
}
/**
* Returns the name of the certificate authority as {@code String} in RFC
* 2253 format.
*
* @return the name of the certificate authority as {@code String} in RFC
* 2253 format or {@code null} if the trust anchor of this instance
* was not created with a CA name.
*/
public final String getCAName() {
return caName;
}
/**
* Returns the public key of the certificate authority.
*
* @return the public key of the certificate authority or {@code null} if
* the trust anchor if this instance was not created with a public
* key.
*/
public final PublicKey getCAPublicKey() {
return caPublicKey;
}
/**
* Returns a string representation of this {@code TrustAnchor} instance.
*
* @return a string representation of this {@code TrustAnchor} instance.
*/
public String toString() {
StringBuilder sb = new StringBuilder("TrustAnchor: [\n");
if (trustedCert != null) {
sb.append("Trusted CA certificate: ");
sb.append(trustedCert);
sb.append("\n");
}
if (caPrincipal != null) {
sb.append("Trusted CA Name: ");
sb.append(caPrincipal);
sb.append("\n");
}
if (caPublicKey != null) {
sb.append("Trusted CA Public Key: ");
sb.append(caPublicKey);
sb.append("\n");
}
// FIXME if needed:
if (nameConstraints != null) {
sb.append("Name Constraints:\n");
sb.append(Array.toString(nameConstraints, " "));
}
sb.append("\n]");
return sb.toString();
}
//
// Private stuff
//
// Decodes and checks NameConstraints structure.
// Throws IllegalArgumentException if NameConstraints
// encoding is invalid.
private void processNameConstraints() {
try {
// decode and check nameConstraints
NameConstraints.ASN1.decode(nameConstraints);
} catch (IOException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
}