XML-Signature 語法和簽名

 

一段  XML-signature  的 demo:java

<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
        <!-- 規範化的算法 -->
        <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
        <!-- 簽名算法:rsa-sha1 -->
        <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
        <!-- 肯定待簽名的引用資源 -->
        <Reference URI="#_d71a3a8e9fcc45c9e9d248ef7049393fc8f04e5f75">
        <!-- 轉換算法 -->
            <Transforms>
                <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
            </Transforms>
            <!-- 消息摘要的算法,好比:SHA1 -->
            <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
            <!-- 消息摘要 -->
            <DigestValue>k9i4QGA5BDFkW5I+Igr8hR1ibZs=</DigestValue>
        </Reference>
    </SignedInfo>
    <!--  簽名內容 -->
    <SignatureValue>
        PLIKGZOFbMt8qEM1yw6f/Uct7R9Xd8KWZXw5925gIJdA8+q9MfY34+sQwDcy1Tqnxzak6hx6A6olQr+zJCQH8O/S+sDgCEUhXG+PFU4j2pxnnYqwI3jKc2yeT7A7f8ShStgwN7IgjZ0TFLx2TO3tlZ76 2GwFHNN0lH9ohtAv8Zs=
    </SignatureValue>
    <!-- KeyInfo 元素可選,代表簽名的公鑰信息 -->
    <KeyInfo>
        <X509Data>
            <X509Certificate>
            MIICaDCCAdGgAaIBAgIEfnIVCzANBgkqhkiG9w0BAQsFADBnMQ4wDAYDVQQGEwVjaGluYTEQMA4G A1UECBMHYmVpamluZzEQMA4GA1UEBxMHYmVpamluZzERMA8GA1UEChMIYmV3aW5uZXIxDTALBgNV BAsTBGJ3MzAxDzANBgNVBAMTBnhpYW9zeTAeFw0xNzExMDgwOTU1NDVaFw0yNzExMDYwOTU1NDVa MGcxDjAMBgNVBAYTBWNoaW5hMRAwDgYDVQQIEwdiZWlqaW5nMRAwDgYDVQQHEwdiZWlqaW5nMREw DwYDVQQKEwhiZXdpbm5lcjENMAsGA1UECxMEYnczMDEPMA0GA1UEAxMGeGlhb3N5MIGfMA0GCSqG SIb3DQEBAQUAA4GNADCBiQKBgQCgmrEMgAMY7zygYqBtYzMal0vTVsQNyjGkD3tbA+pEk18YfN13 UEBoqrp/XQiR4v334xqHjdtG8lxDzEUJ4fQippxMpw6Fab45pz6uOr33DI6X3IwLPxtb7q1MyIj3 TXBY6R01rwIaE+G8/5z76mN5qq4/lhoY3bs0D06pwUSSSQIDAQABoyEwHzAdBgNVHQ4EFgQURAyK 5AjoSEOk32ceEloftZ8TiWcwDQYJKoZIhvcNAQELBQADgYEAZuNWxMO8HOItqAoCI8f6+PfjbL/7 xTwDjs8PxnermmVjACx5JiW0O98M0D5Guo0OABf8mMxiDYQvRwpNoEfMOXr3TjPxqioLMq+s1Nt8 0Duilqel+O6Q/XDJ8rlVdm8vPhLxWZ14FIdI8n7CuuUwUExe4Uj05shCMwgNRo6bmaU=
            </X509Certificate>
        </X509Data>
    </KeyInfo>
</Signature>

 

XML-signature 中的簽名算法就是對  XML文檔樹 進行簽名,說明以下:
一、確認簽名內容,經過 URL 將這些內容表示爲引用資源,用 Reference 標識。對於斷言信息來講,其 URI 是saml:Assertion的 ID
二、對待簽名的數據進行轉化處理,包括執行編碼規則、規範化算法等, Transform 指定了轉化的算法
三、對整個斷言進行消息摘要, DigestMethod 指定了消息摘要算法,消息摘要的結果保存在 DigestValue 元素中
四、構造包含 Reference 的 SignedInfo 元素
五、 CanonicalizationMethod 元素指定了規範化的算法,若是不對其進行規範化處理,驗證 xml 簽名時可能由於xml 結構表示不一樣而失敗
六、計算 SignedInfo 的摘要,使用 SignatureMethod 聲明的簽名算法,並對其進行簽名,結果保存到 SignatureValue 元素中
七、 KeyInfo 元素可選,代表簽名的公鑰信息算法

 

下面這個工具類實現了 XML-signature 的簽名和驗籤dom

import java.io.StringWriter;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.PublicKey;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
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.dom.DOMValidateContext;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import lombok.Cleanup;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.io.DOMWriter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;


@UtilityClass
@Slf4j
public class SignUtils {

  private static final XMLSignatureFactory XML_SIGNATURE_FACTORY = XMLSignatureFactory.getInstance("DOM");

  private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance();

