Создайте PrivateKey и PublicKey с массивом байтов, закодированным в базе 64.

У меня есть byte[] с моим закрытым ключом (llave) и byte[] с моим открытым ключом (certificado=.

Мне нужно преобразовать эти два значения в объект PrivateKey и PublicKey (соответственно), чтобы использовать их позже для цифровой подписи XML-документа.

Я пробовал с KeyStore, и у меня не получилось. Я также пробовал использовать KeyFactory со следующим кодом:

import java.io.StringWriter;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Collections;

import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.DigestMethod;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.SignedInfo;
import javax.xml.crypto.dsig.Transform;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.KeyValue;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class GeneraXMLCancelacion {
public static void main (String [] args) {
    byte[] certificado = args[0].getBytes();
    byte[] llave = args[1].getBytes();
    String uuids = args[2];
    String rfc = args[3];
    String fecha = args[4];

    String [] uuid = uuids.split("/");

    //GENERAR XML
    try {
        //Crear un document XML vacío
        DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
        dbfac.setNamespaceAware(true);
        DocumentBuilder docBuilder;
        docBuilder = dbfac.newDocumentBuilder();
        Document doc = docBuilder.newDocument();
        doc.setXmlVersion("1.0");
        doc.setXmlStandalone(true);

        Element cancelacion = doc.createElementNS("http://cancelacfd.sat.gob.mx","Cancelacion");
        cancelacion.setAttribute("RfcEmisor", rfc);
        cancelacion.setAttribute("Fecha", fecha);
        doc.appendChild(cancelacion);

        Element folios = doc.createElementNS("http://cancelacfd.sat.gob.mx", "Folios");
        cancelacion.appendChild(folios);
        for (int i=0; i<uuid.length; i++) {
            Element u = doc.createElementNS("http://cancelacfd.sat.gob.mx","UUID");
            u.setTextContent(uuid[i]);
            folios.appendChild(u);
        }

        //create a keyfactory - use whichever algorithm and provider
        KeyFactory kf = KeyFactory.getInstance("DSA");
        //for private keys use PKCS8EncodedKeySpec; for public keys use X509EncodedKeySpec
        PKCS8EncodedKeySpec ks = new PKCS8EncodedKeySpec(llave);
        PrivateKey privateKey = kf.generatePrivate(ks);
        X509EncodedKeySpec x = new X509EncodedKeySpec(certificado);
        PublicKey publicKey = kf.generatePublic(x);


        DOMSignContext dsc = new DOMSignContext (privateKey, doc.getDocumentElement()); 
        XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");

        Reference ref = fac.newReference ("", fac.newDigestMethod(DigestMethod.SHA1, null), 
                    Collections.singletonList
                    (fac.newTransform(Transform.ENVELOPED,
                    (TransformParameterSpec) null)), null, null);
        KeyInfoFactory kif = fac.getKeyInfoFactory();
        KeyValue kv = kif.newKeyValue(publicKey);
        KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv)); 

        SignedInfo si = fac.newSignedInfo
                  (fac.newCanonicalizationMethod
                    (CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
                      (C14NMethodParameterSpec) null),
                    fac.newSignatureMethod(SignatureMethod.DSA_SHA1, null),
                    Collections.singletonList(ref)); 

        KeyInfoFactory kif2 = fac.getKeyInfoFactory();
        KeyValue kv2 = kif2.newKeyValue(publicKey);
        KeyInfo ki2 = kif.newKeyInfo(Collections.singletonList(kv)); 

        XMLSignature signature = fac.newXMLSignature(si, ki);

        signature.sign(dsc);

        //IMPRIMIR EL DOCUMENTO XML
        TransformerFactory transfac = TransformerFactory.newInstance();
        Transformer trans = transfac.newTransformer();
        trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
        trans.setOutputProperty(OutputKeys.VERSION, "1.0");
        trans.setOutputProperty(OutputKeys.INDENT, "yes");

        //CREAR STRING DEL ARBOL XML
        StringWriter sw = new StringWriter();
        StreamResult result = new StreamResult(sw);
        DOMSource source = new DOMSource(doc);
        trans.transform(source, result);
        String xmlString = sw.toString();
        System.out.println(xmlString);
    } catch (ParserConfigurationException e) {
        e.printStackTrace();
    } catch (TransformerConfigurationException e) {
        e.printStackTrace();
    } catch (TransformerException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }

}
}

