数字签名

数字签名是证明某个文件上的内容确实是我写的,别人不能冒充我的签名(不可伪造),也不能否认上面的签名是我的(不可抵赖),并且,数字签名可以保证文件在传输的过程中没有被篡改。数字签名技术依赖于消息摘要技术和公钥加密技术。对于数字摘要技术,可以参见相应的文档。

在公钥私钥加密系统中,使用公钥加密,就可以使用私钥来解密;使用私钥加密,可以使用公钥来解密。消息加密利用的是公钥加密,数字签名利用的是私钥加密。

用私钥对消息进行加密,其实就是一个签名的过程。因为私钥是私密的,而且是唯一的,只有拥有者拥有,别人无法获得不是自己的私钥,并且模仿别人的签名(不可伪造)。公钥是公开的,其他人可以很容易地使用公钥去尝试解密生成的密文,就可以知道这个签名是不是私钥拥有者签名的(不可抵赖),公钥和私钥是成双出现的。

实际应用中,如果直接对原消息进行签名,由于原消息往往比较长,直接使用RSA算法进行签名速度会比较慢,所以一般对消息计算其摘要(比如SHA-256等),然后对摘要进行签名。只要使用的摘要算法是安全的,那么这种方式的数字签名就是安全的。

具体的签名过程如下:

用户对外发布公钥,并声明对应的私钥在自己手上
用户对消息计算摘要,并得到摘要
用户使用私钥对摘要进行签名,得到签名
将摘要和签名一起发送出去

验证过程如下:

接收者首先对消息计算摘要,得到一个摘要
使用用户公钥对签名进行解签,得到摘要
比较两个摘要,如果摘要相等,证明消息确实是用户发出的,并且没有被篡改过

如上图所示:

1.计算表示输入数据的消息摘要;
2.然后使用私钥加密摘要;

注意,加密是对摘要而不是数据本身执行的。为了将此签名呈现给另一个实体,原始数据需一起呈现给另一个实体, 签名只是一个消息摘要,并且无法从消息摘要中重建输入数据。
验证数字签名需要相同的路径;必须计算原始数据的消息摘要。使用公钥解密签名的摘要,并且如果解密的摘要与计算的摘要匹配,则签名有效。严格地说,对摘要执行的操作不一定是加密和解密;大多数数字签名算法不能用于加密任意数据。

不能阻止签名数据被截获。因此,伴随数字签名的数据不能是敏感数据;数字签名仅验证消息来自特定实体,并且消息在传输过程中未被更改,实际上并不保护该消息不被任何有权访问的人读取。

Signature引擎类

Signature类是一个引擎类,旨在提供加密数字签名算法(如DSA或RSAwithMD5)的功能。 密码安全签名算法采用任意大小的输入和私钥,并生成一个相对较短(通常是固定大小)的字节串,称为签名,具有以下属性:

• 只有私钥/公钥对的所有者才能创建签名。 任何拥有公钥的人在计算上不可能恢复私钥
• 鉴于与用于生成签名的私钥相对应的公钥,应该有可能验证输入的真实性和完整性
• 签名和公钥没有透露有关私钥的任何信息。
它也可以用来验证所谓的签名是否实际上是与其相关的数据的真实签名。

一个签名对象被初始化为用一个私钥进行签名,并被赋予待签名的数据。 结果签名字节通常与签名数据保持一致。 当需要验证时,另一个签名对象被创建和初始化以进行验证并给出相应的公钥。 数据和签名字节被送到签名对象,重新计算摘要,如果数据和签名匹配,则签名对象报告成功。

尽管签名看起来与消息摘要相似,但它们在提供的保护类型上却有着非常不同的目的。 诸如“SHA256withRSA”的算法使用消息摘要“SHA256”将大数据集初始“压缩”为更易于管理的形式,然后用“RSA”算法对得到的32字节消息摘要进行签名。

Signature对象状态

Signature对象总是处于一个给定的状态,它只能执行一种操作。 状态被表示为在其类中定义的整数常量。

签名对象可能具有的三种状态是:

UNINITIALIZED
SIGN
VERIFY

第一次创建时,Signature对象处于UNINITIALIZED状态。 Signature类定义了两个初始化方法initSign和initVerify,它们分别将状态更改为SIGN和VERIFY。

创建Signature对象

签名或验证签名的第一步是创建一个签名实例。 签名对象是通过使用Signature getInstance()静态工厂方法之一获得的。

初始化Signature对象

Signature对象在使用之前必须被初始化。 初始化方法取决于对象是要用于签名还是验证。

