jdk/src/share/classes/sun/security/ec/ECDSASignature.java
changeset 3492 e549cea58864
child 3863 8e0f58b1c072
equal deleted inserted replaced
3480:c197e38bf15a 3492:e549cea58864
       
     1 /*
       
     2  * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package sun.security.ec;
       
    27 
       
    28 import java.io.IOException;
       
    29 import java.nio.ByteBuffer;
       
    30 import java.math.BigInteger;
       
    31 import java.util.Arrays;
       
    32 
       
    33 import java.security.*;
       
    34 import java.security.interfaces.*;
       
    35 import java.security.spec.*;
       
    36 
       
    37 import sun.security.jca.JCAUtil;
       
    38 import sun.security.util.*;
       
    39 import sun.security.x509.AlgorithmId;
       
    40 
       
    41 /**
       
    42  * ECDSA signature implementation. This class currently supports the
       
    43  * following algorithm names:
       
    44  *
       
    45  *   . "NONEwithECDSA"
       
    46  *   . "SHA1withECDSA"
       
    47  *   . "SHA256withECDSA"
       
    48  *   . "SHA384withECDSA"
       
    49  *   . "SHA512withECDSA"
       
    50  *
       
    51  * @since   1.7
       
    52  */
       
    53 abstract class ECDSASignature extends SignatureSpi {
       
    54 
       
    55     // flag indicating whether the native ECC implementation is present
       
    56     private static boolean implementationPresent = true;
       
    57     static {
       
    58         try {
       
    59             AccessController.doPrivileged(new PrivilegedAction<Void>() {
       
    60                 public Void run() {
       
    61                     System.loadLibrary("sunecc");
       
    62                     return null;
       
    63                 }
       
    64             });
       
    65         } catch (UnsatisfiedLinkError e) {
       
    66             implementationPresent = false;
       
    67         }
       
    68     }
       
    69 
       
    70     // message digest implementation we use
       
    71     private final MessageDigest messageDigest;
       
    72 
       
    73     // supplied entropy
       
    74     private SecureRandom random;
       
    75 
       
    76     // flag indicating whether the digest has been reset
       
    77     private boolean needsReset;
       
    78 
       
    79     // private key, if initialized for signing
       
    80     private ECPrivateKey privateKey;
       
    81 
       
    82     // public key, if initialized for verifying
       
    83     private ECPublicKey publicKey;
       
    84 
       
    85     /**
       
    86      * Constructs a new ECDSASignature. Used by Raw subclass.
       
    87      *
       
    88      * @exception ProviderException if the native ECC library is unavailable.
       
    89      */
       
    90     ECDSASignature() {
       
    91         if (!implementationPresent) {
       
    92             throw new
       
    93                 ProviderException("ECDSA implementation is not available");
       
    94         }
       
    95         messageDigest = null;
       
    96     }
       
    97 
       
    98     /**
       
    99      * Constructs a new ECDSASignature. Used by subclasses.
       
   100      *
       
   101      * @exception ProviderException if the native ECC library is unavailable.
       
   102      */
       
   103     ECDSASignature(String digestName) {
       
   104         if (!implementationPresent) {
       
   105             throw new
       
   106                 ProviderException("ECDSA implementation is not available");
       
   107         }
       
   108 
       
   109         try {
       
   110             messageDigest = MessageDigest.getInstance(digestName);
       
   111         } catch (NoSuchAlgorithmException e) {
       
   112             throw new ProviderException(e);
       
   113         }
       
   114         needsReset = false;
       
   115     }
       
   116 
       
   117     // Nested class for NONEwithECDSA signatures
       
   118     public static final class Raw extends ECDSASignature {
       
   119 
       
   120         // the longest supported digest is 512 bits (SHA-512)
       
   121         private static final int RAW_ECDSA_MAX = 64;
       
   122 
       
   123         private final byte[] precomputedDigest;
       
   124         private int offset = 0;
       
   125 
       
   126         public Raw() {
       
   127             precomputedDigest = new byte[RAW_ECDSA_MAX];
       
   128         }
       
   129 
       
   130         // Stores the precomputed message digest value.
       
   131         @Override
       
   132         protected void engineUpdate(byte b) throws SignatureException {
       
   133             if (offset >= precomputedDigest.length) {
       
   134                 offset = RAW_ECDSA_MAX + 1;
       
   135                 return;
       
   136             }
       
   137             precomputedDigest[offset++] = b;
       
   138         }
       
   139 
       
   140         // Stores the precomputed message digest value.
       
   141         @Override
       
   142         protected void engineUpdate(byte[] b, int off, int len)
       
   143                 throws SignatureException {
       
   144             if (offset >= precomputedDigest.length) {
       
   145                 offset = RAW_ECDSA_MAX + 1;
       
   146                 return;
       
   147             }
       
   148             System.arraycopy(b, off, precomputedDigest, offset, len);
       
   149             offset += len;
       
   150         }
       
   151 
       
   152         // Stores the precomputed message digest value.
       
   153         @Override
       
   154         protected void engineUpdate(ByteBuffer byteBuffer) {
       
   155             int len = byteBuffer.remaining();
       
   156             if (len <= 0) {
       
   157                 return;
       
   158             }
       
   159             if (offset + len >= precomputedDigest.length) {
       
   160                 offset = RAW_ECDSA_MAX + 1;
       
   161                 return;
       
   162             }
       
   163             byteBuffer.get(precomputedDigest, offset, len);
       
   164             offset += len;
       
   165         }
       
   166 
       
   167         @Override
       
   168         protected void resetDigest(){
       
   169             offset = 0;
       
   170         }
       
   171 
       
   172         // Returns the precomputed message digest value.
       
   173         @Override
       
   174         protected byte[] getDigestValue() throws SignatureException {
       
   175             if (offset > RAW_ECDSA_MAX) {
       
   176                 throw new SignatureException("Message digest is too long");
       
   177 
       
   178             }
       
   179             byte[] result = new byte[offset];
       
   180             System.arraycopy(precomputedDigest, 0, result, 0, offset);
       
   181             offset = 0;
       
   182 
       
   183             return result;
       
   184         }
       
   185     }
       
   186 
       
   187     // Nested class for SHA1withECDSA signatures
       
   188     public static final class SHA1 extends ECDSASignature {
       
   189         public SHA1() {
       
   190             super("SHA1");
       
   191         }
       
   192     }
       
   193 
       
   194     // Nested class for SHA256withECDSA signatures
       
   195     public static final class SHA256 extends ECDSASignature {
       
   196         public SHA256() {
       
   197             super("SHA-256");
       
   198         }
       
   199     }
       
   200 
       
   201     // Nested class for SHA384withECDSA signatures
       
   202     public static final class SHA384 extends ECDSASignature {
       
   203         public SHA384() {
       
   204             super("SHA-384");
       
   205         }
       
   206     }
       
   207 
       
   208     // Nested class for SHA512withECDSA signatures
       
   209     public static final class SHA512 extends ECDSASignature {
       
   210         public SHA512() {
       
   211             super("SHA-512");
       
   212         }
       
   213     }
       
   214 
       
   215     // initialize for verification. See JCA doc
       
   216     @Override
       
   217     protected void engineInitVerify(PublicKey publicKey)
       
   218             throws InvalidKeyException {
       
   219         this.publicKey = (ECPublicKey) ECKeyFactory.toECKey(publicKey);
       
   220 
       
   221         // Should check that the supplied key is appropriate for signature
       
   222         // algorithm (e.g. P-256 for SHA256withECDSA)
       
   223         this.privateKey = null;
       
   224         resetDigest();
       
   225     }
       
   226 
       
   227     // initialize for signing. See JCA doc
       
   228     @Override
       
   229     protected void engineInitSign(PrivateKey privateKey)
       
   230             throws InvalidKeyException {
       
   231         engineInitSign(privateKey, null);
       
   232     }
       
   233 
       
   234     // initialize for signing. See JCA doc
       
   235     @Override
       
   236     protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
       
   237             throws InvalidKeyException {
       
   238         this.privateKey = (ECPrivateKey) ECKeyFactory.toECKey(privateKey);
       
   239 
       
   240         // Should check that the supplied key is appropriate for signature
       
   241         // algorithm (e.g. P-256 for SHA256withECDSA)
       
   242         this.publicKey = null;
       
   243         this.random = random;
       
   244         resetDigest();
       
   245     }
       
   246 
       
   247     /**
       
   248      * Resets the message digest if needed.
       
   249      */
       
   250     protected void resetDigest() {
       
   251         if (needsReset) {
       
   252             if (messageDigest != null) {
       
   253                 messageDigest.reset();
       
   254             }
       
   255             needsReset = false;
       
   256         }
       
   257     }
       
   258 
       
   259     /**
       
   260      * Returns the message digest value.
       
   261      */
       
   262     protected byte[] getDigestValue() throws SignatureException {
       
   263         needsReset = false;
       
   264         return messageDigest.digest();
       
   265     }
       
   266 
       
   267     // update the signature with the plaintext data. See JCA doc
       
   268     @Override
       
   269     protected void engineUpdate(byte b) throws SignatureException {
       
   270         messageDigest.update(b);
       
   271         needsReset = true;
       
   272     }
       
   273 
       
   274     // update the signature with the plaintext data. See JCA doc
       
   275     @Override
       
   276     protected void engineUpdate(byte[] b, int off, int len)
       
   277             throws SignatureException {
       
   278         messageDigest.update(b, off, len);
       
   279         needsReset = true;
       
   280     }
       
   281 
       
   282     // update the signature with the plaintext data. See JCA doc
       
   283     @Override
       
   284     protected void engineUpdate(ByteBuffer byteBuffer) {
       
   285         int len = byteBuffer.remaining();
       
   286         if (len <= 0) {
       
   287             return;
       
   288         }
       
   289 
       
   290         messageDigest.update(byteBuffer);
       
   291         needsReset = true;
       
   292     }
       
   293 
       
   294     // sign the data and return the signature. See JCA doc
       
   295     @Override
       
   296     protected byte[] engineSign() throws SignatureException {
       
   297         byte[] s = privateKey.getS().toByteArray();
       
   298         ECParameterSpec params = privateKey.getParams();
       
   299         byte[] encodedParams = ECParameters.encodeParameters(params); // DER OID
       
   300         int keySize = params.getCurve().getField().getFieldSize();
       
   301 
       
   302         // seed is twice the key size (in bytes)
       
   303         byte[] seed = new byte[((keySize + 7) >> 3) * 2];
       
   304         if (random == null) {
       
   305             random = JCAUtil.getSecureRandom();
       
   306         }
       
   307         random.nextBytes(seed);
       
   308 
       
   309         try {
       
   310 
       
   311             return encodeSignature(
       
   312                 signDigest(getDigestValue(), s, encodedParams, seed));
       
   313 
       
   314         } catch (GeneralSecurityException e) {
       
   315             throw new SignatureException("Could not sign data", e);
       
   316         }
       
   317     }
       
   318 
       
   319     // verify the data and return the result. See JCA doc
       
   320     @Override
       
   321     protected boolean engineVerify(byte[] signature) throws SignatureException {
       
   322 
       
   323         byte[] w;
       
   324         ECParameterSpec params = publicKey.getParams();
       
   325         byte[] encodedParams = ECParameters.encodeParameters(params); // DER OID
       
   326 
       
   327         if (publicKey instanceof ECPublicKeyImpl) {
       
   328             w = ((ECPublicKeyImpl)publicKey).getEncodedPublicValue();
       
   329         } else { // instanceof ECPublicKey
       
   330             w = ECParameters.encodePoint(publicKey.getW(), params.getCurve());
       
   331         }
       
   332 
       
   333         try {
       
   334 
       
   335             return verifySignedDigest(
       
   336                 decodeSignature(signature), getDigestValue(), w, encodedParams);
       
   337 
       
   338         } catch (GeneralSecurityException e) {
       
   339             throw new SignatureException("Could not verify signature", e);
       
   340         }
       
   341     }
       
   342 
       
   343     // set parameter, not supported. See JCA doc
       
   344     @Override
       
   345     protected void engineSetParameter(String param, Object value)
       
   346             throws InvalidParameterException {
       
   347         throw new UnsupportedOperationException("setParameter() not supported");
       
   348     }
       
   349 
       
   350     // get parameter, not supported. See JCA doc
       
   351     @Override
       
   352     protected Object engineGetParameter(String param)
       
   353             throws InvalidParameterException {
       
   354         throw new UnsupportedOperationException("getParameter() not supported");
       
   355     }
       
   356 
       
   357     // Convert the concatenation of R and S into their DER encoding
       
   358     private byte[] encodeSignature(byte[] signature) throws SignatureException {
       
   359         try {
       
   360 
       
   361             int n = signature.length >> 1;
       
   362             byte[] bytes = new byte[n];
       
   363             System.arraycopy(signature, 0, bytes, 0, n);
       
   364             BigInteger r = new BigInteger(1, bytes);
       
   365             System.arraycopy(signature, n, bytes, 0, n);
       
   366             BigInteger s = new BigInteger(1, bytes);
       
   367 
       
   368             DerOutputStream out = new DerOutputStream(signature.length + 10);
       
   369             out.putInteger(r);
       
   370             out.putInteger(s);
       
   371             DerValue result =
       
   372                 new DerValue(DerValue.tag_Sequence, out.toByteArray());
       
   373 
       
   374             return result.toByteArray();
       
   375 
       
   376         } catch (Exception e) {
       
   377             throw new SignatureException("Could not encode signature", e);
       
   378         }
       
   379     }
       
   380 
       
   381     // Convert the DER encoding of R and S into a concatenation of R and S
       
   382     private byte[] decodeSignature(byte[] signature) throws SignatureException {
       
   383 
       
   384         try {
       
   385             DerInputStream in = new DerInputStream(signature);
       
   386             DerValue[] values = in.getSequence(2);
       
   387             BigInteger r = values[0].getPositiveBigInteger();
       
   388             BigInteger s = values[1].getPositiveBigInteger();
       
   389             // trim leading zeroes
       
   390             byte[] rBytes = trimZeroes(r.toByteArray());
       
   391             byte[] sBytes = trimZeroes(s.toByteArray());
       
   392             int k = Math.max(rBytes.length, sBytes.length);
       
   393             // r and s each occupy half the array
       
   394             byte[] result = new byte[k << 1];
       
   395             System.arraycopy(rBytes, 0, result, k - rBytes.length,
       
   396                 rBytes.length);
       
   397             System.arraycopy(sBytes, 0, result, result.length - sBytes.length,
       
   398                 sBytes.length);
       
   399             return result;
       
   400 
       
   401         } catch (Exception e) {
       
   402             throw new SignatureException("Could not decode signature", e);
       
   403         }
       
   404     }
       
   405 
       
   406     // trim leading (most significant) zeroes from the result
       
   407     private static byte[] trimZeroes(byte[] b) {
       
   408         int i = 0;
       
   409         while ((i < b.length - 1) && (b[i] == 0)) {
       
   410             i++;
       
   411         }
       
   412         if (i == 0) {
       
   413             return b;
       
   414         }
       
   415         byte[] t = new byte[b.length - i];
       
   416         System.arraycopy(b, i, t, 0, t.length);
       
   417         return t;
       
   418     }
       
   419 
       
   420     /**
       
   421      * Signs the digest using the private key.
       
   422      *
       
   423      * @param digest the digest to be signed.
       
   424      * @param s the private key's S value.
       
   425      * @param encodedParams the curve's DER encoded object identifier.
       
   426      * @param seed the random seed.
       
   427      *
       
   428      * @return byte[] the signature.
       
   429      */
       
   430     private static native byte[] signDigest(byte[] digest, byte[] s,
       
   431         byte[] encodedParams, byte[] seed) throws GeneralSecurityException;
       
   432 
       
   433     /**
       
   434      * Verifies the signed digest using the public key.
       
   435      *
       
   436      * @param signedDigest the signature to be verified. It is encoded
       
   437      *        as a concatenation of the key's R and S values.
       
   438      * @param digest the digest to be used.
       
   439      * @param w the public key's W point (in uncompressed form).
       
   440      * @param encodedParams the curve's DER encoded object identifier.
       
   441      *
       
   442      * @return boolean true if the signature is successfully verified.
       
   443      */
       
   444     private static native boolean verifySignedDigest(byte[] signature,
       
   445         byte[] digest, byte[] w, byte[] encodedParams)
       
   446             throws GeneralSecurityException;
       
   447 }