LoginSignup
10
14

More than 5 years have passed since last update.

JavaのXMLデジタル署名APIを利用してXML署名

Last updated at Posted at 2016-09-16

署名プロセスのイメージ

JavaのXML デジタル署名APIを使用して XML署名を行う時のイメージです。

2016-09-16_155426.png

XML署名検証は、こちらを参照する。

必要なもの

  • 署名対象XML
  • 秘密鍵
  • サーバー証明書

署名対象XML

Product要素配下のProductInfoに対して署名を行う。

<?xml version="1.0" encoding="UTF-8"?>
<Product>
    <ProductInfo>
        <ProductId>PC001</ProductId>
        <ProductName>iPhone</ProductName>
        <ProductPrice>100,000</ProductPrice>
    </ProductInfo>
</Product>

署名手順

1.XML正規化変換処理用アルゴリズムの指定(Transforms要素)
署名対象のダイジェスト値を計算する前に、署名形式および正規化変換するためのアルゴリズムを指定する。
ここは、Enveloped署名(署名要素が署名対象要素の子要素となる署名形式)及びExclusive XML Canonicalization Version 1.0 (omit comments)正規化アルゴリズムを指定する。

ArrayList<Transform> refTransformList = new ArrayList<Transform>();
refTransformList.add(xmlSignFactory.newTransform(Transform.ENVELOPED, (TransformParameterSpec)null));
refTransformList.add(xmlSignFactory.newTransform(CanonicalizationMethod.EXCLUSIVE, (TransformParameterSpec)null));

2.ダイジェスト値計算用アルゴリズムの指定(DigestMethod要素)
署名対象の変換プロセス要素の適用後に、ダイジェスト値を計算するアルゴリズムを指定する。
ここは、SHA256アルゴリズムを指定する。

DigestMethod digestMethod = xmlSignFactory.newDigestMethod(DigestMethod.SHA256, null);

3.署名対象要素の親要素にID属性の作成

Element targetNode = (Element)xmlDom.getElementsByTagName("ProductInfo").item(0);
Element parentNode = (Element)targetNode.getParentNode();
Attr idAttr = xmlDom.createAttribute("id");
idAttr.setValue("ProductInfo");
parentNode.setAttributeNode(idAttr);
parentNode.setIdAttribute("id", true);

4.参照要素の作成(Reference要素)
参照要素(Reference element)を生成する。参照要素に署名対象識別子URI、ダイジェストアルゴリズム要素とダイジェスト値要素が含まれている。

Reference ref = xmlSignFactory.newReference("#ProductInfo", digestMethod, refTransformList, null, null);

参照要素のイメージは以下に示す。

<Reference URI="#Product">      
    <Transforms>    
        <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
        <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
    </Transforms>   
    <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />    
    <DigestValue>TJPbOFEZX36qckS2Y40rb36jzxe7xuyD1DS/dJmzzHc=</DigestValue> 
</Reference>        

5.正規化アルゴリズムの指定(CanonicalizationMethod要素)
署名情報要素の正規化に用いられるアルゴリズムを記述する正規化メソッド要素。
ここは、Exclusive XML Canonicalization Version 1.0 (omit comments)正規化アルゴリズムを指定する。

CanonicalizationMethod cm = xmlSignFactory.newCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec)null);

6.署名アルゴリズムの設定(SignatureMethod要素)
正規化された署名情報要素から署名値を計算するのに用いられるアルゴリズムを指定する。

SignatureMethod sm = xmlSignFactory.newSignatureMethod("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", null);

7.署名情報の作成(SignedInfo要素)
正規化アルゴリズム、署名アルゴリズム及び参照要素により、署名情報(SignedInfo)オブジェクトを作成する。

SignedInfo signedInfo = xmlSignFactory.newSignedInfo(cm, sm, Collections.singletonList(ref));

8.キー情報の作成(KeyInfo要素)
このオブジェクトには、受け側が署名の検証に必要な鍵を見つけられるようにする情報が含まれている。

8.1 証明書の読込
DERファイルから証明書を読み込む。

X509Certificate cert = getCertFromDER();    

private X509Certificate getCertFromDER() throws IOException, CertificateException { 
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    X509Certificate cert = (X509Certificate)cf.generateCertificate(new FileInputStream("./resources/server.crt"));
    return cert;
}   

8.2 オプションの KeyInfo オブジェクトを作成します。
上記の証明書オブジェクトにより、KeyInfoオブジェクトを作成する。

KeyInfoFactory kif = xmlSignFactory.getKeyInfoFactory();
X509Data x509Data = kif.newX509Data(Collections.singletonList(cert));
KeyInfo keyInfo = kif.newKeyInfo(Collections.singletonList(x509Data));

9.XMLSignature オブジェクトの作成
XMLSignature オブジェクトを作成し、先に作成した SignedInfo および KeyInfo オブジェクトをパラメータとして渡す。