При запуске программы я получаю эту ошибку:

java.security.spec.InvalidKeySpecException: Inappropriate key specification: invalid key format
at sun.security.provider.DSAKeyFactory.engineGeneratePrivate(Unknown Source)
at java.security.KeyFactory.generatePrivate(Unknown Source)
at vital.cancelaciones.GeneraXMLCancelacion.main(GeneraXMLCancelacion.java:85)

Строка 85 соответствует:

PrivateKey privateKey = kf.generatePrivate(ks);

Если у кого-то есть лучшее решение, я был бы признателен за вашу помощь.


person user1084509    schedule 09.12.2011    source источник
comment
Какой код находится в строке 85 вашего файла GeneraXMLCancelacion.java? Можете ли вы добавить SSCCE в свое сообщение?   -  person MockerTim    schedule 10.12.2011
comment
Я уже отредактировал код, чтобы включить все это   -  person user1084509    schedule 10.12.2011
comment
Вы должны добавить весь импорт в свой код. Без них трудно догадаться, какие классы вы используете.   -  person MockerTim    schedule 10.12.2011
comment
Попробуйте закомментировать строку 85. После этого компиляция прошла успешно?   -  person MockerTim    schedule 10.12.2011
comment
Попробуйте использовать X509EncodedKeySpec вместо PKCS8EncodedKeySpec.   -  person MockerTim    schedule 10.12.2011
comment
Я получаю ту же ошибку. Если я прокомментирую privateKey, ошибка появится и в PublicKey, и программа не сможет продолжить работу, потому что ей нужны эти два объекта для выполнения цифровой подписи.   -  person user1084509    schedule 10.12.2011
comment
В порядке. Теперь я могу скомпилировать ваш код. С каким параметром вы его запускаете?   -  person MockerTim    schedule 10.12.2011
comment
А как же DSAPrivateKeySpec? См. этот список спецификаций.   -  person MockerTim    schedule 10.12.2011
comment
args[0] --› сертификат (открытый ключ) в базе 64 / args[1] --› закрытый ключ в базе 64 / args[2] --› BD6CA3B1-E565-4985-88A9-694A6DD48448 / args[3] --› не имеет значения / args[4] --› Дата (гггг-ММ-ддЧч:мм:сс)   -  person user1084509    schedule 10.12.2011
comment
Я только что понял, что мои закрытый и открытый ключи закодированы с использованием ASN1 DER. Любые идеи о том, как я могу преобразовать его в PrivateKey и PublicKey в Java?   -  person user1084509    schedule 10.12.2011
comment
Проверьте how-do-i-decode-a- поток der-encoded-string-in-java.   -  person MockerTim    schedule 10.12.2011


Ответы (1)


У меня есть аналогичный код, который, как я знаю, работает, и он похож на ваш:

 public KeyPair createKeyPair(byte[] encodedPrivateKey, byte[] encodedPublicKey) {
    try {
        EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
        KeyFactory generator = KeyFactory.getInstance(ASYM_ALGORITHM);
        PrivateKey privateKey = generator.generatePrivate(privateKeySpec);

        EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedPublicKey);
        PublicKey publicKey = generator.generatePublic(publicKeySpec);
        return new KeyPair(publicKey, privateKey);
    } catch (Exception e) {
        throw new IllegalArgumentException("Failed to create KeyPair from provided encoded keys", e);
    }
}

Я думаю, что массив байтов для ключа неверен. Как вы передаете его в основной метод?

person yossis    schedule 09.12.2011
comment
Массив байтов хранится в базе данных, и я получаю из него эти записи, передаю их в основной метод и пытаюсь создать ключи. Я использовал те же строки в Visual Basic, и алгоритмы шифрования работают нормально. Я не знаю, почему Java не считает его допустимым массивом байтов... - person user1084509; 10.12.2011
comment
В этом случае, возможно, строка закодирована в другом наборе символов. Попробуйте использовать явный набор символов, чтобы превратить массив байтов в строку. Например args[1].getBytes("UTF8"); - person yossis; 11.12.2011