如果要用于签名,则必须首先使用要生成签名的实体的私钥来初始化该对象。 这个初始化通过调用方法完成:

final void initSign(PrivateKey privateKey)

此方法将Signature对象置于SIGN状态。

如果相反,Signature对象将用于验证,则必须首先使用将要验证签名的实体的公钥来初始化。 这个初始化是通过调用以下任一方法来完成的:

final void initVerify(PublicKey publicKey)

final void initVerify(Certificate certificate)

此方法将Signature对象置于VERIFY状态。

签名

如果签名对象已经被初始化为签名(如果它处于SIGN状态),则待签名的数据可以被提供给该对象。 这是通过对其中一个更新方法进行一个或多个调用完成的:

final void update(byte b)
final void update(byte[] data)
final void update(byte[] data, int off, int len)
应该调用update方法,直到所有要签名的数据都被提供给签名对象。

要生成签名,只需调用其中一个签名方法:

final byte[] sign()
final int sign(byte[] outbuf, int offset, int len)

第一个方法以字节数组的形式返回签名结果。 第二个将签名结果存储在提供的缓冲区outbuf中,从offset开始。 len是分配给签名的outbuf中的字节数。 该方法返回实际存储的字节数。
对sign方法的调用会将签名对象重置为之前通过调用initSign进行初始化以进行签名的状态。 也就是说,如果需要,该对象将被重置并可用于生成具有相同私钥的另一个签名,通过新的调用来更新和签名。或者,可以对指定不同私钥的initSign或initVerify(初始化Signature对象以验证签名)进行新的调用。

验证

如果签名对象已被初始化以进行验证(如果它处于VERIFY状态),则它可以验证所谓的签名实际上是与其相关联的数据的真实签名。 为了开始这个过程,要被验证的数据(而不是签名本身)被提供给对象。 通过调用其中一种更新方法将数据传递给对象:

final void update(byte b)
final void update(byte[] data)
final void update(byte[] data, int off, int len)

应该调用更新方法,直到所有要验证的数据都被提供给Signature对象。 现在可以通过调用其中一种验证方法来验证签名:

final boolean verify(byte[] signature)
final boolean verify(byte[] signature, int offset, int length)

参数必须是包含签名的字节数组。 这个字节数组将保存以前调用返回的签名字节。

verify方法返回一个布尔值,指示编码签名是否是提供给更新方法的数据的真实签名。

对verify方法的调用通过调用initVerify将初始化后的签名对象重置为其状态。 也就是说,该对象被重置并可用于验证在initVerify调用中指定了公钥的身份的另一个签名。

或者,可以对initVerify进行一次新的调用,指定一个不同的公钥(初始化Signature对象以验证来自不同实体的签名)或initSign(初始化Signature对象以生成签名)。

数字签名示例

将消息及其摘要保存到文件中,保存消息和数字签名,如下示例创建数字签名:
import java.io.; import java.security.;
public class Send {
public static void main(String args[]) {
String data;
data = “This have I thought good to deliver thee, ” +
“that thou mightst not lose the dues of rejoicing ” +
“by being ignorant of what greatness is promised thee.”;
try {
FileOutputStream fos = new FileOutputStream(“test”);
ObjectOutputStream oos = new ObjectOutputStream(fos);
KeyStoreHandler ksh = new KeyStoreHandler(null);
KeyStore ks = ksh.getKeyStore( );
PrivateKey pk = (PrivateKey) ks.getKey(args[0],
args[1].toCharArray( ));
Signature s = Signature.getInstance(“MD5withRSA”);
s.initSign(pk);
byte buf[] = data.getBytes( );
s.update(buf);
oos.writeObject(data);
oos.writeObject(s.sign( ));
} catch (Exception e) {
e.printStackTrace( );
} }}

为了创建数字签名,必须完成以下任务:

1.获取用于对数据进行签名的私钥。 使用密钥库处理程序和命令行参数来获取想要使用的私钥的别名和密码;
2.通过getInstance()方法获取签名对象并初始化它。 由于在此示例中创建了签名,因此使用initSign()方法进行初始化;
3.将要签名的数据作为一系列字节传递给签名对象的update()方法。 可以对update()方法进行多次调用;
4.通过调用sign()方法获取签名。 保存签名字节并将其写入包含数据的文件,以便以后可以检索数据和签名;

读取数据和验证签名是类似的:

import java.io.; import java.security.;
import javasec.samples.KeyStoreHandler;
public class Receive {
public static void main(String args[]) {
try {
String data = null;
byte signature[] = null;
FileInputStream fis = new FileInputStream(“test”);
ObjectInputStream ois = new ObjectInputStream(fis);
Object o = ois.readObject( );
try {
data = (String) o;
} catch (ClassCastException cce) {
System.out.println(“Unexpected data in file”);
System.exit(−1);
}
o = ois.readObject( );
try {
signature = (byte []) o;
} catch (ClassCastException cce) {
System.out.println(“Unexpected data in file”);
System.exit(−1);
}
System.out.println(“Received message”);
System.out.println(data);
KeyStoreHandler ksh = new KeyStoreHandler(null);
KeyStore ks = ksh.getKeyStore( );
java.security.cert.Certificate c =
ks.getCertificate(args[0]);
PublicKey pk = c.getPublicKey( );
Signature s = Signature.getInstance(“MD5withRSA”);
s.initVerify(pk);
s.update(data.getBytes( ));
if (s.verify(signature)) {
System.out.println(“Message is valid”);
}
} }
else System.out.println(“Message was corrupted”);
} catch (Exception e) {
System.out.println(e);
}

验证签名的过程仍然需要四个步骤,在第二步中,使用initVerify()方法初始化签名对象以进行验证,在第四步中,使用verify()方法验证现有签名。

上述数字签名示例用的 KeyStoreHandler 代码如下,主要对 keystore 的常见方法进行操作。

import java.io.; import java.security.;
import java.security.cert.*;
public class KeyStoreHandler {
KeyStore ks;
private char[] pw;
// We’ll use this to look up the keystore in the default location.
// You can specify a password if you like, but this will also
// work if you pass null (in which case the keystore isn’t
// verified).
public KeyStoreHandler(char[] pw) {
// Make a private copy so the original can be collected so
// that other objects can’t locate it.
if (pw != null) {
this.pw = new char[pw.length];
System.arraycopy(pw, 0, this.pw, 0, pw.length);
}
else this.pw = null;
// Load from the default location
try {
ks = KeyStore.getInstance(KeyStore.getDefaultType( ));
String fname = System.getProperty(“user.home”) +
File.separator + “.keystore”;
FileInputStream fis = new FileInputStream(fname);
ks.load(fis, pw);
} catch (Exception e) {
throw new IllegalArgumentException(e.toString( ));
}
}
public KeyStore getKeyStore( ) {
return ks;
}
// Store to the default location
public void store( ) throws FileNotFoundException,
KeyStoreException, IOException,
NoSuchAlgorithmException,
CertificateException {
// If we didn’t read with a password, we can’t store
if (pw == null) {
throw new IllegalArgumentException(“Can’t store w/o pw”);
}
FileOutputStream fos = new FileOutputStream(
System.getProperty(“user.home”) +
File.separator + “.keystore”);
ks.store(fos, pw);
fos.close( );
}
public static void main(String args[]) {
try {
KeyStore ks = new KeyStoreHandler(null).getKeyStore( );
if (ks.isKeyEntry(args[0])) {
System.out.println(args[0] +
” is a key entry in the keystore”);
char c[] = new char[args[1].length( )];
args[1].getChars(0, c.length, c, 0);
System.out.println(“The private key for ” + args[0] +
” is ” + ks.getKey(args[0], c));
java.security.cert.Certificate certs[] =
ks.getCertificateChain(args[0]);
if (certs[0] instanceof X509Certificate) {
X509Certificate x509 = (X509Certificate) certs[0];
System.out.println(args[0] + ” is really ” +
x509.getSubjectDN( ));
}
if (certs[certs.length − 1] instanceof
X509Certificate) {
X509Certificate x509 = (X509Certificate)
certs[certs.length − 1];
System.out.println(args[0] + ” was verified by ” +
x509.getIssuerDN( ));
else if (ks.isCertificateEntry(args[0])) {
System.out.println(args[0] +
” is a certificate entry in the keystore”);
java.security.cert.Certificate c =
ks.getCertificate(args[0]);
if (c instanceof X509Certificate) {
X509Certificate x509 = (X509Certificate) c;
System.out.println(args[0] + ” is really ” +
x509.getSubjectDN( ));
System.out.println(args[0] + ” was verified by ” +
x509.getIssuerDN( ));
else {
System.out.println(args[0] +
” is unknown to this keystore”);
}
} catch (Exception e) {
e.printStackTrace( );
}
}
}

数字签名

发表评论

电子邮件地址不会被公开。 必填项已用*标注

− 二 = 1