  @SneakyThrows
  public static Reference getTransformReference(String uri) {
    DigestMethod digestMethod = XML_SIGNATURE_FACTORY.newDigestMethod(DigestMethod.SHA1, null);
    List<Transform> transforms = Collections.singletonList(XML_SIGNATURE_FACTORY.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null));
    return XML_SIGNATURE_FACTORY.newReference("#" + uri, digestMethod, transforms, null, null);
  }

  @SneakyThrows
  public static SignedInfo getSignedInfo(Reference ref) {
    CanonicalizationMethod method = XML_SIGNATURE_FACTORY.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null);
    SignatureMethod signatureMethod = XML_SIGNATURE_FACTORY.newSignatureMethod(SignatureMethod.RSA_SHA1, null);
    return XML_SIGNATURE_FACTORY.newSignedInfo(method, signatureMethod, Collections.singletonList(ref));
  }

  @SneakyThrows
  public static String sign(Document w3cDoc, PrivateKeyEntry keyEntry, SignedInfo si) {
 Optional.ofNullable(w3cDoc) .map(Document::getDocumentElement) .map(Node::getFirstChild) .map(Node::getFirstChild) .ifPresent(e -> ((Element) e).setIdAttribute("id", true));
    DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), Optional.ofNullable(w3cDoc).map(Document::getDocumentElement).get());
    XMLSignature signature = XML_SIGNATURE_FACTORY.newXMLSignature(si, null);
    signature.sign(dsc);
    @Cleanup StringWriter strWriter = new StringWriter();
    TRANSFORMER_FACTORY.newTransformer().transform(new DOMSource(w3cDoc), new StreamResult(strWriter));
    return strWriter.toString();
  }

  @SneakyThrows
  public static boolean checkSign(Node signNode, PublicKey publicKey) {
 Optional.ofNullable(signNode.getPreviousSibling()).ifPresent(e -> ((Element) e).setIdAttribute("id", true));     DOMValidateContext valContext = new DOMValidateContext(publicKey, signNode);
    XMLSignature signature = XML_SIGNATURE_FACTORY.unmarshalXMLSignature(valContext);
    return signature.validate(valContext);
  }

  @SneakyThrows
  public static org.w3c.dom.Document convert(org.dom4j.Document doc) {
    log.info("org.dom4j.Document,outXml:[{}]", doc.asXML());
    return new DOMWriter().write(doc);
  }

}

 

下面這個類實現了公、私鑰證書的加載:ide

import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.concurrent.atomic.AtomicBoolean;
import lombok.Cleanup;
import lombok.SneakyThrows;


public final class X509CertificationLoader implements CertificationLoader {

  public static final CertificationLoader INSTANCE = new X509CertificationLoader();

  private PrivateKeyEntry privateKeyEntry;
  private PublicKey publicKey;

  private AtomicBoolean isPrivateKeyInitiated = new AtomicBoolean(false);
  private AtomicBoolean isPublicKeyInitiated = new AtomicBoolean(false);

  private X509CertificationLoader() {
  }

  @Override
  @SneakyThrows
  public PrivateKeyEntry getPrivateKey(CertInfo certInfo) {
    if (null != privateKeyEntry) {
      return privateKeyEntry;
    }
    if (isPrivateKeyInitiated.compareAndSet(false, true)) {
      KeyStore ks = KeyStore.getInstance(KEY_STORE_TYPE);
      @Cleanup InputStream fileInputStream = new FileInputStream(certInfo.getPrivateKeyFile());
      final char[] passwdChars = certInfo.getPasswd().toCharArray();
      ks.load(fileInputStream, passwdChars);
      privateKeyEntry = (PrivateKeyEntry) ks.getEntry(ks.aliases().nextElement(), new KeyStore.PasswordProtection(passwdChars));
    }
    return privateKeyEntry;
  }

  @Override
  @SneakyThrows
  public PublicKey getPublicKey(CertInfo certInfo) {
    if (null != publicKey) {
      return publicKey;
    }
    if (isPublicKeyInitiated.compareAndSet(false, true)) {
      @Cleanup InputStream fin = new FileInputStream(certInfo.getPublicKeyFile());
      CertificateFactory f = CertificateFactory.getInstance(CERTIFICATE_TYPE_NAME);
      X509Certificate certificate = (X509Certificate) f.generateCertificate(fin);
      publicKey = certificate.getPublicKey();
    }
    return publicKey;
  }

}

 

import java.security.KeyStore.PrivateKeyEntry;
import java.security.PublicKey;

public interface CertificationLoader {

  String KEY_STORE_TYPE = "PKCS12";

  String CERTIFICATE_TYPE_NAME = "X.509";

  /**
   * 獲取包含私鑰證書的 KeyStore
   *
   * @param certInfo 證書文件信息 {@link CertInfo}
   * @return KeyStore {@link PrivateKeyEntry}
   */
  PrivateKeyEntry getPrivateKey(CertInfo certInfo);

  /**
   * 獲取 X509 格式的公鑰證書
   *
   * @param certInfo 證書文件信息 {@link CertInfo}
   * @return 公鑰證書 {@link PublicKey}
   */
  PublicKey getPublicKey(CertInfo certInfo);

}
相關文章
相關標籤/搜索