XMLSignature signature = xmlSignFactory.newXMLSignature(signedInfo, keyInfo);

10.秘密鍵の読込
DERファイルから秘密鍵を読み込む。

PrivateKey privateKey = getPrivateKeyFromDER(); 

private PrivateKey getPrivateKeyFromDER() throws NoSuchAlgorithmException, IOException, InvalidKeySpecException {   
    byte[] key = readkeyFile("./resources/private_key.pk8");
    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(key);

    KeyFactory kf = KeyFactory.getInstance("RSA"); 
    RSAPrivateKey privateKey = (RSAPrivateKey)kf.generatePrivate(keySpec);
    return privateKey;
}

11.署名コンテキストの作成

DOMSignContext dsc = new DOMSignContext(privateKey, parentNode);

12.署名を行う。

signature.sign(dsc);

署名後XMLのイメージ

<?xml version="1.0" encoding="UTF-8" standalone="no"?>                      
<ProductInfo id="Product">
    <Product>
        <ProductId>PC001</ProductId>
        <ProductName>iPhone</ProductName>
        <ProductPrice>100,000</ProductPrice>
    </Product>
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
        <SignedInfo>
            <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
            <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
            <Reference URI="#Product">
                <Transforms>
                    <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                    <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                </Transforms>
                <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
                <DigestValue>TJPbOFEZX36qckS2Y40rb36jzxe7xuyD1DS/dJmzzHc=</DigestValue>
            </Reference>
        </SignedInfo>
        <SignatureValue>E+H+n32RWiySjUEw9Dtf0c2D4mRjcm4/B2uezqDU+VXyY7qY7NOyX1HREUm62vJo8yJl7SWazx5Z
            lMYZ19iTADIOzwEPScx1CV98rRkIeYjssKF/6U4Ygu/vHw3Upooe8nV4xZxW+fwxJ389i8MSfg/9
            Y7rLeN1JHQCQiTMhS2VTEvzdzaeaERCTXam1CR5W75T+lLQe7vNc3fLXRLVL5RY9hbxrAXddwWPG
            LKvoTzpfWGJi77qjcDuxY0xV3dXzydptuqHITGIw4s4Q5lwDL1kqByKPf2moQntNAxE7vJ58oavH
            81xdWZpdHXnIIbzzXq4w0FdYW2sWZ9YkhIFf8Q==
        </SignatureValue>
        <KeyInfo>
            <X509Data>
                <X509Certificate>MIID+jCCAuICCQDRm9FEjBNE3TANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCanAxDjAMBgNV
                    BAgMBVRva3lvMQ4wDAYDVQQHDAVUb2t5bzEcMBoGA1UECgwTSmFwYW5lc2UgR292ZXJubWVudDE4
                    MDYGA1UECwwvTWluaXN0cnkgb2YgSW50ZXJuYWwgQWZmYWlycyBhbmQgQ29tbXVuaWNhdGlvbnMx
                    GTAXBgNVBAMMEDA3MTAxMDAwMDAwMDE3MDAxHDAaBgkqhkiG9w0BCQEWDXRlc3RAdGVzdC5jb20w
                    HhcNMTYwOTE0MDU1OTIyWhcNMTYxMDE0MDU1OTIyWjCBvjELMAkGA1UEBhMCanAxDjAMBgNVBAgM
                    BVRva3lvMQ4wDAYDVQQHDAVUb2t5bzEcMBoGA1UECgwTSmFwYW5lc2UgR292ZXJubWVudDE4MDYG
                    A1UECwwvTWluaXN0cnkgb2YgSW50ZXJuYWwgQWZmYWlycyBhbmQgQ29tbXVuaWNhdGlvbnMxGTAX
                    BgNVBAMMEDA3MTAxMDAwMDAwMDE3MDAxHDAaBgkqhkiG9w0BCQEWDXRlc3RAdGVzdC5jb20wggEi
                    MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCl9X4r6sAmb7p6vPGS2mKIlChMe87xjHuF0Z6s
                    VfmU5cfUEfaTnKY2Wte2++T4EDdbTqkza1VtlA3gsuoAsnbnpSxakY34h8UIUn5MpiXE3IViAFx4
                    pGi1Qy3/J+6CEfhSpiLpFHksVY5ruSUD+R9u7crDu/+8nRaMDzofQYxL5andTi5GBT7V62OINijl
                    8Tte6YtoNZSMhtDhJDwolTzUu6/RAsJu1Ybpo+yX49yIyEYJIK8eDfzxiGT+o0yA1UKNEnT3eymV
                    WW17fLfixEcCFRTFvBfWTklXB7YKn01U/5kc6A5Bsb/VXloPoWKUsqf6viFOAcXJS+iEAgSXtfmp
                    AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAB499x9IHQUdEYbj1b95y2t/qxYHLA7mOYuH+YRdPYGr
                    v53zw0gR2bSWfrJOkiaD2JKAG9Vf44xtQOz835SlENWRGMLo19FfnDo1nik4oW6SBxANya7XsX1L
                    zI6PAgdacvmvNCcqwjtdf4jLpIE4epvUVoEQo47S7baxZs97JiJOEl1glxzz5p5JOrDmpgfrUq/+
                    d1PyKzTePmAESjXGL+04oPmoYNg0KkVQvcBCQDAtNp/p1jaVtgT3K2KqlvSBehfY8xJR48M0VRWD
                    rEW1hgtNls/X4084VSND9UQWTCc98dmgzz1cXHx0u0aahwT9leCwxi1HQEO+NuFQdQwRaWY=
                </X509Certificate>
            </X509Data>
        </KeyInfo>
    </Signature>
