/**
   * Converts, if possible, a key specification into a {@link BCMcElieceCCA2PrivateKey}. Currently,
   * the following key specifications are supported: {@link McElieceCCA2PrivateKeySpec}, {@link
   * PKCS8EncodedKeySpec}.
   *
   * @param keySpec the key specification
   * @return the McEliece CCA2 private key
   * @throws InvalidKeySpecException if the KeySpec is not supported.
   */
  public PrivateKey generatePrivate(KeySpec keySpec) throws InvalidKeySpecException {
    if (keySpec instanceof McElieceCCA2PrivateKeySpec) {
      return new BCMcElieceCCA2PrivateKey((McElieceCCA2PrivateKeySpec) keySpec);
    } else if (keySpec instanceof PKCS8EncodedKeySpec) {
      // get the DER-encoded Key according to PKCS#8 from the spec
      byte[] encKey = ((PKCS8EncodedKeySpec) keySpec).getEncoded();

      // decode the PKCS#8 data structure to the pki object
      PrivateKeyInfo pki;

      try {
        pki = PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(encKey));
      } catch (IOException e) {
        throw new InvalidKeySpecException("Unable to decode PKCS8EncodedKeySpec: " + e);
      }

      try {
        // get the inner type inside the BIT STRING
        ASN1Primitive innerType = pki.parsePrivateKey().toASN1Primitive();

        // build and return the actual key
        ASN1Sequence privKey = (ASN1Sequence) innerType;

        // decode oidString (but we don't need it right now)
        String oidString = ((ASN1ObjectIdentifier) privKey.getObjectAt(0)).toString();

        // decode <n>
        BigInteger bigN = ((ASN1Integer) privKey.getObjectAt(1)).getValue();
        int n = bigN.intValue();

        // decode <k>
        BigInteger bigK = ((ASN1Integer) privKey.getObjectAt(2)).getValue();
        int k = bigK.intValue();

        // decode <fieldPoly>
        byte[] encFieldPoly = ((ASN1OctetString) privKey.getObjectAt(3)).getOctets();
        // decode <goppaPoly>
        byte[] encGoppaPoly = ((ASN1OctetString) privKey.getObjectAt(4)).getOctets();
        // decode <p>
        byte[] encP = ((ASN1OctetString) privKey.getObjectAt(5)).getOctets();
        // decode <h>
        byte[] encH = ((ASN1OctetString) privKey.getObjectAt(6)).getOctets();
        // decode <qInv>
        ASN1Sequence qSeq = (ASN1Sequence) privKey.getObjectAt(7);
        byte[][] encQInv = new byte[qSeq.size()][];
        for (int i = 0; i < qSeq.size(); i++) {
          encQInv[i] = ((ASN1OctetString) qSeq.getObjectAt(i)).getOctets();
        }

        return new BCMcElieceCCA2PrivateKey(
            new McElieceCCA2PrivateKeySpec(
                OID, n, k, encFieldPoly, encGoppaPoly, encP, encH, encQInv));

      } catch (IOException cce) {
        throw new InvalidKeySpecException("Unable to decode PKCS8EncodedKeySpec.");
      }
    }

    throw new InvalidKeySpecException("Unsupported key specification: " + keySpec.getClass() + ".");
  }
 public PrivateKey generatePrivate(PrivateKeyInfo pki) throws InvalidKeySpecException {
   // get the inner type inside the BIT STRING
   try {
     ASN1Primitive innerType = pki.parsePrivateKey().toASN1Primitive();
     McElieceCCA2PrivateKey key = McElieceCCA2PrivateKey.getInstance(innerType);
     return new BCMcElieceCCA2PrivateKey(
         key.getOID().getId(),
         key.getN(),
         key.getK(),
         key.getField(),
         key.getGoppaPoly(),
         key.getP(),
         key.getH(),
         key.getQInv());
   } catch (IOException cce) {
     throw new InvalidKeySpecException("Unable to decode PKCS8EncodedKeySpec");
   }
 }
  private PemObject createPemObject(Object o) throws IOException {
    String type;
    byte[] encoding;

    if (o instanceof PemObject) {
      return (PemObject) o;
    }
    if (o instanceof PemObjectGenerator) {
      return ((PemObjectGenerator) o).generate();
    }
    if (o instanceof X509Certificate) {
      type = "CERTIFICATE";
      try {
        encoding = ((X509Certificate) o).getEncoded();
      } catch (CertificateEncodingException e) {
        throw new PemGenerationException("Cannot encode object: " + e.toString());
      }
    } else if (o instanceof X509CRL) {
      type = "X509 CRL";
      try {
        encoding = ((X509CRL) o).getEncoded();
      } catch (CRLException e) {
        throw new PemGenerationException("Cannot encode object: " + e.toString());
      }
    } else if (o instanceof KeyPair) {
      return createPemObject(((KeyPair) o).getPrivate());
    } else if (o instanceof PrivateKey) {
      PrivateKeyInfo info =
          new PrivateKeyInfo((ASN1Sequence) ASN1Primitive.fromByteArray(((Key) o).getEncoded()));

      if (o instanceof RSAPrivateKey) {
        type = "RSA PRIVATE KEY";

        encoding = info.parsePrivateKey().toASN1Primitive().getEncoded();
      } else if (o instanceof DSAPrivateKey) {
        type = "DSA PRIVATE KEY";

        DSAParameter p = DSAParameter.getInstance(info.getPrivateKeyAlgorithm().getParameters());
        ASN1EncodableVector v = new ASN1EncodableVector();

        v.add(new DERInteger(0));
        v.add(new DERInteger(p.getP()));
        v.add(new DERInteger(p.getQ()));
        v.add(new DERInteger(p.getG()));

        BigInteger x = ((DSAPrivateKey) o).getX();
        BigInteger y = p.getG().modPow(x, p.getP());

        v.add(new DERInteger(y));
        v.add(new DERInteger(x));

        encoding = new DERSequence(v).getEncoded();
      } else if (((PrivateKey) o).getAlgorithm().equals("ECDSA")) {
        type = "EC PRIVATE KEY";

        encoding = info.parsePrivateKey().toASN1Primitive().getEncoded();
      } else {
        throw new IOException("Cannot identify private key");
      }
    } else if (o instanceof PublicKey) {
      type = "PUBLIC KEY";

      encoding = ((PublicKey) o).getEncoded();
    } else if (o instanceof X509AttributeCertificate) {
      type = "ATTRIBUTE CERTIFICATE";
      encoding = ((X509V2AttributeCertificate) o).getEncoded();
    } else if (o instanceof PKCS10CertificationRequest) {
      type = "CERTIFICATE REQUEST";
      encoding = ((PKCS10CertificationRequest) o).getEncoded();
    } else if (o instanceof ContentInfo) {
      type = "PKCS7";
      encoding = ((ContentInfo) o).getEncoded();
    } else {
      throw new PemGenerationException("unknown object passed - can't encode.");
    }

    return new PemObject(type, encoding);
  }
  private PemObject createPemObject(
      Object obj, String algorithm, char[] password, SecureRandom random) throws IOException {
    if (obj instanceof KeyPair) {
      return createPemObject(((KeyPair) obj).getPrivate(), algorithm, password, random);
    }

    String type = null;
    byte[] keyData = null;

    if (obj instanceof RSAPrivateCrtKey) {
      type = "RSA PRIVATE KEY";

      RSAPrivateCrtKey k = (RSAPrivateCrtKey) obj;

      org.bouncycastle.asn1.pkcs.RSAPrivateKey keyStruct =
          new org.bouncycastle.asn1.pkcs.RSAPrivateKey(
              k.getModulus(),
              k.getPublicExponent(),
              k.getPrivateExponent(),
              k.getPrimeP(),
              k.getPrimeQ(),
              k.getPrimeExponentP(),
              k.getPrimeExponentQ(),
              k.getCrtCoefficient());

      // convert to bytearray
      keyData = keyStruct.getEncoded();
    } else if (obj instanceof DSAPrivateKey) {
      type = "DSA PRIVATE KEY";

      DSAPrivateKey k = (DSAPrivateKey) obj;
      DSAParams p = k.getParams();
      ASN1EncodableVector v = new ASN1EncodableVector();

      v.add(new DERInteger(0));
      v.add(new DERInteger(p.getP()));
      v.add(new DERInteger(p.getQ()));
      v.add(new DERInteger(p.getG()));

      BigInteger x = k.getX();
      BigInteger y = p.getG().modPow(x, p.getP());

      v.add(new DERInteger(y));
      v.add(new DERInteger(x));

      keyData = new DERSequence(v).getEncoded();
    } else if (obj instanceof PrivateKey && "ECDSA".equals(((PrivateKey) obj).getAlgorithm())) {
      type = "EC PRIVATE KEY";

      PrivateKeyInfo privInfo =
          PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(((PrivateKey) obj).getEncoded()));

      keyData = privInfo.parsePrivateKey().toASN1Primitive().getEncoded();
    }

    if (type == null || keyData == null) {
      // TODO Support other types?
      throw new IllegalArgumentException("Object type not supported: " + obj.getClass().getName());
    }

    String dekAlgName = Strings.toUpperCase(algorithm);

    // Note: For backward compatibility
    if (dekAlgName.equals("DESEDE")) {
      dekAlgName = "DES-EDE3-CBC";
    }

    int ivLength = dekAlgName.startsWith("AES-") ? 16 : 8;

    byte[] iv = new byte[ivLength];
    random.nextBytes(iv);

    byte[] encData = PEMUtilities.crypt(true, provider, keyData, password, dekAlgName, iv);

    List headers = new ArrayList(2);

    headers.add(new PemHeader("Proc-Type", "4,ENCRYPTED"));
    headers.add(new PemHeader("DEK-Info", dekAlgName + "," + getHexEncoded(iv)));

    return new PemObject(type, headers, encData);
  }