Skip to content
This repository was archived by the owner on Feb 2, 2023. It is now read-only.
/ jdk13u-dev Public archive

Commit 2a2a139

Browse files
author
Yuri Nesterenko
committedJan 27, 2022
8259535: ECDSA SignatureValue do not always have the specified length
Reviewed-by: bae Backport-of: a4c2496
1 parent aa134ba commit 2a2a139

File tree

4 files changed

+230
-11
lines changed

4 files changed

+230
-11
lines changed
 

‎src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/ECDSAUtils.java

+12-5
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,15 @@ private ECDSAUtils() {
4242
* The JAVA JCE ECDSA Signature algorithm creates ASN.1 encoded (r, s) value
4343
* pairs; the XML Signature requires the core BigInteger values.
4444
*
45-
* @param asn1Bytes
46-
* @return the decode bytes
45+
* @param asn1Bytes the ASN.1 encoded bytes
46+
* @param rawLen the intended length of decoded bytes for an integer.
47+
* If -1, choose one automatically.
48+
* @return the decoded bytes
4749
* @throws IOException
4850
* @see <A HREF="http://www.w3.org/TR/xmldsig-core/#dsa-sha1">6.4.1 DSA</A>
4951
* @see <A HREF="ftp://ftp.rfc-editor.org/in-notes/rfc4050.txt">3.3. ECDSA Signatures</A>
5052
*/
51-
public static byte[] convertASN1toXMLDSIG(byte asn1Bytes[]) throws IOException {
52-
53+
public static byte[] convertASN1toXMLDSIG(byte asn1Bytes[], int rawLen) throws IOException {
5354
if (asn1Bytes.length < 8 || asn1Bytes[0] != 48) {
5455
throw new IOException("Invalid ASN.1 format of ECDSA signature");
5556
}
@@ -72,7 +73,13 @@ public static byte[] convertASN1toXMLDSIG(byte asn1Bytes[]) throws IOException {
7273

7374
for (j = sLength; j > 0 && asn1Bytes[offset + 2 + rLength + 2 + sLength - j] == 0; j--); //NOPMD
7475

75-
int rawLen = Math.max(i, j);
76+
int maxLen = Math.max(i, j);
77+
78+
if (rawLen < 0) {
79+
rawLen = maxLen;
80+
} else if (rawLen < maxLen) {
81+
throw new IOException("Invalid signature length");
82+
}
7683

7784
if ((asn1Bytes[offset - 1] & 0xff) != asn1Bytes.length - offset
7885
|| (asn1Bytes[offset - 1] & 0xff) != 2 + rLength + 2 + sLength

‎src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/SignatureECDSA.java

+13-4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.security.SecureRandom;
3333
import java.security.Signature;
3434
import java.security.SignatureException;
35+
import java.security.interfaces.ECPrivateKey;
3536
import java.security.spec.AlgorithmParameterSpec;
3637

3738
import com.sun.org.apache.xml.internal.security.algorithms.JCEMapper;
@@ -54,21 +55,25 @@ public abstract class SignatureECDSA extends SignatureAlgorithmSpi {
5455
/** Field algorithm */
5556
private Signature signatureAlgorithm;
5657

58+
/** Length for each integer in signature */
59+
private int signIntLen = -1;
60+
5761
/**
5862
* Converts an ASN.1 ECDSA value to a XML Signature ECDSA Value.
5963
*
6064
* The JAVA JCE ECDSA Signature algorithm creates ASN.1 encoded (r, s) value
6165
* pairs; the XML Signature requires the core BigInteger values.
6266
*
6367
* @param asn1Bytes
68+
* @param rawLen
6469
* @return the decode bytes
6570
*
6671
* @throws IOException
6772
* @see <A HREF="http://www.w3.org/TR/xmldsig-core/#dsa-sha1">6.4.1 DSA</A>
6873
* @see <A HREF="ftp://ftp.rfc-editor.org/in-notes/rfc4050.txt">3.3. ECDSA Signatures</A>
6974
*/
70-
public static byte[] convertASN1toXMLDSIG(byte asn1Bytes[]) throws IOException {
71-
return ECDSAUtils.convertASN1toXMLDSIG(asn1Bytes);
75+
public static byte[] convertASN1toXMLDSIG(byte asn1Bytes[], int rawLen) throws IOException {
76+
return ECDSAUtils.convertASN1toXMLDSIG(asn1Bytes, rawLen);
7277
}
7378

7479
/**
@@ -179,8 +184,7 @@ protected void engineInitVerify(Key publicKey) throws XMLSignatureException {
179184
protected byte[] engineSign() throws XMLSignatureException {
180185
try {
181186
byte jcebytes[] = this.signatureAlgorithm.sign();
182-
183-
return SignatureECDSA.convertASN1toXMLDSIG(jcebytes);
187+
return SignatureECDSA.convertASN1toXMLDSIG(jcebytes, signIntLen);
184188
} catch (SignatureException ex) {
185189
throw new XMLSignatureException(ex);
186190
} catch (IOException ex) {
@@ -203,6 +207,11 @@ protected void engineInitSign(Key privateKey, SecureRandom secureRandom)
203207
}
204208

205209
try {
210+
if (privateKey instanceof ECPrivateKey) {
211+
ECPrivateKey ecKey = (ECPrivateKey)privateKey;
212+
signIntLen = (ecKey.getParams().getCurve().getField().getFieldSize() + 7) / 8;
213+
// If not ECPrivateKey, signIntLen remains -1
214+
}
206215
if (secureRandom == null) {
207216
this.signatureAlgorithm.initSign((PrivateKey) privateKey);
208217
} else {

‎src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMSignatureMethod.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
* under the License.
2222
*/
2323
/*
24-
* Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
24+
* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
2525
*/
2626
/*
2727
* $Id: DOMSignatureMethod.java 1854026 2019-02-21 09:30:01Z coheigea $
@@ -35,6 +35,7 @@
3535
import java.io.IOException;
3636
import java.security.*;
3737
import java.security.interfaces.DSAKey;
38+
import java.security.interfaces.ECPrivateKey;
3839
import java.security.spec.AlgorithmParameterSpec;
3940
import java.security.spec.MGF1ParameterSpec;
4041
import java.security.spec.PSSParameterSpec;
@@ -518,7 +519,12 @@ byte[] postSignFormat(Key key, byte[] sig) throws IOException {
518519
// If signature is in ASN.1 (i.e., if the fallback algorithm
519520
// was used), convert the signature to the P1363 format
520521
if (asn1) {
521-
return SignatureECDSA.convertASN1toXMLDSIG(sig);
522+
int rawLen = -1;
523+
if (key instanceof ECPrivateKey) {
524+
ECPrivateKey ecKey = (ECPrivateKey)key;
525+
rawLen = (ecKey.getParams().getCurve().getField().getFieldSize() + 7) / 8;
526+
}
527+
return SignatureECDSA.convertASN1toXMLDSIG(sig, rawLen);
522528
} else {
523529
return sig;
524530
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates. 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.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/**
25+
* @test
26+
* @bug 8259535
27+
* @summary ECDSA SignatureValue do not always have the specified length
28+
* @modules java.xml.crypto
29+
*/
30+
31+
import org.w3c.dom.Document;
32+
import org.w3c.dom.NodeList;
33+
import org.xml.sax.InputSource;
34+
35+
import javax.xml.crypto.dsig.*;
36+
import javax.xml.crypto.dsig.dom.DOMSignContext;
37+
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
38+
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
39+
import javax.xml.crypto.dsig.keyinfo.X509Data;
40+
import javax.xml.crypto.dsig.keyinfo.X509IssuerSerial;
41+
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
42+
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
43+
import javax.xml.parsers.DocumentBuilderFactory;
44+
import javax.xml.transform.TransformerFactory;
45+
import javax.xml.transform.dom.DOMResult;
46+
import javax.xml.transform.dom.DOMSource;
47+
import java.io.StringReader;
48+
import java.math.BigInteger;
49+
import java.security.Provider;
50+
import java.security.Security;
51+
import java.security.*;
52+
import java.security.spec.AlgorithmParameterSpec;
53+
import java.security.spec.PKCS8EncodedKeySpec;
54+
import java.util.*;
55+
56+
public class ShortECDSA {
57+
58+
public static final String XML = "<testXmlFile>\n"
59+
+ "\t<element>Value</element>\n"
60+
+ "</testXmlFile>";
61+
62+
private static final String PRIVATE_KEY_BASE_64 =
63+
"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgP6yNCRsISznuzY4D"
64+
+ "0cwkBjgV8uu2lQ2tCPxdam7Fx9OhRANCAAS33BazN06tOnXsLYatPvmkrEVDyRWj"
65+
+ "yzxlCU+en8PPJ4ETUGRhz8h1fAELEkKS0Cky5M61oQVyiSxHaXhunH29";
66+
67+
private static final XMLSignatureFactory FAC =
68+
XMLSignatureFactory.getInstance("DOM");
69+
70+
public static void main(String[] args) throws Exception {
71+
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
72+
factory.setNamespaceAware(true);
73+
Document document = factory.newDocumentBuilder().
74+
parse(new InputSource(new StringReader(XML)));
75+
76+
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(
77+
Base64.getDecoder().decode(PRIVATE_KEY_BASE_64));
78+
KeyFactory kf = KeyFactory.getInstance("EC");
79+
PrivateKey privateKey = kf.generatePrivate(keySpec);
80+
81+
// Remove SunEC so neither its ASN.1 or P1363 format ECDSA will be used
82+
Security.removeProvider("SunEC");
83+
Security.addProvider(new MyProvider());
84+
85+
Document signedDocument = XmlSigningUtils.signDocument(document, privateKey);
86+
NodeList nodeList = signedDocument.getElementsByTagName("SignatureValue");
87+
byte[] sig = Base64.getMimeDecoder().decode(
88+
nodeList.item(0).getFirstChild().getNodeValue());
89+
if (sig.length != 64) {
90+
System.out.println("Length: " + sig.length);
91+
//System.out.println(HexFormat.ofDelimiter(":").formatHex(sig));
92+
for (int i = 0; i < sig.length; ++i) {
93+
System.out.print(String.format(i == 0 ? "%02x" : ":%02x", sig[i]));
94+
}
95+
System.out.println();
96+
97+
throw new RuntimeException("Failed");
98+
}
99+
}
100+
101+
public static class XmlSigningUtils {
102+
103+
public static Document signDocument(Document document, PrivateKey privateKey)
104+
throws Exception {
105+
DOMResult result = new DOMResult();
106+
TransformerFactory.newInstance().newTransformer()
107+
.transform(new DOMSource(document), result);
108+
Document newDocument = (Document) result.getNode();
109+
FAC.newXMLSignature(buildSignedInfo(), buildKeyInfo())
110+
.sign(new DOMSignContext(privateKey, newDocument.getDocumentElement()));
111+
return newDocument;
112+
}
113+
114+
private static SignedInfo buildSignedInfo()
115+
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
116+
return FAC.newSignedInfo(
117+
FAC.newCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null),
118+
FAC.newSignatureMethod(SignatureMethod.ECDSA_SHA256, null),
119+
List.of(FAC.newReference(
120+
"",
121+
FAC.newDigestMethod(DigestMethod.SHA256, null),
122+
List.of(FAC.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null, null)));
123+
}
124+
125+
private static KeyInfo buildKeyInfo() {
126+
KeyInfoFactory keyInfoFactory = FAC.getKeyInfoFactory();
127+
X509IssuerSerial x509IssuerSerial = keyInfoFactory
128+
.newX509IssuerSerial("CN=Me", BigInteger.ONE);
129+
X509Data x509Data = keyInfoFactory.newX509Data(Collections.singletonList(x509IssuerSerial));
130+
return keyInfoFactory.newKeyInfo(Collections.singletonList(x509Data));
131+
}
132+
}
133+
134+
// Only provide SHA256withECDSA, no P1363 format. This triggers the convertASN1toXMLDSIG translation.
135+
public static class MyProvider extends Provider {
136+
MyProvider() {
137+
super("MyProvider", "1", "My provider");
138+
put("Signature.SHA256withECDSA", MyECDSA.class.getName());
139+
}
140+
}
141+
142+
public static class MyECDSA extends SignatureSpi {
143+
144+
// Hardcoded signature with short r and s
145+
public static byte[] hardcoded;
146+
147+
static {
148+
hardcoded = new byte[68];
149+
// Fill in with a number <128 so it will look like a
150+
// correct ASN.1 encoding for positive numbers.
151+
Arrays.fill(hardcoded, (byte) 0x55);
152+
hardcoded[0] = 0x30; // SEQUENCE OF
153+
hardcoded[1] = 66; // 2 [tag,len,31] components
154+
hardcoded[2] = hardcoded[35] = 2; // Each being an INTEGER...
155+
hardcoded[3] = hardcoded[36] = 31; // ... of 31 bytes long
156+
}
157+
158+
protected void engineInitVerify(PublicKey publicKey) {
159+
}
160+
161+
protected void engineInitSign(PrivateKey privateKey) {
162+
}
163+
164+
protected void engineUpdate(byte b) {
165+
}
166+
167+
protected void engineUpdate(byte[] b, int off, int len) {
168+
}
169+
170+
protected byte[] engineSign() {
171+
return hardcoded;
172+
}
173+
174+
protected boolean engineVerify(byte[] sigBytes) {
175+
return false;
176+
}
177+
178+
protected void engineSetParameter(String param, Object value) {
179+
}
180+
181+
protected Object engineGetParameter(String param) {
182+
throw new UnsupportedOperationException();
183+
}
184+
185+
protected void engineSetParameter(AlgorithmParameterSpec params) {
186+
}
187+
188+
protected AlgorithmParameters engineGetParameters() {
189+
throw new UnsupportedOperationException();
190+
}
191+
192+
protected int engineSign(byte[] outbuf, int offset, int len) {
193+
System.arraycopy(hardcoded, 0, outbuf, offset, hardcoded.length);
194+
return hardcoded.length;
195+
}
196+
}
197+
}

0 commit comments

Comments
 (0)
Failed to load comments.