Java序列化Serializable初识(2)——与SignedObject结合使用

上一篇文章让我们简单理解了Java中序列化的知识,以及我们Serializable的使用过程。
需要的话请查看上一节内容:
Java序列化Serializable初识

实验

那么我们今天先经行一个小实验。接着上一节的内容。实验内容如下:
(1)先将一个Person类序列化并写入文件,得到文件内容如下。

(2)篡改文件中的内容,将末尾的Jack改成Jeck。

(3)反序列化,得到Person并在控制台输出。

代码在此不展示了,看过上一节的人应该很容易实现该实验内容。根据实验结果发现我们的信息被篡改了,而我们并不知道。如果这发生在网络传输中想想有多可怕,广且根据我们上面的实验,篡改竟然如此容易。因此我们需要收到我们消息之后验证我们的消息有没有被篡改,要怎么办呢?这个时候就要用到密码学中的认证相关知识。这一块知识不太理解的请看下面这篇文章:
数据公钥加密和认证中的私钥公钥
该文章出自月光博客,感谢该博主的分享。
这个认证过程在Java中实现就要使用我们的SignedObject类了。

SignedObject类介绍

如果不熟悉认证过程的去看上边那篇文章,不然下面你看不懂的。下面摘自Java API SignedObject类。
SignedObject 是一个用来创建实际运行时对象的类,在检测不到这些对象的情况下,其完整性不会遭受损害。
更明确地说,SignedObject 包含另外一个 Serializable 对象,即(要)签名的对象及其签名
签名对象是对原始对象的“深层复制”(以序列化形式)。一旦生成了副本,对原始对象的进一步操作就不再影响该副本。
底层签名算法是由传递给构造方法和 verify 方法的 Signature 对象指定。下面是签名的典型用法

Signature signingEngine = Signature.getInstance(algorithm,provider);
SignedObject so = new SignedObject(myobject, signingKey,signingEngine);

下面是对验证的典型用法(已接收到 SignedObject so):

Signature verificationEngine = Signature.getInstance(algorithm, provider);
if (so.verify(publickey, verificationEngine))
    try {
        Object myobj = so.getObject();
    } catch (java.lang.ClassNotFoundException e) {};

以下几点需要注意。首先,不需要初始化签名或验证引擎,因为它将在构造方法和 verify 方法中被重新初始化。其次,为了成功验证,指定的公钥必须是与用来生成 SignedObject 的私钥对应的公钥。
更为重要的是,出于灵活性考虑,构造方法和 verify 方法允许使用自定义的签名引擎,这样可以实现未作为加密提供者一部分正常安装的签名算法。不过,编程人员编写知道使用什么 Signature 引擎的校验器代码至关重要,因为将调用它自己的 verify 方法的实现来验证签名。换句话说,恶意 Signature 在尝试绕过安全检查的验证中会选择始终返回 true。
在所有算法中,签名算法可以是使用 DSA 和 SHA-1 的 NIST 标准 DSA。该算法使用与签名惯例相同的惯例来指定。例如,可以将使用 SHA-1 消息分类算法的 DSA 算法指定为 “SHA/DSA” 或 “SHA-1/DSA”(它们是等效的)。如果使用 RSA 标准,消息分类算法将有多种选择,例如,可将签名算法指定为 “MD2/RSA”、”MD5/RSA” 或 “SHA-1/RSA”。没有默认的算法名称,所以必须为其指定名称。
Cryptography Package Provider 的名称也是由构造方法和 verify 方法的 Signature 参数指定的。如果未指定提供者,则使用默认的提供者。每种安装都可以配置为将特定的提供者作为默认提供者。

SignedObject的使用方法

为了方便的对文件读写我们,我们将对文件读写的操作再次封装:
IOObjectFile类:

public class IOObjectFile {
    public static boolean outputObjectToFile(Serializable object,String file){
        FileOutputStream fileOutputStream = null ;
        ObjectOutputStream objectOutputStream = null ;
        try {
            fileOutputStream = new FileOutputStream(file);
            objectOutputStream = new ObjectOutputStream(fileOutputStream);
            objectOutputStream.writeObject(object);
            fileOutputStream.close();
            objectOutputStream.close();
            return true;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return false;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } 
    }

