blob: 2fd3dad82f397e4148c26f3363a2b8c1b6c474f1 [file] [log] [blame]
package org.bouncycastle.cert.crmf;
import java.io.IOException;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.DERUTF8String;
import org.bouncycastle.asn1.crmf.AttributeTypeAndValue;
import org.bouncycastle.asn1.crmf.CRMFObjectIdentifiers;
import org.bouncycastle.asn1.crmf.CertReqMsg;
import org.bouncycastle.asn1.crmf.CertTemplate;
import org.bouncycastle.asn1.crmf.Controls;
import org.bouncycastle.asn1.crmf.PKIArchiveOptions;
import org.bouncycastle.asn1.crmf.PKMACValue;
import org.bouncycastle.asn1.crmf.POPOSigningKey;
import org.bouncycastle.asn1.crmf.ProofOfPossession;
import org.bouncycastle.cert.CertIOException;
import org.bouncycastle.operator.ContentVerifier;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.util.Encodable;
/**
* Carrier for a CRMF CertReqMsg.
*/
public class CertificateRequestMessage
implements Encodable
{
public static final int popRaVerified = ProofOfPossession.TYPE_RA_VERIFIED;
public static final int popSigningKey = ProofOfPossession.TYPE_SIGNING_KEY;
public static final int popKeyEncipherment = ProofOfPossession.TYPE_KEY_ENCIPHERMENT;
public static final int popKeyAgreement = ProofOfPossession.TYPE_KEY_AGREEMENT;
private final CertReqMsg certReqMsg;
private final Controls controls;
private static CertReqMsg parseBytes(byte[] encoding)
throws IOException
{
try
{
return CertReqMsg.getInstance(ASN1Primitive.fromByteArray(encoding));
}
catch (ClassCastException e)
{
throw new CertIOException("malformed data: " + e.getMessage(), e);
}
catch (IllegalArgumentException e)
{
throw new CertIOException("malformed data: " + e.getMessage(), e);
}
}
/**
* Create a CertificateRequestMessage from the passed in bytes.
*
* @param certReqMsg BER/DER encoding of the CertReqMsg structure.
* @throws IOException in the event of corrupted data, or an incorrect structure.
*/
public CertificateRequestMessage(byte[] certReqMsg)
throws IOException
{
this(parseBytes(certReqMsg));
}
public CertificateRequestMessage(CertReqMsg certReqMsg)
{
this.certReqMsg = certReqMsg;
this.controls = certReqMsg.getCertReq().getControls();
}
/**
* Return the underlying ASN.1 object defining this CertificateRequestMessage object.
*
* @return a CertReqMsg.
*/
public CertReqMsg toASN1Structure()
{
return certReqMsg;
}
/**
* Return the certificate template contained in this message.
*
* @return a CertTemplate structure.
*/
public CertTemplate getCertTemplate()
{
return this.certReqMsg.getCertReq().getCertTemplate();
}
/**
* Return whether or not this request has control values associated with it.
*
* @return true if there are control values present, false otherwise.
*/
public boolean hasControls()
{
return controls != null;
}
/**
* Return whether or not this request has a specific type of control value.
*
* @param type the type OID for the control value we are checking for.
* @return true if a control value of type is present, false otherwise.
*/
public boolean hasControl(ASN1ObjectIdentifier type)
{
return findControl(type) != null;
}
/**
* Return a control value of the specified type.
*
* @param type the type OID for the control value we are checking for.
* @return the control value if present, null otherwise.
*/
public Control getControl(ASN1ObjectIdentifier type)
{
AttributeTypeAndValue found = findControl(type);
if (found != null)
{
if (found.getType().equals(CRMFObjectIdentifiers.id_regCtrl_pkiArchiveOptions))
{
return new PKIArchiveControl(PKIArchiveOptions.getInstance(found.getValue()));
}
if (found.getType().equals(CRMFObjectIdentifiers.id_regCtrl_regToken))
{
return new RegTokenControl(DERUTF8String.getInstance(found.getValue()));
}
if (found.getType().equals(CRMFObjectIdentifiers.id_regCtrl_authenticator))
{
return new AuthenticatorControl(DERUTF8String.getInstance(found.getValue()));
}
}
return null;
}
private AttributeTypeAndValue findControl(ASN1ObjectIdentifier type)
{
if (controls == null)
{
return null;
}
AttributeTypeAndValue[] tAndVs = controls.toAttributeTypeAndValueArray();
AttributeTypeAndValue found = null;
for (int i = 0; i != tAndVs.length; i++)
{
if (tAndVs[i].getType().equals(type))
{
found = tAndVs[i];
break;
}
}
return found;
}
/**
* Return whether or not this request message has a proof-of-possession field in it.
*
* @return true if proof-of-possession is present, false otherwise.
*/
public boolean hasProofOfPossession()
{
return this.certReqMsg.getPopo() != null;
}
/**
* Return the type of the proof-of-possession this request message provides.
*
* @return one of: popRaVerified, popSigningKey, popKeyEncipherment, popKeyAgreement
*/
public int getProofOfPossessionType()
{
return this.certReqMsg.getPopo().getType();
}
/**
* Return whether or not the proof-of-possession (POP) is of the type popSigningKey and
* it has a public key MAC associated with it.
*
* @return true if POP is popSigningKey and a PKMAC is present, false otherwise.
*/
public boolean hasSigningKeyProofOfPossessionWithPKMAC()
{
ProofOfPossession pop = certReqMsg.getPopo();
if (pop.getType() == popSigningKey)
{
POPOSigningKey popoSign = POPOSigningKey.getInstance(pop.getObject());
return popoSign.getPoposkInput().getPublicKeyMAC() != null;
}
return false;
}
/**
* Return whether or not a signing key proof-of-possession (POP) is valid.
*
* @param verifierProvider a provider that can produce content verifiers for the signature contained in this POP.
* @return true if the POP is valid, false otherwise.
* @throws CRMFException if there is a problem in verification or content verifier creation.
* @throws IllegalStateException if POP not appropriate.
*/
public boolean isValidSigningKeyPOP(ContentVerifierProvider verifierProvider)
throws CRMFException, IllegalStateException
{
ProofOfPossession pop = certReqMsg.getPopo();
if (pop.getType() == popSigningKey)
{
POPOSigningKey popoSign = POPOSigningKey.getInstance(pop.getObject());
if (popoSign.getPoposkInput() != null && popoSign.getPoposkInput().getPublicKeyMAC() != null)
{
throw new IllegalStateException("verification requires password check");
}
return verifySignature(verifierProvider, popoSign);
}
else
{
throw new IllegalStateException("not Signing Key type of proof of possession");
}
}
/**
* Return whether or not a signing key proof-of-possession (POP), with an associated PKMAC, is valid.
*
* @param verifierProvider a provider that can produce content verifiers for the signature contained in this POP.
* @param macBuilder a suitable PKMACBuilder to create the MAC verifier.
* @param password the password used to key the MAC calculation.
* @return true if the POP is valid, false otherwise.
* @throws CRMFException if there is a problem in verification or content verifier creation.
* @throws IllegalStateException if POP not appropriate.
*/
public boolean isValidSigningKeyPOP(ContentVerifierProvider verifierProvider, PKMACBuilder macBuilder, char[] password)
throws CRMFException, IllegalStateException
{
ProofOfPossession pop = certReqMsg.getPopo();
if (pop.getType() == popSigningKey)
{
POPOSigningKey popoSign = POPOSigningKey.getInstance(pop.getObject());
if (popoSign.getPoposkInput() == null || popoSign.getPoposkInput().getSender() != null)
{
throw new IllegalStateException("no PKMAC present in proof of possession");
}
PKMACValue pkMAC = popoSign.getPoposkInput().getPublicKeyMAC();
PKMACValueVerifier macVerifier = new PKMACValueVerifier(macBuilder);
if (macVerifier.isValid(pkMAC, password, this.getCertTemplate().getPublicKey()))
{
return verifySignature(verifierProvider, popoSign);
}
return false;
}
else
{
throw new IllegalStateException("not Signing Key type of proof of possession");
}
}
private boolean verifySignature(ContentVerifierProvider verifierProvider, POPOSigningKey popoSign)
throws CRMFException
{
ContentVerifier verifier;
try
{
verifier = verifierProvider.get(popoSign.getAlgorithmIdentifier());
}
catch (OperatorCreationException e)
{
throw new CRMFException("unable to create verifier: " + e.getMessage(), e);
}
if (popoSign.getPoposkInput() != null)
{
CRMFUtil.derEncodeToStream(popoSign.getPoposkInput(), verifier.getOutputStream());
}
else
{
CRMFUtil.derEncodeToStream(certReqMsg.getCertReq(), verifier.getOutputStream());
}
return verifier.verify(popoSign.getSignature().getOctets());
}
/**
* Return the ASN.1 encoding of the certReqMsg we wrap.
*
* @return a byte array containing the binary encoding of the certReqMsg.
* @throws IOException if there is an exception creating the encoding.
*/
public byte[] getEncoded()
throws IOException
{
return certReqMsg.getEncoded();
}
}