package org.bouncycastle.jce.netscape;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;

import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DERIA5String;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;

Handles NetScape certificate request (KEYGEN), these are constructed as:
  SignedPublicKeyAndChallenge ::= SEQUENCE {
    publicKeyAndChallenge    PublicKeyAndChallenge,
    signatureAlgorithm       AlgorithmIdentifier,
    signature                BIT STRING
  }
PublicKey's encoded-format has to be X.509.
/** * * * Handles NetScape certificate request (KEYGEN), these are constructed as: * <pre> * SignedPublicKeyAndChallenge ::= SEQUENCE { * publicKeyAndChallenge PublicKeyAndChallenge, * signatureAlgorithm AlgorithmIdentifier, * signature BIT STRING * } * </pre> * * PublicKey's encoded-format has to be X.509. * **/
public class NetscapeCertRequest extends ASN1Object { AlgorithmIdentifier sigAlg; AlgorithmIdentifier keyAlg; byte sigBits []; String challenge; DERBitString content; PublicKey pubkey ; private static ASN1Sequence getReq( byte[] r) throws IOException { ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(r)); return ASN1Sequence.getInstance(aIn.readObject()); } public NetscapeCertRequest( byte[] req) throws IOException { this(getReq(req)); } public NetscapeCertRequest (ASN1Sequence spkac) { try { // // SignedPublicKeyAndChallenge ::= SEQUENCE { // publicKeyAndChallenge PublicKeyAndChallenge, // signatureAlgorithm AlgorithmIdentifier, // signature BIT STRING // } // if (spkac.size() != 3) { throw new IllegalArgumentException("invalid SPKAC (size):" + spkac.size()); } sigAlg = AlgorithmIdentifier.getInstance(spkac.getObjectAt(1)); sigBits = ((DERBitString)spkac.getObjectAt(2)).getOctets(); // // PublicKeyAndChallenge ::= SEQUENCE { // spki SubjectPublicKeyInfo, // challenge IA5STRING // } // ASN1Sequence pkac = (ASN1Sequence)spkac.getObjectAt(0); if (pkac.size() != 2) { throw new IllegalArgumentException("invalid PKAC (len): " + pkac.size()); } challenge = ((DERIA5String)pkac.getObjectAt(1)).getString(); //this could be dangerous, as ASN.1 decoding/encoding //could potentially alter the bytes content = new DERBitString(pkac); SubjectPublicKeyInfo pubkeyinfo = SubjectPublicKeyInfo.getInstance(pkac.getObjectAt(0)); X509EncodedKeySpec xspec = new X509EncodedKeySpec(new DERBitString( pubkeyinfo).getBytes()); keyAlg = pubkeyinfo.getAlgorithm(); pubkey = KeyFactory.getInstance(keyAlg.getAlgorithm().getId(), "BC") .generatePublic(xspec); } catch (Exception e) { throw new IllegalArgumentException(e.toString()); } } public NetscapeCertRequest( String challenge, AlgorithmIdentifier signing_alg, PublicKey pub_key) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException { this.challenge = challenge; sigAlg = signing_alg; pubkey = pub_key; ASN1EncodableVector content_der = new ASN1EncodableVector(); content_der.add(getKeySpec()); //content_der.add(new SubjectPublicKeyInfo(sigAlg, new RSAPublicKeyStructure(pubkey.getModulus(), pubkey.getPublicExponent()).getDERObject())); content_der.add(new DERIA5String(challenge)); try { content = new DERBitString(new DERSequence(content_der)); } catch (IOException e) { throw new InvalidKeySpecException("exception encoding key: " + e.toString()); } } public String getChallenge() { return challenge; } public void setChallenge(String value) { challenge = value; } public AlgorithmIdentifier getSigningAlgorithm() { return sigAlg; } public void setSigningAlgorithm(AlgorithmIdentifier value) { sigAlg = value; } public AlgorithmIdentifier getKeyAlgorithm() { return keyAlg; } public void setKeyAlgorithm(AlgorithmIdentifier value) { keyAlg = value; } public PublicKey getPublicKey() { return pubkey; } public void setPublicKey(PublicKey value) { pubkey = value; } public boolean verify(String challenge) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, NoSuchProviderException { if (!challenge.equals(this.challenge)) { return false; } // // Verify the signature .. shows the response was generated // by someone who knew the associated private key // Signature sig = Signature.getInstance(sigAlg.getAlgorithm().getId(), "BC"); sig.initVerify(pubkey); sig.update(content.getBytes()); return sig.verify(sigBits); } public void sign(PrivateKey priv_key) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, NoSuchProviderException, InvalidKeySpecException { sign(priv_key, null); } public void sign(PrivateKey priv_key, SecureRandom rand) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, NoSuchProviderException, InvalidKeySpecException { Signature sig = Signature.getInstance(sigAlg.getAlgorithm().getId(), "BC"); if (rand != null) { sig.initSign(priv_key, rand); } else { sig.initSign(priv_key); } ASN1EncodableVector pkac = new ASN1EncodableVector(); pkac.add(getKeySpec()); pkac.add(new DERIA5String(challenge)); try { sig.update(new DERSequence(pkac).getEncoded(ASN1Encoding.DER)); } catch (IOException ioe) { throw new SignatureException(ioe.getMessage()); } sigBits = sig.sign(); } private ASN1Primitive getKeySpec() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ASN1Primitive obj = null; try { baos.write(pubkey.getEncoded()); baos.close(); ASN1InputStream derin = new ASN1InputStream( new ByteArrayInputStream(baos.toByteArray())); obj = derin.readObject(); } catch (IOException ioe) { throw new InvalidKeySpecException(ioe.getMessage()); } return obj; } public ASN1Primitive toASN1Primitive() { ASN1EncodableVector spkac = new ASN1EncodableVector(); ASN1EncodableVector pkac = new ASN1EncodableVector(); try { pkac.add(getKeySpec()); } catch (Exception e) { //ignore } pkac.add(new DERIA5String(challenge)); spkac.add(new DERSequence(pkac)); spkac.add(sigAlg); spkac.add(new DERBitString(sigBits)); return new DERSequence(spkac); } }