Using the Signature Class

Verify the signature within the embedded object with the given key and signature engine. The signature engine parameter may be obtained by calling the getInstance method of the Signature class. The underlying signature engine may throw an InvalidKeyException or SignatureException . Well use this class in examples later in this chapter.

12.1.3 Signing and Certificates

In the previous examples, we specified on the command line the name of the entity that we assumed generated the signature in the file. This was necessary because the file contained only the actual signature of the entity and the data that was signed; it did not contain any information about who the signer actually is. Thats fine for an example, but it is not always appropriate in a real application. We could have asked the user for the name of the entity that was supposed to have signed the data, but that course is fraught with potential errors: The user could have no idea what names are in the keystore of the application. Especially in a corporate environment, users may not know what data the keystore database might contain. • The user could get the name of the keystore alias wrong. Say that the application asks the user to enter the name of the signer; the user, knowing that the data came from me, may enter sdo as the alias of the identity. What the user may not remember is that when the keystore was first created, she received a public key from the San Diego Oil company; that public key was entered into the keystore with the alias sdo. When my identity was added to the keystore, a different alias had to be chosen, so my public key was added with the alias ScottOaks. But that was a long time ago, now forgotten, and because I use the sdo moniker all over my writings, the user assumes that I am the sdo in the keystore. And so the wrong alias will be chosen, and the signature verification will fail when it should have succeeded. • For these reasons, it makes more sense to include the public key with the signature and the signed data. This allows the application to find the identity based on the unique public key in order to determine who the signer of the data is. We could do that by simply sending the encoded public key with the signature and data. A better solution, however, would be to send the certificate that verifies the public key. That way, if the public key is not found in the database, the credentials of the certificate can be presented to the user, and the user can have the opportunity to decide on the fly if the particular entity should be trusted. Although an embedding of signature, data, and certificate is very common, the SignedObject class does not include the capability to contain a certificate. So well use the SignedObject class in this example, but well still need an object that contains the signed object and the certificate. Wed like to do this by extending the SignedObject class, but since that class is final were forced to adopt this approach: package javasec.samples.ch12; import java.io.; import java.security.; import java.security.cert.; public class Message implements Serializable { SignedObject object; transient java.security.cert.Certificate certificate; private void writeObjectObjectOutputStream out throws IOException { out.defaultWriteObject ; try { out.writeObjectcertificate.getEncoded ; } catch CertificateEncodingException cee { throw new IOExceptionCant serialize object + cee; } } private void readObjectObjectInputStream in throws IOException, ClassNotFoundException { in.defaultReadObject ; try { byte b[] = byte [] in.readObject ; CertificateFactory cf = CertificateFactory.getInstanceX509; certificate = cf.generateCertificatenew ByteArrayInputStreamb; } catch CertificateException ce { throw new IOExceptionCant de−serialize object + ce; } } } Weve made the certificate variable in this class transient and have explicitly serialized and deserialized it using its external encoding. As we discussed in Chapter 9, whenever we have an embedded certificate or key, we should follow a procedure like this to ensure that the receiving party is able to deserialize the class. As it turns out, the X509 certificate implementation that comes with the SDK that is, the sun.security.x509.X509CertImpl class also overrides the writeObject and readObject methods, so if we serialize a certificate explicitly, the encoded data is written to or read from the file. It is not sufficient to rely upon that, however −− if we use the default serialization methods for the Message class, a reference to the sun.security.x509.X509CertImpl class is embedded into the serialized stream. A user with another security provider and hence a different implementation of the X509Certificate class would not be able to deserialize the stream because there is no access to the Sun implementation of the X509Certificate class. Explicitly serializing and deserializing the certificate as weve done here avoids embedding any reference to the provider class and makes the data file more portable. When we save the message to the file, we now have to make sure that we save a certificate with it. Other than that, changes to the class are minor: package javasec.samples.ch12; import java.io.; import java.security.; import javasec.samples.ch10.KeyStoreHandler; public class SendObject { public static void mainString args[] { try { FileOutputStream fos = new FileOutputStreamtest.obj; ObjectOutputStream oos = new ObjectOutputStreamfos; KeyStoreHandler ksh = new KeyStoreHandlernull; KeyStore ks = ksh.getKeyStore ; java.security.cert.Certificate certs[] = ks.getCertificateChainargs[0]; PrivateKey pk = PrivateKey ks.getKeyargs[0], args[1].toCharArray ; Message m = new Message ; m.object = new SignedObject 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., pk, Signature.getInstanceMD5withRSA; m.certificate = certs[0]; oos.writeObjectm; } catch Exception e { System.out.printlne; } } } Essentially, the only change we have made is to use the new Message class to store the data that were sending. Retrieving the data is now more complicated, since we must verify both the signature in the signed object and the identity of the authority that signed the embedded certificate: package javasec.samples.ch12; import java.io.; import java.security.; import java.security.cert.; import java.util.; import javasec.samples.ch10.KeyStoreHandler; public class ReceiveObject { private static void verifySignerjava.security.cert.Certificate c, String name throws CertificateException { java.security.cert.Certificate issuerCert = null; X509Certificate sCert = null; KeyStore ks = null; try { KeyStoreHandler ksh = new KeyStoreHandlernull; ks = ksh.getKeyStore ; } catch Exception e { throw new CertificateExceptionInvalid keystore; } try { String signer = ks.getCertificateAliasc; if signer =null{ System.out.printlnWe know the signer as + signer; return; } for Enumeration alias = ks.aliases ; alias.hasMoreElements ;{ String s = String alias.nextElement ; try { sCert = X509Certificate ks.getCertificates; } catch Exception e { continue; } if name.equalssCert.getSubjectDN.getName { issuerCert = sCert; break; } } } catchKeyStoreException kse { throw new CertificateExceptionInvalid keystore; } if issuerCert == null { throw new CertificateExceptionNo such certificate; } try { c.verifyissuerCert.getPublicKey ; } catch Exception e { throw new CertificateExceptione.toString ; } } private static void processCertificateX509Certificate x509 throws CertificateParsingException { Principal p; p = x509.getSubjectDN ; System.out.printlnThis message was signed by + p.getName ; p = x509.getIssuerDN ; System.out.printlnThis certificate was provided by + p.getName ; try { verifySignerx509, p.getName ; } catch CertificateException ce { System.out.printlnWe dont know the certificate signer; } try { x509.checkValidity ; } catch CertificateExpiredException cee { System.out.printlnThat certificate is no longer valid; } catch CertificateNotYetValidException cnyve { System.out.printlnThat certificate is not yet valid; } } public static void mainString args[] { try { FileInputStream fis = new FileInputStreamtest.obj; ObjectInputStream ois = new ObjectInputStreamfis; Object o = ois.readObject ; if o instanceof Message { Message m = Message o; System.out.printlnReceived message; processCertificateX509Certificate m.certificate; PublicKey pk = m.certificate.getPublicKey ; if m.object.verifypk, Signature.getInstanceMD5withRSA { System.out.printlnMessage is valid; System.out.printlnm.object.getObject ; } else System.out.printlnSignature is invalid; } else System.out.printlnMessage is corrupted; } catch Exception e { e.printStackTrace ; } } } Weve seen most of this code in previous chapters; in particular, the processCertificate method uses the standard certificate methods to extract and print information about the certificate. The new code for us is primarily in the verifySigner method, where we search the entire keystore for a name that matches the issuer of the certificate that was sent to us. If we find a match, we use the corresponding public key to verify the certificate we received. Chapter 12. Digital Signatures This method shows yet another need for an alternate implementation of the KeyStore class −− if you have to search the entire list of keys for a matching certificate like this, you clearly dont want to perform a linear search each time. An alternate keystore could provide a more efficient means of searching for certificates.

