/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.harmony.security.provider.crypto; import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.InvalidParameterException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.Signature; import java.security.SignatureException; import java.security.interfaces.DSAKey; import java.security.interfaces.DSAParams; import java.security.interfaces.DSAPrivateKey; import java.security.interfaces.DSAPublicKey; public class SHA1withDSA_SignatureImpl extends Signature { private MessageDigest msgDigest; private DSAKey dsaKey; /** * The solo constructor. */ public SHA1withDSA_SignatureImpl() throws NoSuchAlgorithmException { super("SHA1withDSA"); msgDigest = MessageDigest.getInstance("SHA1"); } /** * Deprecated method. * * @return * null */ protected Object engineGetParameter(String param) throws InvalidParameterException { if (param == null) { throw new NullPointerException("param == null"); } return null; } /** * Initializes this signature object with PrivateKey object * passed as argument to the method. * * @params * privateKey DSAPrivateKey object * @throws * InvalidKeyException if privateKey is not DSAPrivateKey object */ protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { DSAParams params; // parameters and private key BigInteger p, q, x; int n; if (privateKey == null || !(privateKey instanceof DSAPrivateKey)) { throw new InvalidKeyException(); } params = ((DSAPrivateKey) privateKey).getParams(); p = params.getP(); q = params.getQ(); x = ((DSAPrivateKey) privateKey).getX(); // checks described in DSA standard n = p.bitLength(); if (p.compareTo(BigInteger.valueOf(1)) != 1 || n < 512 || n > 1024 || (n & 077) != 0) { throw new InvalidKeyException("bad p"); } if (q.signum() != 1 && q.bitLength() != 160) { throw new InvalidKeyException("bad q"); } if (x.signum() != 1 || x.compareTo(q) != -1) { throw new InvalidKeyException("x <= 0 || x >= q"); } dsaKey = (DSAKey) privateKey; msgDigest.reset(); } /** * Initializes this signature object with PublicKey object * passed as argument to the method. * * @params * publicKey DSAPublicKey object * @throws * InvalidKeyException if publicKey is not DSAPublicKey object */ protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { // parameters and public key BigInteger p, q, y; int n1; if (publicKey == null || !(publicKey instanceof DSAPublicKey)) { throw new InvalidKeyException("publicKey is not an instance of DSAPublicKey"); } DSAParams params = ((DSAPublicKey) publicKey).getParams(); p = params.getP(); q = params.getQ(); y = ((DSAPublicKey) publicKey).getY(); // checks described in DSA standard n1 = p.bitLength(); if (p.compareTo(BigInteger.valueOf(1)) != 1 || n1 < 512 || n1 > 1024 || (n1 & 077) != 0) { throw new InvalidKeyException("bad p"); } if (q.signum() != 1 || q.bitLength() != 160) { throw new InvalidKeyException("bad q"); } if (y.signum() != 1) { throw new InvalidKeyException("y <= 0"); } dsaKey = (DSAKey) publicKey; msgDigest.reset(); } /* * Deprecated method. * * @throws * InvalidParameterException */ protected void engineSetParameter(String param, Object value) throws InvalidParameterException { if (param == null) { throw new NullPointerException("param == null"); } throw new InvalidParameterException("invalid parameter for this engine"); } /** * Returns signature bytes as byte array containing * ASN1 representation for two BigInteger objects * which is SEQUENCE of two INTEGERS. * Length of sequence varies from less than 46 to 48. * * Resets object to the state it was in * when previous call to either "initSign" method was called. * * @return * byte array containing signature in ASN1 representation * @throws * SignatureException if object's state is not SIGN or * signature algorithm cannot process data */ protected byte[] engineSign() throws SignatureException { // names of below BigIntegers are the same as they are defined in DSA standard BigInteger r = null; BigInteger s = null; BigInteger k = null; // parameters and private key BigInteger p, q, g, x; // BigInteger for message digest BigInteger digestBI; // various byte array being used in computing signature byte[] randomBytes; byte[] rBytes; byte[] sBytes; byte[] signature; int n, n1, n2; DSAParams params; if (appRandom == null) { appRandom = new SecureRandom(); } params = dsaKey.getParams(); p = params.getP(); q = params.getQ(); g = params.getG(); x = ((DSAPrivateKey) dsaKey).getX(); // forming signature according algorithm described in chapter 5 of DSA standard digestBI = new BigInteger(1, msgDigest.digest()); randomBytes = new byte[20]; for (;;) { appRandom.nextBytes(randomBytes); k = new BigInteger(1, randomBytes); if (k.compareTo(q) != -1) { continue; } r = g.modPow(k, p).mod(q); if (r.signum() == 0) { continue; } s = k.modInverse(q).multiply(digestBI.add(x.multiply(r)).mod(q)) .mod(q); if (s.signum() != 0) { break; } } // forming signature's ASN1 representation which is SEQUENCE of two INTEGERs // rBytes = r.toByteArray(); n1 = rBytes.length; if ((rBytes[0] & 0x80) != 0) { n1++; } sBytes = s.toByteArray(); n2 = sBytes.length; if ((sBytes[0] & 0x80) != 0) { n2++; } signature = new byte[6 + n1 + n2]; // 48 is max. possible length of signature signature[0] = (byte) 0x30; // ASN1 SEQUENCE tag signature[1] = (byte) (4 + n1 + n2); // total length of two INTEGERs signature[2] = (byte) 0x02; // ASN1 INTEGER tag signature[3] = (byte) n1; // length of r signature[4 + n1] = (byte) 0x02; // ASN1 INTEGER tag signature[5 + n1] = (byte) n2; // length of s if (n1 == rBytes.length) { n = 4; } else { n = 5; } System.arraycopy(rBytes, 0, signature, n, rBytes.length); if (n2 == sBytes.length) { n = 6 + n1; } else { n = 7 + n1; } System.arraycopy(sBytes, 0, signature, n, sBytes.length); return signature; } /** * Updates data to sign or to verify. * * @params * b byte to update * @throws * SignatureException if object was not initialized for signing or verifying */ protected void engineUpdate(byte b) throws SignatureException { msgDigest.update(b); } /** * Updates data to sign or to verify. * * @params * b byte array containing bytes to update * @params * off offset in byte array to start from * @params * len number of bytes to use for updating * @throws * SignatureException if object was not initialized for signing or verifying */ protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { msgDigest.update(b, off, len); } private boolean checkSignature(byte[] sigBytes, int offset, int length) throws SignatureException { // names of below BigIntegers are the same as they are defined in DSA standard BigInteger r, s, w; BigInteger u1, u2, v; // parameters and public key BigInteger p, q, g, y; DSAParams params; int n1, n2; byte[] bytes; byte[] digest; // checking up on signature's ASN1 try { byte dummy; n1 = sigBytes[offset + 3]; n2 = sigBytes[offset + n1 + 5]; if (sigBytes[offset + 0] != 0x30 || sigBytes[offset + 2] != 2 || sigBytes[offset + n1 + 4] != 2 || sigBytes[offset + 1] != (n1 + n2 + 4) || n1 > 21 || n2 > 21 || (length != 0 && (sigBytes[offset + 1] + 2) > length)) { throw new SignatureException("signature bytes have invalid encoding"); } dummy = sigBytes[5 + n1 + n2]; // to check length of sigBytes } catch (ArrayIndexOutOfBoundsException e) { throw new SignatureException("bad argument: byte[] is too small"); } digest = msgDigest.digest(); bytes = new byte[n1]; System.arraycopy(sigBytes, offset + 4, bytes, 0, n1); r = new BigInteger(bytes); bytes = new byte[n2]; System.arraycopy(sigBytes, offset + 6 + n1, bytes, 0, n2); s = new BigInteger(bytes); params = dsaKey.getParams(); p = params.getP(); q = params.getQ(); g = params.getG(); y = ((DSAPublicKey) dsaKey).getY(); // forming signature according algorithm described in chapter 6 of DSA standard if (r.signum() != 1 || r.compareTo(q) != -1 || s.signum() != 1 || s.compareTo(q) != -1) { return false; } w = s.modInverse(q); u1 = (new BigInteger(1, digest)).multiply(w).mod(q); u2 = r.multiply(w).mod(q); v = g.modPow(u1, p).multiply(y.modPow(u2, p)).mod(p).mod(q); if (v.compareTo(r) != 0) { return false; } return true; } /** * Verifies the signature bytes. * * @params * sigBytes byte array with signature bytes to verify. * @return * true if signature bytes were verified, false otherwise * @throws * SignatureException if object's state is not VERIFY or * signature format is not ASN1 representation or * signature algorithm cannot process data */ protected boolean engineVerify(byte[] sigBytes) throws SignatureException { if (sigBytes == null) { throw new NullPointerException("sigBytes == null"); } return checkSignature(sigBytes, 0, 0); } /** * Verifies the signature bytes. * * @params * sigBytes byte array with signature bytes to verify. * @params * offset index in sigBytes to start from * @params * length number of bytes allotted for signature * @return * true if signature bytes were verified, false otherwise * @throws * SignatureException if object's state is not VERIFY or * signature format is not ASN1 representation or * signature algorithm cannot process data */ protected boolean engineVerify(byte[] sigBytes, int offset, int length) throws SignatureException { return checkSignature(sigBytes, offset, length); } }