20.5 Digital Signature Digital signature can guarantee message integrity and authenticity in an open network environment. To generate a signature, you first calculate a hash (also called digest) of your message. Then you encrypt that hash with your private key to generate a signature. The party at the receiving end first decrypts your signature into a hash using your public key. Then she calculates the hash from your message. If the two hashes match, the message is indeed from you and unaltered. The signing and verification processes are demonstrated in Figures 20.5 and 20.6 respectively.

 If someone altered the message during its transmission and generated a new hash based on the modified message, public key algorithms guarantee that he cannot produce a matching signature without knowing your private key. The two parties must share four pieces of information: the message itself, its digital signature, the hash and signing algorithms, and the public key. Our demo MIDlet for digital signature is shown in Figure 20.7.
 Resources" section.
20.5.1 Bouncy Castle Bouncy Castle supports DSA, RSA, and ECC (Elliptic Curve Cryptography) digital signature algorithms. But only RSA seems to have an acceptable performance on MIDP devices. So, the code example here only shows how to work with RSA signatures. Since the RSA key pair generation and serialization part is already covered by the last section, we go directly to the code for signature generation and verification (Listing 20.34).Listing 20.34. Sign and verify a message using the RSA algorithm in BC
public byte [] RSASign (byte [] toSign) throws Exception { if (RSAprivKey == null) throw new Exception("Generate RSA keys first!"); SHA1Digest dig = new SHA1Digest(); RSAEngine eng = new RSAEngine(); PSSSigner signer = new PSSSigner(eng, dig, 64); signer.init(true, RSAprivKey); signer.update(toSign, O, toSign.length); return signer.generateSignature(); } public boolean RSAVerify (byte [] mesg, byte [] sig) throws Exception { if (RSApubKey == null) throw new Exception("Generate RSA keys first!"); SHA1Digest dig = new SHA1Digest(); RSAEngine eng = new RSAEngine(); PSSSigner signer = new PSSSigner(eng, dig, 64); signer.init(false, RSApubKey); signer.update(mesg, 0, mesg.length); return signer.verifySignature(sig); }
20.5.2 IAIK JCE-ME Our IAIK digital signature example also uses the RSA algorithm. So, again, we do not show any code for the key generation and serialization. Listing 20.35 demonstrates the signing and verification processes.Listing 20.35. RSA digital signature sign and verify methods in IAIK
// RSA signature public byte [] RSASign (byte [] toSign) throws Exception { if (RSAprivKey == null) throw new Exception("Generate RSA keys first!"); Signature sharsa = Signature.getInstance("SHA1withRSA"); sharsa.initSign(RSAprivKey, null); sharsa.update(toSign); return sharsa.sign(); } // RSA signature verification public boolean RSAVerify (byte [] mesg, byte [] sig) throws Exception { if (RSApubKey == null) throw new Exception("Generate RSA keys first!"); Signature sharsa = Signature.getInstance("SHA1withRSA"); sharsa.initVerify(RSApubKey); sharsa.update(mesg); return sharsa.verify(sig); }
20.5.3 Phaos Micro Foundation Phaos MF supports DSA and RSA digital signatures. DSA is certainly slower than RSA. But since we already have two RSA examples, we will have a look at a DSA example here. Generating DSA key pairs using the Phaos MF API is illustrated in Listing 20.36. Variable DSApubKeyDer is not the public key itself. Rather, it is a byte array representation of the public key in DER (Distinguished Encoding Rules) format. The public key can be reconstructed from this array using an appropriate algorithm identifier (see Listing 20.38).Listing 20.36. Phaos MF DSA key pair generation
// 1024-bit DSA key private DSAPrivateKey DSAprivKey; // This is the DSA public key data you can serialize private byte [] DSApubKeyDer; public void generateDSAKeyPair () throws Exception { DSAKeyGenParams params = new DSAKeyGenParams(1024, RandomBitsSource.getDefault()); KeyPairGenerator kpg = KeyPairGenerator.getInstance(OIDList.DSA, params); KeyPair kp = kpg.generateKeyPair(); DSAprivKey = (DSAPrivateKey)kp.privateKey; ByteArrayOutputStream baos = new ByteArrayOutputStream (); ((DSAPublicKey)kp.publicKey).output(baos); DSApubKeyDer = baos.toByteArray(); return; }
The Phaos DSA key pair serialization and deserialization processes are illustrated in Listings 20.37 and 20.38.Listing 20.37. Phaos MF DSA key pair serialization
DSAPublicKey pubKey = (DSAPublicKey)kp.publicKey; DSAPrivateKey privKey = (DSAPrivateKey)kp.privateKey; pubKey.output(new FileOutputStream(outdir + "DSApubKey.der")); privKey.output(new FileOutputStream(outdir + "DSAprivKey.der")); DSAParams dsaParams = new DSAParams( new ByteArrayInputStream( privKey.createAlgID(true).encodeParameters())); dsaParams.output(new FileOutputStream(outdir + "DSAparams.der"));
Listing 20.38. Phaos MF DSA key pair deserialization
// The DSA private key is = c.getResourceAsStream("/keys/DSAparams.der"); DSAParams params = new DSAParams(is); AlgorithmIdentifier algID = getDSAalgID(params); is.close(); is = c.getResourceAsStream("/keys/DSAprivKey.der"); DSAprivKey = new DSAPrivateKey(algID, is); is.close(); // The DSA public key byte array is = c.getResourceAsStream("/keys/DSApubKey.der"); baos = new ByteArrayOutputStream(); b = new byte[1]; while ( is.read(b) != -1 ) { baos.write(b); } is.close(); DSApubKeyDer = baos.toByteArray(); baos.close();
The method getDSAalgID() (Listing 20.38) retrieves the algorithm identifier from DSA parameters. It is also used in DSA sign and verify examples (Listing 20.39).Listing 20.39. Phaos MF DSA sign and verify methods
private AlgorithmIdentifier getDSAalgID (DSAParams params) throws Exception { ByteArrayOutputStream paramsOut = new ByteArrayOutputStream(); params.output(paramsOut); paramsOut.close(); return new AlgorithmIdentifier(OIDList.DSA, paramsOut.toByteArray()); } public byte [] DSASign (byte [] toSign) throws Exception { if (DSAprivKey == null) throw new Exception("Generate DSA keys first!"); Signature signature = Signature.getInstance(AlgIDList.SHA1_WITH_DSA, DSAprivKey); byte [] result = signature.sign(toSign, 0, toSign.length).toByteArray(true); DSAParams params = new DSAParams( new ByteArrayInputStream( DSAprivKey.createAlgID(true).encodeParameters())); AlgorithmIdentifier algID = getDSAalgID (params); ByteArrayOutputStream baos = new ByteArrayOutputStream (); algID.output(baos); baos.write(result, 0, result.length); baos.flush(); baos.close(); return baos.toByteArray(); } public boolean DSAVerify (byte [] mesg, byte [] sig) throws Exception { InputStream is = new ByteArrayInputStream(sig); AlgorithmIdentifier algID = new AlgorithmIdentifier(is); PooledArray sigBytes = ByteArrayPool.getArray(is.available()); is.read(sigBytes.buffer, 0, sigBytes.length); is.close(); DSAPublicKey DSApubKey = new DSAPublicKey(algID, new ByteArrayInputStream(DSApubKeyDer)); Signature signature = Signature.getInstance(AlgIDList.SHA1_WITH_DSA, DSApubKey); return signature.verify(mesg, 0, mesg.length, sigBytes.buffer, 0, sigBytes.length); }
20.5.4 NTRU jNeo The NTRU digital signature algorithm (NTRUSign) uses different keys from the NTRUEncrypt algorithm. The key generation process is simple (Listing 20.40).NoteUnlike RSA and DSA, the public and private keys in the NTRU digital signature algorithm are not exchangeable. You can sign only with the private key and verify only with the public key.Listing 20.40. Generate an NTRU signature key pair
public void generateNTRUsgnKeys () throws Exception { NTRUsgnKeys = new SgnKeys(ctx, NTRUConst.NTRU_KEYSTRENGTH_251, NTRUConst.NTRU_SHA1_HASH); return; }
The serialization and deserialization of the key pair are illustrated in Listing 20.41 and 20.42.Listing 20.41. Serialize the NTRUSign key pair
byte [] pubKey = NTRUsgnKeys.exportPrivKey(null, 0); out = new FileOutputStream(outdir + "SgnPubKey.dat"); out.write(pubKey); out.flush(); out.close(); byte [] privKey = NTRUsgnKeys.exportPrivKey(null, 0); out = new FileOutputStream(outdir + "SgnPrivKey.dat"); out.write(privKey); out.flush(); out.close();
Listing 20.42. Deserialize the NTRUSign key pair
is = c.getResourceAsStream("/keys/SgnPubKey.dat"); byte [] sgnPubKeyData = readFromStream(is); is.close(); is = c.getResourceAsStream("/keys/SgnPrivKey.dat"); byte [] sgnPrivKeyData = readFromStream(is); is.close(); NTRUsgnKeys = new SgnKeys (sgnPubKeyData, 0, sgnPubKeyData.length, sgnPrivKeyData, 0, sgnPrivKeyData.length);
The NTRU signature generation and verification processes are illustrated in Listing 20.43.Listing 20.43. NTRUSign signature signing and verification using jNeo
public byte [] NTRUSign (byte [] message) throws Exception { if ( NTRUsgnKeys == null ) throw new Exception("Generate keys first!"); MessageDigest dig = new MessageDigest(NTRUConst.NTRU_SHA160_HASH); Signature sig = new Signature(NTRUConst.NTRU_KEYSTRENGTH_251, NTRUConst.NTRU_SHA160_HASH); dig.updateMessageDigest(message, 0, message.length); dig.completeMessageDigest(); sig.sign(ctx, NTRUsgnKeys, dig); return sig.export(); } public boolean NTRUVerify (byte [] message, byte [] sigData) throws Exception { Signature sig = new Signature(sigData, 0, sigData.length); MessageDigest dig = new MessageDigest(sig.getHashAlg()); dig.updateMessageDigest(message, 0, message.length); dig.completeMessageDigest(); try { sig.verify(ctx, NTRUsgnKeys, dig); return true; } catch (NTRUException e) { return false; } }
|