• XML-Signature 语法和签名


    一段  XML-signature  的 demo:

    <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文档树 进行签名,说明如下:
    1、确认签名内容,通过 URL 将这些内容表示为引用资源,用 Reference 标识。对于断言信息来说,其 URI 是saml:Assertion的 ID
    2、对待签名的数据进行转化处理,包括执行编码规则、规范化算法等, Transform 指定了转化的算法
    3、对整个断言进行消息摘要, DigestMethod 指定了消息摘要算法,消息摘要的结果保存在 DigestValue 元素中
    4、构造包含 Reference 的 SignedInfo 元素
    5、 CanonicalizationMethod 元素指定了规范化的算法,如果不对其进行规范化处理,验证 xml 签名时可能因为xml 结构表示不同而失败
    6、计算 SignedInfo 的摘要,使用 SignatureMethod 声明的签名算法,并对其进行签名,结果保存到 SignatureValue 元素中
    7、 KeyInfo 元素可选,表明签名的公钥信息

    下面这个工具类实现了 XML-signature 的签名和验签

    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);
      }
    
    }

    下面这个类实现了公、私钥证书的加载:

    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);
    
    }
  • 相关阅读:
    下载文件总结
    用PHP,怎么获取PHP.ini中的文件上传最大的字节数。也就是默认的2M
    TP框架下载功能 - 不想下天桥 - 博客园
    CSS,font-family,好看常用的中文字体
    安装Wamp后 Apache无法启动的解决方法
    :nth-child(n)
    list-style
    netbeans常用快捷键
    ideaic快捷键
    配置nutch
  • 原文地址:https://www.cnblogs.com/frankyou/p/11187616.html
Copyright © 2020-2023  润新知