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

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/20629.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论