    public static Object inputObjectFromFile(String file) throws IOException, ClassNotFoundException{
        FileInputStream fileInputStream = null ;
        ObjectInputStream objectInputStream = null ;
        fileInputStream = new FileInputStream(file);
        objectInputStream = new ObjectInputStream(fileInputStream);
        Object object = objectInputStream.readObject();
        objectInputStream.close();
        fileInputStream.close();
        return object;
    }
}

Serializable的实现类Person:

public class Person implements Serializable{

    private static final long serialVersionUID = 987882963008866333L;

    private int age;
    private String firstName;  
    private String lastName;
    private Gender gender;

    enum Gender{
        MALE,FEMALE
    }

    public Person() {
        super();
    }

    public Person(int age, String firstName, String lastName) {
        super();
        this.age = age;
        this.firstName = firstName;
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        return "Person [age=" + age + ", firstName=" + firstName
                + ", lastName=" + lastName + ", gender=" + gender + "]";
    }
}

主函数类:

public class SignMain {

    public static void main(String[] args) {
        Person jack = new Person(22,"Jack","Chai") ;
        try {
            // 1. 生成公钥私钥对
            KeyPairGenerator keyPairGenerator;
            keyPairGenerator = KeyPairGenerator.getInstance("DSA");   
            keyPairGenerator.initialize(1024);
            KeyPair keyPair = keyPairGenerator.genKeyPair();
            PrivateKey privateKey = keyPair.getPrivate();
            PublicKey publicKey = keyPair.getPublic();
            // 2. 输出key到文件
            if(IOObjectFile.outputObjectToFile(publicKey, "storage\\publicKey.txt")){
                System.out.println("输出publicKey成功");
            }else{
                System.out.println("输出publicKey失败");
            }
            // 3. 生成签名,并用SignedObject类包装
            Signature signingEngine = Signature.getInstance("DSA");
            SignedObject so = null;
            so = new SignedObject(jack, privateKey, signingEngine);
            // 4. 输出Object到文件
            if (IOObjectFile.outputObjectToFile(so, "storage\\serializable.txt")) {
                System.out.println("输出SignedObject成功");
            }else{
                System.out.println("输出SignedObject失败");
            }
            // 5. 从文件输入Key
            PublicKey publicKey2 = null;
            publicKey2 = (PublicKey) IOObjectFile.inputObjectFromFile("storage\\publicKey.txt");
            // 6. 从文件输入SignedObject,用同样的算法获得签名,
            // 在调用verify方法验证是否被篡改
            SignedObject signedObject = (SignedObject)IOObjectFile.inputObjectFromFile("storage\\serializable.txt");
            Signature verificationEngine = Signature.getInstance("DSA");
            if (signedObject.verify(publicKey2, verificationEngine)){
                try {
                    Person person = (Person) signedObject.getObject();
                    System.out.println(person.toString());
                } catch (java.lang.ClassNotFoundException e) {
                }
            }else{
                System.out.println("内容已被篡改");
            }
        } catch (NoSuchAlgorithmException e1) {
            e1.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (SignatureException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException | IOException e) {
            e.printStackTrace();
        }
    }
}

运行结果:
运行结果
最后验证成功并输出了我们的Person类。这时我们去篡改一下文件中的内容试一试。
依然是将Jack改为Jeck。
篡改内容
然后我们将上面向文件输出的代码注释掉。只读取认证,运行结果:
运行结果
这时候篡改了信息之后,发生了错误,该异常表示什么呢?查看API解释为:
当从对象流中读取的控制信息与内部一致性检查相冲突时,抛出此异常。如此看来被篡改没那么容易了。 捕捉异常做处理:

SignedObject signedObject = null;
try {
    signedObject = (SignedObject)IOObjectFile.inputObjectFromFile("storage\\serializable.txt");
} catch (StreamCorruptedException e) {
    System.out.println("控制信息与内部一致性检查相冲突!");
    return ;
}

运行结果:

通过这样的异常捕捉我们可以做其他处理。我还不能篡改之后使其不抛出异常,如果有哪位大神可以,求分享。基本上这就是SignedObject的作用。介绍这个类的文章很少,自己仅是查看文档摸索出的,如有错误请各位大神斧正。

你可能感兴趣的:(java,序列化)