cn.teleinfo.idpointer.sdk.core.HandleSignature Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of id-pointer-sdk Show documentation
Show all versions of id-pointer-sdk Show documentation
基于Java语言开发的工业互联网标识解析体系客户端软件开发工具包,应用通过集成 id-pointer-sdk,快速对接标识解析、标识注册、标识维护等功能服务。
The newest version!
/**********************************************************************\
© COPYRIGHT 2019 Corporation for National Research Initiatives (CNRI);
All rights reserved.
The HANDLE.NET software is made available subject to the
Handle.Net Public License Agreement, which may be obtained at
http://hdl.handle.net/20.1000/112 or hdl:20.1000/112
\**********************************************************************/
package cn.teleinfo.idpointer.sdk.core;
import cn.teleinfo.idpointer.sdk.core.stream.xml.XParser;
import cn.teleinfo.idpointer.sdk.core.stream.xml.XTag;
import cn.teleinfo.idpointer.sdk.core.trust.JsonWebSignature;
import java.io.StringReader;
import java.security.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* A signature over some handle values. The form of the signature is two handle values, one with type 10320/sig.digest containing a digest of all the signed values,
* and a second of type 10320/sig.sig containing a signature of the digest value. Example data:
* {@code
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* }
* and
* {@code
*
* 302C02142306D496402DC1CE701244AD0905A38122CFA9FD0214432390E2C7132EFDC2F516FB6B9C670538B8CA32
*
* }
*
* The hash of a handle value is a hash of its binary representation starting at offset 8, which corresponds to excluding the index and the timestamp from the hash.
* The signature is a signature of the binary represenation of the digest value, again excluding offset and timestamp.
* The signature value specifies the index of the corresponding digest value. The signed handle is specifed in the digest value.
* The signature value specifies the signer as handle and index. It can actually contain multiple signatures of multiple signers.
* The signature algorithm defaults to SHA1with(key-algorithm) if not specified.
*
* @deprecated Use {@link JsonWebSignature} and allied classes.
*/
@Deprecated
public class HandleSignature {
public static final byte[] METADATA_TYPE = Util.encodeString("10320/sig.digest");
public static final byte[] SIGNATURE_TYPE = Util.encodeString("10320/sig.sig");
public static class Digest {
final String algorithm;
final byte[] digest;
public Digest(String algorithm, byte[] digest) {
this.algorithm = algorithm;
this.digest = digest;
}
public String getAlgorithm() {
return algorithm;
}
public byte[] getDigest() {
return digest;
}
}
public static class DigestsValue {
final String handle;
@SuppressWarnings("hiding")
final Map> digests;
public DigestsValue(String handle, Map> digests) {
this.handle = handle;
this.digests = digests;
}
public String getHandle() {
return handle;
}
public Map> getDigests() {
return digests;
}
public static DigestsValue ofXml(String xml) throws Exception {
XParser parser = new XParser();
XTag root = parser.parse(new StringReader(xml), false);
String handle = root.getAttribute("hdl");
Map> digests = new HashMap<>();
if (root.getSubTags() != null) {
for (XTag child : root.getSubTags()) {
int index;
try {
index = Integer.parseInt(child.getAttribute("index"));
} catch (Exception e) {
throw new Exception("bad index attribute " + child, e);
}
List digestList = new ArrayList<>();
for (Map.Entry entry : child.getAttributes().entrySet()) {
String name = entry.getKey();
if ("index".equals(name)) continue;
digestList.add(new Digest(name, Util.encodeHexString(entry.getValue())));
}
if (digests.containsKey(Integer.valueOf(index))) throw new Exception("duplicate index attribute " + child);
digests.put(Integer.valueOf(index), digestList);
}
}
return new DigestsValue(handle, digests);
}
}
final HandleValue digestsValue;
final DigestsValue parsedDigestsValue;
final String algorithm;
final ValueReference signer;
final byte[] signature;
public HandleSignature(HandleValue digestsValue, DigestsValue parsedDigestValue, String algorithm, ValueReference signer, byte[] signature) throws Exception {
this.digestsValue = digestsValue;
this.parsedDigestsValue = parsedDigestValue;
this.algorithm = algorithm;
this.signer = signer;
this.signature = signature;
}
public String getHandle() {
return parsedDigestsValue.getHandle();
}
public HandleValue getDigestsValue() {
return digestsValue;
}
public DigestsValue getParsedDigestsValue() {
return parsedDigestsValue;
}
public String getAlgorithm() {
return algorithm;
}
public ValueReference getSigner() {
return signer;
}
public byte[] getSignature() {
return signature;
}
@Override
public String toString() {
return "HandleSignature [digestsValue=" + digestsValue + ", algorithm=" + algorithm + ", signer=" + signer + "]";
}
public static final int VALUE_DIGEST_OFFSET = Encoder.INT_SIZE * 2;
public static void updateForHandleValue(MessageDigest digest, byte[] encodedHandleValue) {
digest.update(encodedHandleValue, VALUE_DIGEST_OFFSET, encodedHandleValue.length - VALUE_DIGEST_OFFSET);
}
public static void updateForHandleValue(Signature sig, byte[] encodedHandleValue) throws SignatureException {
sig.update(encodedHandleValue, VALUE_DIGEST_OFFSET, encodedHandleValue.length - VALUE_DIGEST_OFFSET);
}
public boolean verifySignature(PublicKey pubKey) throws Exception {
String alg = algorithm;
if (alg == null) alg = Util.getSigIdFromHashAlgId(Common.HASH_ALG_SHA1, pubKey.getAlgorithm());
Signature sig = Signature.getInstance(alg);
sig.initVerify(pubKey);
updateForHandleValue(sig, Encoder.encodeHandleValue(digestsValue));
return sig.verify(signature);
}
public boolean verifyValue(String handle, HandleValue value) throws NoSuchAlgorithmException {
if (!Util.equalsPrefixCI(handle, getHandle())) return false;
@SuppressWarnings("hiding")
List digests = parsedDigestsValue.getDigests().get(Integer.valueOf(value.getIndex()));
if (digests == null) return false;
byte[] encodedHandleValue = Encoder.encodeHandleValue(value);
boolean foundSha = false;
for (Digest digest : digests) {
MessageDigest messageDigest = getMessageDigest(digest.getAlgorithm());
byte[] checkDigest;
synchronized (messageDigest) {
messageDigest.reset();
updateForHandleValue(messageDigest, encodedHandleValue);
checkDigest = messageDigest.digest();
}
if (!Util.equals(checkDigest, digest.getDigest())) return false;
if (digest.getAlgorithm().toLowerCase().startsWith("sha")) foundSha = true;
}
return foundSha;
}
public boolean signsMissingValues(HandleValue[] values) {
List signedIndices = new ArrayList<>(parsedDigestsValue.getDigests().keySet());
for (HandleValue value : values) {
signedIndices.remove(Integer.valueOf(value.getIndex()));
}
return !signedIndices.isEmpty();
}
private static ConcurrentMap digests = new ConcurrentHashMap<>();
private static MessageDigest getMessageDigest(String algorithm) throws NoSuchAlgorithmException {
MessageDigest res = digests.get(algorithm);
if (res != null) return res;
MessageDigest newDigest = MessageDigest.getInstance(algorithm);
digests.putIfAbsent(algorithm, newDigest);
return newDigest;
}
public static List getSignatures(HandleValue[] values, HandleValue value, boolean throwOnError) throws Exception {
try {
if (!value.hasType(SIGNATURE_TYPE)) return null;
XParser parser = new XParser();
XTag root = parser.parse(new StringReader(value.getDataAsString()), false);
String defaultSigner = root.getAttribute("signer");
String defaultSignerIndex = root.getAttribute("signerIndex", "300");
String ofIndex = root.getAttribute("ofIndex", root.getAttribute("ofindex"));
String handle = root.getAttribute("hdl");
HandleValue digestsValue = getValueOfIndex(values, ofIndex);
if (digestsValue == null) throw new Exception("Unable to find digests value for signature value " + root);
DigestsValue parsedDigestValue = DigestsValue.ofXml(digestsValue.getDataAsString());
if (handle != null && !Util.equalsPrefixCI(handle, parsedDigestValue.getHandle())) {
throw new Exception("Handle does not match in signature and digests value " + root);
}
List res = new ArrayList<>();
for (XTag child : root.getSubTags()) {
try {
res.add(getHandleSignature(child, digestsValue, parsedDigestValue, defaultSigner, defaultSignerIndex));
} catch (Exception e) {
if (throwOnError) throw e;
}
}
return res;
} catch (Exception e) {
if (throwOnError) throw e;
else return null;
}
}
public static List getSignaturesQuietly(HandleValue[] values) {
try {
return getSignatures(values, false);
} catch (Exception e) {
throw new AssertionError(e);
}
}
public static List getSignatures(HandleValue[] values, boolean throwOnError) throws Exception {
List res = new ArrayList<>();
for (HandleValue value : values) {
List sublist = getSignatures(values, value, throwOnError);
if (sublist != null) res.addAll(sublist);
}
return res;
}
private static HandleValue getValueOfIndex(HandleValue[] values, String ofIndex) {
HandleValue digestsValue = null;
for (HandleValue candidate : values) {
if (String.valueOf(candidate.getIndex()).equals(ofIndex)) {
digestsValue = candidate;
break;
}
}
return digestsValue;
}
private static HandleSignature getHandleSignature(XTag child, HandleValue digestsValue, DigestsValue parsedDigestValue, String defaultSigner, String defaultSignerIndex) throws Exception {
String algorithm = child.getAttribute("alg");
String signer = child.getAttribute("signer", defaultSigner);
String signerIndex = child.getAttribute("signerIndex", defaultSignerIndex);
String signatureHex = child.getStrValue();
ValueReference signerRef;
try {
signerRef = new ValueReference(Util.encodeString(signer), Integer.parseInt(signerIndex));
} catch (NumberFormatException e) {
throw new Exception("Invalid signer index " + signerIndex + " in " + child, e);
}
return new HandleSignature(digestsValue, parsedDigestValue, algorithm, signerRef, Util.encodeHexString(signatureHex));
}
public static HandleValue createDigestsValue(int index, String handle, HandleValue[] values) {
XTag root = new XTag("digests");
root.setAttribute("hdl", handle);
for (HandleValue value : values) {
XTag child = new XTag("val");
child.setAttribute("index", value.getIndex());
byte[] encodedHandleValue = Encoder.encodeHandleValue(value);
for (String alg : new String[] { "md5", "sha1" }) {
MessageDigest messageDigest;
try {
messageDigest = getMessageDigest(alg);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
byte[] digest;
synchronized (messageDigest) {
messageDigest.reset();
updateForHandleValue(messageDigest, encodedHandleValue);
digest = messageDigest.digest();
}
child.setAttribute(alg, Util.decodeHexString(digest, false));
}
root.addSubTag(child);
}
return new HandleValue(index, METADATA_TYPE, Util.encodeString(root.toString()));
}
public static HandleValue createSignatureValue(int index, String handle, ValueReference signer, String alg, PrivateKey privKey, HandleValue digestsValue) throws Exception {
XTag root = new XTag("signature");
if (handle != null) root.setAttribute("hdl", handle);
root.setAttribute("ofindex", digestsValue.getIndex());
root.setAttribute("signer", Util.decodeString(signer.handle));
root.setAttribute("signerIndex", signer.index);
if (alg == null) alg = Util.getDefaultSigId(privKey.getAlgorithm());
XTag child = new XTag("sig");
child.setAttribute("alg", alg);
child.setAttribute("signer", Util.decodeString(signer.handle));
child.setAttribute("signerIndex", signer.index);
Signature sig = Signature.getInstance(alg);
sig.initSign(privKey);
updateForHandleValue(sig, Encoder.encodeHandleValue(digestsValue));
byte[] signature = sig.sign();
child.setValue(Util.decodeHexString(signature, false));
root.addSubTag(child);
return new HandleValue(index, SIGNATURE_TYPE, Util.encodeString(root.toString()));
}
public static HandleValue[] signedHandleValues(String handle, HandleValue[] values, HandleSignature signature, PublicKey pubKey, boolean throwOnError) throws Exception {
if (!Util.equalsPrefixCI(handle, signature.getHandle())) {
return new HandleValue[0];
}
try {
if (!signature.verifySignature(pubKey)) return new HandleValue[0];
} catch (Exception e) {
if (throwOnError) throw e;
else return new HandleValue[0];
}
List res = new ArrayList<>();
for (HandleValue value : values) {
try {
if (signature.verifyValue(handle, value)) res.add(value);
} catch (Exception e) {
if (throwOnError) throw e;
}
}
return res.toArray(new HandleValue[res.size()]);
}
public static boolean signsAllValues(String handle, HandleValue[] values, HandleSignature signature, PublicKey pubKey, boolean throwOnError) throws Exception {
if (!Util.equalsPrefixCI(handle, signature.getHandle())) {
return false;
}
try {
if (!signature.verifySignature(pubKey)) return false;
} catch (Exception e) {
if (throwOnError) throw e;
else return false;
}
for (HandleValue value : values) {
try {
if (valueNeedsSignature(value) && !signature.verifyValue(handle, value)) return false;
} catch (Exception e) {
if (throwOnError) throw e;
}
}
return true;
}
public static boolean valueNeedsSignature(HandleValue value) {
if (value.hasType(SIGNATURE_TYPE) || value.hasType(METADATA_TYPE) || !value.publicRead) return false;
else return true;
}
}