|
| 1 | +import { describe, it, assert } from 'vitest'; |
| 2 | +import '../test/config.js'; |
| 3 | +import * as xmldsig from './index.js'; |
| 4 | + |
| 5 | +class TestSignedXml extends xmldsig.SignedXml { |
| 6 | + public getPublicKeys(): Promise<CryptoKey[]> { |
| 7 | + return this.GetPublicKeys(); |
| 8 | + } |
| 9 | +} |
| 10 | + |
| 11 | +function createSignedXmlWithMethod(method: xmldsig.SignatureMethod): TestSignedXml { |
| 12 | + const doc = xmldsig.Parse('<root xmlns:ds="http://www.w3.org/2000/09/xmldsig#" />'); |
| 13 | + const signedXml = new TestSignedXml(doc); |
| 14 | + signedXml.XmlSignature.SignedInfo.SignatureMethod = method; |
| 15 | + return signedXml; |
| 16 | +} |
| 17 | + |
| 18 | +async function createRsaPssPublicKey(): Promise<CryptoKey> { |
| 19 | + const keyPair = (await xmldsig.Application.crypto.subtle.generateKey( |
| 20 | + { |
| 21 | + name: 'RSA-PSS', |
| 22 | + hash: 'SHA-256', |
| 23 | + modulusLength: 2048, |
| 24 | + publicExponent: new Uint8Array([1, 0, 1]), |
| 25 | + }, |
| 26 | + true, |
| 27 | + ['sign', 'verify'], |
| 28 | + )) as CryptoKeyPair; |
| 29 | + |
| 30 | + return keyPair.publicKey; |
| 31 | +} |
| 32 | + |
| 33 | +function addX509ExportStub(signedXml: xmldsig.SignedXml, publicKey: CryptoKey) { |
| 34 | + let seenAlgorithm: Algorithm | undefined; |
| 35 | + const cert = { |
| 36 | + exportKey: async (alg?: Algorithm) => { |
| 37 | + seenAlgorithm = alg; |
| 38 | + return publicKey; |
| 39 | + }, |
| 40 | + } as xmldsig.X509Certificate; |
| 41 | + |
| 42 | + const x509Data = new xmldsig.KeyInfoX509Data(); |
| 43 | + x509Data.AddCertificate(cert); |
| 44 | + signedXml.XmlSignature.KeyInfo.Add(x509Data); |
| 45 | + |
| 46 | + return (): Algorithm | undefined => seenAlgorithm; |
| 47 | +} |
| 48 | + |
| 49 | +describe('SignedXml', () => { |
| 50 | + describe('GetPublicKeys', () => { |
| 51 | + it('passes the resolved signature algorithm to X509 certificate export', async () => { |
| 52 | + const algorithm = { name: 'RSA-PSS', hash: { name: 'SHA-256' }, saltLength: 32 } as Algorithm; |
| 53 | + const signatureAlgorithm = xmldsig.CryptoConfig.GetSignatureAlgorithm(algorithm); |
| 54 | + const method = xmldsig.CryptoConfig.CreateSignatureMethod(signatureAlgorithm); |
| 55 | + const signedXml = createSignedXmlWithMethod(method); |
| 56 | + const publicKey = await createRsaPssPublicKey(); |
| 57 | + const getSeenAlgorithm = addX509ExportStub(signedXml, publicKey); |
| 58 | + |
| 59 | + const keys = await signedXml.getPublicKeys(); |
| 60 | + const seenAlgorithm = getSeenAlgorithm(); |
| 61 | + |
| 62 | + assert.equal(keys.length, 1); |
| 63 | + assert.equal(seenAlgorithm?.name, 'RSA-PSS'); |
| 64 | + assert.equal((seenAlgorithm as RsaHashedImportParams).hash.name, 'SHA-256'); |
| 65 | + assert.equal((seenAlgorithm as RsaPssParams).saltLength, 32); |
| 66 | + }); |
| 67 | + |
| 68 | + it('supports RSA-PSS no-params URI (sha256-rsa-MGF1) for X509 certificate export', async () => { |
| 69 | + const method = xmldsig.CryptoConfig.CreateSignatureMethod( |
| 70 | + new xmldsig.RsaPssWithoutParamsSha256(), |
| 71 | + ); |
| 72 | + const signedXml = createSignedXmlWithMethod(method); |
| 73 | + const publicKey = await createRsaPssPublicKey(); |
| 74 | + const getSeenAlgorithm = addX509ExportStub(signedXml, publicKey); |
| 75 | + |
| 76 | + const keys = await signedXml.getPublicKeys(); |
| 77 | + const seenAlgorithm = getSeenAlgorithm(); |
| 78 | + |
| 79 | + assert.equal(keys.length, 1); |
| 80 | + assert.equal( |
| 81 | + signedXml.XmlSignature.SignedInfo.SignatureMethod.Algorithm, |
| 82 | + xmldsig.RSA_PSS_SHA256_NAMESPACE, |
| 83 | + ); |
| 84 | + assert.equal(signedXml.XmlSignature.SignedInfo.SignatureMethod.Any.Count, 0); |
| 85 | + assert.equal(seenAlgorithm?.name, 'RSA-PSS'); |
| 86 | + assert.equal((seenAlgorithm as RsaHashedImportParams).hash.name, 'SHA-256'); |
| 87 | + assert.equal((seenAlgorithm as RsaPssParams).saltLength, 32); |
| 88 | + }); |
| 89 | + }); |
| 90 | +}); |
0 commit comments