文章目录
1 什么是电子签章
百度百科定义:电子签章,与我们所使用的数字证书一样,是用来做为身份验证的一种手段,泛指所有以电子形式存在,依附在电子文件并与其逻辑关联,可用以辨识电子文件签署者身份,保证文件的完整性,并表示签署者同意电子文件所陈述事实的内容。 [3]一般来说,对电子签章的认定,都是从技术角度而言的。主要是指通过特定的技术方案来鉴别当事人的身份及确保交易资料内容不被篡改的安全保障措施。从广义上讲,电子签章不仅包括我们通常意义上讲的"非对称性密钥加密",也包括计算机口令、生物笔迹辨别、指纹识别,以及新近出现的眼虹膜透视辨别法、面纹识别等。而电子签章技术作为目前最成熟的"数字签章",是以公钥及密钥的"非对称型"密码技术制作的。电子签章是电子签名的一种表现形式,利用图像处理技术将电子签名操作转化为与纸质文件盖章操作相同的可视效果,同时利用电子签名技术保障电子信息的真实性和完整性以及签名人的不可否认性。
2 Java怎么实现电子签章
这里以ofd文件为例
2.1 电子签章规范
地址:https://gitee.com/dongdong-0421/file
2.2 GMT 0031-2014 安全电子签章密码技术规范介绍
该规范定义了电子印章数据结构,电子签章数据结构,电子印章如何签名,电子签章如何签名和验签
我这里就不一个个截图了 地址在上面自行下载即可
2.3 OFD文件介绍
OFD是我国电子公文交换和存储格式标准。OFD版式文档处理软件作为电子公文的核心组件,为用户提供电子公文的成文、存储、签章、交换、校验、批注、阅读、归档等业务环节的技术支撑,保障电子公文的真实性、完整性、可用性和安全性,确保公文的长期保存和凭证价值。
3.实现OFD文件签名验签
3.1 环境
ofd 签名验签需要核心库需要依赖于ofdrw
地址:ofd验签地址
该库提供了对ofd各功能的实现 详细自行查看
3.2 ofdrw 签名和验签接口
public interface ExtendSignatureContainer {
/**
* 提供文件的摘要算法功能
*
* @return 摘要算法功能 比如SM3 SHA256 等
*/
MessageDigest getDigestFnc();
/**
* 签名方法OID
*
* @return 签名方法OID sm2算法oid: 1.2.156.10197.1.501 rsa算法oid: 1.2.840.113549.1.1.11
*/
ASN1ObjectIdentifier getSignAlgOID();
/**
* 对待签名数据签名
* <p>
* 在操作过程中请勿对流进行关闭
*
* @param inData 待签名数据流
* @param propertyInfo 签章属性信息
* @return 签名或签章结果值
* @throws IOException 流操作异常
* @throws GeneralSecurityException 签名计算异常
*/
byte[] sign(InputStream inData, String propertyInfo) throws IOException, GeneralSecurityException;
/**
* 获取电子印章二进制编码
* <p>
* 如果{@link #getSignType()} 返还类型为{@link SigType#Sign}那么请返回null
*
* @return 电子印章二进制编码
* @throws IOException 获取印章IO异常
*/
byte[] getSeal() throws IOException;
/**
* 获取签名节点类型
*
* @return 签名节点类型
*/
SigType getSignType();
}
public interface SignedDataValidateContainer {
/**
* 签名数据验证
* <p>
* 如果验证不通过请抛出异常
*
* @param type 电子签名类型(Sign/Seal)
* @param signAlgName 签名算法名称或OID
* @param tbsContent 待签章内容
* @param signedValue 电子签章数据或签名值(SignedValue.xml文件内容)
* @throws InvalidSignedValueException 电子签章数据失效
* @throws IOException IO异常
* @throws GeneralSecurityException 运算过程中异常
*/
void validate(SigType type, String signAlgName, byte[] tbsContent, byte[] signedValue) throws InvalidSignedValueException, IOException, GeneralSecurityException;
}
# 如果需要实现自己的签名和验签逻辑 实现上面接口即可
3.3 实现签名
3.3.1 签名前准备工作
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.26</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>1.69</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.69</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.69</version>
</dependency>
<dependency>
<groupId>org.ofdrw</groupId>
<artifactId>ofdrw-full</artifactId>
<version>2.2.12</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</dependency>
package com.dongdong.sign.util;
import cn.hutool.crypto.SecureUtil;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.X509Certificate;
import java.security.spec.ECGenParameterSpec;
import java.util.Date;
public class SM2CertUtil {
public static KeyPair createKeyPair(String algorithm) {
return SecureUtil.generateKeyPair(algorithm);
}
public static KeyPair generateKeyPair(String algorithm, Provider provider, int keySize) throws NoSuchAlgorithmException {
KeyPairGenerator keyPair = KeyPairGenerator.getInstance(algorithm, provider);
keyPair.initialize(keySize, new SecureRandom());
return keyPair.generateKeyPair();
}
// 生成密钥对
public static KeyPair generateKeyPair(Provider provider) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
KeyPairGenerator keyPair = KeyPairGenerator.getInstance("EC", provider);
// SM2 需要单独定义曲线
final ECGenParameterSpec sm2p256v1 = new ECGenParameterSpec("sm2p256v1");
keyPair.initialize(sm2p256v1);
keyPair.initialize(256, new SecureRandom());
return keyPair.generateKeyPair();
}
public static X509Certificate generateSelfSignedCertificate(KeyPair keyPair, X500Name subject)
throws Exception {
// 设置证书有效期
Date notBefore = new Date();
Date notAfter = new Date(notBefore.getTime() + (1000L * 60 * 60 * 24 * 365 * 10)); // 10年有效期
// 创建一个自签名证书生成器
JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
subject, // issuer
BigInteger.valueOf(System.currentTimeMillis()), // serial number
notBefore, // start date
notAfter, // expiry date
subject, // subject
keyPair.getPublic()); // public key
// 创建一个签名生成器
ContentSigner signer = new JcaContentSignerBuilder("SM3withSM2").setProvider(new BouncyCastleProvider()).build(keyPair.getPrivate());
return new JcaX509CertificateConverter().setProvider(new BouncyCastleProvider()).getCertificate(certBuilder.build(signer));
}
public static void main(String[] args) throws Exception {
X500Name subject = new X500Name("CN=Test SM2 ");
KeyPair keyPair = generateKeyPair(new BouncyCastleProvider());
X509Certificate certificate = generateSelfSignedCertificate(keyPair, subject);
}
}
3.4 实现签名
package com.yfkj.sign.demo;
import com.yfkj.sign.util.SM2CertUtil;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.jupiter.api.Test;
import org.ofdrw.gm.ses.v1.*;
import org.ofdrw.reader.OFDReader;
import org.ofdrw.sign.ExtendSignatureContainer;
import org.ofdrw.sign.OFDSigner;
import org.ofdrw.sign.SignMode;
import org.ofdrw.sign.signContainer.SESV1Container;
import org.ofdrw.sign.stamppos.NormalStampPos;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Date;
import java.util.UUID;
public class TestSign {
@Test
void test() throws Exception {
Path pngPath = Paths.get("src/test/resources", "test.png");
Path ofdPath = Paths.get("src/test/resources", "test.ofd");
Path outPath = Paths.get("target/test_sign.ofd");
// 生成证书 和 密钥
X500Name x500Name = new X500Name("CN=root");
KeyPair pair = SM2CertUtil.createKeyPair("SM2");
PrivateKey privateKey = pair.getPrivate();
X509Certificate certificate = SM2CertUtil.generateSelfSignedCertificate(pair, x500Name);
// 印章头信息
SES_Header header = new SES_Header(new ASN1Integer(1), new DERIA5String("test"));
ASN1EncodableVector v = new ASN1EncodableVector(1);
v.add(new DEROctetString(certificate.getEncoded()));
Calendar then = Calendar.getInstance();
then.add(Calendar.YEAR, 2);
// 印章属性信息
SES_ESPropertyInfo property = new SES_ESPropertyInfo()
.setType(SES_ESPropertyInfo.OrgType)
.setName(new DERUTF8String("测试印章"))
.setCertList(new DERSequence(v))
.setCreateDate(new ASN1UTCTime(new Date()))
.setValidStart(new ASN1UTCTime(new Date()))
.setValidEnd(new ASN1UTCTime(then.getTime()));
// 印章图片信息 构造
SES_ESPictrueInfo picture = new SES_ESPictrueInfo()
.setType("png")
.setData(Files.readAllBytes(pngPath))
.setWidth(40)
.setHeight(40);
// 印章信息构造
SES_SealInfo sealInfo = new SES_SealInfo()
.setHeader(header)
.setEsID(UUID.randomUUID().toString().replace("-", "").toUpperCase())
.setProperty(property)
.setPicture(picture);
DEROctetString signCert = new DEROctetString(certificate.getEncoded());
// 印章信息、制章人证书、签名算法标识符组成的信息作为签名原文
v = new ASN1EncodableVector(3);
v.add(sealInfo);
v.add(signCert);
v.add(GMObjectIdentifiers.sm2sign_with_sm3);
// 签名对象
Signature signature = Signature.getInstance("SM3withSm2", new BouncyCastleProvider());
signature.initSign(privateKey);
signature.update(new DERSequence(v).getEncoded(ASN1Encoding.DER));
byte[] sign = signature.sign();
// 印章签名信息
SES_SignInfo signInfo = new SES_SignInfo()
.setCert(signCert)
.setSignatureAlgorithm(GMObjectIdentifiers.sm2sign_with_sm3)
.setSignData(sign);
// 印章
SESeal seal = new SESeal(sealInfo, signInfo);
try (OFDReader reader = new OFDReader(ofdPath); OFDSigner signer = new OFDSigner(reader, outPath)) {
// 2. 实现电子签章容器(这个签名容器是这个库实现的,生产肯定是自定义实现)
ExtendSignatureContainer signContainer = new SESV1Container(privateKey,seal,certificate);
// 3. 设置签名模式
signer.setSignMode(SignMode.ContinueSign);
// 4. 设置签名使用的扩展签名容器
signer.setSignContainer(signContainer);
signer.addApPos(new NormalStampPos(1, 70.0, 100.0, 40, 40));
// 5. 执行签名
signer.exeSign();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
System.out.println(">> 生成文件位置: " + outPath.toAbsolutePath().toAbsolutePath());
}
}
3.5 验签
- 验签的方式有很多中 可以基于第三方验签 比如 数科OFD 数科通过在线和桌面端验签方式,也可以基于ofdrw 通过的验签接口
3.5.1 数科ofd在线验签
-
地址: 数科在线 上传签名后的文件即可
-
验签成功
3.5.2 数科OFD桌面端验签
- 官网地址:http://www.ofd.cn/
3.5.3 ofdrw库提供的验签接口
- 这里就不提供代码了
- 如果需要可以在下方留言
4 签章过程中遇到的坑
因为ofd是国内自主研发的,肯定要支持国内的算法 SM2 但是公司有需求就是需要支持国际算法RSA 好在规范里面也说支持RSA算法签章 ,只需要把签名算法替换从RSA 看似非常完美 实则有大坑
我们签名使用的是ofdrw提供的签名功能 算法提供是依靠于org.bouncycastle 这个库
问题就出现在org.bouncycastle 这个库 中 如果我们使用这个提供的SM3摘要算法没问题 如果使用这个库提供的SHA256算法 就有问题了 因为在我们签名的过程中是需要进行杂凑值计算的依赖于杂凑算法 而且org.bouncycastle 提供的SHA256 里面有一出
org.bouncycastle.crypto.digests.SHA256Digest 里面的public String getAlgorithmName() { return "SHA-256"; } 这里返回的字符串中间多了一个- 导致第三方验证验证过不了 ofdrw库会调用这个方法拿到签名算法的名称 然后存入ofd中 签名后的ofd文件 可以修改后缀名为.zip 然后解压 找到Signature.xml 里面可以看到算法名称 如果你用org.bouncycastle默认提供的 就会是SHA-256 第三方检验发现算法对不上就会验证不通过
4.1 解决方案
- 修改ofdrw源码找到调用getAlgorithmName() 方法地方 显然这种方式不行
- 覆盖SHA256Digest 里面的getAlgorithmName方法 修改成SHA256 这种方式可行
4.2 修改后的样子
直接复制下面代码即可 一共三个类
package com.dongdong.sign.digest;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
abstract class DigestAlgorithmProvider extends AlgorithmProvider {
protected void addHMACAlgorithm(
ConfigurableProvider provider,
String algorithm,
String algorithmClassName,
String keyGeneratorClassName) {
String mainName = "HMAC" + algorithm;
provider.addAlgorithm("Mac." + mainName, algorithmClassName);
provider.addAlgorithm("Alg.Alias.Mac.HMAC-" + algorithm, mainName);
provider.addAlgorithm("Alg.Alias.Mac.HMAC/" + algorithm, mainName);
provider.addAlgorithm("KeyGenerator." + mainName, keyGeneratorClassName);
provider.addAlgorithm("Alg.Alias.KeyGenerator.HMAC-" + algorithm, mainName);
provider.addAlgorithm("Alg.Alias.KeyGenerator.HMAC/" + algorithm, mainName);
}
protected void addHMACAlias(
ConfigurableProvider provider,
String algorithm,
ASN1ObjectIdentifier oid) {
String mainName = "HMAC" + algorithm;
provider.addAlgorithm("Alg.Alias.Mac." + oid, mainName);
provider.addAlgorithm("Alg.Alias.KeyGenerator." + oid, mainName);
}
}
package com.dongdong.sign.digest;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.crypto.CipherKeyGenerator;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
import org.bouncycastle.jcajce.provider.digest.BCMessageDigest;
import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac;
import org.bouncycastle.jcajce.provider.symmetric.util.PBESecretKeyFactory;
public class SHA256 {
private SHA256() {
}
static public class Digest
extends BCMessageDigest
implements Cloneable {
public Digest() {
super(new SHA256Digest());
}
public Object clone()
throws CloneNotSupportedException {
Digest d = (Digest) super.clone();
d.digest = new SHA256Digest((SHA256Digest) digest);
return d;
}
}
public static class HashMac
extends BaseMac {
public HashMac() {
super(new HMac(new SHA256Digest()));
}
}
public static class PBEWithMacKeyFactory
extends PBESecretKeyFactory {
public PBEWithMacKeyFactory() {
super("PBEwithHmacSHA256", null, false, PKCS12, SHA256, 256, 0);
}
}
public static class KeyGenerator
extends BaseKeyGenerator {
public KeyGenerator() {
super("HMACSHA256", 256, new CipherKeyGenerator());
}
}
public static class Mappings
extends DigestAlgorithmProvider {
private static final String PREFIX = SHA256.class.getName();
public Mappings() {
}
public void configure(ConfigurableProvider provider) {
provider.addAlgorithm("MessageDigest.SHA-256", PREFIX + "$Digest");
provider.addAlgorithm("Alg.Alias.MessageDigest.SHA256", "SHA-256");
provider.addAlgorithm("Alg.Alias.MessageDigest." + NISTObjectIdentifiers.id_sha256, "SHA-256");
provider.addAlgorithm("SecretKeyFactory.PBEWITHHMACSHA256", PREFIX + "$PBEWithMacKeyFactory");
provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHHMACSHA-256", "PBEWITHHMACSHA256");
provider.addAlgorithm("Alg.Alias.SecretKeyFactory." + NISTObjectIdentifiers.id_sha256, "PBEWITHHMACSHA256");
provider.addAlgorithm("Mac.PBEWITHHMACSHA256", PREFIX + "$HashMac");
addHMACAlgorithm(provider, "SHA256", PREFIX + "$HashMac", PREFIX + "$KeyGenerator");
addHMACAlias(provider, "SHA256", PKCSObjectIdentifiers.id_hmacWithSHA256);
addHMACAlias(provider, "SHA256", NISTObjectIdentifiers.id_sha256);
}
}
}
package com.dongdong.sign.digest;
import org.bouncycastle.crypto.digests.EncodableDigest;
import org.bouncycastle.crypto.digests.GeneralDigest;
import org.bouncycastle.util.Memoable;
import org.bouncycastle.util.Pack;
public class SHA256Digest extends GeneralDigest implements EncodableDigest {
private static final int DIGEST_LENGTH = 32;
private int H1, H2, H3, H4, H5, H6, H7, H8;
private int[] X = new int[64];
private int xOff;
public SHA256Digest() {
reset();
}
public SHA256Digest(SHA256Digest t) {
super(t);
copyIn(t);
}
private void copyIn(SHA256Digest t) {
super.copyIn(t);
H1 = t.H1;
H2 = t.H2;
H3 = t.H3;
H4 = t.H4;
H5 = t.H5;
H6 = t.H6;
H7 = t.H7;
H8 = t.H8;
System.arraycopy(t.X, 0, X, 0, t.X.length);
xOff = t.xOff;
}
/**
* State constructor - create a digest initialised with the state of a previous one.
*
* @param encodedState the encoded state from the originating digest.
*/
public SHA256Digest(byte[] encodedState) {
super(encodedState);
H1 = Pack.bigEndianToInt(encodedState, 16);
H2 = Pack.bigEndianToInt(encodedState, 20);
H3 = Pack.bigEndianToInt(encodedState, 24);
H4 = Pack.bigEndianToInt(encodedState, 28);
H5 = Pack.bigEndianToInt(encodedState, 32);
H6 = Pack.bigEndianToInt(encodedState, 36);
H7 = Pack.bigEndianToInt(encodedState, 40);
H8 = Pack.bigEndianToInt(encodedState, 44);
xOff = Pack.bigEndianToInt(encodedState, 48);
for (int i = 0; i != xOff; i++) {
X[i] = Pack.bigEndianToInt(encodedState, 52 + (i * 4));
}
}
@Override
protected void processWord(byte[] in, int inOff) {
// Note: Inlined for performance
// X[xOff] = Pack.bigEndianToInt(in, inOff);
int n = in[inOff] << 24;
n |= (in[++inOff] & 0xff) << 16;
n |= (in[++inOff] & 0xff) << 8;
n |= (in[++inOff] & 0xff);
X[xOff] = n;
if (++xOff == 16) {
processBlock();
}
}
@Override
protected void processLength(long bitLength) {
if (xOff > 14) {
processBlock();
}
X[14] = (int) (bitLength >>> 32);
X[15] = (int) (bitLength & 0xffffffff);
}
public void reset() {
super.reset();
/* SHA-256 initial hash value
* The first 32 bits of the fractional parts of the square roots
* of the first eight prime numbers
*/
H1 = 0x6a09e667;
H2 = 0xbb67ae85;
H3 = 0x3c6ef372;
H4 = 0xa54ff53a;
H5 = 0x510e527f;
H6 = 0x9b05688c;
H7 = 0x1f83d9ab;
H8 = 0x5be0cd19;
xOff = 0;
for (int i = 0; i != X.length; i++) {
X[i] = 0;
}
}
@Override
protected void processBlock() {
//
// expand 16 word block into 64 word blocks.
//
for (int t = 16; t <= 63; t++) {
X[t] = Theta1(X[t - 2]) + X[t - 7] + Theta0(X[t - 15]) + X[t - 16];
}
//
// set up working variables.
//
int a = H1;
int b = H2;
int c = H3;
int d = H4;
int e = H5;
int f = H6;
int g = H7;
int h = H8;
int t = 0;
for (int i = 0; i < 8; i++) {
// t = 8 * i
h += Sum1(e) + Ch(e, f, g) + K[t] + X[t];
d += h;
h += Sum0(a) + Maj(a, b, c);
++t;
// t = 8 * i + 1
g += Sum1(d) + Ch(d, e, f) + K[t] + X[t];
c += g;
g += Sum0(h) + Maj(h, a, b);
++t;
// t = 8 * i + 2
f += Sum1(c) + Ch(c, d, e) + K[t] + X[t];
b += f;
f += Sum0(g) + Maj(g, h, a);
++t;
// t = 8 * i + 3
e += Sum1(b) + Ch(b, c, d) + K[t] + X[t];
a += e;
e += Sum0(f) + Maj(f, g, h);
++t;
// t = 8 * i + 4
d += Sum1(a) + Ch(a, b, c) + K[t] + X[t];
h += d;
d += Sum0(e) + Maj(e, f, g);
++t;
// t = 8 * i + 5
c += Sum1(h) + Ch(h, a, b) + K[t] + X[t];
g += c;
c += Sum0(d) + Maj(d, e, f);
++t;
// t = 8 * i + 6
b += Sum1(g) + Ch(g, h, a) + K[t] + X[t];
f += b;
b += Sum0(c) + Maj(c, d, e);
++t;
// t = 8 * i + 7
a += Sum1(f) + Ch(f, g, h) + K[t] + X[t];
e += a;
a += Sum0(b) + Maj(b, c, d);
++t;
}
H1 += a;
H2 += b;
H3 += c;
H4 += d;
H5 += e;
H6 += f;
H7 += g;
H8 += h;
//
// reset the offset and clean out the word buffer.
//
xOff = 0;
for (int i = 0; i < 16; i++) {
X[i] = 0;
}
}
@Override
public String getAlgorithmName() {
return "SHA256";
}
@Override
public int getDigestSize() {
return DIGEST_LENGTH;
}
@Override
public int doFinal(byte[] out, int outOff) {
finish();
Pack.intToBigEndian(H1, out, outOff);
Pack.intToBigEndian(H2, out, outOff + 4);
Pack.intToBigEndian(H3, out, outOff + 8);
Pack.intToBigEndian(H4, out, outOff + 12);
Pack.intToBigEndian(H5, out, outOff + 16);
Pack.intToBigEndian(H6, out, outOff + 20);
Pack.intToBigEndian(H7, out, outOff + 24);
Pack.intToBigEndian(H8, out, outOff + 28);
reset();
return DIGEST_LENGTH;
}
@Override
public byte[] getEncodedState() {
byte[] state = new byte[52 + xOff * 4];
super.populateState(state);
Pack.intToBigEndian(H1, state, 16);
Pack.intToBigEndian(H2, state, 20);
Pack.intToBigEndian(H3, state, 24);
Pack.intToBigEndian(H4, state, 28);
Pack.intToBigEndian(H5, state, 32);
Pack.intToBigEndian(H6, state, 36);
Pack.intToBigEndian(H7, state, 40);
Pack.intToBigEndian(H8, state, 44);
Pack.intToBigEndian(xOff, state, 48);
for (int i = 0; i != xOff; i++) {
Pack.intToBigEndian(X[i], state, 52 + (i * 4));
}
return state;
}
/* SHA-256 functions */
private static int Ch(int x, int y, int z) {
return (x & y) ^ ((~x) & z);
// return z ^ (x & (y ^ z));
}
private static int Maj(int x, int y, int z) {
// return (x & y) ^ (x & z) ^ (y & z);
return (x & y) | (z & (x ^ y));
}
private static int Sum0(int x) {
return ((x >>> 2) | (x << 30)) ^ ((x >>> 13) | (x << 19)) ^ ((x >>> 22) | (x << 10));
}
private static int Sum1(int x) {
return ((x >>> 6) | (x << 26)) ^ ((x >>> 11) | (x << 21)) ^ ((x >>> 25) | (x << 7));
}
private static int Theta0(int x) {
return ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3);
}
private static int Theta1(int x) {
return ((x >>> 17) | (x << 15)) ^ ((x >>> 19) | (x << 13)) ^ (x >>> 10);
}
/* SHA-256 Constants
* (represent the first 32 bits of the fractional parts of the
* cube roots of the first sixty-four prime numbers)
*/
static final int K[] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
@Override
public Memoable copy() {
return new SHA256Digest(this);
}
@Override
public void reset(Memoable other) {
SHA256Digest d = (SHA256Digest) other;
copyIn(d);
}
}
至此完毕