12.2 Signed Classes

One of the primary applications of digital signatures in Java is to create and verify signed classes. Signed classes allow the expansion of Javas sandbox in two ways: The policy file can insist that classes coming from a particular site be signed by a particular entity before the access controller will grant that particular set of permissions. In the policy file, such an entry contains a signedBy directive: grant signedBy sdo, codeBase http:piccolo.East.Sun.COM { java.io.FilePermission −, read,write; } This entry allows classes that are loaded from piccolo.East.Sun.COM to read and write any local files under the current directory only if the classes have been signed by sdo . • The security manager can cooperate with the class loader in order to determine whether or not a particular class is signed; the security manager is then free to grant permissions to that class based on its own internal policy. However, while this was an important technique in Java 1.1, it is rarely used in Java 2. • We talked about these operations throughout this book; in this section, well fill in the last details about how the digital signatures are created and verified. There are three necessary ingredients to expand the Java sandbox with signed classes: A method to create the signed class. The jarsigner utility is used for this. • A class loader that knows how to understand the digital signature associated with the class. The URLClassLoader class knows how to do this, but well show an example of how to do that for a custom class loader as well. • A security manager or access controller that grants the desired permissions based on the digital signature. The access controller will do this for us; well show how the security manager might do this directly in Appendix D. •

12.2.1 The jarsigner Tool

Signed jar files are managed with jarsigner . jarsigner uses the information in a keystore to look up information about a particular entity and uses that information either to sign or to verify a jar file. As we discussed in Chapter 10, the keystore that jarsigner uses is subject to the KeyStore class that has been installed into the virtual machine; if you have your own keystore implementation, jarsigner will be able to use it. Similarly, if you use the standard keystore implementation but hold the keys in a file other than the default .keystore file, jarsigner will allow you to use that other file as well. A signed jar file is identical to a standard jar file except that a signed jar file contains two additional entries: SIGNER.SF A file containing an SHA message digest for each class file in the archive. The digest is calculated from the three lines in the manifest for the class file. The base of this name SIGNER varies; it is typically based upon the alias of the keystore entry used to sign the archive. 230