</ProductInfo>

XMLデジタル署名

以下は上記署名手順のまとめです。

/**
 * XML署名
 * 
 * @param xmlDom
 */
public void signature(Document xmlDom) {

    try {
        // 秘密鍵の取得
        PrivateKey privateKey = getPrivateKeyFromDER();
        // 証明書の取得
        X509Certificate cert = getCertFromDER();

        // 対象要素の親要素にID属性を付与する
        Element targetNode = (Element)xmlDom.getElementsByTagName("ProductInfo").item(0);
        Element parentNode = (Element)targetNode.getParentNode();
        Attr idAttr = xmlDom.createAttribute("id");
        idAttr.setValue("ProductInfo");
        parentNode.setAttributeNode(idAttr);
        parentNode.setIdAttribute("id", true);

        // 署名情報の設定
        XMLSignatureFactory xmlSignFactory = XMLSignatureFactory.getInstance("DOM");

        // 変換アルゴリズムの作成
        ArrayList<Transform> refTransformList = new ArrayList<Transform>();
        refTransformList.add(xmlSignFactory.newTransform(Transform.ENVELOPED, (TransformParameterSpec)null));
        refTransformList.add(xmlSignFactory.newTransform(CanonicalizationMethod.EXCLUSIVE, (TransformParameterSpec)null));

        // ダイジェスト計算アルゴリズムの生成
        DigestMethod digestMethod = xmlSignFactory.newDigestMethod(DigestMethod.SHA256, null);

        // 参照要素の設定
        Reference ref = xmlSignFactory.newReference("#ProductInfo", 
                digestMethod, 
                refTransformList, null, null);

        // 正規化アルゴリズムの生成
        CanonicalizationMethod cm = xmlSignFactory.newCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec)null);

        // 署名アルゴリズムの生成
        SignatureMethod sm = xmlSignFactory.newSignatureMethod("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", null);

        // 署名情報の設定
        SignedInfo signedInfo = xmlSignFactory.newSignedInfo(cm, sm, Collections.singletonList(ref));

        KeyInfoFactory kif = xmlSignFactory.getKeyInfoFactory();
        X509Data x509Data = kif.newX509Data(Collections.singletonList(cert));
        KeyInfo keyInfo = kif.newKeyInfo(Collections.singletonList(x509Data));

        // 署名対象と秘密鍵の設定
        DOMSignContext dsc = new DOMSignContext(privateKey, parentNode);

        // XML署名の実施
        XMLSignature signature = xmlSignFactory.newXMLSignature(signedInfo, keyInfo);
        signature.sign(dsc);

        // コンソールに結果を表示する。
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer trans = tf.newTransformer();
        trans.transform(new DOMSource(xmlDom), new StreamResult(System.out));

    } catch (Exception e) {
        e.printStackTrace();
    }

}

/**
 * 秘密鍵の読込
 * 
 * @return
 * @throws NoSuchAlgorithmException
 * @throws IOException
 * @throws InvalidKeySpecException
 */
private PrivateKey getPrivateKeyFromDER() throws NoSuchAlgorithmException, IOException, InvalidKeySpecException {

    byte[] key = readkeyFile("./resources/private_key.pk8");
    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(key);

    KeyFactory kf = KeyFactory.getInstance("RSA"); 
    RSAPrivateKey privateKey = (RSAPrivateKey)kf.generatePrivate(keySpec);
    return privateKey;
}

/**
 * サーバー証明書の読込
 * 
 * @return
 * @throws IOException
 * @throws CertificateException
 */
private X509Certificate getCertFromDER() throws IOException, CertificateException {

    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    X509Certificate cert = (X509Certificate)cf.generateCertificate(new FileInputStream("./resources/server.crt"));

    return cert;
}

/**
 * バイナリファイルの読込
 * 
 * @param fileName
 * @return
 * @throws IOException
 */
private byte[] readkeyFile(String fileName) throws IOException {
    byte[] data = null;
    FileInputStream input = new FileInputStream(fileName);
    data = new byte[input.available()];
    input.read(data);
    input.close();

    return data;
}
10
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
14